ghc-lib 9.4.8.20231111 → 9.6.1.20230312
raw patch · 369 files changed
+52092/−35059 lines, 369 filesdep ~basedep ~bytestringdep ~deepseqPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, bytestring, deepseq, ghc-lib-parser, ghc-prim
API changes (from Hackage documentation)
- GHC: AnnAt :: AnnKeywordId
- GHC: Interpreter :: Backend
- GHC: LLVM :: Backend
- GHC: NCG :: Backend
- GHC: NoBackend :: Backend
- GHC: ViaC :: Backend
- GHC: [llvmConfig] :: DynFlags -> LlvmConfig
- GHC: data ModuleName
- GHC: data PrintUnqualified
- GHC: getPrintUnqual :: GhcMonad m => m PrintUnqualified
- GHC: isFunTyCon :: TyCon -> Bool
- GHC: mkModuleName :: String -> ModuleName
- GHC: mkPrintUnqualifiedForModule :: GhcMonad m => ModuleInfo -> m (Maybe PrintUnqualified)
- GHC: moduleNameString :: ModuleName -> String
- GHC.Cmm.DebugBlock: instance GHC.Utils.Outputable.OutputableP env GHC.Cmm.CLabel.CLabel => GHC.Utils.Outputable.OutputableP env GHC.Cmm.DebugBlock.DebugBlock
- GHC.Cmm.DebugBlock: instance GHC.Utils.Outputable.OutputableP env GHC.Cmm.CLabel.CLabel => GHC.Utils.Outputable.OutputableP env GHC.Cmm.DebugBlock.UnwindExpr
- GHC.Cmm.DebugBlock: instance GHC.Utils.Outputable.OutputableP env GHC.Cmm.CLabel.CLabel => GHC.Utils.Outputable.OutputableP env GHC.Cmm.DebugBlock.UnwindPoint
- GHC.Cmm.Parser.Monad: instance GHC.Driver.Session.HasDynFlags GHC.Cmm.Parser.Monad.PD
- GHC.Cmm.Ppr: instance GHC.Utils.Outputable.Outputable GHC.Cmm.CmmStackInfo
- GHC.Cmm.Ppr: instance GHC.Utils.Outputable.Outputable GHC.Cmm.Node.CmmReturnInfo
- GHC.Cmm.Ppr: instance GHC.Utils.Outputable.Outputable GHC.Cmm.Node.Convention
- GHC.Cmm.Ppr: instance GHC.Utils.Outputable.Outputable GHC.Cmm.Node.ForeignConvention
- GHC.Cmm.Ppr: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform (GHC.Cmm.Dataflow.Block.Block GHC.Cmm.Node.CmmNode GHC.Cmm.Dataflow.Block.C GHC.Cmm.Dataflow.Block.C)
- GHC.Cmm.Ppr: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform (GHC.Cmm.Dataflow.Block.Block GHC.Cmm.Node.CmmNode GHC.Cmm.Dataflow.Block.C GHC.Cmm.Dataflow.Block.O)
- GHC.Cmm.Ppr: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform (GHC.Cmm.Dataflow.Block.Block GHC.Cmm.Node.CmmNode GHC.Cmm.Dataflow.Block.O GHC.Cmm.Dataflow.Block.C)
- GHC.Cmm.Ppr: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform (GHC.Cmm.Dataflow.Block.Block GHC.Cmm.Node.CmmNode GHC.Cmm.Dataflow.Block.O GHC.Cmm.Dataflow.Block.O)
- GHC.Cmm.Ppr: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform (GHC.Cmm.Dataflow.Graph.Graph GHC.Cmm.Node.CmmNode e x)
- GHC.Cmm.Ppr: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform (GHC.Cmm.Node.CmmNode e x)
- GHC.Cmm.Ppr: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform GHC.Cmm.CLabel.InfoProvEnt
- GHC.Cmm.Ppr: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform GHC.Cmm.CmmGraph
- GHC.Cmm.Ppr: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform GHC.Cmm.CmmTopInfo
- GHC.Cmm.Ppr: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform GHC.Cmm.Node.ForeignTarget
- GHC.Cmm.Ppr.Decl: instance (GHC.Utils.Outputable.OutputableP GHC.Platform.Platform d, GHC.Utils.Outputable.OutputableP GHC.Platform.Platform info, GHC.Utils.Outputable.OutputableP GHC.Platform.Platform i) => GHC.Utils.Outputable.OutputableP GHC.Platform.Platform (GHC.Cmm.GenCmmDecl d info i)
- GHC.Cmm.Ppr.Decl: instance GHC.Utils.Outputable.Outputable GHC.Cmm.Type.ForeignHint
- GHC.Cmm.Ppr.Decl: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform (GHC.Cmm.GenCmmStatics a)
- GHC.Cmm.Ppr.Decl: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform GHC.Cmm.CmmInfoTable
- GHC.Cmm.Ppr.Decl: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform GHC.Cmm.CmmStatic
- GHC.Cmm.Ppr.Decl: pprCmmGroup :: (OutputableP Platform d, OutputableP Platform info, OutputableP Platform g) => Platform -> GenCmmGroup d info g -> SDoc
- GHC.Cmm.Ppr.Decl: pprCmms :: (OutputableP Platform info, OutputableP Platform g) => Platform -> [GenCmmGroup RawCmmStatics info g] -> SDoc
- GHC.Cmm.Ppr.Decl: pprSection :: Platform -> Section -> SDoc
- GHC.Cmm.Ppr.Decl: pprStatic :: Platform -> CmmStatic -> SDoc
- GHC.Cmm.Ppr.Expr: instance GHC.Utils.Outputable.Outputable GHC.Cmm.Expr.Area
- GHC.Cmm.Ppr.Expr: instance GHC.Utils.Outputable.Outputable GHC.Cmm.Expr.CmmReg
- GHC.Cmm.Ppr.Expr: instance GHC.Utils.Outputable.Outputable GHC.Cmm.Expr.GlobalReg
- GHC.Cmm.Ppr.Expr: instance GHC.Utils.Outputable.Outputable GHC.Cmm.Expr.LocalReg
- GHC.Cmm.Ppr.Expr: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform GHC.Cmm.Expr.CmmExpr
- GHC.Cmm.Ppr.Expr: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform GHC.Cmm.Expr.CmmLit
- GHC.Cmm.Ppr.Expr: instance GHC.Utils.Outputable.OutputableP env GHC.Cmm.Expr.GlobalReg
- GHC.Cmm.Ppr.Expr: pprExpr :: Platform -> CmmExpr -> SDoc
- GHC.Cmm.Ppr.Expr: pprLit :: Platform -> CmmLit -> SDoc
- GHC.Cmm.Utils: bodyToBlockList :: Body CmmNode -> [CmmBlock]
- GHC.Cmm.Utils: hasNoGlobalRegs :: CmmExpr -> Bool
- GHC.Cmm.Utils: isComparisonExpr :: CmmExpr -> Bool
- GHC.Cmm.Utils: isLit :: CmmExpr -> Bool
- GHC.Cmm.Utils: isTrivialCmmExpr :: CmmExpr -> Bool
- GHC.Cmm.Utils: revPostorder :: CmmGraph -> [CmmBlock]
- GHC.CmmToAsm: [pprNatCmmDecl] :: NcgImpl statics instr jumpDest -> NatCmmDecl statics instr -> SDoc
- GHC.CmmToAsm.AArch64.CodeGen: makeFarBranches :: Platform -> LabelMap RawCmmStatics -> [NatBasicBlock Instr] -> UniqSM [NatBasicBlock Instr]
- GHC.CmmToAsm.AArch64.Instr: instance GHC.Classes.Eq GHC.CmmToAsm.AArch64.Instr.Target
- GHC.CmmToAsm.AArch64.Instr: instance GHC.Classes.Ord GHC.CmmToAsm.AArch64.Instr.Target
- GHC.CmmToAsm.AArch64.Ppr: instance GHC.Utils.Outputable.Outputable GHC.CmmToAsm.AArch64.Instr.Instr
- GHC.CmmToAsm.AArch64.Ppr: pprBasicBlock :: NCGConfig -> LabelMap RawCmmStatics -> NatBasicBlock Instr -> SDoc
- GHC.CmmToAsm.AArch64.Regs: instance GHC.Classes.Eq GHC.Utils.Outputable.SDoc
- GHC.CmmToAsm.AArch64.Regs: instance GHC.Show.Show GHC.Utils.Outputable.SDoc
- GHC.CmmToAsm.Dwarf.Types: [dwLineLabel] :: DwarfInfo -> SDoc
- GHC.CmmToAsm.Dwarf.Types: instance GHC.Utils.Outputable.OutputableP env GHC.Cmm.CLabel.CLabel => GHC.Utils.Outputable.OutputableP env GHC.CmmToAsm.Dwarf.Types.DwarfFrameBlock
- GHC.CmmToAsm.Monad: [natm_modloc] :: NatM_State -> ModLocation
- GHC.CmmToAsm.Monad: [pprNatCmmDecl] :: NcgImpl statics instr jumpDest -> NatCmmDecl statics instr -> SDoc
- GHC.CmmToAsm.Monad: getModLoc :: NatM ModLocation
- GHC.CmmToAsm.Monad: mapAccumLNat :: (acc -> x -> NatM (acc, y)) -> acc -> [x] -> NatM (acc, [y])
- GHC.CmmToAsm.X86.Ppr: pprData :: NCGConfig -> CmmStatic -> SDoc
- GHC.CmmToAsm.X86.Ppr: pprDataItem :: NCGConfig -> CmmLit -> SDoc
- GHC.CmmToAsm.X86.Ppr: pprFormat :: Format -> SDoc
- GHC.CmmToAsm.X86.Ppr: pprImm :: Platform -> Imm -> SDoc
- GHC.CmmToLlvm.Base: data LlvmVersion
- GHC.CmmToLlvm.Base: llvmVersionList :: LlvmVersion -> [Int]
- GHC.CmmToLlvm.Base: llvmVersionStr :: LlvmVersion -> String
- GHC.CmmToLlvm.Base: llvmVersionSupported :: LlvmVersion -> Bool
- GHC.CmmToLlvm.Base: parseLlvmVersion :: String -> Maybe LlvmVersion
- GHC.CmmToLlvm.Base: supportedLlvmVersionLowerBound :: LlvmVersion
- GHC.CmmToLlvm.Base: supportedLlvmVersionUpperBound :: LlvmVersion
- GHC.CmmToLlvm.Config: LlvmCgConfig :: !Platform -> !SDocContext -> !Bool -> !Bool -> Maybe BmiVersion -> Maybe LlvmVersion -> !Bool -> !String -> !LlvmConfig -> LlvmCgConfig
- GHC.CmmToLlvm.Config: LlvmVersion :: NonEmpty Int -> LlvmVersion
- GHC.CmmToLlvm.Config: [llvmCgBmiVersion] :: LlvmCgConfig -> Maybe BmiVersion
- GHC.CmmToLlvm.Config: [llvmCgContext] :: LlvmCgConfig -> !SDocContext
- GHC.CmmToLlvm.Config: [llvmCgDoWarn] :: LlvmCgConfig -> !Bool
- GHC.CmmToLlvm.Config: [llvmCgFillUndefWithGarbage] :: LlvmCgConfig -> !Bool
- GHC.CmmToLlvm.Config: [llvmCgLlvmConfig] :: LlvmCgConfig -> !LlvmConfig
- GHC.CmmToLlvm.Config: [llvmCgLlvmTarget] :: LlvmCgConfig -> !String
- GHC.CmmToLlvm.Config: [llvmCgLlvmVersion] :: LlvmCgConfig -> Maybe LlvmVersion
- GHC.CmmToLlvm.Config: [llvmCgPlatform] :: LlvmCgConfig -> !Platform
- GHC.CmmToLlvm.Config: [llvmCgSplitSection] :: LlvmCgConfig -> !Bool
- GHC.CmmToLlvm.Config: [llvmVersionNE] :: LlvmVersion -> NonEmpty Int
- GHC.CmmToLlvm.Config: data LlvmCgConfig
- GHC.CmmToLlvm.Config: instance GHC.Classes.Eq GHC.CmmToLlvm.Config.LlvmVersion
- GHC.CmmToLlvm.Config: instance GHC.Classes.Ord GHC.CmmToLlvm.Config.LlvmVersion
- GHC.CmmToLlvm.Config: newtype LlvmVersion
- GHC.Core.Opt.Simplify: simplExpr :: SimplEnv -> CoreExpr -> SimplM CoreExpr
- GHC.Core.Opt.Simplify: simplImpRules :: SimplEnv -> [CoreRule] -> SimplM [CoreRule]
- GHC.Core.Opt.Simplify: simplTopBinds :: SimplEnv -> [InBind] -> SimplM (SimplFloats, SimplEnv)
- GHC.Core.Opt.Simplify.Env: ContEx :: TvSubstEnv -> CvSubstEnv -> SimplIdSubst -> InExpr -> SimplSR
- GHC.Core.Opt.Simplify.Env: DoneEx :: OutExpr -> Maybe JoinArity -> SimplSR
- GHC.Core.Opt.Simplify.Env: DoneId :: OutId -> SimplSR
- GHC.Core.Opt.Simplify.Env: SimplEnv :: !SimplMode -> TvSubstEnv -> CvSubstEnv -> SimplIdSubst -> !UnVarSet -> !InScopeSet -> !Int -> SimplEnv
- GHC.Core.Opt.Simplify.Env: SimplFloats :: LetFloats -> JoinFloats -> InScopeSet -> SimplFloats
- GHC.Core.Opt.Simplify.Env: [seCaseDepth] :: SimplEnv -> !Int
- GHC.Core.Opt.Simplify.Env: [seCvSubst] :: SimplEnv -> CvSubstEnv
- GHC.Core.Opt.Simplify.Env: [seIdSubst] :: SimplEnv -> SimplIdSubst
- GHC.Core.Opt.Simplify.Env: [seInScope] :: SimplEnv -> !InScopeSet
- GHC.Core.Opt.Simplify.Env: [seMode] :: SimplEnv -> !SimplMode
- GHC.Core.Opt.Simplify.Env: [seRecIds] :: SimplEnv -> !UnVarSet
- GHC.Core.Opt.Simplify.Env: [seTvSubst] :: SimplEnv -> TvSubstEnv
- GHC.Core.Opt.Simplify.Env: [sfInScope] :: SimplFloats -> InScopeSet
- GHC.Core.Opt.Simplify.Env: [sfJoinFloats] :: SimplFloats -> JoinFloats
- GHC.Core.Opt.Simplify.Env: [sfLetFloats] :: SimplFloats -> LetFloats
- GHC.Core.Opt.Simplify.Env: addFloats :: SimplFloats -> SimplFloats -> SimplFloats
- GHC.Core.Opt.Simplify.Env: addJoinFloats :: SimplFloats -> JoinFloats -> SimplFloats
- GHC.Core.Opt.Simplify.Env: addJoinFlts :: JoinFloats -> JoinFloats -> JoinFloats
- GHC.Core.Opt.Simplify.Env: addLetFloats :: SimplFloats -> LetFloats -> SimplFloats
- GHC.Core.Opt.Simplify.Env: addLetFlts :: LetFloats -> LetFloats -> LetFloats
- GHC.Core.Opt.Simplify.Env: addNewInScopeIds :: SimplEnv -> [CoreBndr] -> SimplEnv
- GHC.Core.Opt.Simplify.Env: bumpCaseDepth :: SimplEnv -> SimplEnv
- GHC.Core.Opt.Simplify.Env: data LetFloats
- GHC.Core.Opt.Simplify.Env: data SimplEnv
- GHC.Core.Opt.Simplify.Env: data SimplFloats
- GHC.Core.Opt.Simplify.Env: data SimplSR
- GHC.Core.Opt.Simplify.Env: doFloatFromRhs :: TopLevelFlag -> RecFlag -> Bool -> SimplFloats -> OutExpr -> Bool
- GHC.Core.Opt.Simplify.Env: emptyFloats :: SimplEnv -> SimplFloats
- GHC.Core.Opt.Simplify.Env: emptyJoinFloats :: JoinFloats
- GHC.Core.Opt.Simplify.Env: emptyLetFloats :: LetFloats
- GHC.Core.Opt.Simplify.Env: enterRecGroupRHSs :: SimplEnv -> [OutBndr] -> (SimplEnv -> SimplM (r, SimplEnv)) -> SimplM (r, SimplEnv)
- GHC.Core.Opt.Simplify.Env: extendCvSubst :: SimplEnv -> CoVar -> Coercion -> SimplEnv
- GHC.Core.Opt.Simplify.Env: extendFloats :: SimplFloats -> OutBind -> SimplFloats
- GHC.Core.Opt.Simplify.Env: extendIdSubst :: SimplEnv -> Id -> SimplSR -> SimplEnv
- GHC.Core.Opt.Simplify.Env: extendTvSubst :: SimplEnv -> TyVar -> Type -> SimplEnv
- GHC.Core.Opt.Simplify.Env: getInScope :: SimplEnv -> InScopeSet
- GHC.Core.Opt.Simplify.Env: getMode :: SimplEnv -> SimplMode
- GHC.Core.Opt.Simplify.Env: getSimplRules :: SimplM RuleEnv
- GHC.Core.Opt.Simplify.Env: getTCvSubst :: SimplEnv -> TCvSubst
- GHC.Core.Opt.Simplify.Env: getTopFloatBinds :: SimplFloats -> [CoreBind]
- GHC.Core.Opt.Simplify.Env: instance GHC.Utils.Outputable.Outputable GHC.Core.Opt.Simplify.Env.FloatFlag
- GHC.Core.Opt.Simplify.Env: instance GHC.Utils.Outputable.Outputable GHC.Core.Opt.Simplify.Env.LetFloats
- GHC.Core.Opt.Simplify.Env: instance GHC.Utils.Outputable.Outputable GHC.Core.Opt.Simplify.Env.SimplFloats
- GHC.Core.Opt.Simplify.Env: instance GHC.Utils.Outputable.Outputable GHC.Core.Opt.Simplify.Env.SimplSR
- GHC.Core.Opt.Simplify.Env: isEmptyFloats :: SimplFloats -> Bool
- GHC.Core.Opt.Simplify.Env: letFloatBinds :: LetFloats -> [CoreBind]
- GHC.Core.Opt.Simplify.Env: lookupRecBndr :: SimplEnv -> InId -> OutId
- GHC.Core.Opt.Simplify.Env: mapLetFloats :: LetFloats -> ((Id, CoreExpr) -> (Id, CoreExpr)) -> LetFloats
- GHC.Core.Opt.Simplify.Env: mkContEx :: SimplEnv -> InExpr -> SimplSR
- GHC.Core.Opt.Simplify.Env: mkFloatBind :: SimplEnv -> OutBind -> (SimplFloats, SimplEnv)
- GHC.Core.Opt.Simplify.Env: mkRecFloats :: SimplFloats -> SimplFloats
- GHC.Core.Opt.Simplify.Env: mkSimplEnv :: SimplMode -> SimplEnv
- GHC.Core.Opt.Simplify.Env: modifyInScope :: SimplEnv -> CoreBndr -> SimplEnv
- GHC.Core.Opt.Simplify.Env: pprSimplEnv :: SimplEnv -> SDoc
- GHC.Core.Opt.Simplify.Env: refineFromInScope :: InScopeSet -> Var -> Var
- GHC.Core.Opt.Simplify.Env: seDynFlags :: SimplEnv -> DynFlags
- GHC.Core.Opt.Simplify.Env: seLogger :: SimplEnv -> Logger
- GHC.Core.Opt.Simplify.Env: seUnfoldingOpts :: SimplEnv -> UnfoldingOpts
- GHC.Core.Opt.Simplify.Env: setInScopeFromE :: SimplEnv -> SimplEnv -> SimplEnv
- GHC.Core.Opt.Simplify.Env: setInScopeFromF :: SimplEnv -> SimplFloats -> SimplEnv
- GHC.Core.Opt.Simplify.Env: setInScopeSet :: SimplEnv -> InScopeSet -> SimplEnv
- GHC.Core.Opt.Simplify.Env: setMode :: SimplMode -> SimplEnv -> SimplEnv
- GHC.Core.Opt.Simplify.Env: setSubstEnv :: SimplEnv -> TvSubstEnv -> CvSubstEnv -> SimplIdSubst -> SimplEnv
- GHC.Core.Opt.Simplify.Env: simplBinder :: SimplEnv -> InBndr -> SimplM (SimplEnv, OutBndr)
- GHC.Core.Opt.Simplify.Env: simplBinders :: SimplEnv -> [InBndr] -> SimplM (SimplEnv, [OutBndr])
- GHC.Core.Opt.Simplify.Env: simplNonRecBndr :: SimplEnv -> InBndr -> SimplM (SimplEnv, OutBndr)
- GHC.Core.Opt.Simplify.Env: simplNonRecJoinBndr :: SimplEnv -> InBndr -> Mult -> OutType -> SimplM (SimplEnv, OutBndr)
- GHC.Core.Opt.Simplify.Env: simplRecBndrs :: SimplEnv -> [InBndr] -> SimplM SimplEnv
- GHC.Core.Opt.Simplify.Env: simplRecJoinBndrs :: SimplEnv -> [InBndr] -> Mult -> OutType -> SimplM SimplEnv
- GHC.Core.Opt.Simplify.Env: substCo :: SimplEnv -> Coercion -> Coercion
- GHC.Core.Opt.Simplify.Env: substCoVar :: SimplEnv -> CoVar -> Coercion
- GHC.Core.Opt.Simplify.Env: substId :: SimplEnv -> InId -> SimplSR
- GHC.Core.Opt.Simplify.Env: substTy :: SimplEnv -> Type -> Type
- GHC.Core.Opt.Simplify.Env: substTyVar :: SimplEnv -> TyVar -> Type
- GHC.Core.Opt.Simplify.Env: type JoinFloat = OutBind
- GHC.Core.Opt.Simplify.Env: type JoinFloats = OrdList JoinFloat
- GHC.Core.Opt.Simplify.Env: unitJoinFloat :: OutBind -> JoinFloats
- GHC.Core.Opt.Simplify.Env: unitLetFloat :: OutBind -> LetFloats
- GHC.Core.Opt.Simplify.Env: updMode :: (SimplMode -> SimplMode) -> SimplEnv -> SimplEnv
- GHC.Core.Opt.Simplify.Env: wrapFloats :: SimplFloats -> OutExpr -> OutExpr
- GHC.Core.Opt.Simplify.Env: wrapJoinFloats :: JoinFloats -> OutExpr -> OutExpr
- GHC.Core.Opt.Simplify.Env: wrapJoinFloatsX :: SimplFloats -> OutExpr -> (SimplFloats, OutExpr)
- GHC.Core.Opt.Simplify.Env: zapSubstEnv :: SimplEnv -> SimplEnv
- GHC.Core.Opt.Simplify.Monad: checkedTick :: Tick -> SimplM ()
- GHC.Core.Opt.Simplify.Monad: class Monad m => MonadUnique (m :: Type -> Type)
- GHC.Core.Opt.Simplify.Monad: data SimplCount
- GHC.Core.Opt.Simplify.Monad: data SimplM result
- GHC.Core.Opt.Simplify.Monad: freeTick :: Tick -> SimplM ()
- GHC.Core.Opt.Simplify.Monad: getFamEnvs :: SimplM (FamInstEnv, FamInstEnv)
- GHC.Core.Opt.Simplify.Monad: getOptCoercionOpts :: SimplM OptCoercionOpts
- GHC.Core.Opt.Simplify.Monad: getSimplCount :: SimplM SimplCount
- GHC.Core.Opt.Simplify.Monad: getSimplRules :: SimplM RuleEnv
- GHC.Core.Opt.Simplify.Monad: getUniqueM :: MonadUnique m => m Unique
- GHC.Core.Opt.Simplify.Monad: getUniqueSupplyM :: MonadUnique m => m UniqSupply
- GHC.Core.Opt.Simplify.Monad: getUniquesM :: MonadUnique m => m [Unique]
- GHC.Core.Opt.Simplify.Monad: initSmpl :: Logger -> DynFlags -> IO RuleBase -> RuleEnv -> (FamInstEnv, FamInstEnv) -> Int -> SimplM a -> IO (a, SimplCount)
- GHC.Core.Opt.Simplify.Monad: instance Control.Monad.IO.Class.MonadIO GHC.Core.Opt.Simplify.Monad.SimplM
- GHC.Core.Opt.Simplify.Monad: instance GHC.Base.Applicative GHC.Core.Opt.Simplify.Monad.SimplM
- GHC.Core.Opt.Simplify.Monad: instance GHC.Base.Functor GHC.Core.Opt.Simplify.Monad.SimplM
- GHC.Core.Opt.Simplify.Monad: instance GHC.Base.Monad GHC.Core.Opt.Simplify.Monad.SimplM
- GHC.Core.Opt.Simplify.Monad: instance GHC.Driver.Session.HasDynFlags GHC.Core.Opt.Simplify.Monad.SimplM
- GHC.Core.Opt.Simplify.Monad: instance GHC.Types.Unique.Supply.MonadUnique GHC.Core.Opt.Simplify.Monad.SimplM
- GHC.Core.Opt.Simplify.Monad: instance GHC.Utils.Logger.HasLogger GHC.Core.Opt.Simplify.Monad.SimplM
- GHC.Core.Opt.Simplify.Monad: isZeroSimplCount :: SimplCount -> Bool
- GHC.Core.Opt.Simplify.Monad: newId :: FastString -> Mult -> Type -> SimplM Id
- GHC.Core.Opt.Simplify.Monad: newJoinId :: [Var] -> Type -> SimplM Id
- GHC.Core.Opt.Simplify.Monad: plusSimplCount :: SimplCount -> SimplCount -> SimplCount
- GHC.Core.Opt.Simplify.Monad: pprSimplCount :: SimplCount -> SDoc
- GHC.Core.Opt.Simplify.Monad: tick :: Tick -> SimplM ()
- GHC.Core.Opt.Simplify.Monad: traceSmpl :: String -> SDoc -> SimplM ()
- GHC.Core.Opt.Simplify.Monad: zeroSimplCount :: DynFlags -> SimplCount
- GHC.Core.Opt.Simplify.Utils: ApplyToTy :: OutType -> OutType -> SimplCont -> SimplCont
- GHC.Core.Opt.Simplify.Utils: ApplyToVal :: DupFlag -> OutType -> InExpr -> StaticEnv -> SimplCont -> SimplCont
- GHC.Core.Opt.Simplify.Utils: ArgInfo :: OutId -> [ArgSpec] -> FunRules -> Bool -> [Demand] -> [Int] -> ArgInfo
- GHC.Core.Opt.Simplify.Utils: BC_Join :: SimplCont -> BindContext
- GHC.Core.Opt.Simplify.Utils: BC_Let :: TopLevelFlag -> RecFlag -> BindContext
- GHC.Core.Opt.Simplify.Utils: CastBy :: OutCoercion -> ArgSpec
- GHC.Core.Opt.Simplify.Utils: CastIt :: OutCoercion -> SimplCont -> SimplCont
- GHC.Core.Opt.Simplify.Utils: NoDup :: DupFlag
- GHC.Core.Opt.Simplify.Utils: OkToDup :: DupFlag
- GHC.Core.Opt.Simplify.Utils: Select :: DupFlag -> InId -> [InAlt] -> StaticEnv -> SimplCont -> SimplCont
- GHC.Core.Opt.Simplify.Utils: Simplified :: DupFlag
- GHC.Core.Opt.Simplify.Utils: Stop :: OutType -> CallCtxt -> SimplCont
- GHC.Core.Opt.Simplify.Utils: StrictArg :: DupFlag -> ArgInfo -> OutType -> SimplCont -> SimplCont
- GHC.Core.Opt.Simplify.Utils: StrictBind :: DupFlag -> InId -> InExpr -> StaticEnv -> SimplCont -> SimplCont
- GHC.Core.Opt.Simplify.Utils: TickIt :: CoreTickish -> SimplCont -> SimplCont
- GHC.Core.Opt.Simplify.Utils: TyArg :: OutType -> OutType -> ArgSpec
- GHC.Core.Opt.Simplify.Utils: ValArg :: Demand -> OutExpr -> OutType -> ArgSpec
- GHC.Core.Opt.Simplify.Utils: [ai_args] :: ArgInfo -> [ArgSpec]
- GHC.Core.Opt.Simplify.Utils: [ai_discs] :: ArgInfo -> [Int]
- GHC.Core.Opt.Simplify.Utils: [ai_dmds] :: ArgInfo -> [Demand]
- GHC.Core.Opt.Simplify.Utils: [ai_encl] :: ArgInfo -> Bool
- GHC.Core.Opt.Simplify.Utils: [ai_fun] :: ArgInfo -> OutId
- GHC.Core.Opt.Simplify.Utils: [ai_rules] :: ArgInfo -> FunRules
- GHC.Core.Opt.Simplify.Utils: [as_arg] :: ArgSpec -> OutExpr
- GHC.Core.Opt.Simplify.Utils: [as_arg_ty] :: ArgSpec -> OutType
- GHC.Core.Opt.Simplify.Utils: [as_dmd] :: ArgSpec -> Demand
- GHC.Core.Opt.Simplify.Utils: [as_hole_ty] :: ArgSpec -> OutType
- GHC.Core.Opt.Simplify.Utils: [sc_alts] :: SimplCont -> [InAlt]
- GHC.Core.Opt.Simplify.Utils: [sc_arg] :: SimplCont -> InExpr
- GHC.Core.Opt.Simplify.Utils: [sc_arg_ty] :: SimplCont -> OutType
- GHC.Core.Opt.Simplify.Utils: [sc_bndr] :: SimplCont -> InId
- GHC.Core.Opt.Simplify.Utils: [sc_body] :: SimplCont -> InExpr
- GHC.Core.Opt.Simplify.Utils: [sc_cont] :: SimplCont -> SimplCont
- GHC.Core.Opt.Simplify.Utils: [sc_dup] :: SimplCont -> DupFlag
- GHC.Core.Opt.Simplify.Utils: [sc_env] :: SimplCont -> StaticEnv
- GHC.Core.Opt.Simplify.Utils: [sc_fun] :: SimplCont -> ArgInfo
- GHC.Core.Opt.Simplify.Utils: [sc_fun_ty] :: SimplCont -> OutType
- GHC.Core.Opt.Simplify.Utils: [sc_hole_ty] :: SimplCont -> OutType
- GHC.Core.Opt.Simplify.Utils: abstractFloats :: UnfoldingOpts -> TopLevelFlag -> [OutTyVar] -> SimplFloats -> OutExpr -> SimplM ([OutBind], OutExpr)
- GHC.Core.Opt.Simplify.Utils: activeRule :: SimplMode -> Activation -> Bool
- GHC.Core.Opt.Simplify.Utils: activeUnfolding :: SimplMode -> Id -> Bool
- GHC.Core.Opt.Simplify.Utils: addCastTo :: ArgInfo -> OutCoercion -> ArgInfo
- GHC.Core.Opt.Simplify.Utils: addTyArgTo :: ArgInfo -> OutType -> OutType -> ArgInfo
- GHC.Core.Opt.Simplify.Utils: addValArgTo :: ArgInfo -> OutExpr -> OutType -> ArgInfo
- GHC.Core.Opt.Simplify.Utils: argInfoAppArgs :: [ArgSpec] -> [OutExpr]
- GHC.Core.Opt.Simplify.Utils: argInfoExpr :: OutId -> [ArgSpec] -> OutExpr
- GHC.Core.Opt.Simplify.Utils: bindContextLevel :: BindContext -> TopLevelFlag
- GHC.Core.Opt.Simplify.Utils: contArgs :: SimplCont -> (Bool, [ArgSummary], SimplCont)
- GHC.Core.Opt.Simplify.Utils: contHoleScaling :: SimplCont -> Mult
- GHC.Core.Opt.Simplify.Utils: contHoleType :: SimplCont -> OutType
- GHC.Core.Opt.Simplify.Utils: contIsDupable :: SimplCont -> Bool
- GHC.Core.Opt.Simplify.Utils: contIsRhsOrArg :: SimplCont -> Bool
- GHC.Core.Opt.Simplify.Utils: contIsStop :: SimplCont -> Bool
- GHC.Core.Opt.Simplify.Utils: contIsTrivial :: SimplCont -> Bool
- GHC.Core.Opt.Simplify.Utils: contResultType :: SimplCont -> OutType
- GHC.Core.Opt.Simplify.Utils: countArgs :: SimplCont -> Int
- GHC.Core.Opt.Simplify.Utils: data ArgInfo
- GHC.Core.Opt.Simplify.Utils: data ArgSpec
- GHC.Core.Opt.Simplify.Utils: data BindContext
- GHC.Core.Opt.Simplify.Utils: data DupFlag
- GHC.Core.Opt.Simplify.Utils: data SimplCont
- GHC.Core.Opt.Simplify.Utils: getUnfoldingInRuleMatch :: SimplEnv -> InScopeEnv
- GHC.Core.Opt.Simplify.Utils: instance GHC.Utils.Outputable.Outputable GHC.Core.Opt.Simplify.Utils.ArgInfo
- GHC.Core.Opt.Simplify.Utils: instance GHC.Utils.Outputable.Outputable GHC.Core.Opt.Simplify.Utils.ArgSpec
- GHC.Core.Opt.Simplify.Utils: instance GHC.Utils.Outputable.Outputable GHC.Core.Opt.Simplify.Utils.DupFlag
- GHC.Core.Opt.Simplify.Utils: instance GHC.Utils.Outputable.Outputable GHC.Core.Opt.Simplify.Utils.SimplCont
- GHC.Core.Opt.Simplify.Utils: interestingCallContext :: SimplEnv -> SimplCont -> CallCtxt
- GHC.Core.Opt.Simplify.Utils: isExitJoinId :: Var -> Bool
- GHC.Core.Opt.Simplify.Utils: isSimplified :: DupFlag -> Bool
- GHC.Core.Opt.Simplify.Utils: isStrictArgInfo :: ArgInfo -> Bool
- GHC.Core.Opt.Simplify.Utils: lazyArgContext :: ArgInfo -> CallCtxt
- GHC.Core.Opt.Simplify.Utils: mkArgInfo :: SimplEnv -> Id -> [CoreRule] -> Int -> SimplCont -> ArgInfo
- GHC.Core.Opt.Simplify.Utils: mkBoringStop :: OutType -> SimplCont
- GHC.Core.Opt.Simplify.Utils: mkCase :: DynFlags -> OutExpr -> OutId -> OutType -> [OutAlt] -> SimplM OutExpr
- GHC.Core.Opt.Simplify.Utils: mkLam :: SimplEnv -> [OutBndr] -> OutExpr -> SimplCont -> SimplM OutExpr
- GHC.Core.Opt.Simplify.Utils: mkLazyArgStop :: OutType -> CallCtxt -> SimplCont
- GHC.Core.Opt.Simplify.Utils: mkRhsStop :: OutType -> SimplCont
- GHC.Core.Opt.Simplify.Utils: postInlineUnconditionally :: SimplEnv -> BindContext -> OutId -> OccInfo -> OutExpr -> Bool
- GHC.Core.Opt.Simplify.Utils: preInlineUnconditionally :: SimplEnv -> TopLevelFlag -> InId -> InExpr -> StaticEnv -> Maybe SimplEnv
- GHC.Core.Opt.Simplify.Utils: prepareAlts :: OutExpr -> InId -> [InAlt] -> SimplM ([AltCon], [InAlt])
- GHC.Core.Opt.Simplify.Utils: pushSimplifiedArgs :: SimplEnv -> [ArgSpec] -> SimplCont -> SimplCont
- GHC.Core.Opt.Simplify.Utils: simplEnvForGHCi :: Logger -> DynFlags -> SimplEnv
- GHC.Core.Opt.Simplify.Utils: tryEtaExpandRhs :: SimplEnv -> OutId -> OutExpr -> SimplM (ArityType, OutExpr)
- GHC.Core.Opt.Simplify.Utils: type StaticEnv = SimplEnv
- GHC.Core.Opt.Simplify.Utils: updModeForRules :: SimplMode -> SimplMode
- GHC.Core.Opt.Simplify.Utils: updModeForStableUnfoldings :: Activation -> SimplMode -> SimplMode
- GHC.Core.Opt.WorkWrap.Utils: StopUnboxing :: UnboxingDecision s
- GHC.Core.Opt.WorkWrap.Utils: Unbox :: !DataConPatContext -> [s] -> UnboxingDecision s
- GHC.Core.Opt.WorkWrap.Utils: Unlift :: UnboxingDecision s
- GHC.Core.Opt.WorkWrap.Utils: badWorker :: Bool
- GHC.Core.Opt.WorkWrap.Utils: goodWorker :: Bool
- GHC.Core.Opt.WorkWrap.Utils: initWwOpts :: Module -> DynFlags -> FamInstEnvs -> WwOpts
- GHC.Core.Opt.WorkWrap.Utils: isGoodWorker :: Bool -> Bool
- GHC.Core.Opt.WorkWrap.Utils: wantToUnboxArg :: Bool -> FamInstEnvs -> Type -> Demand -> UnboxingDecision Demand
- GHC.CoreToStg: instance GHC.Driver.Session.HasDynFlags GHC.CoreToStg.CtsM
- GHC.Driver.Pipeline.Execute: doCpp :: Logger -> TmpFs -> DynFlags -> UnitEnv -> Bool -> FilePath -> FilePath -> IO ()
- GHC.Driver.Pipeline.Execute: fixchar :: Char -> Char
- GHC.Driver.Pipeline.Execute: generateMacros :: String -> String -> Version -> String
- GHC.Driver.Pipeline.Execute: generatePackageVersionMacros :: [UnitInfo] -> String
- GHC.Driver.Pipeline.Execute: getBackendDefs :: Logger -> DynFlags -> IO [String]
- GHC.Driver.Pipeline.Execute: getGhcVersionPathName :: DynFlags -> UnitEnv -> IO FilePath
- GHC.Driver.Pipeline.Execute: offsetIncludePaths :: DynFlags -> IncludeSpecs -> IncludeSpecs
- GHC.HsToCore.Binds: dsMkUserRule :: Module -> Bool -> RuleName -> Activation -> Name -> [CoreBndr] -> [CoreExpr] -> CoreExpr -> DsM CoreRule
- GHC.HsToCore.Coverage: CoverageConfig :: Logger -> DynFlags -> Maybe Interp -> CoverageConfig
- GHC.HsToCore.Coverage: [coverageConfig_dynFlags] :: CoverageConfig -> DynFlags
- GHC.HsToCore.Coverage: [coverageConfig_logger] :: CoverageConfig -> Logger
- GHC.HsToCore.Coverage: [coverageConfig_mInterp] :: CoverageConfig -> Maybe Interp
- GHC.HsToCore.Coverage: addTicksToBinds :: CoverageConfig -> Module -> ModLocation -> NameSet -> [TyCon] -> LHsBinds GhcTc -> IO (LHsBinds GhcTc, HpcInfo, Maybe ModBreaks)
- GHC.HsToCore.Coverage: data CoverageConfig
- GHC.HsToCore.Coverage: instance GHC.Base.Applicative GHC.HsToCore.Coverage.TM
- GHC.HsToCore.Coverage: instance GHC.Base.Functor GHC.HsToCore.Coverage.TM
- GHC.HsToCore.Coverage: instance GHC.Base.Monad GHC.HsToCore.Coverage.TM
- GHC.HsToCore.Coverage: instance GHC.Classes.Eq GHC.HsToCore.Coverage.TickDensity
- GHC.HsToCore.Coverage: instance GHC.Classes.Eq GHC.HsToCore.Coverage.TickishType
- GHC.HsToCore.Monad: mkPrintUnqualifiedDs :: DsM PrintUnqualified
- GHC.HsToCore.Types: [ds_unqual] :: DsGblEnv -> PrintUnqualified
- GHC.Iface.Binary: BinDictionary :: !FastMutInt -> !IORef (UniqFM FastString (Int, FastString)) -> BinDictionary
- GHC.Iface.Binary: [bin_dict_map] :: BinDictionary -> !IORef (UniqFM FastString (Int, FastString))
- GHC.Iface.Binary: [bin_dict_next] :: BinDictionary -> !FastMutInt
- GHC.Iface.Binary: data BinDictionary
- GHC.Iface.Binary: getDictFastString :: Dictionary -> BinHandle -> IO FastString
- GHC.Iface.Binary: putDictionary :: BinHandle -> Int -> UniqFM FastString (Int, FastString) -> IO ()
- GHC.Iface.Binary: putFastString :: BinDictionary -> BinHandle -> FastString -> IO ()
- GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.HiePass p => GHC.Iface.Ext.Ast.ToHie (GHC.Parser.Annotation.LocatedA (Language.Haskell.Syntax.Expr.HsSplice (GHC.Hs.Extension.GhcPass p)))
- GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.ToHie (GHC.Iface.Ext.Ast.IEContext (GHC.Hs.ImpExp.LIEWrappedName GHC.Types.Name.Name))
- GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.ToHie (GHC.Iface.Ext.Ast.IEContext (GHC.Parser.Annotation.LocatedA (GHC.Hs.ImpExp.IE GHC.Hs.Extension.GhcRn)))
- GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.ToHie (GHC.Iface.Ext.Ast.IEContext (GHC.Parser.Annotation.LocatedA GHC.Unit.Module.Name.ModuleName))
- GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.ToHie (GHC.Parser.Annotation.LocatedA (GHC.Hs.ImpExp.ImportDecl GHC.Hs.Extension.GhcRn))
- GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.ToHie (Language.Haskell.Syntax.Expr.HsQuote a)
- GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.ToHie Language.Haskell.Syntax.Decls.ForeignExport
- GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.ToHie Language.Haskell.Syntax.Decls.ForeignImport
- GHC.Linker.Static: linkBinary' :: Bool -> Logger -> TmpFs -> DynFlags -> UnitEnv -> [FilePath] -> [UnitId] -> IO ()
- GHC.Plugins: InvisArg :: AnonArgFlag
- GHC.Plugins: TCvSubst :: InScopeSet -> TvSubstEnv -> CvSubstEnv -> TCvSubst
- GHC.Plugins: VisArg :: AnonArgFlag
- GHC.Plugins: appTyArgFlags :: Type -> [Type] -> [ArgFlag]
- GHC.Plugins: binderArgFlag :: VarBndr tv argf -> argf
- GHC.Plugins: binderRelevantType_maybe :: TyCoBinder -> Maybe Type
- GHC.Plugins: classifiesTypeWithValues :: Kind -> Bool
- GHC.Plugins: composeTCvSubstEnv :: InScopeSet -> (TvSubstEnv, CvSubstEnv) -> (TvSubstEnv, CvSubstEnv) -> (TvSubstEnv, CvSubstEnv)
- GHC.Plugins: data AnonArgFlag
- GHC.Plugins: data ArgFlag
- GHC.Plugins: data TCvSubst
- GHC.Plugins: data TyCoBinder
- GHC.Plugins: emptyTCvSubst :: TCvSubst
- GHC.Plugins: eqType :: Type -> Type -> Bool
- GHC.Plugins: eqTypeX :: RnEnv2 -> Type -> Type -> Bool
- GHC.Plugins: eqTypes :: [Type] -> [Type] -> Bool
- GHC.Plugins: eqVarBndrs :: RnEnv2 -> [Var] -> [Var] -> Maybe RnEnv2
- GHC.Plugins: extendTCvInScope :: TCvSubst -> Var -> TCvSubst
- GHC.Plugins: extendTCvInScopeList :: TCvSubst -> [Var] -> TCvSubst
- GHC.Plugins: extendTCvInScopeSet :: TCvSubst -> VarSet -> TCvSubst
- GHC.Plugins: funTyCon :: TyCon
- GHC.Plugins: getLevity_maybe :: HasDebugCallStack => Type -> Maybe Type
- GHC.Plugins: getRuntimeRep_maybe :: HasDebugCallStack => Type -> Maybe Type
- GHC.Plugins: getTCvInScope :: TCvSubst -> InScopeSet
- GHC.Plugins: getTCvSubst :: Subst -> TCvSubst
- GHC.Plugins: getTCvSubstRangeFVs :: TCvSubst -> VarSet
- GHC.Plugins: idStateHackOneShotInfo :: Id -> OneShotInfo
- GHC.Plugins: isAnonTyCoBinder :: TyCoBinder -> Bool
- GHC.Plugins: isBoxedTypeKind :: Kind -> Bool
- GHC.Plugins: isCoVar_maybe :: Coercion -> Maybe CoVar
- GHC.Plugins: isConstraintKindCon :: TyCon -> Bool
- GHC.Plugins: isInvisibleArgFlag :: ArgFlag -> Bool
- GHC.Plugins: isInvisibleBinder :: TyCoBinder -> Bool
- GHC.Plugins: isManyDataConTy :: Mult -> Bool
- GHC.Plugins: isNamedBinder :: TyCoBinder -> Bool
- GHC.Plugins: isNeverRepPolyId :: Id -> Bool
- GHC.Plugins: isOneDataConTy :: Mult -> Bool
- GHC.Plugins: isOneShotBndr :: Var -> Bool
- GHC.Plugins: isProbablyOneShotLambda :: Id -> Bool
- GHC.Plugins: isStateHackType :: Type -> Bool
- GHC.Plugins: isStateType :: Type -> Bool
- GHC.Plugins: isVisibleArgFlag :: ArgFlag -> Bool
- GHC.Plugins: isVisibleBinder :: TyCoBinder -> Bool
- GHC.Plugins: lcTCvSubst :: LiftingContext -> TCvSubst
- GHC.Plugins: mapStepResult :: (ev1 -> ev2) -> NormaliseStepResult ev1 -> NormaliseStepResult ev2
- GHC.Plugins: mkAnonBinder :: AnonArgFlag -> Scaled Type -> TyCoBinder
- GHC.Plugins: mkEmptyTCvSubst :: InScopeSet -> TCvSubst
- GHC.Plugins: mkFamilyTyConAppCo :: TyCon -> [CoercionN] -> CoercionN
- GHC.Plugins: mkFunCo :: Role -> CoercionN -> Coercion -> Coercion -> Coercion
- GHC.Plugins: mkHeteroCoercionType :: Role -> Kind -> Kind -> Type -> Type -> Type
- GHC.Plugins: mkInvisFunTyMany :: Type -> Type -> Type
- GHC.Plugins: mkInvisFunTysMany :: [Type] -> Type -> Type
- GHC.Plugins: mkNthCo :: HasDebugCallStack => Role -> Int -> Coercion -> Coercion
- GHC.Plugins: mkNthCoFunCo :: Int -> CoercionN -> Coercion -> Coercion -> Coercion
- GHC.Plugins: mkTCvSubst :: InScopeSet -> (TvSubstEnv, CvSubstEnv) -> TCvSubst
- GHC.Plugins: mkTyCoVarBinder :: vis -> TyCoVar -> VarBndr TyCoVar vis
- GHC.Plugins: mkTyCoVarBinders :: vis -> [TyCoVar] -> [VarBndr TyCoVar vis]
- GHC.Plugins: mkVisFunTys :: [Scaled Type] -> Type -> Type
- GHC.Plugins: nonDetCmpTc :: TyCon -> TyCon -> Ordering
- GHC.Plugins: nonDetCmpType :: Type -> Type -> Ordering
- GHC.Plugins: nonDetCmpTypeX :: RnEnv2 -> Type -> Type -> Ordering
- GHC.Plugins: nonDetCmpTypes :: [Type] -> [Type] -> Ordering
- GHC.Plugins: nonDetCmpTypesX :: RnEnv2 -> [Type] -> [Type] -> Ordering
- GHC.Plugins: notElemTCvSubst :: Var -> TCvSubst -> Bool
- GHC.Plugins: nthCoRole :: Int -> Coercion -> Role
- GHC.Plugins: nthRole :: Role -> TyCon -> Int -> Role
- GHC.Plugins: pattern Many :: Mult
- GHC.Plugins: pattern One :: Mult
- GHC.Plugins: repSplitAppTy_maybe :: HasDebugCallStack => Type -> Maybe (Type, Type)
- GHC.Plugins: repSplitAppTys :: HasDebugCallStack => Type -> (Type, [Type])
- GHC.Plugins: repSplitTyConApp_maybe :: HasDebugCallStack => Type -> Maybe (TyCon, [Type])
- GHC.Plugins: resultHasFixedRuntimeRep :: Type -> Bool
- GHC.Plugins: sameVis :: ArgFlag -> ArgFlag -> Bool
- GHC.Plugins: setTvSubstEnv :: TCvSubst -> TvSubstEnv -> TCvSubst
- GHC.Plugins: splitForAllInvisTVBinders :: Type -> ([InvisTVBinder], Type)
- GHC.Plugins: splitForAllReqTVBinders :: Type -> ([ReqTVBinder], Type)
- GHC.Plugins: splitForAllTyCoVarBinders :: Type -> ([TyCoVarBinder], Type)
- GHC.Plugins: splitListTyConApp_maybe :: Type -> Maybe Type
- GHC.Plugins: splitTyConAppCo_maybe :: Coercion -> Maybe (TyCon, [Coercion])
- GHC.Plugins: splitVisVarsOfType :: Type -> Pair TyCoVarSet
- GHC.Plugins: splitVisVarsOfTypes :: [Type] -> Pair TyCoVarSet
- GHC.Plugins: stateHackOneShot :: OneShotInfo
- GHC.Plugins: substInScope :: Subst -> InScopeSet
- GHC.Plugins: substTy :: Subst -> Type -> Type
- GHC.Plugins: tcIsConstraintKind :: Kind -> Bool
- GHC.Plugins: tcIsRuntimeTypeKind :: Kind -> Bool
- GHC.Plugins: tcRepSplitAppTy_maybe :: Type -> Maybe (Type, Type)
- GHC.Plugins: tcRepSplitTyConApp_maybe :: HasDebugCallStack => Type -> Maybe (TyCon, [Type])
- GHC.Plugins: tcReturnsConstraintKind :: Kind -> Bool
- GHC.Plugins: tcTypeKind :: HasDebugCallStack => Type -> Kind
- GHC.Plugins: tcView :: Type -> Maybe Type
- GHC.Plugins: tidyTyCoVarBinder :: TidyEnv -> VarBndr TyCoVar vis -> (TidyEnv, VarBndr TyCoVar vis)
- GHC.Plugins: tidyTyCoVarBinders :: TidyEnv -> [VarBndr TyCoVar vis] -> (TidyEnv, [VarBndr TyCoVar vis])
- GHC.Plugins: tyBinderType :: TyBinder -> Type
- GHC.Plugins: tyCoBinderType :: TyCoBinder -> Type
- GHC.Plugins: tyCoBinderVar_maybe :: TyCoBinder -> Maybe TyCoVar
- GHC.Plugins: tyConAppArgN :: Int -> Type -> Type
- GHC.Plugins: tyConArgFlags :: TyCon -> [Type] -> [ArgFlag]
- GHC.Plugins: tyConBindersTyCoBinders :: [TyConBinder] -> [TyCoBinder]
- GHC.Plugins: type TyCoVarBinder = VarBndr TyCoVar ArgFlag
- GHC.Plugins: typeOneShot :: Type -> OneShotInfo
- GHC.Plugins: unionTCvSubst :: TCvSubst -> TCvSubst -> TCvSubst
- GHC.Plugins: unrestrictedFunTyCon :: TyCon
- GHC.Plugins: zapSubstEnv :: Subst -> Subst
- GHC.Plugins: zapTCvSubst :: TCvSubst -> TCvSubst
- GHC.Rename.Pat: rnPatAndThen :: NameMaker -> Pat GhcPs -> CpsRn (Pat GhcRn)
- GHC.Rename.Splice: rnSpliceExpr :: HsSplice GhcPs -> RnM (HsExpr GhcRn, FreeVars)
- GHC.Settings.Utils: getRawBooleanSetting :: FilePath -> RawSettings -> String -> Either String Bool
- GHC.Settings.Utils: getRawFilePathSetting :: FilePath -> FilePath -> RawSettings -> String -> Either String String
- GHC.Settings.Utils: getRawSetting :: FilePath -> RawSettings -> String -> Either String String
- GHC.Settings.Utils: getTargetArchOS :: FilePath -> RawSettings -> Either String ArchOS
- GHC.Settings.Utils: maybeRead :: Read a => String -> Maybe a
- GHC.Settings.Utils: maybeReadFuzzy :: Read a => String -> Maybe a
- GHC.Settings.Utils: readRawSetting :: (Show a, Read a) => FilePath -> RawSettings -> String -> Either String a
- GHC.Settings.Utils: type RawSettings = Map String String
- GHC.StgToCmm.Foreign: emitCCallNeverReturns :: [(CmmFormal, ForeignHint)] -> CmmExpr -> [(CmmActual, ForeignHint)] -> FCode ()
- GHC.StgToCmm.Monad: [stgToCmmAllowFabsInstrs] :: StgToCmmConfig -> !Bool
- GHC.StgToCmm.Monad: [stgToCmmDebugLevel] :: StgToCmmConfig -> !Int
- GHC.SysTools: lazyInitLlvmConfig :: String -> IO LlvmConfig
- GHC.SysTools.Tasks: runLibtool :: Logger -> DynFlags -> [Option] -> IO ()
- GHC.SysTools.Tasks: traceToolCommand :: Logger -> String -> IO a -> IO a
- GHC.Tc.Errors: solverReportInfo_ExpectedActuals :: TcSolverReportInfo -> [(Type, Type)]
- GHC.Tc.Gen.Expr: getFixedTyVars :: [FieldLabelString] -> [TyVar] -> [ConLike] -> TyVarSet
- GHC.Tc.Gen.Pat: polyPatSig :: TcType -> SDoc
- GHC.Tc.Gen.Splice: tcSpliceExpr :: HsSplice GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)
- GHC.Tc.Solver.Canonical: rewriteEqEvidence :: RewriterSet -> CtEvidence -> SwapFlag -> Reduction -> Reduction -> TcS CtEvidence
- GHC.Tc.Solver.Monad: addUsedGRE :: Bool -> GlobalRdrElt -> TcS ()
- GHC.Tc.Solver.Monad: addUsedGREs :: [GlobalRdrElt] -> TcS ()
- GHC.Tc.Solver.Monad: instFlexi :: [TKVar] -> TcS TCvSubst
- GHC.Tc.Solver.Monad: keepAlive :: Name -> TcS ()
- GHC.Tc.TyCl.Class: badMethodErr :: Outputable a => a -> Name -> TcRnMessage
- GHC.Tc.TyCl.Class: substATBndrs :: TCvSubst -> [TyVar] -> (TCvSubst, [Type])
- GHC.Tc.Utils.Monad: getPrintUnqualified :: TcRn PrintUnqualified
- GHC.Tc.Utils.Unify: tcSubTypeNC :: CtOrigin -> UserTypeCtxt -> Maybe TypedThing -> TcSigmaType -> ExpRhoType -> TcM HsWrapper
- GHC.Tc.Validity: allDistinctTyVars :: TyVarSet -> [KindOrType] -> Bool
- GHC.ThToHs: instance GHC.Base.Applicative GHC.ThToHs.CvtM
- GHC.ThToHs: instance GHC.Base.Functor GHC.ThToHs.CvtM
- GHC.ThToHs: instance GHC.Base.Monad GHC.ThToHs.CvtM
+ GHC: Opt_ByteCodeAndObjectCode :: GeneralFlag
+ GHC: Opt_CmmThreadSanitizer :: GeneralFlag
+ GHC: Opt_DumpWithWays :: GeneralFlag
+ GHC: Opt_LocalFloatOut :: GeneralFlag
+ GHC: Opt_LocalFloatOutTopLevel :: GeneralFlag
+ GHC: Opt_PrintRedundantPromotionTicks :: GeneralFlag
+ GHC: Opt_ShowErrorContext :: GeneralFlag
+ GHC: Opt_UseBytecodeRatherThanObjects :: GeneralFlag
+ GHC: Opt_WriteIfSimplifiedCore :: GeneralFlag
+ GHC: [externalPluginSpecs] :: DynFlags -> [ExternalPluginSpec]
+ GHC: [mi_extra_decls] :: ModIface_ (phase :: ModIfacePhase) -> Maybe [IfaceBindingX IfaceMaybeRhs IfaceTopBndrInfo]
+ GHC: data NamePprCtx
+ GHC: getNamePprCtx :: GhcMonad m => m NamePprCtx
+ GHC: interpreterBackend :: Backend
+ GHC: llvmBackend :: Backend
+ GHC: mkNamePprCtxForModule :: GhcMonad m => ModuleInfo -> m (Maybe NamePprCtx)
+ GHC: ncgBackend :: Backend
+ GHC: noBackend :: Backend
+ GHC: viaCBackend :: Backend
+ GHC.Builtin.Names.TH: mkNameQIdKey :: Unique
+ GHC.Builtin.Names.TH: mkNameQName :: Name
+ GHC.Builtin.Names.TH: typeDataDIdKey :: Unique
+ GHC.Builtin.Names.TH: typeDataDName :: Name
+ GHC.ByteCode.Instr: PUSH_ALTS_UNLIFTED :: ProtoBCO Name -> ArgRep -> BCInstr
+ GHC.ByteCode.Instr: RETURN_UNLIFTED :: ArgRep -> BCInstr
+ GHC.Cmm.Config: [cmmOptThreadSanitizer] :: CmmConfig -> !Bool
+ GHC.Cmm.DebugBlock: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform GHC.Cmm.DebugBlock.DebugBlock
+ GHC.Cmm.DebugBlock: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform GHC.Cmm.DebugBlock.UnwindExpr
+ GHC.Cmm.DebugBlock: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform GHC.Cmm.DebugBlock.UnwindPoint
+ GHC.Cmm.DebugBlock: pprUnwindTable :: IsLine doc => Platform -> UnwindTable -> doc
+ GHC.Cmm.Dominators: EntryNode :: DominatorSet
+ GHC.Cmm.Dominators: GraphWithDominators :: GenCmmGraph node -> LabelMap DominatorSet -> LabelMap RPNum -> GraphWithDominators node
+ GHC.Cmm.Dominators: ImmediateDominator :: Label -> DominatorSet -> DominatorSet
+ GHC.Cmm.Dominators: [ds_label] :: DominatorSet -> Label
+ GHC.Cmm.Dominators: [ds_parent] :: DominatorSet -> DominatorSet
+ GHC.Cmm.Dominators: [gwd_dominators] :: GraphWithDominators node -> LabelMap DominatorSet
+ GHC.Cmm.Dominators: [gwd_graph] :: GraphWithDominators node -> GenCmmGraph node
+ GHC.Cmm.Dominators: [gwd_rpnumbering] :: GraphWithDominators node -> LabelMap RPNum
+ GHC.Cmm.Dominators: data DominatorSet
+ GHC.Cmm.Dominators: data GraphWithDominators node
+ GHC.Cmm.Dominators: data RPNum
+ GHC.Cmm.Dominators: dominatorsMember :: Label -> DominatorSet -> Bool
+ GHC.Cmm.Dominators: graphMap :: GenCmmGraph n -> LabelMap (Block n C C)
+ GHC.Cmm.Dominators: graphWithDominators :: forall node. (NonLocal node, HasDebugCallStack) => GenCmmGraph node -> GraphWithDominators node
+ GHC.Cmm.Dominators: gwdDominatorTree :: GraphWithDominators node -> Tree Label
+ GHC.Cmm.Dominators: gwdDominatorsOf :: HasDebugCallStack => GraphWithDominators node -> Label -> DominatorSet
+ GHC.Cmm.Dominators: gwdRPNumber :: HasDebugCallStack => GraphWithDominators node -> Label -> RPNum
+ GHC.Cmm.Dominators: instance GHC.Classes.Eq GHC.Cmm.Dominators.DominatorSet
+ GHC.Cmm.Dominators: instance GHC.Classes.Eq GHC.Cmm.Dominators.RPNum
+ GHC.Cmm.Dominators: instance GHC.Classes.Ord GHC.Cmm.Dominators.RPNum
+ GHC.Cmm.Dominators: instance GHC.Show.Show GHC.Cmm.Dominators.RPNum
+ GHC.Cmm.Dominators: instance GHC.Utils.Outputable.Outputable GHC.Cmm.Dominators.DominatorSet
+ GHC.Cmm.Dominators: instance GHC.Utils.Outputable.Outputable GHC.Cmm.Dominators.RPNum
+ GHC.Cmm.Dominators: intersectDominators :: DominatorSet -> DominatorSet -> DominatorSet
+ GHC.Cmm.Parser: CmmParserConfig :: !ParserOpts -> !PDConfig -> !StgToCmmConfig -> CmmParserConfig
+ GHC.Cmm.Parser: [cmmpPDConfig] :: CmmParserConfig -> !PDConfig
+ GHC.Cmm.Parser: [cmmpParserOpts] :: CmmParserConfig -> !ParserOpts
+ GHC.Cmm.Parser: [cmmpStgToCmmConfig] :: CmmParserConfig -> !StgToCmmConfig
+ GHC.Cmm.Parser: data CmmParserConfig
+ GHC.Cmm.Parser.Config: CmmParserConfig :: !ParserOpts -> !PDConfig -> !StgToCmmConfig -> CmmParserConfig
+ GHC.Cmm.Parser.Config: PDConfig :: !Profile -> !Bool -> PDConfig
+ GHC.Cmm.Parser.Config: [cmmpPDConfig] :: CmmParserConfig -> !PDConfig
+ GHC.Cmm.Parser.Config: [cmmpParserOpts] :: CmmParserConfig -> !ParserOpts
+ GHC.Cmm.Parser.Config: [cmmpStgToCmmConfig] :: CmmParserConfig -> !StgToCmmConfig
+ GHC.Cmm.Parser.Config: [pdProfile] :: PDConfig -> !Profile
+ GHC.Cmm.Parser.Config: [pdSanitizeAlignment] :: PDConfig -> !Bool
+ GHC.Cmm.Parser.Config: data CmmParserConfig
+ GHC.Cmm.Parser.Config: data PDConfig
+ GHC.Cmm.Parser.Monad: PDConfig :: !Profile -> !Bool -> PDConfig
+ GHC.Cmm.Parser.Monad: [pdProfile] :: PDConfig -> !Profile
+ GHC.Cmm.Parser.Monad: [pdSanitizeAlignment] :: PDConfig -> !Bool
+ GHC.Cmm.Parser.Monad: data PDConfig
+ GHC.Cmm.Parser.Monad: getPDConfig :: PD PDConfig
+ GHC.Cmm.Reducibility: Irreducible :: Reducibility
+ GHC.Cmm.Reducibility: Reducible :: Reducibility
+ GHC.Cmm.Reducibility: asReducible :: GraphWithDominators CmmNode -> UniqSM (GraphWithDominators CmmNode)
+ GHC.Cmm.Reducibility: data Reducibility
+ GHC.Cmm.Reducibility: instance GHC.Base.Semigroup GHC.Cmm.Reducibility.CmmSuper
+ GHC.Cmm.Reducibility: instance GHC.Classes.Eq GHC.Cmm.Reducibility.Reducibility
+ GHC.Cmm.Reducibility: instance GHC.Data.Graph.Collapse.PureSupernode GHC.Cmm.Reducibility.CmmSuper
+ GHC.Cmm.Reducibility: instance GHC.Data.Graph.Collapse.Supernode GHC.Cmm.Reducibility.CmmSuper GHC.Data.Graph.Collapse.NullCollapseViz
+ GHC.Cmm.Reducibility: instance GHC.Show.Show GHC.Cmm.Reducibility.Reducibility
+ GHC.Cmm.Reducibility: reducibility :: NonLocal node => GraphWithDominators node -> Reducibility
+ GHC.Cmm.ThreadSanitizer: annotateTSAN :: Platform -> CmmGraph -> UniqSM CmmGraph
+ GHC.CmmToAsm: [pprNatCmmDeclH] :: NcgImpl statics instr jumpDest -> NatCmmDecl statics instr -> HDoc
+ GHC.CmmToAsm: [pprNatCmmDeclS] :: NcgImpl statics instr jumpDest -> NatCmmDecl statics instr -> SDoc
+ GHC.CmmToAsm.AArch64.Cond: NEVER :: Cond
+ GHC.CmmToAsm.AArch64.Instr: LDAR :: Format -> Operand -> Operand -> Instr
+ GHC.CmmToAsm.AArch64.Instr: STLR :: Format -> Operand -> Operand -> Instr
+ GHC.CmmToAsm.Dwarf.Types: instance GHC.Utils.Outputable.OutputableP GHC.Platform.Platform GHC.CmmToAsm.Dwarf.Types.DwarfFrameBlock
+ GHC.CmmToAsm.Monad: [pprNatCmmDeclH] :: NcgImpl statics instr jumpDest -> NatCmmDecl statics instr -> HDoc
+ GHC.CmmToAsm.Monad: [pprNatCmmDeclS] :: NcgImpl statics instr jumpDest -> NatCmmDecl statics instr -> SDoc
+ GHC.CmmToAsm.Reg.Graph.Base: ClassF64 :: RegClass
+ GHC.CmmToAsm.Reg.Graph.Base: ClassG16 :: RegClass
+ GHC.CmmToAsm.Reg.Graph.Base: ClassG32 :: RegClass
+ GHC.CmmToAsm.Reg.Graph.Base: ClassG8 :: RegClass
+ GHC.CmmToAsm.Reg.Graph.Base: Reg :: RegClass -> Int -> Reg
+ GHC.CmmToAsm.Reg.Graph.Base: RegSub :: RegSub -> Reg -> Reg
+ GHC.CmmToAsm.Reg.Graph.Base: SubL16 :: RegSub
+ GHC.CmmToAsm.Reg.Graph.Base: SubL8 :: RegSub
+ GHC.CmmToAsm.Reg.Graph.Base: SubL8H :: RegSub
+ GHC.CmmToAsm.Reg.Graph.Base: bound :: (RegClass -> UniqSet Reg) -> (Reg -> UniqSet Reg) -> RegClass -> [RegClass] -> Int
+ GHC.CmmToAsm.Reg.Graph.Base: data Reg
+ GHC.CmmToAsm.Reg.Graph.Base: data RegClass
+ GHC.CmmToAsm.Reg.Graph.Base: data RegSub
+ GHC.CmmToAsm.Reg.Graph.Base: instance GHC.Classes.Eq GHC.CmmToAsm.Reg.Graph.Base.Reg
+ GHC.CmmToAsm.Reg.Graph.Base: instance GHC.Classes.Eq GHC.CmmToAsm.Reg.Graph.Base.RegClass
+ GHC.CmmToAsm.Reg.Graph.Base: instance GHC.Classes.Eq GHC.CmmToAsm.Reg.Graph.Base.RegSub
+ GHC.CmmToAsm.Reg.Graph.Base: instance GHC.Classes.Ord GHC.CmmToAsm.Reg.Graph.Base.RegSub
+ GHC.CmmToAsm.Reg.Graph.Base: instance GHC.Enum.Enum GHC.CmmToAsm.Reg.Graph.Base.RegClass
+ GHC.CmmToAsm.Reg.Graph.Base: instance GHC.Enum.Enum GHC.CmmToAsm.Reg.Graph.Base.RegSub
+ GHC.CmmToAsm.Reg.Graph.Base: instance GHC.Show.Show GHC.CmmToAsm.Reg.Graph.Base.Reg
+ GHC.CmmToAsm.Reg.Graph.Base: instance GHC.Show.Show GHC.CmmToAsm.Reg.Graph.Base.RegClass
+ GHC.CmmToAsm.Reg.Graph.Base: instance GHC.Show.Show GHC.CmmToAsm.Reg.Graph.Base.RegSub
+ GHC.CmmToAsm.Reg.Graph.Base: instance GHC.Types.Unique.Uniquable GHC.CmmToAsm.Reg.Graph.Base.Reg
+ GHC.CmmToAsm.Reg.Graph.Base: squeese :: (RegClass -> UniqSet Reg) -> (Reg -> UniqSet Reg) -> RegClass -> [(Int, RegClass)] -> Int
+ GHC.CmmToAsm.Reg.Graph.Base: worst :: (RegClass -> UniqSet Reg) -> (Reg -> UniqSet Reg) -> Int -> RegClass -> RegClass -> Int
+ GHC.CmmToAsm.Reg.Graph.Coalesce: regCoalesce :: Instruction instr => [LiveCmmDecl statics instr] -> UniqSM [LiveCmmDecl statics instr]
+ GHC.CmmToAsm.Reg.Graph.Coalesce: slurpJoinMovs :: Instruction instr => LiveCmmDecl statics instr -> Bag (Reg, Reg)
+ GHC.CmmToAsm.Reg.Graph.X86: classOfReg :: Reg -> RegClass
+ GHC.CmmToAsm.Reg.Graph.X86: regAlias :: Reg -> UniqSet Reg
+ GHC.CmmToAsm.Reg.Graph.X86: regName :: Reg -> Maybe String
+ GHC.CmmToAsm.Reg.Graph.X86: regsOfClass :: RegClass -> UniqSet Reg
+ GHC.CmmToAsm.Reg.Graph.X86: squeese :: RegClass -> [(Int, RegClass)] -> Int
+ GHC.CmmToAsm.Reg.Graph.X86: worst :: Int -> RegClass -> RegClass -> Int
+ GHC.CmmToAsm.Wasm: ncgWasm :: Platform -> ToolSettings -> UniqSupply -> ModLocation -> Handle -> Stream IO RawCmmGroup a -> IO a
+ GHC.CmmToAsm.Wasm.Asm: asmTellEverything :: WasmTypeTag w -> WasmCodeGenState w -> WasmAsmM ()
+ GHC.CmmToAsm.Wasm.Asm: execWasmAsmM :: Bool -> WasmAsmM a -> Builder
+ GHC.CmmToAsm.Wasm.Asm: instance GHC.Base.Applicative GHC.CmmToAsm.Wasm.Asm.WasmAsmM
+ GHC.CmmToAsm.Wasm.Asm: instance GHC.Base.Functor GHC.CmmToAsm.Wasm.Asm.WasmAsmM
+ GHC.CmmToAsm.Wasm.Asm: instance GHC.Base.Monad GHC.CmmToAsm.Wasm.Asm.WasmAsmM
+ GHC.CmmToAsm.Wasm.Asm: instance GHC.Base.Monoid a => GHC.Base.Monoid (GHC.CmmToAsm.Wasm.Asm.WasmAsmM a)
+ GHC.CmmToAsm.Wasm.Asm: instance GHC.Base.Semigroup a => GHC.Base.Semigroup (GHC.CmmToAsm.Wasm.Asm.WasmAsmM a)
+ GHC.CmmToAsm.Wasm.FromCmm: alignmentFromWordType :: WasmTypeTag w -> Alignment
+ GHC.CmmToAsm.Wasm.FromCmm: globalInfoFromCmmGlobalReg :: WasmTypeTag w -> GlobalReg -> Maybe GlobalInfo
+ GHC.CmmToAsm.Wasm.FromCmm: onCmmGroup :: RawCmmGroup -> WasmCodeGenM w ()
+ GHC.CmmToAsm.Wasm.FromCmm: supportedCmmGlobalRegs :: [GlobalReg]
+ GHC.CmmToAsm.Wasm.Types: BrTableInterval :: Integer -> Integer -> BrTableInterval
+ GHC.CmmToAsm.Wasm.Types: DataASCII :: ByteString -> DataSectionContent
+ GHC.CmmToAsm.Wasm.Types: DataF32 :: Float -> DataSectionContent
+ GHC.CmmToAsm.Wasm.Types: DataF64 :: Double -> DataSectionContent
+ GHC.CmmToAsm.Wasm.Types: DataI16 :: Word16 -> DataSectionContent
+ GHC.CmmToAsm.Wasm.Types: DataI32 :: Word32 -> DataSectionContent
+ GHC.CmmToAsm.Wasm.Types: DataI64 :: Word64 -> DataSectionContent
+ GHC.CmmToAsm.Wasm.Types: DataI8 :: Word8 -> DataSectionContent
+ GHC.CmmToAsm.Wasm.Types: DataIncBin :: FilePath -> Int -> DataSectionContent
+ GHC.CmmToAsm.Wasm.Types: DataSection :: DataSectionKind -> Alignment -> [DataSectionContent] -> DataSection
+ GHC.CmmToAsm.Wasm.Types: DataSkip :: Int -> DataSectionContent
+ GHC.CmmToAsm.Wasm.Types: DataSym :: SymName -> Int -> DataSectionContent
+ GHC.CmmToAsm.Wasm.Types: F32 :: WasmType
+ GHC.CmmToAsm.Wasm.Types: F64 :: WasmType
+ GHC.CmmToAsm.Wasm.Types: FuncBody :: [SomeWasmType] -> WasmControl (WasmStatements w) (WasmExpr w w) '[] '[w] -> FuncBody w
+ GHC.CmmToAsm.Wasm.Types: I32 :: WasmType
+ GHC.CmmToAsm.Wasm.Types: I64 :: WasmType
+ GHC.CmmToAsm.Wasm.Types: SectionData :: DataSectionKind
+ GHC.CmmToAsm.Wasm.Types: SectionROData :: DataSectionKind
+ GHC.CmmToAsm.Wasm.Types: Signed :: Signage
+ GHC.CmmToAsm.Wasm.Types: SymData :: SymKind
+ GHC.CmmToAsm.Wasm.Types: SymDefault :: SymVisibility
+ GHC.CmmToAsm.Wasm.Types: SymFunc :: SymKind
+ GHC.CmmToAsm.Wasm.Types: SymName :: FastString -> SymName
+ GHC.CmmToAsm.Wasm.Types: SymStatic :: SymVisibility
+ GHC.CmmToAsm.Wasm.Types: SymUndefined :: SymVisibility
+ GHC.CmmToAsm.Wasm.Types: Unsigned :: Signage
+ GHC.CmmToAsm.Wasm.Types: WasmCodeGenM :: State (WasmCodeGenState w) a -> WasmCodeGenM w a
+ GHC.CmmToAsm.Wasm.Types: WasmCodeGenState :: Platform -> SymSet -> SymMap ([SomeWasmType], [SomeWasmType]) -> SymMap (FuncBody w) -> SymMap DataSection -> [SymName] -> UniqFM LocalReg LocalInfo -> Int -> UniqSupply -> WasmCodeGenState w
+ GHC.CmmToAsm.Wasm.Types: WasmExpr :: (forall pre. WasmInstr w pre (t : pre)) -> WasmExpr w t
+ GHC.CmmToAsm.Wasm.Types: WasmFunctionType :: TypeList pre -> TypeList post -> WasmFunctionType pre post
+ GHC.CmmToAsm.Wasm.Types: WasmStatements :: (forall pre. WasmInstr w pre pre) -> WasmStatements w
+ GHC.CmmToAsm.Wasm.Types: [SomeWasmExpr] :: WasmTypeTag t -> WasmExpr w t -> SomeWasmExpr w
+ GHC.CmmToAsm.Wasm.Types: [SomeWasmType] :: WasmTypeTag t -> SomeWasmType
+ GHC.CmmToAsm.Wasm.Types: [TagF32] :: WasmTypeTag 'F32
+ GHC.CmmToAsm.Wasm.Types: [TagF64] :: WasmTypeTag 'F64
+ GHC.CmmToAsm.Wasm.Types: [TagI32] :: WasmTypeTag 'I32
+ GHC.CmmToAsm.Wasm.Types: [TagI64] :: WasmTypeTag 'I64
+ GHC.CmmToAsm.Wasm.Types: [TypeListCons] :: WasmTypeTag t -> TypeList ts -> TypeList (t : ts)
+ GHC.CmmToAsm.Wasm.Types: [TypeListNil] :: TypeList '[]
+ GHC.CmmToAsm.Wasm.Types: [WasmAbs] :: WasmTypeTag t -> WasmInstr w (t : pre) (t : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmActions] :: s -> WasmControl s e stack stack
+ GHC.CmmToAsm.Wasm.Types: [WasmAdd] :: WasmTypeTag t -> WasmInstr w (t : (t : pre)) (t : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmAnd] :: WasmTypeTag t -> WasmInstr w (t : (t : pre)) (t : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmBlock] :: WasmFunctionType pre post -> WasmControl s e pre post -> WasmControl s e pre post
+ GHC.CmmToAsm.Wasm.Types: [WasmBrTable] :: e -> BrTableInterval -> [Int] -> Int -> WasmControl s e dropped destination
+ GHC.CmmToAsm.Wasm.Types: [WasmBr] :: Int -> WasmControl s e dropped destination
+ GHC.CmmToAsm.Wasm.Types: [WasmCCallIndirect] :: TypeList arg_tys -> TypeList ret_tys -> WasmInstr w (w : pre) post
+ GHC.CmmToAsm.Wasm.Types: [WasmCCall] :: SymName -> WasmInstr w pre post
+ GHC.CmmToAsm.Wasm.Types: [WasmComment] :: String -> WasmInstr w pre pre
+ GHC.CmmToAsm.Wasm.Types: [WasmConcat] :: WasmInstr w pre mid -> WasmInstr w mid post -> WasmInstr w pre post
+ GHC.CmmToAsm.Wasm.Types: [WasmCond] :: WasmInstr w pre pre -> WasmInstr w (w : pre) pre
+ GHC.CmmToAsm.Wasm.Types: [WasmConst] :: WasmTypeTag t -> Integer -> WasmInstr w pre (t : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmConvert] :: Signage -> WasmTypeTag t0 -> WasmTypeTag t1 -> WasmInstr w (t0 : pre) (t1 : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmDiv] :: Signage -> WasmTypeTag t -> WasmInstr w (t : (t : pre)) (t : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmDrop] :: WasmInstr w (t : pre) pre
+ GHC.CmmToAsm.Wasm.Types: [WasmEq] :: WasmTypeTag t -> WasmInstr w (t : (t : pre)) (w : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmF32DemoteF64] :: WasmInstr w ('F64 : pre) ('F32 : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmF64PromoteF32] :: WasmInstr w ('F32 : pre) ('F64 : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmFallthrough] :: WasmControl s e dropped destination
+ GHC.CmmToAsm.Wasm.Types: [WasmGe] :: Signage -> WasmTypeTag t -> WasmInstr w (t : (t : pre)) (w : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmGlobalGet] :: WasmTypeTag t -> SymName -> WasmInstr w pre (t : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmGlobalSet] :: WasmTypeTag t -> SymName -> WasmInstr w (t : pre) pre
+ GHC.CmmToAsm.Wasm.Types: [WasmGt] :: Signage -> WasmTypeTag t -> WasmInstr w (t : (t : pre)) (w : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmI32Extend16S] :: WasmInstr w ('I32 : pre) ('I32 : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmI32Extend8S] :: WasmInstr w ('I32 : pre) ('I32 : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmI32WrapI64] :: WasmInstr w ('I64 : pre) ('I32 : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmI64Extend16S] :: WasmInstr w ('I64 : pre) ('I64 : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmI64Extend32S] :: WasmInstr w ('I64 : pre) ('I64 : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmI64Extend8S] :: WasmInstr w ('I64 : pre) ('I64 : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmI64ExtendI32] :: Signage -> WasmInstr w ('I32 : pre) ('I64 : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmIfTop] :: WasmFunctionType pre post -> WasmControl s e pre post -> WasmControl s e pre post -> WasmControl s e ('I32 : pre) post
+ GHC.CmmToAsm.Wasm.Types: [WasmLe] :: Signage -> WasmTypeTag t -> WasmInstr w (t : (t : pre)) (w : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmLoad] :: WasmTypeTag t -> Maybe Int -> Signage -> Int -> AlignmentSpec -> WasmInstr w (w : pre) (t : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmLocalGet] :: WasmTypeTag t -> Int -> WasmInstr w pre (t : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmLocalSet] :: WasmTypeTag t -> Int -> WasmInstr w (t : pre) pre
+ GHC.CmmToAsm.Wasm.Types: [WasmLocalTee] :: WasmTypeTag t -> Int -> WasmInstr w (t : pre) (t : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmLoop] :: WasmFunctionType pre post -> WasmControl s e pre post -> WasmControl s e pre post
+ GHC.CmmToAsm.Wasm.Types: [WasmLt] :: Signage -> WasmTypeTag t -> WasmInstr w (t : (t : pre)) (w : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmMul] :: WasmTypeTag t -> WasmInstr w (t : (t : pre)) (t : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmNe] :: WasmTypeTag t -> WasmInstr w (t : (t : pre)) (w : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmNeg] :: WasmTypeTag t -> WasmInstr w (t : pre) (t : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmNop] :: WasmInstr w pre pre
+ GHC.CmmToAsm.Wasm.Types: [WasmOr] :: WasmTypeTag t -> WasmInstr w (t : (t : pre)) (t : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmPush] :: WasmTypeTag t -> e -> WasmControl s e stack (t : stack)
+ GHC.CmmToAsm.Wasm.Types: [WasmReinterpret] :: WasmTypeTag t0 -> WasmTypeTag t1 -> WasmInstr w (t0 : pre) (t1 : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmRem] :: Signage -> WasmTypeTag t -> WasmInstr w (t : (t : pre)) (t : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmSeq] :: WasmControl s e pre mid -> WasmControl s e mid post -> WasmControl s e pre post
+ GHC.CmmToAsm.Wasm.Types: [WasmShl] :: WasmTypeTag t -> WasmInstr w (t : (t : pre)) (t : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmShr] :: Signage -> WasmTypeTag t -> WasmInstr w (t : (t : pre)) (t : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmStore] :: WasmTypeTag t -> Maybe Int -> Int -> AlignmentSpec -> WasmInstr w (t : (w : pre)) pre
+ GHC.CmmToAsm.Wasm.Types: [WasmSub] :: WasmTypeTag t -> WasmInstr w (t : (t : pre)) (t : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmSymConst] :: SymName -> WasmInstr w pre (w : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmTailCall] :: e -> WasmControl s e t1star t2star
+ GHC.CmmToAsm.Wasm.Types: [WasmTruncSat] :: Signage -> WasmTypeTag t0 -> WasmTypeTag t1 -> WasmInstr w (t0 : pre) (t1 : pre)
+ GHC.CmmToAsm.Wasm.Types: [WasmUnreachable] :: WasmInstr w pre post
+ GHC.CmmToAsm.Wasm.Types: [WasmXor] :: WasmTypeTag t -> WasmInstr w (t : (t : pre)) (t : pre)
+ GHC.CmmToAsm.Wasm.Types: [bti_count] :: BrTableInterval -> Integer
+ GHC.CmmToAsm.Wasm.Types: [bti_lo] :: BrTableInterval -> Integer
+ GHC.CmmToAsm.Wasm.Types: [ctors] :: WasmCodeGenState w -> [SymName]
+ GHC.CmmToAsm.Wasm.Types: [dataSectionAlignment] :: DataSection -> Alignment
+ GHC.CmmToAsm.Wasm.Types: [dataSectionContents] :: DataSection -> [DataSectionContent]
+ GHC.CmmToAsm.Wasm.Types: [dataSectionKind] :: DataSection -> DataSectionKind
+ GHC.CmmToAsm.Wasm.Types: [dataSections] :: WasmCodeGenState w -> SymMap DataSection
+ GHC.CmmToAsm.Wasm.Types: [defaultSyms] :: WasmCodeGenState w -> SymSet
+ GHC.CmmToAsm.Wasm.Types: [ft_pops] :: WasmFunctionType pre post -> TypeList pre
+ GHC.CmmToAsm.Wasm.Types: [ft_pushes] :: WasmFunctionType pre post -> TypeList post
+ GHC.CmmToAsm.Wasm.Types: [funcBodies] :: WasmCodeGenState w -> SymMap (FuncBody w)
+ GHC.CmmToAsm.Wasm.Types: [funcBody] :: FuncBody w -> WasmControl (WasmStatements w) (WasmExpr w w) '[] '[w]
+ GHC.CmmToAsm.Wasm.Types: [funcLocals] :: FuncBody w -> [SomeWasmType]
+ GHC.CmmToAsm.Wasm.Types: [funcTypes] :: WasmCodeGenState w -> SymMap ([SomeWasmType], [SomeWasmType])
+ GHC.CmmToAsm.Wasm.Types: [localRegsCount] :: WasmCodeGenState w -> Int
+ GHC.CmmToAsm.Wasm.Types: [localRegs] :: WasmCodeGenState w -> UniqFM LocalReg LocalInfo
+ GHC.CmmToAsm.Wasm.Types: [wasmPlatform] :: WasmCodeGenState w -> Platform
+ GHC.CmmToAsm.Wasm.Types: [wasmUniqSupply] :: WasmCodeGenState w -> UniqSupply
+ GHC.CmmToAsm.Wasm.Types: data BrTableInterval
+ GHC.CmmToAsm.Wasm.Types: data DataSection
+ GHC.CmmToAsm.Wasm.Types: data DataSectionContent
+ GHC.CmmToAsm.Wasm.Types: data DataSectionKind
+ GHC.CmmToAsm.Wasm.Types: data FuncBody w
+ GHC.CmmToAsm.Wasm.Types: data Signage
+ GHC.CmmToAsm.Wasm.Types: data SomeWasmExpr w
+ GHC.CmmToAsm.Wasm.Types: data SomeWasmType
+ GHC.CmmToAsm.Wasm.Types: data SymKind
+ GHC.CmmToAsm.Wasm.Types: data SymVisibility
+ GHC.CmmToAsm.Wasm.Types: data TypeList :: [WasmType] -> Type
+ GHC.CmmToAsm.Wasm.Types: data WasmCodeGenState w
+ GHC.CmmToAsm.Wasm.Types: data WasmControl :: Type -> Type -> [WasmType] -> [WasmType] -> Type
+ GHC.CmmToAsm.Wasm.Types: data WasmFunctionType pre post
+ GHC.CmmToAsm.Wasm.Types: data WasmInstr :: WasmType -> [WasmType] -> [WasmType] -> Type
+ GHC.CmmToAsm.Wasm.Types: data WasmType
+ GHC.CmmToAsm.Wasm.Types: data WasmTypeTag :: WasmType -> Type
+ GHC.CmmToAsm.Wasm.Types: initialWasmCodeGenState :: Platform -> UniqSupply -> WasmCodeGenState w
+ GHC.CmmToAsm.Wasm.Types: instance Data.String.IsString GHC.CmmToAsm.Wasm.Types.SymName
+ GHC.CmmToAsm.Wasm.Types: instance Data.Type.Equality.TestEquality GHC.CmmToAsm.Wasm.Types.WasmTypeTag
+ GHC.CmmToAsm.Wasm.Types: instance GHC.Base.Applicative (GHC.CmmToAsm.Wasm.Types.WasmCodeGenM w)
+ GHC.CmmToAsm.Wasm.Types: instance GHC.Base.Functor (GHC.CmmToAsm.Wasm.Types.WasmCodeGenM w)
+ GHC.CmmToAsm.Wasm.Types: instance GHC.Base.Monad (GHC.CmmToAsm.Wasm.Types.WasmCodeGenM w)
+ GHC.CmmToAsm.Wasm.Types: instance GHC.Classes.Eq GHC.CmmToAsm.Wasm.Types.SomeWasmType
+ GHC.CmmToAsm.Wasm.Types: instance GHC.Classes.Eq GHC.CmmToAsm.Wasm.Types.SymKind
+ GHC.CmmToAsm.Wasm.Types: instance GHC.Classes.Eq GHC.CmmToAsm.Wasm.Types.SymName
+ GHC.CmmToAsm.Wasm.Types: instance GHC.Classes.Ord GHC.CmmToAsm.Wasm.Types.SymName
+ GHC.CmmToAsm.Wasm.Types: instance GHC.Show.Show (GHC.CmmToAsm.Wasm.Types.WasmTypeTag t)
+ GHC.CmmToAsm.Wasm.Types: instance GHC.Show.Show GHC.CmmToAsm.Wasm.Types.BrTableInterval
+ GHC.CmmToAsm.Wasm.Types: instance GHC.Show.Show GHC.CmmToAsm.Wasm.Types.SymName
+ GHC.CmmToAsm.Wasm.Types: instance GHC.Types.Unique.Uniquable GHC.CmmToAsm.Wasm.Types.SymName
+ GHC.CmmToAsm.Wasm.Types: instance GHC.Utils.Outputable.Outputable GHC.CmmToAsm.Wasm.Types.BrTableInterval
+ GHC.CmmToAsm.Wasm.Types: newtype SymName
+ GHC.CmmToAsm.Wasm.Types: newtype WasmCodeGenM w a
+ GHC.CmmToAsm.Wasm.Types: newtype WasmExpr w t
+ GHC.CmmToAsm.Wasm.Types: newtype WasmStatements w
+ GHC.CmmToAsm.Wasm.Types: someWasmTypesFromTypeList :: TypeList ts -> [SomeWasmType]
+ GHC.CmmToAsm.Wasm.Types: type GlobalInfo = (SymName, SomeWasmType)
+ GHC.CmmToAsm.Wasm.Types: type LocalInfo = (Int, SomeWasmType)
+ GHC.CmmToAsm.Wasm.Types: wasmControlCast :: WasmControl s e pre post -> WasmControl s e pre' post'
+ GHC.CmmToAsm.Wasm.Types: wasmExecM :: WasmCodeGenM w a -> WasmCodeGenState w -> WasmCodeGenState w
+ GHC.CmmToAsm.Wasm.Types: wasmGetsM :: (WasmCodeGenState w -> a) -> WasmCodeGenM w a
+ GHC.CmmToAsm.Wasm.Types: wasmModifyM :: (WasmCodeGenState w -> WasmCodeGenState w) -> WasmCodeGenM w ()
+ GHC.CmmToAsm.Wasm.Types: wasmPlatformM :: WasmCodeGenM w Platform
+ GHC.CmmToAsm.Wasm.Types: wasmStateM :: (WasmCodeGenState w -> (# a, WasmCodeGenState w #)) -> WasmCodeGenM w a
+ GHC.CmmToAsm.Wasm.Types: wasmUniq :: WasmCodeGenM w Unique
+ GHC.CmmToAsm.Wasm.Types: wasmWordCmmTypeM :: WasmCodeGenM w CmmType
+ GHC.CmmToAsm.Wasm.Types: wasmWordTypeM :: WasmCodeGenM w (WasmTypeTag w)
+ GHC.CmmToAsm.Wasm.Utils: builderCommas :: (a -> Builder) -> [a] -> Builder
+ GHC.CmmToAsm.Wasm.Utils: detEltsUFM :: Ord k => UniqFM k0 (k, a) -> [(k, a)]
+ GHC.CmmToAsm.Wasm.Utils: detEltsUniqMap :: Ord k => UniqMap k a -> [(k, a)]
+ GHC.CmmToAsm.Wasm.Utils: widthMax :: Width -> Integer
+ GHC.Core.Opt.DmdAnal: [dmd_do_boxity] :: DmdAnalOpts -> !Bool
+ GHC.Core.Opt.LiberateCase: LibCaseOpts :: !Maybe Int -> !UnfoldingOpts -> LibCaseOpts
+ GHC.Core.Opt.LiberateCase: [lco_threshold] :: LibCaseOpts -> !Maybe Int
+ GHC.Core.Opt.LiberateCase: [lco_unfolding_opts] :: LibCaseOpts -> !UnfoldingOpts
+ GHC.Core.Opt.LiberateCase: data LibCaseOpts
+ GHC.Core.Opt.WorkWrap: MkWwOpts :: !FamInstEnvs -> !SimpleOpts -> !Bool -> !Module -> !Bool -> WwOpts
+ GHC.Core.Opt.WorkWrap: [wo_cpr_anal] :: WwOpts -> !Bool
+ GHC.Core.Opt.WorkWrap: [wo_fam_envs] :: WwOpts -> !FamInstEnvs
+ GHC.Core.Opt.WorkWrap: [wo_module] :: WwOpts -> !Module
+ GHC.Core.Opt.WorkWrap: [wo_simple_opts] :: WwOpts -> !SimpleOpts
+ GHC.Core.Opt.WorkWrap: [wo_unlift_strict] :: WwOpts -> !Bool
+ GHC.Core.Opt.WorkWrap: data WwOpts
+ GHC.Core.Opt.WorkWrap.Utils: DoUnbox :: !unboxing_info -> UnboxingDecision unboxing_info
+ GHC.Core.Opt.WorkWrap.Utils: DontUnbox :: UnboxingDecision unboxing_info
+ GHC.Core.Opt.WorkWrap.Utils: [dcpc_args] :: DataConPatContext s -> ![s]
+ GHC.Core.Opt.WorkWrap.Utils: boringSplit :: WwUse
+ GHC.Core.Opt.WorkWrap.Utils: canUnboxArg :: FamInstEnvs -> Type -> Demand -> UnboxingDecision (DataConPatContext Demand)
+ GHC.Core.Opt.WorkWrap.Utils: instance GHC.Utils.Outputable.Outputable i => GHC.Utils.Outputable.Outputable (GHC.Core.Opt.WorkWrap.Utils.UnboxingDecision i)
+ GHC.Core.Opt.WorkWrap.Utils: usefulSplit :: WwUse
+ GHC.CoreToStg: CoreToStgOpts :: Platform -> Ways -> Bool -> Bool -> Bool -> StgDebugOpts -> CoreToStgOpts
+ GHC.CoreToStg: [coreToStg_AutoSccsOnIndividualCafs] :: CoreToStgOpts -> Bool
+ GHC.CoreToStg: [coreToStg_ExternalDynamicRefs] :: CoreToStgOpts -> Bool
+ GHC.CoreToStg: [coreToStg_InfoTableMap] :: CoreToStgOpts -> Bool
+ GHC.CoreToStg: [coreToStg_platform] :: CoreToStgOpts -> Platform
+ GHC.CoreToStg: [coreToStg_stgDebugOpts] :: CoreToStgOpts -> StgDebugOpts
+ GHC.CoreToStg: [coreToStg_ways] :: CoreToStgOpts -> Ways
+ GHC.CoreToStg: data CoreToStgOpts
+ GHC.CoreToStg.Prep: CorePrepConfig :: !Bool -> !LitNumType -> Integer -> Maybe CoreExpr -> CorePrepConfig
+ GHC.CoreToStg.Prep: CorePrepPgmConfig :: !EndPassConfig -> !Bool -> CorePrepPgmConfig
+ GHC.CoreToStg.Prep: [cpPgm_endPassConfig] :: CorePrepPgmConfig -> !EndPassConfig
+ GHC.CoreToStg.Prep: [cpPgm_generateDebugInfo] :: CorePrepPgmConfig -> !Bool
+ GHC.CoreToStg.Prep: [cp_catchNonexhaustiveCases] :: CorePrepConfig -> !Bool
+ GHC.CoreToStg.Prep: [cp_convertNumLit] :: CorePrepConfig -> !LitNumType -> Integer -> Maybe CoreExpr
+ GHC.CoreToStg.Prep: data CorePrepConfig
+ GHC.CoreToStg.Prep: data CorePrepPgmConfig
+ GHC.Data.Graph.Collapse: NullCollapseViz :: UniqSM a -> NullCollapseViz a
+ GHC.Data.Graph.Collapse: [unNCV] :: NullCollapseViz a -> UniqSM a
+ GHC.Data.Graph.Collapse: class (Monad m) => MonadUniqSM m
+ GHC.Data.Graph.Collapse: class (Semigroup node) => PureSupernode node
+ GHC.Data.Graph.Collapse: class (MonadUnique m, PureSupernode node) => Supernode node m
+ GHC.Data.Graph.Collapse: class (MonadUniqSM m, Graph gr, Supernode s m) => VizCollapseMonad m gr s
+ GHC.Data.Graph.Collapse: collapseInductiveGraph :: (DynGraph gr, Supernode s m, VizCollapseMonad m gr s) => gr s () -> m (gr s ())
+ GHC.Data.Graph.Collapse: consumeByInGraph :: VizCollapseMonad m gr s => Node -> Node -> gr s () -> m ()
+ GHC.Data.Graph.Collapse: finalGraph :: VizCollapseMonad m gr s => gr s () -> m ()
+ GHC.Data.Graph.Collapse: freshen :: Supernode node m => node -> m node
+ GHC.Data.Graph.Collapse: instance (GHC.Data.Graph.Inductive.Graph.Graph gr, GHC.Data.Graph.Collapse.Supernode s GHC.Data.Graph.Collapse.NullCollapseViz) => GHC.Data.Graph.Collapse.VizCollapseMonad GHC.Data.Graph.Collapse.NullCollapseViz gr s
+ GHC.Data.Graph.Collapse: instance GHC.Base.Applicative GHC.Data.Graph.Collapse.NullCollapseViz
+ GHC.Data.Graph.Collapse: instance GHC.Base.Functor GHC.Data.Graph.Collapse.NullCollapseViz
+ GHC.Data.Graph.Collapse: instance GHC.Base.Monad GHC.Data.Graph.Collapse.NullCollapseViz
+ GHC.Data.Graph.Collapse: instance GHC.Data.Graph.Collapse.MonadUniqSM GHC.Data.Graph.Collapse.NullCollapseViz
+ GHC.Data.Graph.Collapse: instance GHC.Types.Unique.Supply.MonadUnique GHC.Data.Graph.Collapse.NullCollapseViz
+ GHC.Data.Graph.Collapse: liftUniqSM :: MonadUniqSM m => UniqSM a -> m a
+ GHC.Data.Graph.Collapse: mapLabels :: PureSupernode node => (Label -> Label) -> node -> node
+ GHC.Data.Graph.Collapse: newtype NullCollapseViz a
+ GHC.Data.Graph.Collapse: runNullCollapse :: NullCollapseViz a -> UniqSM a
+ GHC.Data.Graph.Collapse: splitGraphAt :: VizCollapseMonad m gr s => gr s () -> LNode s -> m ()
+ GHC.Data.Graph.Collapse: superLabel :: PureSupernode node => node -> Label
+ GHC.Data.Graph.Inductive.Graph: (&) :: DynGraph gr => Context a b -> gr a b -> gr a b
+ GHC.Data.Graph.Inductive.Graph: LP :: [LNode a] -> LPath a
+ GHC.Data.Graph.Inductive.Graph: OrdGr :: gr a b -> OrdGr gr a b
+ GHC.Data.Graph.Inductive.Graph: [unLPath] :: LPath a -> [LNode a]
+ GHC.Data.Graph.Inductive.Graph: [unOrdGr] :: OrdGr gr a b -> gr a b
+ GHC.Data.Graph.Inductive.Graph: buildGr :: DynGraph gr => [Context a b] -> gr a b
+ GHC.Data.Graph.Inductive.Graph: class (Graph gr) => DynGraph gr
+ GHC.Data.Graph.Inductive.Graph: class Graph gr
+ GHC.Data.Graph.Inductive.Graph: context :: Graph gr => gr a b -> Node -> Context a b
+ GHC.Data.Graph.Inductive.Graph: deg :: Graph gr => gr a b -> Node -> Int
+ GHC.Data.Graph.Inductive.Graph: deg' :: Context a b -> Int
+ GHC.Data.Graph.Inductive.Graph: delAllLEdge :: (DynGraph gr, Eq b) => LEdge b -> gr a b -> gr a b
+ GHC.Data.Graph.Inductive.Graph: delEdge :: DynGraph gr => Edge -> gr a b -> gr a b
+ GHC.Data.Graph.Inductive.Graph: delEdges :: DynGraph gr => [Edge] -> gr a b -> gr a b
+ GHC.Data.Graph.Inductive.Graph: delLEdge :: (DynGraph gr, Eq b) => LEdge b -> gr a b -> gr a b
+ GHC.Data.Graph.Inductive.Graph: delNode :: Graph gr => Node -> gr a b -> gr a b
+ GHC.Data.Graph.Inductive.Graph: delNodes :: Graph gr => [Node] -> gr a b -> gr a b
+ GHC.Data.Graph.Inductive.Graph: edgeLabel :: LEdge b -> b
+ GHC.Data.Graph.Inductive.Graph: edges :: Graph gr => gr a b -> [Edge]
+ GHC.Data.Graph.Inductive.Graph: emap :: DynGraph gr => (b -> c) -> gr a b -> gr a c
+ GHC.Data.Graph.Inductive.Graph: empty :: Graph gr => gr a b
+ GHC.Data.Graph.Inductive.Graph: equal :: (Eq a, Eq b, Graph gr) => gr a b -> gr a b -> Bool
+ GHC.Data.Graph.Inductive.Graph: gelem :: Graph gr => Node -> gr a b -> Bool
+ GHC.Data.Graph.Inductive.Graph: gfiltermap :: DynGraph gr => (Context a b -> MContext c d) -> gr a b -> gr c d
+ GHC.Data.Graph.Inductive.Graph: gmap :: DynGraph gr => (Context a b -> Context c d) -> gr a b -> gr c d
+ GHC.Data.Graph.Inductive.Graph: hasEdge :: Graph gr => gr a b -> Edge -> Bool
+ GHC.Data.Graph.Inductive.Graph: hasLEdge :: (Graph gr, Eq b) => gr a b -> LEdge b -> Bool
+ GHC.Data.Graph.Inductive.Graph: hasNeighbor :: Graph gr => gr a b -> Node -> Node -> Bool
+ GHC.Data.Graph.Inductive.Graph: hasNeighborAdj :: (Graph gr, Eq b) => gr a b -> Node -> (b, Node) -> Bool
+ GHC.Data.Graph.Inductive.Graph: indeg :: Graph gr => gr a b -> Node -> Int
+ GHC.Data.Graph.Inductive.Graph: indeg' :: Context a b -> Int
+ GHC.Data.Graph.Inductive.Graph: inn :: Graph gr => gr a b -> Node -> [LEdge b]
+ GHC.Data.Graph.Inductive.Graph: inn' :: Context a b -> [LEdge b]
+ GHC.Data.Graph.Inductive.Graph: insEdge :: DynGraph gr => LEdge b -> gr a b -> gr a b
+ GHC.Data.Graph.Inductive.Graph: insEdges :: DynGraph gr => [LEdge b] -> gr a b -> gr a b
+ GHC.Data.Graph.Inductive.Graph: insNode :: DynGraph gr => LNode a -> gr a b -> gr a b
+ GHC.Data.Graph.Inductive.Graph: insNodes :: DynGraph gr => [LNode a] -> gr a b -> gr a b
+ GHC.Data.Graph.Inductive.Graph: instance (GHC.Data.Graph.Inductive.Graph.Graph gr, GHC.Classes.Ord a, GHC.Classes.Ord b) => GHC.Classes.Eq (GHC.Data.Graph.Inductive.Graph.OrdGr gr a b)
+ GHC.Data.Graph.Inductive.Graph: instance (GHC.Data.Graph.Inductive.Graph.Graph gr, GHC.Classes.Ord a, GHC.Classes.Ord b) => GHC.Classes.Ord (GHC.Data.Graph.Inductive.Graph.OrdGr gr a b)
+ GHC.Data.Graph.Inductive.Graph: instance GHC.Classes.Eq a => GHC.Classes.Eq (GHC.Data.Graph.Inductive.Graph.LPath a)
+ GHC.Data.Graph.Inductive.Graph: instance GHC.Classes.Eq b => GHC.Classes.Eq (GHC.Data.Graph.Inductive.Graph.GroupEdges b)
+ GHC.Data.Graph.Inductive.Graph: instance GHC.Classes.Ord a => GHC.Classes.Ord (GHC.Data.Graph.Inductive.Graph.LPath a)
+ GHC.Data.Graph.Inductive.Graph: instance GHC.Read.Read (gr a b) => GHC.Read.Read (GHC.Data.Graph.Inductive.Graph.OrdGr gr a b)
+ GHC.Data.Graph.Inductive.Graph: instance GHC.Read.Read b => GHC.Read.Read (GHC.Data.Graph.Inductive.Graph.GroupEdges b)
+ GHC.Data.Graph.Inductive.Graph: instance GHC.Show.Show (gr a b) => GHC.Show.Show (GHC.Data.Graph.Inductive.Graph.OrdGr gr a b)
+ GHC.Data.Graph.Inductive.Graph: instance GHC.Show.Show a => GHC.Show.Show (GHC.Data.Graph.Inductive.Graph.LPath a)
+ GHC.Data.Graph.Inductive.Graph: instance GHC.Show.Show b => GHC.Show.Show (GHC.Data.Graph.Inductive.Graph.GroupEdges b)
+ GHC.Data.Graph.Inductive.Graph: isEmpty :: Graph gr => gr a b -> Bool
+ GHC.Data.Graph.Inductive.Graph: lab :: Graph gr => gr a b -> Node -> Maybe a
+ GHC.Data.Graph.Inductive.Graph: lab' :: Context a b -> a
+ GHC.Data.Graph.Inductive.Graph: labEdges :: Graph gr => gr a b -> [LEdge b]
+ GHC.Data.Graph.Inductive.Graph: labNode' :: Context a b -> LNode a
+ GHC.Data.Graph.Inductive.Graph: labNodes :: Graph gr => gr a b -> [LNode a]
+ GHC.Data.Graph.Inductive.Graph: labfilter :: DynGraph gr => (a -> Bool) -> gr a b -> gr a b
+ GHC.Data.Graph.Inductive.Graph: labnfilter :: Graph gr => (LNode a -> Bool) -> gr a b -> gr a b
+ GHC.Data.Graph.Inductive.Graph: lneighbors :: Graph gr => gr a b -> Node -> Adj b
+ GHC.Data.Graph.Inductive.Graph: lneighbors' :: Context a b -> Adj b
+ GHC.Data.Graph.Inductive.Graph: lpre :: Graph gr => gr a b -> Node -> [(Node, b)]
+ GHC.Data.Graph.Inductive.Graph: lpre' :: Context a b -> [(Node, b)]
+ GHC.Data.Graph.Inductive.Graph: lsuc :: Graph gr => gr a b -> Node -> [(Node, b)]
+ GHC.Data.Graph.Inductive.Graph: lsuc' :: Context a b -> [(Node, b)]
+ GHC.Data.Graph.Inductive.Graph: match :: Graph gr => Node -> gr a b -> Decomp gr a b
+ GHC.Data.Graph.Inductive.Graph: matchAny :: Graph gr => gr a b -> GDecomp gr a b
+ GHC.Data.Graph.Inductive.Graph: mkGraph :: Graph gr => [LNode a] -> [LEdge b] -> gr a b
+ GHC.Data.Graph.Inductive.Graph: mkUGraph :: Graph gr => [Node] -> [Edge] -> gr () ()
+ GHC.Data.Graph.Inductive.Graph: neighbors :: Graph gr => gr a b -> Node -> [Node]
+ GHC.Data.Graph.Inductive.Graph: neighbors' :: Context a b -> [Node]
+ GHC.Data.Graph.Inductive.Graph: nemap :: DynGraph gr => (a -> c) -> (b -> d) -> gr a b -> gr c d
+ GHC.Data.Graph.Inductive.Graph: newNodes :: Graph gr => Int -> gr a b -> [Node]
+ GHC.Data.Graph.Inductive.Graph: newtype LPath a
+ GHC.Data.Graph.Inductive.Graph: newtype OrdGr gr a b
+ GHC.Data.Graph.Inductive.Graph: nfilter :: DynGraph gr => (Node -> Bool) -> gr a b -> gr a b
+ GHC.Data.Graph.Inductive.Graph: nmap :: DynGraph gr => (a -> c) -> gr a b -> gr c b
+ GHC.Data.Graph.Inductive.Graph: noNodes :: Graph gr => gr a b -> Int
+ GHC.Data.Graph.Inductive.Graph: node' :: Context a b -> Node
+ GHC.Data.Graph.Inductive.Graph: nodeRange :: Graph gr => gr a b -> (Node, Node)
+ GHC.Data.Graph.Inductive.Graph: nodes :: Graph gr => gr a b -> [Node]
+ GHC.Data.Graph.Inductive.Graph: order :: Graph gr => gr a b -> Int
+ GHC.Data.Graph.Inductive.Graph: out :: Graph gr => gr a b -> Node -> [LEdge b]
+ GHC.Data.Graph.Inductive.Graph: out' :: Context a b -> [LEdge b]
+ GHC.Data.Graph.Inductive.Graph: outdeg :: Graph gr => gr a b -> Node -> Int
+ GHC.Data.Graph.Inductive.Graph: outdeg' :: Context a b -> Int
+ GHC.Data.Graph.Inductive.Graph: pre :: Graph gr => gr a b -> Node -> [Node]
+ GHC.Data.Graph.Inductive.Graph: pre' :: Context a b -> [Node]
+ GHC.Data.Graph.Inductive.Graph: prettify :: (DynGraph gr, Show a, Show b) => gr a b -> String
+ GHC.Data.Graph.Inductive.Graph: prettyPrint :: (DynGraph gr, Show a, Show b) => gr a b -> IO ()
+ GHC.Data.Graph.Inductive.Graph: size :: Graph gr => gr a b -> Int
+ GHC.Data.Graph.Inductive.Graph: subgraph :: DynGraph gr => [Node] -> gr a b -> gr a b
+ GHC.Data.Graph.Inductive.Graph: suc :: Graph gr => gr a b -> Node -> [Node]
+ GHC.Data.Graph.Inductive.Graph: suc' :: Context a b -> [Node]
+ GHC.Data.Graph.Inductive.Graph: toEdge :: LEdge b -> Edge
+ GHC.Data.Graph.Inductive.Graph: toLEdge :: Edge -> b -> LEdge b
+ GHC.Data.Graph.Inductive.Graph: type Adj b = [(b, Node)]
+ GHC.Data.Graph.Inductive.Graph: type Context a b = (Adj b, Node, a, Adj b)
+ GHC.Data.Graph.Inductive.Graph: type Decomp g a b = (MContext a b, g a b)
+ GHC.Data.Graph.Inductive.Graph: type Edge = (Node, Node)
+ GHC.Data.Graph.Inductive.Graph: type GDecomp g a b = (Context a b, g a b)
+ GHC.Data.Graph.Inductive.Graph: type LEdge b = (Node, Node, b)
+ GHC.Data.Graph.Inductive.Graph: type LNode a = (Node, a)
+ GHC.Data.Graph.Inductive.Graph: type MContext a b = Maybe (Context a b)
+ GHC.Data.Graph.Inductive.Graph: type Node = Int
+ GHC.Data.Graph.Inductive.Graph: type Path = [Node]
+ GHC.Data.Graph.Inductive.Graph: type UContext = ([Node], Node, [Node])
+ GHC.Data.Graph.Inductive.Graph: type UDecomp g = (Maybe UContext, g)
+ GHC.Data.Graph.Inductive.Graph: type UEdge = LEdge ()
+ GHC.Data.Graph.Inductive.Graph: type UNode = LNode ()
+ GHC.Data.Graph.Inductive.Graph: type UPath = [UNode]
+ GHC.Data.Graph.Inductive.Graph: ufold :: Graph gr => (Context a b -> c -> c) -> c -> gr a b -> c
+ GHC.Data.Graph.Inductive.PatriciaTree: data Gr a b
+ GHC.Data.Graph.Inductive.PatriciaTree: instance (GHC.Classes.Eq a, GHC.Classes.Ord b) => GHC.Classes.Eq (GHC.Data.Graph.Inductive.PatriciaTree.Gr a b)
+ GHC.Data.Graph.Inductive.PatriciaTree: instance (GHC.Read.Read a, GHC.Read.Read b) => GHC.Read.Read (GHC.Data.Graph.Inductive.PatriciaTree.Gr a b)
+ GHC.Data.Graph.Inductive.PatriciaTree: instance (GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (GHC.Data.Graph.Inductive.PatriciaTree.Gr a b)
+ GHC.Data.Graph.Inductive.PatriciaTree: instance Data.Bifunctor.Bifunctor GHC.Data.Graph.Inductive.PatriciaTree.Gr
+ GHC.Data.Graph.Inductive.PatriciaTree: instance GHC.Base.Functor (GHC.Data.Graph.Inductive.PatriciaTree.Gr a)
+ GHC.Data.Graph.Inductive.PatriciaTree: instance GHC.Classes.Eq a => GHC.Classes.Eq (GHC.Data.Graph.Inductive.PatriciaTree.FromListCounting a)
+ GHC.Data.Graph.Inductive.PatriciaTree: instance GHC.Data.Graph.Inductive.Graph.DynGraph GHC.Data.Graph.Inductive.PatriciaTree.Gr
+ GHC.Data.Graph.Inductive.PatriciaTree: instance GHC.Data.Graph.Inductive.Graph.Graph GHC.Data.Graph.Inductive.PatriciaTree.Gr
+ GHC.Data.Graph.Inductive.PatriciaTree: instance GHC.Generics.Generic (GHC.Data.Graph.Inductive.PatriciaTree.Gr a b)
+ GHC.Data.Graph.Inductive.PatriciaTree: instance GHC.Read.Read a => GHC.Read.Read (GHC.Data.Graph.Inductive.PatriciaTree.FromListCounting a)
+ GHC.Data.Graph.Inductive.PatriciaTree: instance GHC.Show.Show a => GHC.Show.Show (GHC.Data.Graph.Inductive.PatriciaTree.FromListCounting a)
+ GHC.Data.Graph.Inductive.PatriciaTree: type UGr = Gr () ()
+ GHC.Driver.Backpack: doBackpack :: [FilePath] -> Ghc ()
+ GHC.Driver.Backpack: instance GHC.Classes.Eq GHC.Driver.Backpack.SessionType
+ GHC.Driver.Backpack: instance GHC.Driver.Monad.GhcMonad GHC.Driver.Backpack.BkpM
+ GHC.Driver.Backpack: instance GHC.Driver.Session.HasDynFlags GHC.Driver.Backpack.BkpM
+ GHC.Driver.Backpack: instance GHC.Utils.Logger.HasLogger GHC.Driver.Backpack.BkpM
+ GHC.Driver.Config.Cmm.Parser: initCmmParserConfig :: DynFlags -> CmmParserConfig
+ GHC.Driver.Config.Core.Lint.Interactive: lintInteractiveExpr :: SDoc -> HscEnv -> CoreExpr -> IO ()
+ GHC.Driver.Config.Core.Opt.Arity: initArityOpts :: DynFlags -> ArityOpts
+ GHC.Driver.Config.Core.Opt.LiberateCase: initLiberateCaseOpts :: DynFlags -> LibCaseOpts
+ GHC.Driver.Config.Core.Opt.Simplify: initGentleSimplMode :: DynFlags -> SimplMode
+ GHC.Driver.Config.Core.Opt.Simplify: initSimplMode :: DynFlags -> CompilerPhase -> String -> SimplMode
+ GHC.Driver.Config.Core.Opt.Simplify: initSimplifyExprOpts :: DynFlags -> InteractiveContext -> SimplifyExprOpts
+ GHC.Driver.Config.Core.Opt.Simplify: initSimplifyOpts :: DynFlags -> [Var] -> Int -> SimplMode -> RuleBase -> SimplifyOpts
+ GHC.Driver.Config.Core.Opt.WorkWrap: initWorkWrapOpts :: Module -> DynFlags -> FamInstEnvs -> WwOpts
+ GHC.Driver.Config.Core.Rules: initRuleOpts :: DynFlags -> RuleOpts
+ GHC.Driver.Config.CoreToStg: initCoreToStgOpts :: DynFlags -> CoreToStgOpts
+ GHC.Driver.Config.CoreToStg.Prep: initCorePrepConfig :: HscEnv -> IO CorePrepConfig
+ GHC.Driver.Config.CoreToStg.Prep: initCorePrepPgmConfig :: DynFlags -> [Var] -> CorePrepPgmConfig
+ GHC.Driver.Config.HsToCore.Ticks: initTicksConfig :: DynFlags -> TicksConfig
+ GHC.Driver.Config.HsToCore.Usage: initUsageConfig :: HscEnv -> UsageConfig
+ GHC.Driver.Config.Linker: initFrameworkOpts :: DynFlags -> FrameworkOpts
+ GHC.Driver.Config.StgToJS: initStgToJSConfig :: DynFlags -> StgToJSConfig
+ GHC.Driver.Main: data CgInteractiveGuts
+ GHC.Driver.Main: generateByteCode :: HscEnv -> CgInteractiveGuts -> ModLocation -> IO [Unlinked]
+ GHC.Driver.Main: generateFreshByteCode :: HscEnv -> ModuleName -> CgInteractiveGuts -> ModLocation -> IO Linkable
+ GHC.Driver.Main: initHscEnv :: Maybe FilePath -> IO HscEnv
+ GHC.Driver.Main: initWholeCoreBindings :: HscEnv -> ModIface -> ModDetails -> Linkable -> IO Linkable
+ GHC.Driver.Main: mkCgInteractiveGuts :: CgGuts -> CgInteractiveGuts
+ GHC.Driver.Make: instance GHC.Classes.Eq GHC.Driver.Make.CodeGenEnable
+ GHC.Driver.Make: instance GHC.Classes.Ord GHC.Driver.Make.CodeGenEnable
+ GHC.Driver.Make: instance GHC.Show.Show GHC.Driver.Make.CodeGenEnable
+ GHC.Driver.Make: instance GHC.Show.Show GHC.Driver.Make.ResultLoopOrigin
+ GHC.Driver.Make: instance GHC.Show.Show GHC.Driver.Make.ResultOrigin
+ GHC.Driver.Make: instance GHC.Utils.Outputable.Outputable GHC.Driver.Make.CodeGenEnable
+ GHC.Driver.MakeFile: doMkDependHS :: GhcMonad m => [FilePath] -> m ()
+ GHC.Driver.Pipeline: [T_ForeignJs] :: PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> TPhase [Char]
+ GHC.Driver.Pipeline: [T_Js] :: PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> TPhase [Char]
+ GHC.Driver.Pipeline: jsPipeline :: P m => PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> m FilePath
+ GHC.Driver.Pipeline.Execute: applyAssemblerInfoGetter :: DefunctionalizedAssemblerInfoGetter -> Logger -> DynFlags -> Platform -> IO CompilerInfo
+ GHC.Driver.Pipeline.Execute: applyAssemblerProg :: DefunctionalizedAssemblerProg -> Logger -> DynFlags -> Platform -> [Option] -> IO ()
+ GHC.Driver.Pipeline.Execute: runForeignJsPhase :: PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> IO FilePath
+ GHC.Driver.Pipeline.Execute: runJsPhase :: PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> IO FilePath
+ GHC.HandleEncoding: configureHandleEncoding :: IO ()
+ GHC.HsToCore.Binds: dsWarnOrphanRule :: CoreRule -> DsM ()
+ GHC.HsToCore.Breakpoints: mkModBreaks :: Interp -> Module -> SizedSeq Tick -> IO ModBreaks
+ GHC.HsToCore.Coverage: writeMixEntries :: FilePath -> Module -> SizedSeq Tick -> FilePath -> IO Int
+ GHC.HsToCore.Foreign.C: dsCFExport :: Id -> Coercion -> CLabelString -> CCallConv -> Bool -> DsM (CHeader, CStub, String, Int)
+ GHC.HsToCore.Foreign.C: dsCFExportDynamic :: Id -> Coercion -> CCallConv -> DsM ([Binding], CHeader, CStub)
+ GHC.HsToCore.Foreign.C: dsCImport :: Id -> Coercion -> CImportSpec -> CCallConv -> Safety -> Maybe Header -> DsM ([Binding], CHeader, CStub)
+ GHC.HsToCore.Foreign.JavaScript: dsJsFExport :: Id -> Coercion -> CLabelString -> CCallConv -> Bool -> DsM (CHeader, CStub, String, Int)
+ GHC.HsToCore.Foreign.JavaScript: dsJsFExportDynamic :: Id -> Coercion -> CCallConv -> DsM ([Binding], CHeader, CStub)
+ GHC.HsToCore.Foreign.JavaScript: dsJsImport :: Id -> Coercion -> CImportSpec -> CCallConv -> Safety -> Maybe Header -> DsM ([Binding], CHeader, CStub)
+ GHC.HsToCore.Foreign.Prim: dsPrimCall :: Id -> Coercion -> ForeignCall -> DsM ([(Id, Expr TyVar)], CHeader, CStub)
+ GHC.HsToCore.Foreign.Utils: getPrimTyOf :: Type -> UnaryType
+ GHC.HsToCore.Foreign.Utils: ppPrimTyConStgType :: TyCon -> Maybe String
+ GHC.HsToCore.Foreign.Utils: primTyDescChar :: Platform -> Type -> Char
+ GHC.HsToCore.Foreign.Utils: type Binding = (Id, CoreExpr)
+ GHC.HsToCore.Monad: mkNamePprCtxDs :: DsM NamePprCtx
+ GHC.HsToCore.Ticks: Breakpoints :: TickishType
+ GHC.HsToCore.Ticks: HpcTicks :: TickishType
+ GHC.HsToCore.Ticks: ProfNotes :: TickishType
+ GHC.HsToCore.Ticks: SourceNotes :: TickishType
+ GHC.HsToCore.Ticks: Tick :: SrcSpan -> [String] -> [OccName] -> BoxLabel -> Tick
+ GHC.HsToCore.Ticks: TicksConfig :: ![TickishType] -> !ProfAuto -> !Bool -> TicksConfig
+ GHC.HsToCore.Ticks: [tick_ids] :: Tick -> [OccName]
+ GHC.HsToCore.Ticks: [tick_label] :: Tick -> BoxLabel
+ GHC.HsToCore.Ticks: [tick_loc] :: Tick -> SrcSpan
+ GHC.HsToCore.Ticks: [tick_path] :: Tick -> [String]
+ GHC.HsToCore.Ticks: [ticks_countEntries] :: TicksConfig -> !Bool
+ GHC.HsToCore.Ticks: [ticks_passes] :: TicksConfig -> ![TickishType]
+ GHC.HsToCore.Ticks: [ticks_profAuto] :: TicksConfig -> !ProfAuto
+ GHC.HsToCore.Ticks: addTicksToBinds :: Logger -> TicksConfig -> Module -> ModLocation -> NameSet -> [TyCon] -> LHsBinds GhcTc -> IO (LHsBinds GhcTc, Maybe (FilePath, SizedSeq Tick))
+ GHC.HsToCore.Ticks: data Tick
+ GHC.HsToCore.Ticks: data TickishType
+ GHC.HsToCore.Ticks: data TicksConfig
+ GHC.HsToCore.Ticks: instance GHC.Base.Applicative GHC.HsToCore.Ticks.TM
+ GHC.HsToCore.Ticks: instance GHC.Base.Functor GHC.HsToCore.Ticks.TM
+ GHC.HsToCore.Ticks: instance GHC.Base.Monad GHC.HsToCore.Ticks.TM
+ GHC.HsToCore.Ticks: instance GHC.Classes.Eq GHC.HsToCore.Ticks.TickDensity
+ GHC.HsToCore.Ticks: instance GHC.Classes.Eq GHC.HsToCore.Ticks.TickishType
+ GHC.HsToCore.Ticks: isGoodSrcSpan' :: SrcSpan -> Bool
+ GHC.HsToCore.Ticks: stripTicksTopHsExpr :: HsExpr GhcTc -> ([CoreTickish], HsExpr GhcTc)
+ GHC.HsToCore.Types: [ds_name_ppr_ctx] :: DsGblEnv -> NamePprCtx
+ GHC.HsToCore.Usage: UsageConfig :: !Bool -> UsageConfig
+ GHC.HsToCore.Usage: [uc_safe_implicit_imps_req] :: UsageConfig -> !Bool
+ GHC.HsToCore.Usage: data UsageConfig
+ GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.HasLoc a => GHC.Iface.Ext.Ast.HasLoc (Language.Haskell.Syntax.Decls.DataDefnCons a)
+ GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.HiePass p => GHC.Iface.Ext.Ast.ToHie (GHC.Parser.Annotation.LocatedA (Language.Haskell.Syntax.Expr.HsUntypedSplice (GHC.Hs.Extension.GhcPass p)))
+ GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.ToHie (GHC.Iface.Ext.Ast.IEContext (GHC.Parser.Annotation.LocatedA (Language.Haskell.Syntax.ImpExp.IE GHC.Hs.Extension.GhcRn)))
+ GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.ToHie (GHC.Iface.Ext.Ast.IEContext (GHC.Parser.Annotation.LocatedA (Language.Haskell.Syntax.ImpExp.IEWrappedName GHC.Hs.Extension.GhcRn)))
+ GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.ToHie (GHC.Iface.Ext.Ast.IEContext (GHC.Parser.Annotation.LocatedA Language.Haskell.Syntax.Module.Name.ModuleName))
+ GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.ToHie (GHC.Parser.Annotation.LocatedA (Language.Haskell.Syntax.ImpExp.ImportDecl GHC.Hs.Extension.GhcRn))
+ GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.ToHie (Language.Haskell.Syntax.Decls.ForeignExport GHC.Hs.Extension.GhcRn)
+ GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.ToHie (Language.Haskell.Syntax.Decls.ForeignImport GHC.Hs.Extension.GhcRn)
+ GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.ToHie (Language.Haskell.Syntax.Expr.HsQuote GHC.Hs.Extension.GhcRn)
+ GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.ToHie a => GHC.Iface.Ext.Ast.ToHie (GHC.Base.NonEmpty a)
+ GHC.Iface.Ext.Ast: instance GHC.Iface.Ext.Ast.ToHie a => GHC.Iface.Ext.Ast.ToHie (Language.Haskell.Syntax.Decls.DataDefnCons a)
+ GHC.Iface.Recomp: instance GHC.Utils.Outputable.Outputable a => GHC.Utils.Outputable.Outputable (GHC.Iface.Recomp.MaybeValidated a)
+ GHC.IfaceToCore: tcTopIfaceBindings :: IORef TypeEnv -> [IfaceBindingX IfaceMaybeRhs IfaceTopBndrInfo] -> IfL [CoreBind]
+ GHC.IfaceToCore: typecheckWholeCoreBindings :: IORef TypeEnv -> WholeCoreBindings -> IfG [CoreBind]
+ GHC.JS.Make: (.!) :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: (.!=.) :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: (.!==.) :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: (.&&.) :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: (.<.) :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: (.<<.) :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: (.<=.) :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: (.==.) :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: (.===.) :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: (.>.) :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: (.>=.) :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: (.>>.) :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: (.>>>.) :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: (.^) :: JExpr -> FastString -> JExpr
+ GHC.JS.Make: (.|.) :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: (.||.) :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: (|=) :: JExpr -> JExpr -> JStat
+ GHC.JS.Make: (||=) :: Ident -> JExpr -> JStat
+ GHC.JS.Make: allocClsA :: Int -> JExpr
+ GHC.JS.Make: allocData :: Int -> JExpr
+ GHC.JS.Make: app :: FastString -> [JExpr] -> JExpr
+ GHC.JS.Make: appS :: FastString -> [JExpr] -> JStat
+ GHC.JS.Make: assignAll :: [JExpr] -> [JExpr] -> JStat
+ GHC.JS.Make: assignAllEqual :: HasDebugCallStack => [JExpr] -> [JExpr] -> JStat
+ GHC.JS.Make: assignAllReverseOrder :: [JExpr] -> [JExpr] -> JStat
+ GHC.JS.Make: class ToJExpr a
+ GHC.JS.Make: class ToStat a
+ GHC.JS.Make: dataFieldName :: Int -> FastString
+ GHC.JS.Make: dataFieldNames :: [FastString]
+ GHC.JS.Make: decl :: Ident -> JStat
+ GHC.JS.Make: declAssignAll :: [Ident] -> [JExpr] -> JStat
+ GHC.JS.Make: false_ :: JExpr
+ GHC.JS.Make: if01 :: JExpr -> JExpr
+ GHC.JS.Make: if10 :: JExpr -> JExpr
+ GHC.JS.Make: ifBlockS :: JExpr -> [JStat] -> [JStat] -> JStat
+ GHC.JS.Make: ifS :: JExpr -> JStat -> JStat -> JStat
+ GHC.JS.Make: if_ :: JExpr -> JExpr -> JExpr -> JExpr
+ GHC.JS.Make: infixl 2 |=
+ GHC.JS.Make: infixl 6 .!==.
+ GHC.JS.Make: infixl 7 .<=.
+ GHC.JS.Make: infixl 8 .^
+ GHC.JS.Make: infixl 9 .>>>.
+ GHC.JS.Make: instance (GHC.JS.Make.ToJExpr a, GHC.JS.Make.ToJExpr b) => GHC.JS.Make.ToJExpr (a, b)
+ GHC.JS.Make: instance (GHC.JS.Make.ToJExpr a, GHC.JS.Make.ToJExpr b, GHC.JS.Make.ToJExpr c) => GHC.JS.Make.ToJExpr (a, b, c)
+ GHC.JS.Make: instance (GHC.JS.Make.ToJExpr a, GHC.JS.Make.ToJExpr b, GHC.JS.Make.ToJExpr c, GHC.JS.Make.ToJExpr d) => GHC.JS.Make.ToJExpr (a, b, c, d)
+ GHC.JS.Make: instance (GHC.JS.Make.ToJExpr a, GHC.JS.Make.ToJExpr b, GHC.JS.Make.ToJExpr c, GHC.JS.Make.ToJExpr d, GHC.JS.Make.ToJExpr e) => GHC.JS.Make.ToJExpr (a, b, c, d, e)
+ GHC.JS.Make: instance (GHC.JS.Make.ToJExpr a, GHC.JS.Make.ToJExpr b, GHC.JS.Make.ToJExpr c, GHC.JS.Make.ToJExpr d, GHC.JS.Make.ToJExpr e, GHC.JS.Make.ToJExpr f) => GHC.JS.Make.ToJExpr (a, b, c, d, e, f)
+ GHC.JS.Make: instance (GHC.JS.Make.ToSat a, b GHC.Types.~ GHC.JS.Syntax.JExpr) => GHC.JS.Make.ToSat (b -> a)
+ GHC.JS.Make: instance GHC.JS.Make.ToJExpr ()
+ GHC.JS.Make: instance GHC.JS.Make.ToJExpr GHC.Data.FastString.FastString
+ GHC.JS.Make: instance GHC.JS.Make.ToJExpr GHC.JS.Syntax.Ident
+ GHC.JS.Make: instance GHC.JS.Make.ToJExpr GHC.JS.Syntax.JExpr
+ GHC.JS.Make: instance GHC.JS.Make.ToJExpr GHC.JS.Syntax.JVal
+ GHC.JS.Make: instance GHC.JS.Make.ToJExpr GHC.Num.Integer.Integer
+ GHC.JS.Make: instance GHC.JS.Make.ToJExpr GHC.Types.Bool
+ GHC.JS.Make: instance GHC.JS.Make.ToJExpr GHC.Types.Char
+ GHC.JS.Make: instance GHC.JS.Make.ToJExpr GHC.Types.Double
+ GHC.JS.Make: instance GHC.JS.Make.ToJExpr GHC.Types.Int
+ GHC.JS.Make: instance GHC.JS.Make.ToJExpr a => GHC.JS.Make.ToJExpr (Data.Map.Internal.Map GHC.Base.String a)
+ GHC.JS.Make: instance GHC.JS.Make.ToJExpr a => GHC.JS.Make.ToJExpr (GHC.Types.Unique.Map.UniqMap GHC.Data.FastString.FastString a)
+ GHC.JS.Make: instance GHC.JS.Make.ToJExpr a => GHC.JS.Make.ToJExpr [a]
+ GHC.JS.Make: instance GHC.JS.Make.ToSat GHC.JS.Syntax.JExpr
+ GHC.JS.Make: instance GHC.JS.Make.ToSat GHC.JS.Syntax.JStat
+ GHC.JS.Make: instance GHC.JS.Make.ToSat [GHC.JS.Syntax.JExpr]
+ GHC.JS.Make: instance GHC.JS.Make.ToSat [GHC.JS.Syntax.JStat]
+ GHC.JS.Make: instance GHC.JS.Make.ToStat GHC.JS.Syntax.JExpr
+ GHC.JS.Make: instance GHC.JS.Make.ToStat GHC.JS.Syntax.JStat
+ GHC.JS.Make: instance GHC.JS.Make.ToStat [GHC.JS.Syntax.JExpr]
+ GHC.JS.Make: instance GHC.JS.Make.ToStat [GHC.JS.Syntax.JStat]
+ GHC.JS.Make: instance GHC.Num.Num GHC.JS.Syntax.JExpr
+ GHC.JS.Make: instance GHC.Real.Fractional GHC.JS.Syntax.JExpr
+ GHC.JS.Make: jFor :: (ToJExpr a, ToStat b) => JStat -> a -> JStat -> b -> JStat
+ GHC.JS.Make: jForEachIn :: ToSat a => JExpr -> (JExpr -> a) -> JStat
+ GHC.JS.Make: jForIn :: ToSat a => JExpr -> (JExpr -> a) -> JStat
+ GHC.JS.Make: jLam :: ToSat a => a -> JExpr
+ GHC.JS.Make: jString :: FastString -> JExpr
+ GHC.JS.Make: jTryCatchFinally :: ToSat a => JStat -> a -> JStat -> JStat
+ GHC.JS.Make: jVar :: ToSat a => a -> JStat
+ GHC.JS.Make: jhAdd :: (Ord k, ToJExpr a) => k -> a -> Map k JExpr -> Map k JExpr
+ GHC.JS.Make: jhEmpty :: Map k JExpr
+ GHC.JS.Make: jhFromList :: [(FastString, JExpr)] -> JVal
+ GHC.JS.Make: jhSingle :: (Ord k, ToJExpr a) => k -> a -> Map k JExpr
+ GHC.JS.Make: jwhenS :: JExpr -> JStat -> JStat
+ GHC.JS.Make: loop :: JExpr -> (JExpr -> JExpr) -> (JExpr -> JStat) -> JStat
+ GHC.JS.Make: loopBlockS :: JExpr -> (JExpr -> JExpr) -> (JExpr -> [JStat]) -> JStat
+ GHC.JS.Make: mask16 :: JExpr -> JExpr
+ GHC.JS.Make: mask8 :: JExpr -> JExpr
+ GHC.JS.Make: math_abs :: [JExpr] -> JExpr
+ GHC.JS.Make: math_acos :: [JExpr] -> JExpr
+ GHC.JS.Make: math_acosh :: [JExpr] -> JExpr
+ GHC.JS.Make: math_asin :: [JExpr] -> JExpr
+ GHC.JS.Make: math_asinh :: [JExpr] -> JExpr
+ GHC.JS.Make: math_atan :: [JExpr] -> JExpr
+ GHC.JS.Make: math_atanh :: [JExpr] -> JExpr
+ GHC.JS.Make: math_cos :: [JExpr] -> JExpr
+ GHC.JS.Make: math_cosh :: [JExpr] -> JExpr
+ GHC.JS.Make: math_exp :: [JExpr] -> JExpr
+ GHC.JS.Make: math_expm1 :: [JExpr] -> JExpr
+ GHC.JS.Make: math_fround :: [JExpr] -> JExpr
+ GHC.JS.Make: math_log :: [JExpr] -> JExpr
+ GHC.JS.Make: math_log1p :: [JExpr] -> JExpr
+ GHC.JS.Make: math_pow :: [JExpr] -> JExpr
+ GHC.JS.Make: math_sin :: [JExpr] -> JExpr
+ GHC.JS.Make: math_sinh :: [JExpr] -> JExpr
+ GHC.JS.Make: math_sqrt :: [JExpr] -> JExpr
+ GHC.JS.Make: math_tan :: [JExpr] -> JExpr
+ GHC.JS.Make: math_tanh :: [JExpr] -> JExpr
+ GHC.JS.Make: nullStat :: JStat
+ GHC.JS.Make: null_ :: JExpr
+ GHC.JS.Make: off16 :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: off32 :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: off64 :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: off8 :: JExpr -> JExpr -> JExpr
+ GHC.JS.Make: one_ :: JExpr
+ GHC.JS.Make: postDecrS :: JExpr -> JStat
+ GHC.JS.Make: postIncrS :: JExpr -> JStat
+ GHC.JS.Make: preDecrS :: JExpr -> JStat
+ GHC.JS.Make: preIncrS :: JExpr -> JStat
+ GHC.JS.Make: returnS :: JExpr -> JStat
+ GHC.JS.Make: returnStack :: JStat
+ GHC.JS.Make: signExtend16 :: JExpr -> JExpr
+ GHC.JS.Make: signExtend8 :: JExpr -> JExpr
+ GHC.JS.Make: three_ :: JExpr
+ GHC.JS.Make: toJExpr :: ToJExpr a => a -> JExpr
+ GHC.JS.Make: toJExprFromList :: ToJExpr a => [a] -> JExpr
+ GHC.JS.Make: toStat :: ToStat a => a -> JStat
+ GHC.JS.Make: trace :: ToJExpr a => a -> JStat
+ GHC.JS.Make: true_ :: JExpr
+ GHC.JS.Make: two_ :: JExpr
+ GHC.JS.Make: typeof :: JExpr -> JExpr
+ GHC.JS.Make: undefined_ :: JExpr
+ GHC.JS.Make: var :: FastString -> JExpr
+ GHC.JS.Make: zero_ :: JExpr
+ GHC.JS.Ppr: RenderJs :: !RenderJs -> JStat -> Doc -> !RenderJs -> JExpr -> Doc -> !RenderJs -> JVal -> Doc -> !RenderJs -> Ident -> Doc -> RenderJs
+ GHC.JS.Ppr: [renderJsE] :: RenderJs -> !RenderJs -> JExpr -> Doc
+ GHC.JS.Ppr: [renderJsI] :: RenderJs -> !RenderJs -> Ident -> Doc
+ GHC.JS.Ppr: [renderJsS] :: RenderJs -> !RenderJs -> JStat -> Doc
+ GHC.JS.Ppr: [renderJsV] :: RenderJs -> !RenderJs -> JVal -> Doc
+ GHC.JS.Ppr: braceNest :: Doc -> Doc
+ GHC.JS.Ppr: class JsToDoc a
+ GHC.JS.Ppr: data RenderJs
+ GHC.JS.Ppr: defaultRenderJs :: RenderJs
+ GHC.JS.Ppr: flattenBlocks :: [JStat] -> [JStat]
+ GHC.JS.Ppr: hangBrace :: Doc -> Doc -> Doc
+ GHC.JS.Ppr: instance GHC.JS.Ppr.JsToDoc GHC.JS.Syntax.Ident
+ GHC.JS.Ppr: instance GHC.JS.Ppr.JsToDoc GHC.JS.Syntax.JExpr
+ GHC.JS.Ppr: instance GHC.JS.Ppr.JsToDoc GHC.JS.Syntax.JStat
+ GHC.JS.Ppr: instance GHC.JS.Ppr.JsToDoc GHC.JS.Syntax.JVal
+ GHC.JS.Ppr: instance GHC.JS.Ppr.JsToDoc [GHC.JS.Syntax.JExpr]
+ GHC.JS.Ppr: instance GHC.JS.Ppr.JsToDoc [GHC.JS.Syntax.JStat]
+ GHC.JS.Ppr: instance GHC.Utils.Outputable.Outputable GHC.JS.Syntax.JExpr
+ GHC.JS.Ppr: instance GHC.Utils.Outputable.Outputable GHC.JS.Syntax.JVal
+ GHC.JS.Ppr: jsToDoc :: JsToDoc a => a -> Doc
+ GHC.JS.Ppr: jsToDocR :: JsToDoc a => RenderJs -> a -> Doc
+ GHC.JS.Ppr: pprStringLit :: FastString -> Doc
+ GHC.JS.Ppr: renderJs :: (JsToDoc a, JMacro a) => a -> Doc
+ GHC.JS.Ppr: renderJs' :: (JsToDoc a, JMacro a) => RenderJs -> a -> Doc
+ GHC.JS.Ppr: renderPrefixJs :: (JsToDoc a, JMacro a) => FastString -> a -> Doc
+ GHC.JS.Ppr: renderPrefixJs' :: (JsToDoc a, JMacro a) => RenderJs -> FastString -> a -> Doc
+ GHC.JS.Syntax: AddOp :: JOp
+ GHC.JS.Syntax: ApplExpr :: JExpr -> [JExpr] -> JExpr
+ GHC.JS.Syntax: ApplStat :: JExpr -> [JExpr] -> JStat
+ GHC.JS.Syntax: AssignStat :: JExpr -> JExpr -> JStat
+ GHC.JS.Syntax: BAndOp :: JOp
+ GHC.JS.Syntax: BNotOp :: JUOp
+ GHC.JS.Syntax: BOrOp :: JOp
+ GHC.JS.Syntax: BXorOp :: JOp
+ GHC.JS.Syntax: BlockStat :: [JStat] -> JStat
+ GHC.JS.Syntax: BreakStat :: Maybe JsLabel -> JStat
+ GHC.JS.Syntax: ContinueStat :: Maybe JsLabel -> JStat
+ GHC.JS.Syntax: DeclStat :: !Ident -> !Maybe JExpr -> JStat
+ GHC.JS.Syntax: DeleteOp :: JUOp
+ GHC.JS.Syntax: DivOp :: JOp
+ GHC.JS.Syntax: EqOp :: JOp
+ GHC.JS.Syntax: ForInStat :: Bool -> Ident -> JExpr -> JStat -> JStat
+ GHC.JS.Syntax: GeOp :: JOp
+ GHC.JS.Syntax: GtOp :: JOp
+ GHC.JS.Syntax: IS :: State [Ident] a -> IdentSupply a
+ GHC.JS.Syntax: IdxExpr :: JExpr -> JExpr -> JExpr
+ GHC.JS.Syntax: IfExpr :: JExpr -> JExpr -> JExpr -> JExpr
+ GHC.JS.Syntax: IfStat :: JExpr -> JStat -> JStat -> JStat
+ GHC.JS.Syntax: InOp :: JOp
+ GHC.JS.Syntax: InfixExpr :: JOp -> JExpr -> JExpr -> JExpr
+ GHC.JS.Syntax: InstanceofOp :: JOp
+ GHC.JS.Syntax: JDouble :: SaneDouble -> JVal
+ GHC.JS.Syntax: JFunc :: [Ident] -> JStat -> JVal
+ GHC.JS.Syntax: JHash :: UniqMap FastString JExpr -> JVal
+ GHC.JS.Syntax: JInt :: Integer -> JVal
+ GHC.JS.Syntax: JList :: [JExpr] -> JVal
+ GHC.JS.Syntax: JRegEx :: FastString -> JVal
+ GHC.JS.Syntax: JStr :: FastString -> JVal
+ GHC.JS.Syntax: JVar :: Ident -> JVal
+ GHC.JS.Syntax: LAndOp :: JOp
+ GHC.JS.Syntax: LOrOp :: JOp
+ GHC.JS.Syntax: LabelStat :: JsLabel -> JStat -> JStat
+ GHC.JS.Syntax: LeOp :: JOp
+ GHC.JS.Syntax: LeftShiftOp :: JOp
+ GHC.JS.Syntax: LtOp :: JOp
+ GHC.JS.Syntax: ModOp :: JOp
+ GHC.JS.Syntax: MulOp :: JOp
+ GHC.JS.Syntax: NegOp :: JUOp
+ GHC.JS.Syntax: NeqOp :: JOp
+ GHC.JS.Syntax: NewOp :: JUOp
+ GHC.JS.Syntax: NotOp :: JUOp
+ GHC.JS.Syntax: PlusOp :: JUOp
+ GHC.JS.Syntax: PostDecOp :: JUOp
+ GHC.JS.Syntax: PostIncOp :: JUOp
+ GHC.JS.Syntax: PreDecOp :: JUOp
+ GHC.JS.Syntax: PreIncOp :: JUOp
+ GHC.JS.Syntax: ReturnStat :: JExpr -> JStat
+ GHC.JS.Syntax: RightShiftOp :: JOp
+ GHC.JS.Syntax: SaneDouble :: Double -> SaneDouble
+ GHC.JS.Syntax: SelExpr :: JExpr -> Ident -> JExpr
+ GHC.JS.Syntax: StrictEqOp :: JOp
+ GHC.JS.Syntax: StrictNeqOp :: JOp
+ GHC.JS.Syntax: SubOp :: JOp
+ GHC.JS.Syntax: SwitchStat :: JExpr -> [(JExpr, JStat)] -> JStat -> JStat
+ GHC.JS.Syntax: TryStat :: JStat -> Ident -> JStat -> JStat -> JStat
+ GHC.JS.Syntax: TxtI :: FastString -> Ident
+ GHC.JS.Syntax: TypeofOp :: JUOp
+ GHC.JS.Syntax: UOpExpr :: JUOp -> JExpr -> JExpr
+ GHC.JS.Syntax: UOpStat :: JUOp -> JExpr -> JStat
+ GHC.JS.Syntax: UnsatBlock :: IdentSupply JStat -> JStat
+ GHC.JS.Syntax: UnsatExpr :: IdentSupply JExpr -> JExpr
+ GHC.JS.Syntax: UnsatVal :: IdentSupply JVal -> JVal
+ GHC.JS.Syntax: ValExpr :: JVal -> JExpr
+ GHC.JS.Syntax: VoidOp :: JUOp
+ GHC.JS.Syntax: WhileStat :: Bool -> JExpr -> JStat -> JStat
+ GHC.JS.Syntax: YieldOp :: JUOp
+ GHC.JS.Syntax: ZRightShiftOp :: JOp
+ GHC.JS.Syntax: [itxt] :: Ident -> FastString
+ GHC.JS.Syntax: [runIdentSupply] :: IdentSupply a -> State [Ident] a
+ GHC.JS.Syntax: [unSaneDouble] :: SaneDouble -> Double
+ GHC.JS.Syntax: data JExpr
+ GHC.JS.Syntax: data JOp
+ GHC.JS.Syntax: data JStat
+ GHC.JS.Syntax: data JUOp
+ GHC.JS.Syntax: data JVal
+ GHC.JS.Syntax: identFS :: Ident -> FastString
+ GHC.JS.Syntax: instance Control.DeepSeq.NFData (GHC.JS.Syntax.IdentSupply a)
+ GHC.JS.Syntax: instance Control.DeepSeq.NFData GHC.JS.Syntax.JOp
+ GHC.JS.Syntax: instance Control.DeepSeq.NFData GHC.JS.Syntax.JUOp
+ GHC.JS.Syntax: instance Control.DeepSeq.NFData GHC.JS.Syntax.SaneDouble
+ GHC.JS.Syntax: instance Data.Data.Data GHC.JS.Syntax.JOp
+ GHC.JS.Syntax: instance Data.Data.Data GHC.JS.Syntax.JUOp
+ GHC.JS.Syntax: instance Data.Data.Data GHC.JS.Syntax.SaneDouble
+ GHC.JS.Syntax: instance GHC.Base.Functor GHC.JS.Syntax.IdentSupply
+ GHC.JS.Syntax: instance GHC.Base.Monoid GHC.JS.Syntax.JStat
+ GHC.JS.Syntax: instance GHC.Base.Semigroup GHC.JS.Syntax.JStat
+ GHC.JS.Syntax: instance GHC.Classes.Eq GHC.JS.Syntax.Ident
+ GHC.JS.Syntax: instance GHC.Classes.Eq GHC.JS.Syntax.JExpr
+ GHC.JS.Syntax: instance GHC.Classes.Eq GHC.JS.Syntax.JOp
+ GHC.JS.Syntax: instance GHC.Classes.Eq GHC.JS.Syntax.JStat
+ GHC.JS.Syntax: instance GHC.Classes.Eq GHC.JS.Syntax.JUOp
+ GHC.JS.Syntax: instance GHC.Classes.Eq GHC.JS.Syntax.JVal
+ GHC.JS.Syntax: instance GHC.Classes.Eq GHC.JS.Syntax.SaneDouble
+ GHC.JS.Syntax: instance GHC.Classes.Eq a => GHC.Classes.Eq (GHC.JS.Syntax.IdentSupply a)
+ GHC.JS.Syntax: instance GHC.Classes.Ord GHC.JS.Syntax.JOp
+ GHC.JS.Syntax: instance GHC.Classes.Ord GHC.JS.Syntax.JUOp
+ GHC.JS.Syntax: instance GHC.Classes.Ord GHC.JS.Syntax.SaneDouble
+ GHC.JS.Syntax: instance GHC.Classes.Ord a => GHC.Classes.Ord (GHC.JS.Syntax.IdentSupply a)
+ GHC.JS.Syntax: instance GHC.Enum.Enum GHC.JS.Syntax.JOp
+ GHC.JS.Syntax: instance GHC.Enum.Enum GHC.JS.Syntax.JUOp
+ GHC.JS.Syntax: instance GHC.Generics.Generic GHC.JS.Syntax.JExpr
+ GHC.JS.Syntax: instance GHC.Generics.Generic GHC.JS.Syntax.JOp
+ GHC.JS.Syntax: instance GHC.Generics.Generic GHC.JS.Syntax.JStat
+ GHC.JS.Syntax: instance GHC.Generics.Generic GHC.JS.Syntax.JUOp
+ GHC.JS.Syntax: instance GHC.Generics.Generic GHC.JS.Syntax.JVal
+ GHC.JS.Syntax: instance GHC.Generics.Generic GHC.JS.Syntax.SaneDouble
+ GHC.JS.Syntax: instance GHC.Num.Num GHC.JS.Syntax.SaneDouble
+ GHC.JS.Syntax: instance GHC.Real.Fractional GHC.JS.Syntax.SaneDouble
+ GHC.JS.Syntax: instance GHC.Show.Show GHC.JS.Syntax.Ident
+ GHC.JS.Syntax: instance GHC.Show.Show GHC.JS.Syntax.JOp
+ GHC.JS.Syntax: instance GHC.Show.Show GHC.JS.Syntax.JUOp
+ GHC.JS.Syntax: instance GHC.Show.Show GHC.JS.Syntax.SaneDouble
+ GHC.JS.Syntax: instance GHC.Show.Show a => GHC.Show.Show (GHC.JS.Syntax.IdentSupply a)
+ GHC.JS.Syntax: instance GHC.Types.Unique.Uniquable GHC.JS.Syntax.Ident
+ GHC.JS.Syntax: newIdentSupply :: Maybe FastString -> [Ident]
+ GHC.JS.Syntax: newtype Ident
+ GHC.JS.Syntax: newtype IdentSupply a
+ GHC.JS.Syntax: newtype SaneDouble
+ GHC.JS.Syntax: pattern Add :: JExpr -> JExpr -> JExpr
+ GHC.JS.Syntax: pattern BAnd :: JExpr -> JExpr -> JExpr
+ GHC.JS.Syntax: pattern BNot :: JExpr -> JExpr
+ GHC.JS.Syntax: pattern BOr :: JExpr -> JExpr -> JExpr
+ GHC.JS.Syntax: pattern BXor :: JExpr -> JExpr -> JExpr
+ GHC.JS.Syntax: pattern Div :: JExpr -> JExpr -> JExpr
+ GHC.JS.Syntax: pattern Int :: Integer -> JExpr
+ GHC.JS.Syntax: pattern LAnd :: JExpr -> JExpr -> JExpr
+ GHC.JS.Syntax: pattern LOr :: JExpr -> JExpr -> JExpr
+ GHC.JS.Syntax: pattern Mod :: JExpr -> JExpr -> JExpr
+ GHC.JS.Syntax: pattern Mul :: JExpr -> JExpr -> JExpr
+ GHC.JS.Syntax: pattern Negate :: JExpr -> JExpr
+ GHC.JS.Syntax: pattern New :: JExpr -> JExpr
+ GHC.JS.Syntax: pattern Not :: JExpr -> JExpr
+ GHC.JS.Syntax: pattern PostDec :: JExpr -> JExpr
+ GHC.JS.Syntax: pattern PostInc :: JExpr -> JExpr
+ GHC.JS.Syntax: pattern PreDec :: JExpr -> JExpr
+ GHC.JS.Syntax: pattern PreInc :: JExpr -> JExpr
+ GHC.JS.Syntax: pattern String :: FastString -> JExpr
+ GHC.JS.Syntax: pattern Sub :: JExpr -> JExpr -> JExpr
+ GHC.JS.Syntax: pseudoSaturate :: IdentSupply a -> a
+ GHC.JS.Syntax: type JsLabel = LexicalFastString
+ GHC.JS.Transform: [JMGExpr] :: JExpr -> JMGadt JExpr
+ GHC.JS.Transform: [JMGId] :: Ident -> JMGadt Ident
+ GHC.JS.Transform: [JMGStat] :: JStat -> JMGadt JStat
+ GHC.JS.Transform: [JMGVal] :: JVal -> JMGadt JVal
+ GHC.JS.Transform: class Compos t
+ GHC.JS.Transform: class JMacro a
+ GHC.JS.Transform: compos :: Compos t => (forall a. a -> m a) -> (forall a b. m (a -> b) -> m a -> m b) -> (forall a. t a -> m (t a)) -> t c -> m (t c)
+ GHC.JS.Transform: composOp :: Compos t => (forall a. t a -> t a) -> t b -> t b
+ GHC.JS.Transform: composOpFold :: Compos t => b -> (b -> b -> b) -> (forall a. t a -> b) -> t c -> b
+ GHC.JS.Transform: composOpM :: (Compos t, Monad m) => (forall a. t a -> m (t a)) -> t b -> m (t b)
+ GHC.JS.Transform: composOpM_ :: (Compos t, Monad m) => (forall a. t a -> m ()) -> t b -> m ()
+ GHC.JS.Transform: data JMGadt a
+ GHC.JS.Transform: identsE :: JExpr -> [Ident]
+ GHC.JS.Transform: identsS :: JStat -> [Ident]
+ GHC.JS.Transform: identsV :: JVal -> [Ident]
+ GHC.JS.Transform: instance GHC.JS.Transform.Compos GHC.JS.Transform.JMGadt
+ GHC.JS.Transform: instance GHC.JS.Transform.JMacro GHC.JS.Syntax.Ident
+ GHC.JS.Transform: instance GHC.JS.Transform.JMacro GHC.JS.Syntax.JExpr
+ GHC.JS.Transform: instance GHC.JS.Transform.JMacro GHC.JS.Syntax.JStat
+ GHC.JS.Transform: instance GHC.JS.Transform.JMacro GHC.JS.Syntax.JVal
+ GHC.JS.Transform: jfromGADT :: JMacro a => JMGadt a -> a
+ GHC.JS.Transform: jsSaturate :: JMacro a => Maybe FastString -> a -> a
+ GHC.JS.Transform: jtoGADT :: JMacro a => a -> JMGadt a
+ GHC.JS.Transform: mapExprIdent :: (Ident -> JExpr) -> JExpr -> JExpr
+ GHC.JS.Transform: mapIdent :: (Ident -> JExpr) -> (JExpr -> JExpr, JStat -> JStat)
+ GHC.JS.Transform: mapStatIdent :: (Ident -> JExpr) -> JStat -> JStat
+ GHC.Linker.Config: FrameworkOpts :: [String] -> [String] -> FrameworkOpts
+ GHC.Linker.Config: [foCmdlineFrameworks] :: FrameworkOpts -> [String]
+ GHC.Linker.Config: [foFrameworkPaths] :: FrameworkOpts -> [String]
+ GHC.Linker.Config: data FrameworkOpts
+ GHC.Platform.Host: hostPlatformArch :: Arch
+ GHC.Platform.Host: hostPlatformArchOS :: ArchOS
+ GHC.Platform.Host: hostPlatformOS :: OS
+ GHC.Plugins: FTF_C_C :: FunTyFlag
+ GHC.Plugins: FTF_C_T :: FunTyFlag
+ GHC.Plugins: FTF_T_C :: FunTyFlag
+ GHC.Plugins: FTF_T_T :: FunTyFlag
+ GHC.Plugins: SelArg :: FunSel
+ GHC.Plugins: SelForAll :: CoSel
+ GHC.Plugins: SelFun :: FunSel -> CoSel
+ GHC.Plugins: SelMult :: FunSel
+ GHC.Plugins: SelRes :: FunSel
+ GHC.Plugins: SelTyCon :: Int -> Role -> CoSel
+ GHC.Plugins: alwaysActiveUnfoldingFun :: IdUnfoldingFun
+ GHC.Plugins: anonPiTyBinderType_maybe :: PiTyBinder -> Maybe Type
+ GHC.Plugins: appTyForAllTyFlags :: Type -> [Type] -> [ForAllTyFlag]
+ GHC.Plugins: argsHaveFixedRuntimeRep :: Type -> Bool
+ GHC.Plugins: binderFlag :: VarBndr tv argf -> argf
+ GHC.Plugins: binderFlags :: [VarBndr tv argf] -> [argf]
+ GHC.Plugins: chooseFunTyFlag :: HasDebugCallStack => Type -> Type -> FunTyFlag
+ GHC.Plugins: data CoSel
+ GHC.Plugins: data ForAllTyFlag
+ GHC.Plugins: data FunSel
+ GHC.Plugins: data FunTyFlag
+ GHC.Plugins: data PiTyBinder
+ GHC.Plugins: extendIdSubstWithClone :: Subst -> Id -> Id -> Subst
+ GHC.Plugins: extendSubstInScopeSet :: Subst -> VarSet -> Subst
+ GHC.Plugins: funRole :: Role -> FunSel -> Role
+ GHC.Plugins: funTyConAppTy_maybe :: FunTyFlag -> Type -> Type -> Type -> Maybe (TyCon, [Type])
+ GHC.Plugins: funTyFlagTyCon :: FunTyFlag -> TyCon
+ GHC.Plugins: getNthFromType :: HasDebugCallStack => CoSel -> Type -> Type
+ GHC.Plugins: getNthFun :: FunSel -> a -> a -> a -> a
+ GHC.Plugins: getSubstInScope :: Subst -> InScopeSet
+ GHC.Plugins: getSubstRangeTyCoFVs :: Subst -> VarSet
+ GHC.Plugins: isAnonPiTyBinder :: PiTyBinder -> Bool
+ GHC.Plugins: isConstraintKind :: Kind -> Bool
+ GHC.Plugins: isConstraintLikeKind :: Kind -> Bool
+ GHC.Plugins: isInvisibleForAllTyFlag :: ForAllTyFlag -> Bool
+ GHC.Plugins: isInvisiblePiTyBinder :: PiTyBinder -> Bool
+ GHC.Plugins: isManyTy :: Mult -> Bool
+ GHC.Plugins: isNamedPiTyBinder :: PiTyBinder -> Bool
+ GHC.Plugins: isOneTy :: Mult -> Bool
+ GHC.Plugins: isTYPEorCONSTRAINT :: Kind -> Bool
+ GHC.Plugins: isTypeLikeKind :: Kind -> Bool
+ GHC.Plugins: isVisibleForAllTyFlag :: ForAllTyFlag -> Bool
+ GHC.Plugins: isVisiblePiTyBinder :: PiTyBinder -> Bool
+ GHC.Plugins: kindBoxedRepLevity_maybe :: Type -> Maybe Levity
+ GHC.Plugins: lcSubst :: LiftingContext -> Subst
+ GHC.Plugins: levityType_maybe :: LevityType -> Maybe Levity
+ GHC.Plugins: lookupIdSubst_maybe :: HasDebugCallStack => Subst -> Id -> Maybe CoreExpr
+ GHC.Plugins: mkBoxedRepApp_maybe :: LevityType -> Maybe Type
+ GHC.Plugins: mkCONSTRAINTapp :: RuntimeRepType -> Type
+ GHC.Plugins: mkCONSTRAINTapp_maybe :: RuntimeRepType -> Maybe Type
+ GHC.Plugins: mkForAllTyBinder :: vis -> TyCoVar -> VarBndr TyCoVar vis
+ GHC.Plugins: mkForAllTyBinders :: vis -> [TyCoVar] -> [VarBndr TyCoVar vis]
+ GHC.Plugins: mkFunCo1 :: HasDebugCallStack => Role -> FunTyFlag -> CoercionN -> Coercion -> Coercion -> Coercion
+ GHC.Plugins: mkFunCo2 :: HasDebugCallStack => Role -> FunTyFlag -> FunTyFlag -> CoercionN -> Coercion -> Coercion -> Coercion
+ GHC.Plugins: mkFunCoNoFTF :: HasDebugCallStack => Role -> CoercionN -> Coercion -> Coercion -> Coercion
+ GHC.Plugins: mkFunctionType :: HasDebugCallStack => Mult -> Type -> Type -> Type
+ GHC.Plugins: mkInvisFunTys :: HasDebugCallStack => [Type] -> Type -> Type
+ GHC.Plugins: mkNakedFunCo1 :: Role -> FunTyFlag -> CoercionN -> Coercion -> Coercion -> Coercion
+ GHC.Plugins: mkNakedFunCo2 :: Role -> FunTyFlag -> FunTyFlag -> CoercionN -> Coercion -> Coercion -> Coercion
+ GHC.Plugins: mkRuntimeRepCo :: HasDebugCallStack => Coercion -> Coercion
+ GHC.Plugins: mkScaledFunTys :: HasDebugCallStack => [Scaled Type] -> Type -> Type
+ GHC.Plugins: mkScaledFunctionTys :: [Scaled Type] -> Type -> Type
+ GHC.Plugins: mkSelCo :: HasDebugCallStack => CoSel -> Coercion -> Coercion
+ GHC.Plugins: mkTYPEapp_maybe :: RuntimeRepType -> Maybe Type
+ GHC.Plugins: mkTupleRepApp_maybe :: Type -> Maybe Type
+ GHC.Plugins: namePun_maybe :: Name -> Maybe FastString
+ GHC.Plugins: namedPiTyBinder_maybe :: PiTyBinder -> Maybe TyCoVar
+ GHC.Plugins: noUnfoldingFun :: IdUnfoldingFun
+ GHC.Plugins: notElemSubst :: Var -> Subst -> Bool
+ GHC.Plugins: pattern ManyTy :: Mult
+ GHC.Plugins: pattern OneTy :: Mult
+ GHC.Plugins: piTyBinderType :: PiTyBinder -> Type
+ GHC.Plugins: pprName :: IsLine doc => Name -> doc
+ GHC.Plugins: returnsConstraintKind :: Kind -> Bool
+ GHC.Plugins: sORTKind_maybe :: Kind -> Maybe (TypeOrConstraint, Type)
+ GHC.Plugins: splitAppTyNoView_maybe :: HasDebugCallStack => Type -> Maybe (Type, Type)
+ GHC.Plugins: splitAppTysNoView :: HasDebugCallStack => Type -> (Type, [Type])
+ GHC.Plugins: splitForAllForAllTyBinders :: Type -> ([ForAllTyBinder], Type)
+ GHC.Plugins: splitForAllInvisTyBinders :: Type -> ([InvisTyBinder], Type)
+ GHC.Plugins: splitForAllReqTyBinders :: Type -> ([ReqTyBinder], Type)
+ GHC.Plugins: splitRuntimeRep_maybe :: RuntimeRepType -> Maybe (TyCon, [Type])
+ GHC.Plugins: splitTyConAppNoView_maybe :: Type -> Maybe (TyCon, [Type])
+ GHC.Plugins: substTyVarToTyVar :: HasDebugCallStack => Subst -> TyVar -> TyVar
+ GHC.Plugins: tcMkInvisFunTy :: TypeOrConstraint -> Type -> Type -> Type
+ GHC.Plugins: tcMkScaledFunTys :: [Scaled Type] -> Type -> Type
+ GHC.Plugins: tcMkVisFunTy :: Mult -> Type -> Type -> Type
+ GHC.Plugins: tcSplitAppTyNoView_maybe :: Type -> Maybe (Type, Type)
+ GHC.Plugins: tcSplitTyConApp :: Type -> (TyCon, [Type])
+ GHC.Plugins: tidyForAllTyBinder :: TidyEnv -> VarBndr TyCoVar vis -> (TidyEnv, VarBndr TyCoVar vis)
+ GHC.Plugins: tidyForAllTyBinders :: TidyEnv -> [VarBndr TyCoVar vis] -> (TidyEnv, [VarBndr TyCoVar vis])
+ GHC.Plugins: tyConAppFunCo_maybe :: HasDebugCallStack => Role -> TyCon -> [Coercion] -> Maybe Coercion
+ GHC.Plugins: tyConAppFunTy_maybe :: HasDebugCallStack => TyCon -> [Type] -> Maybe Type
+ GHC.Plugins: tyConBindersPiTyBinders :: [TyConBinder] -> [PiTyBinder]
+ GHC.Plugins: tyConForAllTyFlags :: TyCon -> [Type] -> [ForAllTyFlag]
+ GHC.Plugins: tyConIsTYPEorCONSTRAINT :: TyCon -> Bool
+ GHC.Plugins: tyConRole :: Role -> TyCon -> Int -> Role
+ GHC.Plugins: tyConRoleListRepresentational :: TyCon -> [Role]
+ GHC.Plugins: tyConRoleListX :: Role -> TyCon -> [Role]
+ GHC.Plugins: type ForAllTyBinder = VarBndr TyCoVar ForAllTyFlag
+ GHC.Plugins: type IdUnfoldingFun = Id -> Unfolding
+ GHC.Plugins: type RuntimeRepType = Type
+ GHC.Plugins: typeOrConstraintKind :: TypeOrConstraint -> RuntimeRepType -> Kind
+ GHC.Plugins: typeTypeOrConstraint :: HasDebugCallStack => Type -> TypeOrConstraint
+ GHC.Plugins: unionSubst :: Subst -> Subst -> Subst
+ GHC.Plugins: whenActiveUnfoldingFun :: (Activation -> Bool) -> IdUnfoldingFun
+ GHC.Plugins: zapSubst :: Subst -> Subst
+ GHC.Rename.Expr: [UnexpectedStatement] :: forall body. Outputable (StmtLR GhcPs GhcPs body) => StmtLR GhcPs GhcPs body -> UnexpectedStatement
+ GHC.Rename.Expr: data UnexpectedStatement
+ GHC.Rename.Expr: mkExpandedExpr :: HsExpr GhcRn -> HsExpr GhcRn -> HsExpr GhcRn
+ GHC.Rename.Splice: rnTypedSplice :: LHsExpr GhcPs -> RnM (HsExpr GhcRn, FreeVars)
+ GHC.Rename.Splice: rnUntypedSpliceExpr :: HsUntypedSplice GhcPs -> RnM (HsExpr GhcRn, FreeVars)
+ GHC.Rename.Utils: genFunBind :: LocatedN Name -> [LMatch GhcRn (LHsExpr GhcRn)] -> HsBind GhcRn
+ GHC.Rename.Utils: genSimpleConPat :: Name -> [LPat GhcRn] -> LPat GhcRn
+ GHC.Rename.Utils: genSimpleFunBind :: Name -> [LPat GhcRn] -> LHsExpr GhcRn -> LHsBind GhcRn
+ GHC.Rename.Utils: genVarPat :: Name -> LPat GhcRn
+ GHC.Rename.Utils: genWildPat :: LPat GhcRn
+ GHC.Runtime.Debugger: pprTypeAndContents :: GhcMonad m => Id -> m SDoc
+ GHC.Runtime.Debugger: pprintClosureCommand :: GhcMonad m => Bool -> Bool -> String -> m ()
+ GHC.Runtime.Debugger: showTerm :: GhcMonad m => Term -> m SDoc
+ GHC.Runtime.Loader: initializeSessionPlugins :: GhcMonad m => m ()
+ GHC.Stg.CSE: instance GHC.Base.Functor GHC.Stg.CSE.ConAppMap
+ GHC.Stg.CSE: instance GHC.Base.Functor GHC.Stg.CSE.StgArgMap
+ GHC.StgToCmm.Monad: [stgToCmmEmitDebugInfo] :: StgToCmmConfig -> !Bool
+ GHC.StgToJS: stgToJS :: Logger -> StgToJSConfig -> [CgStgTopBinding] -> Module -> [SptEntry] -> ForeignStubs -> CollectedCCs -> FilePath -> IO ()
+ GHC.StgToJS.Apply: genApp :: HasDebugCallStack => ExprCtx -> Id -> [StgArg] -> G (JStat, ExprResult)
+ GHC.StgToJS.Apply: instance GHC.Classes.Eq GHC.StgToJS.Apply.ApplyConv
+ GHC.StgToJS.Apply: instance GHC.Classes.Eq GHC.StgToJS.Apply.ApplySpec
+ GHC.StgToJS.Apply: instance GHC.Classes.Ord GHC.StgToJS.Apply.ApplyConv
+ GHC.StgToJS.Apply: instance GHC.Classes.Ord GHC.StgToJS.Apply.ApplySpec
+ GHC.StgToJS.Apply: instance GHC.Show.Show GHC.StgToJS.Apply.ApplyConv
+ GHC.StgToJS.Apply: instance GHC.Show.Show GHC.StgToJS.Apply.ApplySpec
+ GHC.StgToJS.Apply: rtsApply :: StgToJSConfig -> JStat
+ GHC.StgToJS.Arg: allocConStatic :: HasDebugCallStack => Ident -> CostCentreStack -> DataCon -> [StgArg] -> G ()
+ GHC.StgToJS.Arg: allocUnboxedConStatic :: DataCon -> [StaticArg] -> StaticArg
+ GHC.StgToJS.Arg: allocateStaticList :: [StgArg] -> StgArg -> G StaticVal
+ GHC.StgToJS.Arg: genArg :: HasDebugCallStack => StgArg -> G [JExpr]
+ GHC.StgToJS.Arg: genIdArg :: HasDebugCallStack => Id -> G [JExpr]
+ GHC.StgToJS.Arg: genIdArgI :: HasDebugCallStack => Id -> G [Ident]
+ GHC.StgToJS.Arg: genIdStackArgI :: HasDebugCallStack => Id -> G [(Ident, StackSlot)]
+ GHC.StgToJS.Arg: jsStaticArg :: StaticArg -> JExpr
+ GHC.StgToJS.Arg: jsStaticArgs :: [StaticArg] -> JExpr
+ GHC.StgToJS.Closure: Closure :: JExpr -> JExpr -> JExpr -> JExpr -> Maybe JExpr -> Closure
+ GHC.StgToJS.Closure: CopyCC :: CopyCC
+ GHC.StgToJS.Closure: DontCopyCC :: CopyCC
+ GHC.StgToJS.Closure: [clCC] :: Closure -> Maybe JExpr
+ GHC.StgToJS.Closure: [clEntry] :: Closure -> JExpr
+ GHC.StgToJS.Closure: [clField1] :: Closure -> JExpr
+ GHC.StgToJS.Closure: [clField2] :: Closure -> JExpr
+ GHC.StgToJS.Closure: [clMeta] :: Closure -> JExpr
+ GHC.StgToJS.Closure: assignClosure :: JExpr -> Closure -> JStat
+ GHC.StgToJS.Closure: closure :: ClosureInfo -> JStat -> JStat
+ GHC.StgToJS.Closure: closureInfoStat :: Bool -> ClosureInfo -> JStat
+ GHC.StgToJS.Closure: conClosure :: Ident -> FastString -> CILayout -> Int -> JStat
+ GHC.StgToJS.Closure: copyClosure :: CopyCC -> JExpr -> JExpr -> JStat
+ GHC.StgToJS.Closure: data Closure
+ GHC.StgToJS.Closure: data CopyCC
+ GHC.StgToJS.Closure: newClosure :: Closure -> JExpr
+ GHC.StgToJS.CodeGen: stgToJS :: Logger -> StgToJSConfig -> [CgStgTopBinding] -> Module -> [SptEntry] -> ForeignStubs -> CollectedCCs -> FilePath -> IO ()
+ GHC.StgToJS.CoreUtils: NoSlot :: SlotCount
+ GHC.StgToJS.CoreUtils: OneSlot :: SlotCount
+ GHC.StgToJS.CoreUtils: TwoSlots :: SlotCount
+ GHC.StgToJS.CoreUtils: argVt :: StgArg -> VarType
+ GHC.StgToJS.CoreUtils: assocIdExprs :: Id -> [JExpr] -> [TypedExpr]
+ GHC.StgToJS.CoreUtils: assocIdPrimReps :: Outputable a => Id -> [a] -> [(PrimRep, [a])]
+ GHC.StgToJS.CoreUtils: assocPrimReps :: Outputable a => [PrimRep] -> [a] -> [(PrimRep, [a])]
+ GHC.StgToJS.CoreUtils: data SlotCount
+ GHC.StgToJS.CoreUtils: dataConType :: DataCon -> Type
+ GHC.StgToJS.CoreUtils: fixedLayout :: [VarType] -> CILayout
+ GHC.StgToJS.CoreUtils: idPrimReps :: Id -> [PrimRep]
+ GHC.StgToJS.CoreUtils: idVt :: HasDebugCallStack => Id -> [VarType]
+ GHC.StgToJS.CoreUtils: instance GHC.Classes.Eq GHC.StgToJS.CoreUtils.SlotCount
+ GHC.StgToJS.CoreUtils: instance GHC.Classes.Ord GHC.StgToJS.CoreUtils.SlotCount
+ GHC.StgToJS.CoreUtils: instance GHC.Show.Show GHC.StgToJS.CoreUtils.SlotCount
+ GHC.StgToJS.CoreUtils: instance GHC.Utils.Outputable.Outputable GHC.StgToJS.CoreUtils.SlotCount
+ GHC.StgToJS.CoreUtils: isBoolDataCon :: DataCon -> Bool
+ GHC.StgToJS.CoreUtils: isMatchable :: [VarType] -> Bool
+ GHC.StgToJS.CoreUtils: isMultiVar :: VarType -> Bool
+ GHC.StgToJS.CoreUtils: isPtr :: VarType -> Bool
+ GHC.StgToJS.CoreUtils: isSingleVar :: VarType -> Bool
+ GHC.StgToJS.CoreUtils: isUnboxable :: VarType -> Bool
+ GHC.StgToJS.CoreUtils: isUnboxableCon :: DataCon -> Bool
+ GHC.StgToJS.CoreUtils: isVoid :: VarType -> Bool
+ GHC.StgToJS.CoreUtils: kindPrimRep' :: HasDebugCallStack => SDoc -> Kind -> [PrimRep]
+ GHC.StgToJS.CoreUtils: might_be_a_function :: HasDebugCallStack => Type -> Bool
+ GHC.StgToJS.CoreUtils: mkArityTag :: Int -> Int -> Int
+ GHC.StgToJS.CoreUtils: primRepSize :: PrimRep -> SlotCount
+ GHC.StgToJS.CoreUtils: primRepVt :: HasDebugCallStack => PrimRep -> VarType
+ GHC.StgToJS.CoreUtils: primTypeVt :: HasDebugCallStack => Type -> VarType
+ GHC.StgToJS.CoreUtils: slotCount :: SlotCount -> Int
+ GHC.StgToJS.CoreUtils: stackSlotType :: Id -> VarType
+ GHC.StgToJS.CoreUtils: toTypeList :: [VarType] -> [Int]
+ GHC.StgToJS.CoreUtils: tyConPrimRep' :: HasDebugCallStack => TyCon -> [PrimRep]
+ GHC.StgToJS.CoreUtils: tyConVt :: HasDebugCallStack => TyCon -> [VarType]
+ GHC.StgToJS.CoreUtils: typePrimRep' :: HasDebugCallStack => UnaryType -> [PrimRep]
+ GHC.StgToJS.CoreUtils: typePrimReps :: Type -> [PrimRep]
+ GHC.StgToJS.CoreUtils: typeSize :: Type -> Int
+ GHC.StgToJS.CoreUtils: typeVt :: HasDebugCallStack => Type -> [VarType]
+ GHC.StgToJS.CoreUtils: uTypeVt :: HasDebugCallStack => UnaryType -> VarType
+ GHC.StgToJS.CoreUtils: varSize :: VarType -> Int
+ GHC.StgToJS.CoreUtils: varSlotCount :: VarType -> SlotCount
+ GHC.StgToJS.DataCon: allocCon :: Ident -> DataCon -> CostCentreStack -> [JExpr] -> G JStat
+ GHC.StgToJS.DataCon: allocDynamic :: StgToJSConfig -> Bool -> Ident -> JExpr -> [JExpr] -> Maybe JExpr -> JStat
+ GHC.StgToJS.DataCon: allocDynamicE :: Bool -> JExpr -> [JExpr] -> Maybe JExpr -> JExpr
+ GHC.StgToJS.DataCon: allocUnboxedCon :: DataCon -> [JExpr] -> JExpr
+ GHC.StgToJS.DataCon: genCon :: ExprCtx -> DataCon -> [JExpr] -> G JStat
+ GHC.StgToJS.Deps: genDependencyData :: HasDebugCallStack => Module -> [LinkableUnit] -> G Deps
+ GHC.StgToJS.Expr: genBody :: HasDebugCallStack => ExprCtx -> Id -> StgReg -> [Id] -> CgStgExpr -> G JStat
+ GHC.StgToJS.Expr: genEntryType :: HasDebugCallStack => [Id] -> G CIType
+ GHC.StgToJS.Expr: genExpr :: HasDebugCallStack => ExprCtx -> CgStgExpr -> G (JStat, ExprResult)
+ GHC.StgToJS.Expr: genStaticRefs :: LiveVars -> G CIStatic
+ GHC.StgToJS.Expr: genStaticRefsRhs :: CgStgRhs -> G CIStatic
+ GHC.StgToJS.Expr: instance GHC.Base.Functor GHC.StgToJS.Expr.Branch
+ GHC.StgToJS.Expr: instance GHC.Classes.Eq a => GHC.Classes.Eq (GHC.StgToJS.Expr.Branch a)
+ GHC.StgToJS.Expr: loadLiveFun :: [Id] -> G JStat
+ GHC.StgToJS.ExprCtx: ctxAssertEvaluated :: Id -> ExprCtx -> ExprCtx
+ GHC.StgToJS.ExprCtx: ctxClearLneFrame :: ExprCtx -> ExprCtx
+ GHC.StgToJS.ExprCtx: ctxEvaluatedIds :: ExprCtx -> UniqSet Id
+ GHC.StgToJS.ExprCtx: ctxIsEvaluated :: ExprCtx -> Id -> Bool
+ GHC.StgToJS.ExprCtx: ctxIsLneBinding :: ExprCtx -> Id -> Bool
+ GHC.StgToJS.ExprCtx: ctxIsLneLiveVar :: ExprCtx -> Id -> Bool
+ GHC.StgToJS.ExprCtx: ctxLneBindingStackSize :: ExprCtx -> Id -> Maybe Int
+ GHC.StgToJS.ExprCtx: ctxLneFrameSize :: ExprCtx -> Int
+ GHC.StgToJS.ExprCtx: ctxLneFrameVars :: ExprCtx -> [(Id, Int)]
+ GHC.StgToJS.ExprCtx: ctxLneShrinkStack :: ExprCtx -> Int -> ExprCtx
+ GHC.StgToJS.ExprCtx: ctxSetSrcSpan :: RealSrcSpan -> ExprCtx -> ExprCtx
+ GHC.StgToJS.ExprCtx: ctxSetTarget :: [TypedExpr] -> ExprCtx -> ExprCtx
+ GHC.StgToJS.ExprCtx: ctxSetTop :: Id -> ExprCtx -> ExprCtx
+ GHC.StgToJS.ExprCtx: ctxSrcSpan :: ExprCtx -> Maybe RealSrcSpan
+ GHC.StgToJS.ExprCtx: ctxTarget :: ExprCtx -> [TypedExpr]
+ GHC.StgToJS.ExprCtx: ctxUpdateLneFrame :: [(Id, Int)] -> [Id] -> ExprCtx -> ExprCtx
+ GHC.StgToJS.ExprCtx: data ExprCtx
+ GHC.StgToJS.ExprCtx: initExprCtx :: Id -> ExprCtx
+ GHC.StgToJS.FFI: genForeignCall :: HasDebugCallStack => ExprCtx -> ForeignCall -> Type -> [JExpr] -> [StgArg] -> G (JStat, ExprResult)
+ GHC.StgToJS.FFI: genPrimCall :: ExprCtx -> PrimCall -> [StgArg] -> Type -> G (JStat, ExprResult)
+ GHC.StgToJS.FFI: saturateFFI :: JMacro a => Int -> a -> a
+ GHC.StgToJS.Heap: closureCC :: JExpr -> JExpr
+ GHC.StgToJS.Heap: closureCC_ :: FastString
+ GHC.StgToJS.Heap: closureEntry :: JExpr -> JExpr
+ GHC.StgToJS.Heap: closureEntry_ :: FastString
+ GHC.StgToJS.Heap: closureField1 :: JExpr -> JExpr
+ GHC.StgToJS.Heap: closureField1_ :: FastString
+ GHC.StgToJS.Heap: closureField2 :: JExpr -> JExpr
+ GHC.StgToJS.Heap: closureField2_ :: FastString
+ GHC.StgToJS.Heap: closureMeta :: JExpr -> JExpr
+ GHC.StgToJS.Heap: closureMeta_ :: FastString
+ GHC.StgToJS.Heap: closureType :: JExpr -> JExpr
+ GHC.StgToJS.Heap: conTag :: JExpr -> JExpr
+ GHC.StgToJS.Heap: conTag' :: JExpr -> JExpr
+ GHC.StgToJS.Heap: entryClosureType :: JExpr -> JExpr
+ GHC.StgToJS.Heap: funArity :: JExpr -> JExpr
+ GHC.StgToJS.Heap: funArity' :: JExpr -> JExpr
+ GHC.StgToJS.Heap: funOrPapArity :: JExpr -> Maybe JExpr -> JExpr
+ GHC.StgToJS.Heap: isBlackhole :: JExpr -> JExpr
+ GHC.StgToJS.Heap: isCon :: JExpr -> JExpr
+ GHC.StgToJS.Heap: isCon' :: JExpr -> JExpr
+ GHC.StgToJS.Heap: isFun :: JExpr -> JExpr
+ GHC.StgToJS.Heap: isFun' :: JExpr -> JExpr
+ GHC.StgToJS.Heap: isObject :: JExpr -> JExpr
+ GHC.StgToJS.Heap: isPap :: JExpr -> JExpr
+ GHC.StgToJS.Heap: isPap' :: JExpr -> JExpr
+ GHC.StgToJS.Heap: isThunk :: JExpr -> JExpr
+ GHC.StgToJS.Heap: isThunk' :: JExpr -> JExpr
+ GHC.StgToJS.Heap: jTyObject :: JExpr
+ GHC.StgToJS.Heap: papArity :: JExpr -> JExpr
+ GHC.StgToJS.Ids: cachedIdentForId :: Id -> Maybe Int -> IdType -> G Ident
+ GHC.StgToJS.Ids: declVarsForId :: Id -> G JStat
+ GHC.StgToJS.Ids: freshIdent :: G Ident
+ GHC.StgToJS.Ids: freshUnique :: G Int
+ GHC.StgToJS.Ids: identForDataConEntryId :: Id -> G Ident
+ GHC.StgToJS.Ids: identForDataConWorker :: DataCon -> G Ident
+ GHC.StgToJS.Ids: identForEntryId :: Id -> G Ident
+ GHC.StgToJS.Ids: identForId :: Id -> G Ident
+ GHC.StgToJS.Ids: identForIdN :: Id -> Int -> G Ident
+ GHC.StgToJS.Ids: identsForId :: Id -> G [Ident]
+ GHC.StgToJS.Ids: makeIdentForId :: Id -> Maybe Int -> IdType -> Module -> Ident
+ GHC.StgToJS.Ids: varForDataConEntryId :: Id -> G JExpr
+ GHC.StgToJS.Ids: varForDataConWorker :: DataCon -> G JExpr
+ GHC.StgToJS.Ids: varForEntryId :: Id -> G JExpr
+ GHC.StgToJS.Ids: varForId :: Id -> G JExpr
+ GHC.StgToJS.Ids: varForIdN :: Id -> Int -> G JExpr
+ GHC.StgToJS.Ids: varsForId :: Id -> G [JExpr]
+ GHC.StgToJS.Linker.Linker: embedJsFile :: Logger -> DynFlags -> TmpFs -> UnitEnv -> FilePath -> FilePath -> IO ()
+ GHC.StgToJS.Linker.Linker: jsLinkBinary :: JSLinkConfig -> StgToJSConfig -> [FilePath] -> Logger -> DynFlags -> UnitEnv -> [FilePath] -> [UnitId] -> IO ()
+ GHC.StgToJS.Linker.Types: GhcjsEnv :: MVar (Map (Set FilePath) (Map Module (Deps, DepsLocation), [LinkableUnit])) -> GhcjsEnv
+ GHC.StgToJS.Linker.Types: JSLinkConfig :: Bool -> Bool -> Bool -> Bool -> Bool -> JSLinkConfig
+ GHC.StgToJS.Linker.Types: ObjFile :: FilePath -> LinkedObj
+ GHC.StgToJS.Linker.Types: ObjLoaded :: String -> Object -> LinkedObj
+ GHC.StgToJS.Linker.Types: [lcNoHsMain] :: JSLinkConfig -> Bool
+ GHC.StgToJS.Linker.Types: [lcNoJSExecutables] :: JSLinkConfig -> Bool
+ GHC.StgToJS.Linker.Types: [lcNoRts] :: JSLinkConfig -> Bool
+ GHC.StgToJS.Linker.Types: [lcNoStats] :: JSLinkConfig -> Bool
+ GHC.StgToJS.Linker.Types: [lcOnlyOut] :: JSLinkConfig -> Bool
+ GHC.StgToJS.Linker.Types: [linkerArchiveDeps] :: GhcjsEnv -> MVar (Map (Set FilePath) (Map Module (Deps, DepsLocation), [LinkableUnit]))
+ GHC.StgToJS.Linker.Types: data GhcjsEnv
+ GHC.StgToJS.Linker.Types: data JSLinkConfig
+ GHC.StgToJS.Linker.Types: data LinkedObj
+ GHC.StgToJS.Linker.Types: defaultJSLinkConfig :: JSLinkConfig
+ GHC.StgToJS.Linker.Types: generateAllJs :: JSLinkConfig -> Bool
+ GHC.StgToJS.Linker.Types: instance GHC.Utils.Outputable.Outputable GHC.StgToJS.Linker.Types.LinkedObj
+ GHC.StgToJS.Linker.Types: newGhcjsEnv :: IO GhcjsEnv
+ GHC.StgToJS.Linker.Types: type LinkableUnit = (Module, Int)
+ GHC.StgToJS.Linker.Utils: CPP :: JSOption
+ GHC.StgToJS.Linker.Utils: commonCppDefs :: Bool -> ByteString
+ GHC.StgToJS.Linker.Utils: data JSOption
+ GHC.StgToJS.Linker.Utils: getInstalledPackageHsLibs :: UnitState -> UnitId -> [ShortText]
+ GHC.StgToJS.Linker.Utils: getInstalledPackageLibDirs :: UnitState -> UnitId -> [ShortText]
+ GHC.StgToJS.Linker.Utils: getOptionsFromJsFile :: FilePath -> IO [JSOption]
+ GHC.StgToJS.Linker.Utils: instance GHC.Classes.Eq GHC.StgToJS.Linker.Utils.JSOption
+ GHC.StgToJS.Linker.Utils: instance GHC.Classes.Ord GHC.StgToJS.Linker.Utils.JSOption
+ GHC.StgToJS.Linker.Utils: jsExeFileName :: DynFlags -> FilePath
+ GHC.StgToJS.Literal: genLit :: HasDebugCallStack => Literal -> G [JExpr]
+ GHC.StgToJS.Literal: genStaticLit :: Literal -> G [StaticLit]
+ GHC.StgToJS.Monad: GlobalOcc :: !Ident -> !Id -> !Word -> GlobalOcc
+ GHC.StgToJS.Monad: [global_count] :: GlobalOcc -> !Word
+ GHC.StgToJS.Monad: [global_id] :: GlobalOcc -> !Id
+ GHC.StgToJS.Monad: [global_ident] :: GlobalOcc -> !Ident
+ GHC.StgToJS.Monad: addDependency :: OtherSymb -> G ()
+ GHC.StgToJS.Monad: assertRtsStat :: G JStat -> G JStat
+ GHC.StgToJS.Monad: data GlobalOcc
+ GHC.StgToJS.Monad: emitClosureInfo :: ClosureInfo -> G ()
+ GHC.StgToJS.Monad: emitForeign :: Maybe RealSrcSpan -> FastString -> Safety -> CCallConv -> [FastString] -> FastString -> G ()
+ GHC.StgToJS.Monad: emitGlobal :: JStat -> G ()
+ GHC.StgToJS.Monad: emitStatic :: FastString -> StaticVal -> Maybe Ident -> G ()
+ GHC.StgToJS.Monad: emitToplevel :: JStat -> G ()
+ GHC.StgToJS.Monad: getGlobalIdCache :: G GlobalIdCache
+ GHC.StgToJS.Monad: getSettings :: G StgToJSConfig
+ GHC.StgToJS.Monad: globalOccs :: JStat -> G [GlobalOcc]
+ GHC.StgToJS.Monad: modifyGroup :: (GenGroupState -> GenGroupState) -> G ()
+ GHC.StgToJS.Monad: resetGroup :: G ()
+ GHC.StgToJS.Monad: runG :: StgToJSConfig -> Module -> UniqFM Id CgStgExpr -> G a -> IO a
+ GHC.StgToJS.Monad: setGlobalIdCache :: GlobalIdCache -> G ()
+ GHC.StgToJS.Object: ArchiveFile :: FilePath -> DepsLocation
+ GHC.StgToJS.Object: BlockDeps :: [Int] -> [ExportedFun] -> BlockDeps
+ GHC.StgToJS.Object: Deps :: !Module -> !BlockIds -> !Map ExportedFun BlockId -> !Array BlockId BlockDeps -> Deps
+ GHC.StgToJS.Object: ExportedFun :: !Module -> !LexicalFastString -> ExportedFun
+ GHC.StgToJS.Object: InMemory :: String -> Object -> DepsLocation
+ GHC.StgToJS.Object: IndexEntry :: ![FastString] -> !Bin ObjUnit -> IndexEntry
+ GHC.StgToJS.Object: Object :: !ModuleName -> !BinHandle -> !Bin ObjUnit -> !Deps -> !Index -> Object
+ GHC.StgToJS.Object: ObjectFile :: FilePath -> DepsLocation
+ GHC.StgToJS.Object: [blockBlockDeps] :: BlockDeps -> [Int]
+ GHC.StgToJS.Object: [blockFunDeps] :: BlockDeps -> [ExportedFun]
+ GHC.StgToJS.Object: [depsBlocks] :: Deps -> !Array BlockId BlockDeps
+ GHC.StgToJS.Object: [depsHaskellExported] :: Deps -> !Map ExportedFun BlockId
+ GHC.StgToJS.Object: [depsModule] :: Deps -> !Module
+ GHC.StgToJS.Object: [depsRequired] :: Deps -> !BlockIds
+ GHC.StgToJS.Object: [funModule] :: ExportedFun -> !Module
+ GHC.StgToJS.Object: [funSymbol] :: ExportedFun -> !LexicalFastString
+ GHC.StgToJS.Object: [idxOffset] :: IndexEntry -> !Bin ObjUnit
+ GHC.StgToJS.Object: [idxSymbols] :: IndexEntry -> ![FastString]
+ GHC.StgToJS.Object: [objDeps] :: Object -> !Deps
+ GHC.StgToJS.Object: [objHandle] :: Object -> !BinHandle
+ GHC.StgToJS.Object: [objIndex] :: Object -> !Index
+ GHC.StgToJS.Object: [objModuleName] :: Object -> !ModuleName
+ GHC.StgToJS.Object: [objPayloadOffset] :: Object -> !Bin ObjUnit
+ GHC.StgToJS.Object: data BlockDeps
+ GHC.StgToJS.Object: data Deps
+ GHC.StgToJS.Object: data DepsLocation
+ GHC.StgToJS.Object: data ExportedFun
+ GHC.StgToJS.Object: data IndexEntry
+ GHC.StgToJS.Object: data Object
+ GHC.StgToJS.Object: getObject :: BinHandle -> IO (Maybe Object)
+ GHC.StgToJS.Object: getObjectBody :: BinHandle -> ModuleName -> IO Object
+ GHC.StgToJS.Object: getObjectHeader :: BinHandle -> IO (Either String ModuleName)
+ GHC.StgToJS.Object: getObjectUnits :: Object -> (Word -> IndexEntry -> Bool) -> IO [ObjUnit]
+ GHC.StgToJS.Object: instance GHC.Classes.Eq GHC.StgToJS.Object.ExportedFun
+ GHC.StgToJS.Object: instance GHC.Classes.Ord GHC.StgToJS.Object.ExportedFun
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.JS.Syntax.Ident
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.JS.Syntax.JExpr
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.JS.Syntax.JOp
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.JS.Syntax.JStat
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.JS.Syntax.JUOp
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.JS.Syntax.JVal
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.JS.Syntax.SaneDouble
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Object.BlockDeps
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Object.Deps
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Object.ExportedFun
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Object.IndexEntry
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Types.CILayout
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Types.CIRegs
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Types.CIStatic
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Types.CIType
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Types.ClosureInfo
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Types.ExpFun
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Types.ForeignJSRef
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Types.JSFFIType
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Types.StaticArg
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Types.StaticInfo
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Types.StaticLit
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Types.StaticUnboxed
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Types.StaticVal
+ GHC.StgToJS.Object: instance GHC.Utils.Binary.Binary GHC.StgToJS.Types.VarType
+ GHC.StgToJS.Object: instance GHC.Utils.Outputable.Outputable GHC.StgToJS.Object.Deps
+ GHC.StgToJS.Object: instance GHC.Utils.Outputable.Outputable GHC.StgToJS.Object.DepsLocation
+ GHC.StgToJS.Object: instance GHC.Utils.Outputable.Outputable GHC.StgToJS.Object.ExportedFun
+ GHC.StgToJS.Object: isGlobalUnit :: Int -> Bool
+ GHC.StgToJS.Object: isJsObjectFile :: FilePath -> IO Bool
+ GHC.StgToJS.Object: putObject :: BinHandle -> ModuleName -> Deps -> [ObjUnit] -> IO ()
+ GHC.StgToJS.Object: readObject :: FilePath -> IO (Maybe Object)
+ GHC.StgToJS.Object: readObjectDeps :: FilePath -> IO (Maybe Deps)
+ GHC.StgToJS.Object: readObjectUnits :: FilePath -> (Word -> IndexEntry -> Bool) -> IO [ObjUnit]
+ GHC.StgToJS.Prim: genPrim :: Bool -> Bool -> Type -> PrimOp -> [JExpr] -> [JExpr] -> PrimRes
+ GHC.StgToJS.Printer: ghcjsRenderJs :: RenderJs
+ GHC.StgToJS.Printer: pretty :: JStat -> Doc
+ GHC.StgToJS.Printer: prettyBlock :: RenderJs -> [JStat] -> Doc
+ GHC.StgToJS.Profiling: ccsVarJ :: CostCentreStack -> G (Maybe JExpr)
+ GHC.StgToJS.Profiling: costCentreLbl :: CostCentre -> G Ident
+ GHC.StgToJS.Profiling: costCentreStackLbl :: CostCentreStack -> G (Maybe Ident)
+ GHC.StgToJS.Profiling: emitCostCentreDecl :: CostCentre -> G ()
+ GHC.StgToJS.Profiling: emitCostCentreStackDecl :: CostCentreStack -> G ()
+ GHC.StgToJS.Profiling: enterCostCentreFun :: CostCentreStack -> JStat
+ GHC.StgToJS.Profiling: enterCostCentreThunk :: JStat
+ GHC.StgToJS.Profiling: ifProfiling :: Monoid m => m -> G m
+ GHC.StgToJS.Profiling: ifProfilingM :: Monoid m => G m -> G m
+ GHC.StgToJS.Profiling: initCostCentres :: CollectedCCs -> G ()
+ GHC.StgToJS.Profiling: jCafCCS :: JExpr
+ GHC.StgToJS.Profiling: jCurrentCCS :: JExpr
+ GHC.StgToJS.Profiling: jSystemCCS :: JExpr
+ GHC.StgToJS.Profiling: profStat :: StgToJSConfig -> JStat -> JStat
+ GHC.StgToJS.Profiling: profiling :: G Bool
+ GHC.StgToJS.Profiling: pushRestoreCCS :: JStat
+ GHC.StgToJS.Profiling: setCC :: CostCentre -> Bool -> Bool -> G JStat
+ GHC.StgToJS.Profiling: singletonCCSLbl :: CostCentre -> G Ident
+ GHC.StgToJS.Regs: R1 :: StgReg
+ GHC.StgToJS.Regs: R10 :: StgReg
+ GHC.StgToJS.Regs: R100 :: StgReg
+ GHC.StgToJS.Regs: R101 :: StgReg
+ GHC.StgToJS.Regs: R102 :: StgReg
+ GHC.StgToJS.Regs: R103 :: StgReg
+ GHC.StgToJS.Regs: R104 :: StgReg
+ GHC.StgToJS.Regs: R105 :: StgReg
+ GHC.StgToJS.Regs: R106 :: StgReg
+ GHC.StgToJS.Regs: R107 :: StgReg
+ GHC.StgToJS.Regs: R108 :: StgReg
+ GHC.StgToJS.Regs: R109 :: StgReg
+ GHC.StgToJS.Regs: R11 :: StgReg
+ GHC.StgToJS.Regs: R110 :: StgReg
+ GHC.StgToJS.Regs: R111 :: StgReg
+ GHC.StgToJS.Regs: R112 :: StgReg
+ GHC.StgToJS.Regs: R113 :: StgReg
+ GHC.StgToJS.Regs: R114 :: StgReg
+ GHC.StgToJS.Regs: R115 :: StgReg
+ GHC.StgToJS.Regs: R116 :: StgReg
+ GHC.StgToJS.Regs: R117 :: StgReg
+ GHC.StgToJS.Regs: R118 :: StgReg
+ GHC.StgToJS.Regs: R119 :: StgReg
+ GHC.StgToJS.Regs: R12 :: StgReg
+ GHC.StgToJS.Regs: R120 :: StgReg
+ GHC.StgToJS.Regs: R121 :: StgReg
+ GHC.StgToJS.Regs: R122 :: StgReg
+ GHC.StgToJS.Regs: R123 :: StgReg
+ GHC.StgToJS.Regs: R124 :: StgReg
+ GHC.StgToJS.Regs: R125 :: StgReg
+ GHC.StgToJS.Regs: R126 :: StgReg
+ GHC.StgToJS.Regs: R127 :: StgReg
+ GHC.StgToJS.Regs: R128 :: StgReg
+ GHC.StgToJS.Regs: R13 :: StgReg
+ GHC.StgToJS.Regs: R14 :: StgReg
+ GHC.StgToJS.Regs: R15 :: StgReg
+ GHC.StgToJS.Regs: R16 :: StgReg
+ GHC.StgToJS.Regs: R17 :: StgReg
+ GHC.StgToJS.Regs: R18 :: StgReg
+ GHC.StgToJS.Regs: R19 :: StgReg
+ GHC.StgToJS.Regs: R2 :: StgReg
+ GHC.StgToJS.Regs: R20 :: StgReg
+ GHC.StgToJS.Regs: R21 :: StgReg
+ GHC.StgToJS.Regs: R22 :: StgReg
+ GHC.StgToJS.Regs: R23 :: StgReg
+ GHC.StgToJS.Regs: R24 :: StgReg
+ GHC.StgToJS.Regs: R25 :: StgReg
+ GHC.StgToJS.Regs: R26 :: StgReg
+ GHC.StgToJS.Regs: R27 :: StgReg
+ GHC.StgToJS.Regs: R28 :: StgReg
+ GHC.StgToJS.Regs: R29 :: StgReg
+ GHC.StgToJS.Regs: R3 :: StgReg
+ GHC.StgToJS.Regs: R30 :: StgReg
+ GHC.StgToJS.Regs: R31 :: StgReg
+ GHC.StgToJS.Regs: R32 :: StgReg
+ GHC.StgToJS.Regs: R33 :: StgReg
+ GHC.StgToJS.Regs: R34 :: StgReg
+ GHC.StgToJS.Regs: R35 :: StgReg
+ GHC.StgToJS.Regs: R36 :: StgReg
+ GHC.StgToJS.Regs: R37 :: StgReg
+ GHC.StgToJS.Regs: R38 :: StgReg
+ GHC.StgToJS.Regs: R39 :: StgReg
+ GHC.StgToJS.Regs: R4 :: StgReg
+ GHC.StgToJS.Regs: R40 :: StgReg
+ GHC.StgToJS.Regs: R41 :: StgReg
+ GHC.StgToJS.Regs: R42 :: StgReg
+ GHC.StgToJS.Regs: R43 :: StgReg
+ GHC.StgToJS.Regs: R44 :: StgReg
+ GHC.StgToJS.Regs: R45 :: StgReg
+ GHC.StgToJS.Regs: R46 :: StgReg
+ GHC.StgToJS.Regs: R47 :: StgReg
+ GHC.StgToJS.Regs: R48 :: StgReg
+ GHC.StgToJS.Regs: R49 :: StgReg
+ GHC.StgToJS.Regs: R5 :: StgReg
+ GHC.StgToJS.Regs: R50 :: StgReg
+ GHC.StgToJS.Regs: R51 :: StgReg
+ GHC.StgToJS.Regs: R52 :: StgReg
+ GHC.StgToJS.Regs: R53 :: StgReg
+ GHC.StgToJS.Regs: R54 :: StgReg
+ GHC.StgToJS.Regs: R55 :: StgReg
+ GHC.StgToJS.Regs: R56 :: StgReg
+ GHC.StgToJS.Regs: R57 :: StgReg
+ GHC.StgToJS.Regs: R58 :: StgReg
+ GHC.StgToJS.Regs: R59 :: StgReg
+ GHC.StgToJS.Regs: R6 :: StgReg
+ GHC.StgToJS.Regs: R60 :: StgReg
+ GHC.StgToJS.Regs: R61 :: StgReg
+ GHC.StgToJS.Regs: R62 :: StgReg
+ GHC.StgToJS.Regs: R63 :: StgReg
+ GHC.StgToJS.Regs: R64 :: StgReg
+ GHC.StgToJS.Regs: R65 :: StgReg
+ GHC.StgToJS.Regs: R66 :: StgReg
+ GHC.StgToJS.Regs: R67 :: StgReg
+ GHC.StgToJS.Regs: R68 :: StgReg
+ GHC.StgToJS.Regs: R69 :: StgReg
+ GHC.StgToJS.Regs: R7 :: StgReg
+ GHC.StgToJS.Regs: R70 :: StgReg
+ GHC.StgToJS.Regs: R71 :: StgReg
+ GHC.StgToJS.Regs: R72 :: StgReg
+ GHC.StgToJS.Regs: R73 :: StgReg
+ GHC.StgToJS.Regs: R74 :: StgReg
+ GHC.StgToJS.Regs: R75 :: StgReg
+ GHC.StgToJS.Regs: R76 :: StgReg
+ GHC.StgToJS.Regs: R77 :: StgReg
+ GHC.StgToJS.Regs: R78 :: StgReg
+ GHC.StgToJS.Regs: R79 :: StgReg
+ GHC.StgToJS.Regs: R8 :: StgReg
+ GHC.StgToJS.Regs: R80 :: StgReg
+ GHC.StgToJS.Regs: R81 :: StgReg
+ GHC.StgToJS.Regs: R82 :: StgReg
+ GHC.StgToJS.Regs: R83 :: StgReg
+ GHC.StgToJS.Regs: R84 :: StgReg
+ GHC.StgToJS.Regs: R85 :: StgReg
+ GHC.StgToJS.Regs: R86 :: StgReg
+ GHC.StgToJS.Regs: R87 :: StgReg
+ GHC.StgToJS.Regs: R88 :: StgReg
+ GHC.StgToJS.Regs: R89 :: StgReg
+ GHC.StgToJS.Regs: R9 :: StgReg
+ GHC.StgToJS.Regs: R90 :: StgReg
+ GHC.StgToJS.Regs: R91 :: StgReg
+ GHC.StgToJS.Regs: R92 :: StgReg
+ GHC.StgToJS.Regs: R93 :: StgReg
+ GHC.StgToJS.Regs: R94 :: StgReg
+ GHC.StgToJS.Regs: R95 :: StgReg
+ GHC.StgToJS.Regs: R96 :: StgReg
+ GHC.StgToJS.Regs: R97 :: StgReg
+ GHC.StgToJS.Regs: R98 :: StgReg
+ GHC.StgToJS.Regs: R99 :: StgReg
+ GHC.StgToJS.Regs: Ret1 :: StgRet
+ GHC.StgToJS.Regs: Ret10 :: StgRet
+ GHC.StgToJS.Regs: Ret2 :: StgRet
+ GHC.StgToJS.Regs: Ret3 :: StgRet
+ GHC.StgToJS.Regs: Ret4 :: StgRet
+ GHC.StgToJS.Regs: Ret5 :: StgRet
+ GHC.StgToJS.Regs: Ret6 :: StgRet
+ GHC.StgToJS.Regs: Ret7 :: StgRet
+ GHC.StgToJS.Regs: Ret8 :: StgRet
+ GHC.StgToJS.Regs: Ret9 :: StgRet
+ GHC.StgToJS.Regs: Sp :: Special
+ GHC.StgToJS.Regs: Stack :: Special
+ GHC.StgToJS.Regs: data Special
+ GHC.StgToJS.Regs: data StgReg
+ GHC.StgToJS.Regs: data StgRet
+ GHC.StgToJS.Regs: instance GHC.Classes.Eq GHC.StgToJS.Regs.Special
+ GHC.StgToJS.Regs: instance GHC.Classes.Eq GHC.StgToJS.Regs.StgReg
+ GHC.StgToJS.Regs: instance GHC.Classes.Eq GHC.StgToJS.Regs.StgRet
+ GHC.StgToJS.Regs: instance GHC.Classes.Ord GHC.StgToJS.Regs.StgReg
+ GHC.StgToJS.Regs: instance GHC.Classes.Ord GHC.StgToJS.Regs.StgRet
+ GHC.StgToJS.Regs: instance GHC.Enum.Bounded GHC.StgToJS.Regs.StgReg
+ GHC.StgToJS.Regs: instance GHC.Enum.Bounded GHC.StgToJS.Regs.StgRet
+ GHC.StgToJS.Regs: instance GHC.Enum.Enum GHC.StgToJS.Regs.StgReg
+ GHC.StgToJS.Regs: instance GHC.Enum.Enum GHC.StgToJS.Regs.StgRet
+ GHC.StgToJS.Regs: instance GHC.Ix.Ix GHC.StgToJS.Regs.StgReg
+ GHC.StgToJS.Regs: instance GHC.Ix.Ix GHC.StgToJS.Regs.StgRet
+ GHC.StgToJS.Regs: instance GHC.JS.Make.ToJExpr GHC.StgToJS.Regs.Special
+ GHC.StgToJS.Regs: instance GHC.JS.Make.ToJExpr GHC.StgToJS.Regs.StgReg
+ GHC.StgToJS.Regs: instance GHC.JS.Make.ToJExpr GHC.StgToJS.Regs.StgRet
+ GHC.StgToJS.Regs: instance GHC.Show.Show GHC.StgToJS.Regs.Special
+ GHC.StgToJS.Regs: instance GHC.Show.Show GHC.StgToJS.Regs.StgReg
+ GHC.StgToJS.Regs: instance GHC.Show.Show GHC.StgToJS.Regs.StgRet
+ GHC.StgToJS.Regs: intToJSReg :: Int -> StgReg
+ GHC.StgToJS.Regs: jsReg :: Int -> JExpr
+ GHC.StgToJS.Regs: jsRegToInt :: StgReg -> Int
+ GHC.StgToJS.Regs: jsRegsFromR1 :: [JExpr]
+ GHC.StgToJS.Regs: jsRegsFromR2 :: [JExpr]
+ GHC.StgToJS.Regs: maxReg :: Int
+ GHC.StgToJS.Regs: minReg :: Int
+ GHC.StgToJS.Regs: r1 :: JExpr
+ GHC.StgToJS.Regs: r2 :: JExpr
+ GHC.StgToJS.Regs: r3 :: JExpr
+ GHC.StgToJS.Regs: r4 :: JExpr
+ GHC.StgToJS.Regs: regsFromR1 :: [StgReg]
+ GHC.StgToJS.Regs: regsFromR2 :: [StgReg]
+ GHC.StgToJS.Regs: sp :: JExpr
+ GHC.StgToJS.Regs: stack :: JExpr
+ GHC.StgToJS.Rts.Rts: assignRegs :: StgToJSConfig -> [JExpr] -> JStat
+ GHC.StgToJS.Rts.Rts: assignRegs' :: Array Int Ident
+ GHC.StgToJS.Rts.Rts: bhLneStats :: StgToJSConfig -> JExpr -> JExpr -> JStat
+ GHC.StgToJS.Rts.Rts: bitsIdx :: Integer -> [Int]
+ GHC.StgToJS.Rts.Rts: closureConstructors :: StgToJSConfig -> JStat
+ GHC.StgToJS.Rts.Rts: closureTypes :: JStat
+ GHC.StgToJS.Rts.Rts: declRegs :: JStat
+ GHC.StgToJS.Rts.Rts: declRets :: JStat
+ GHC.StgToJS.Rts.Rts: garbageCollector :: JStat
+ GHC.StgToJS.Rts.Rts: loadRegs :: JStat
+ GHC.StgToJS.Rts.Rts: regGettersSetters :: JStat
+ GHC.StgToJS.Rts.Rts: resetRegister :: StgReg -> JStat
+ GHC.StgToJS.Rts.Rts: resetResultVar :: StgRet -> JStat
+ GHC.StgToJS.Rts.Rts: rts :: StgToJSConfig -> JStat
+ GHC.StgToJS.Rts.Rts: rts' :: StgToJSConfig -> JStat
+ GHC.StgToJS.Rts.Rts: rtsDecls :: JStat
+ GHC.StgToJS.Rts.Rts: rtsDeclsText :: String
+ GHC.StgToJS.Rts.Rts: rtsText :: StgToJSConfig -> String
+ GHC.StgToJS.Rts.Rts: stackManip :: JStat
+ GHC.StgToJS.Rts.Types: assertRts :: ToJExpr a => StgToJSConfig -> JExpr -> a -> JStat
+ GHC.StgToJS.Rts.Types: clName :: JExpr -> JExpr
+ GHC.StgToJS.Rts.Types: clTypeName :: JExpr -> JExpr
+ GHC.StgToJS.Rts.Types: stackFrameSize :: JExpr -> JExpr -> JStat
+ GHC.StgToJS.Rts.Types: traceRts :: StgToJSConfig -> JExpr -> JStat
+ GHC.StgToJS.Rts.Types: withRegs :: StgReg -> StgReg -> (StgReg -> JStat) -> JStat
+ GHC.StgToJS.Sinker: sinkPgm :: Module -> [CgStgTopBinding] -> (UniqFM Id CgStgExpr, [CgStgTopBinding])
+ GHC.StgToJS.Stack: addSlots :: [StackSlot] -> G ()
+ GHC.StgToJS.Stack: addUnknownSlots :: Int -> G ()
+ GHC.StgToJS.Stack: adjSp :: Int -> G JStat
+ GHC.StgToJS.Stack: adjSp' :: Int -> JStat
+ GHC.StgToJS.Stack: adjSpN :: Int -> G JStat
+ GHC.StgToJS.Stack: adjSpN' :: Int -> JStat
+ GHC.StgToJS.Stack: bhStats :: StgToJSConfig -> Bool -> JStat
+ GHC.StgToJS.Stack: dropSlots :: Int -> G ()
+ GHC.StgToJS.Stack: getSlots :: G [StackSlot]
+ GHC.StgToJS.Stack: isolateSlots :: G a -> G a
+ GHC.StgToJS.Stack: loadSkip :: Int -> [JExpr] -> JStat
+ GHC.StgToJS.Stack: popN :: Int -> G JStat
+ GHC.StgToJS.Stack: popSkip :: Int -> [JExpr] -> JStat
+ GHC.StgToJS.Stack: popSkipI :: Int -> [(Ident, StackSlot)] -> G JStat
+ GHC.StgToJS.Stack: push :: [JExpr] -> G JStat
+ GHC.StgToJS.Stack: push' :: StgToJSConfig -> [JExpr] -> JStat
+ GHC.StgToJS.Stack: pushLneFrame :: HasDebugCallStack => Int -> ExprCtx -> G JStat
+ GHC.StgToJS.Stack: pushN :: Array Int Ident
+ GHC.StgToJS.Stack: pushN' :: Array Int JExpr
+ GHC.StgToJS.Stack: pushNN :: Array Integer Ident
+ GHC.StgToJS.Stack: pushNN' :: Array Integer JExpr
+ GHC.StgToJS.Stack: pushOptimized :: [(JExpr, Bool)] -> G JStat
+ GHC.StgToJS.Stack: pushOptimized' :: [(Id, Int)] -> G JStat
+ GHC.StgToJS.Stack: resetSlots :: G a -> G a
+ GHC.StgToJS.Stack: setSlots :: [StackSlot] -> G ()
+ GHC.StgToJS.Stack: updateThunk :: G JStat
+ GHC.StgToJS.Stack: updateThunk' :: StgToJSConfig -> JStat
+ GHC.StgToJS.StaticPtr: initStaticPtrs :: [SptEntry] -> G JStat
+ GHC.StgToJS.StgUtils: bindingRefs :: UniqFM Id CgStgExpr -> CgStgBinding -> Set Id
+ GHC.StgToJS.StgUtils: collectIds :: UniqFM Id CgStgExpr -> CgStgBinding -> [Id]
+ GHC.StgToJS.StgUtils: collectTopIds :: CgStgBinding -> [Id]
+ GHC.StgToJS.StgUtils: exprRefs :: UniqFM Id CgStgExpr -> CgStgExpr -> Set Id
+ GHC.StgToJS.StgUtils: hasExport :: CgStgBinding -> Bool
+ GHC.StgToJS.StgUtils: isInlineExpr :: UniqSet Id -> CgStgExpr -> (UniqSet Id, Bool)
+ GHC.StgToJS.StgUtils: isUpdatableRhs :: CgStgRhs -> Bool
+ GHC.StgToJS.StgUtils: liveStatic :: LiveVars -> LiveVars
+ GHC.StgToJS.StgUtils: liveVars :: LiveVars -> LiveVars
+ GHC.StgToJS.StgUtils: removeTick :: CgStgExpr -> CgStgExpr
+ GHC.StgToJS.StgUtils: stgExprLive :: Bool -> CgStgExpr -> LiveVars
+ GHC.StgToJS.StgUtils: stgLetNoEscapeLive :: Bool -> StgBinding -> StgExpr -> LiveVars
+ GHC.StgToJS.StgUtils: stgLneLive :: CgStgBinding -> [Id]
+ GHC.StgToJS.StgUtils: stgLneLive' :: CgStgBinding -> [Id]
+ GHC.StgToJS.StgUtils: stgLneLiveExpr :: CgStgRhs -> [Id]
+ GHC.StgToJS.StgUtils: stgRhsLive :: CgStgRhs -> LiveVars
+ GHC.StgToJS.StgUtils: stgTopBindLive :: CgStgTopBinding -> [(Id, LiveVars)]
+ GHC.StgToJS.StgUtils: type LiveVars = DVarSet
+ GHC.StgToJS.Symbols: intBS :: Int -> ByteString
+ GHC.StgToJS.Symbols: mkFreshJsSymbol :: Module -> Int -> FastString
+ GHC.StgToJS.Symbols: mkJsSymbol :: Bool -> Module -> FastString -> FastString
+ GHC.StgToJS.Symbols: mkJsSymbolBS :: Bool -> Module -> FastString -> ByteString
+ GHC.StgToJS.Symbols: mkRawSymbol :: Bool -> FastString -> FastString
+ GHC.StgToJS.Symbols: moduleExportsSymbol :: Module -> FastString
+ GHC.StgToJS.Symbols: moduleGlobalSymbol :: Module -> FastString
+ GHC.StgToJS.Types: AddrV :: VarType
+ GHC.StgToJS.Types: ArrV :: VarType
+ GHC.StgToJS.Types: BinLit :: !ByteString -> StaticLit
+ GHC.StgToJS.Types: Blackhole :: ClosureType
+ GHC.StgToJS.Types: Blocked :: ThreadStatus
+ GHC.StgToJS.Types: BoolLit :: !Bool -> StaticLit
+ GHC.StgToJS.Types: ByteArrayType :: JSFFIType
+ GHC.StgToJS.Types: CIBlackhole :: CIType
+ GHC.StgToJS.Types: CICon :: !Int -> CIType
+ GHC.StgToJS.Types: CIFun :: !Int -> !Int -> CIType
+ GHC.StgToJS.Types: CILayoutFixed :: !Int -> [VarType] -> CILayout
+ GHC.StgToJS.Types: CILayoutUnknown :: !Int -> CILayout
+ GHC.StgToJS.Types: CILayoutVariable :: CILayout
+ GHC.StgToJS.Types: CIPap :: CIType
+ GHC.StgToJS.Types: CIRegs :: Int -> [VarType] -> CIRegs
+ GHC.StgToJS.Types: CIRegsUnknown :: CIRegs
+ GHC.StgToJS.Types: CIStackFrame :: CIType
+ GHC.StgToJS.Types: CIStaticRefs :: [FastString] -> CIStatic
+ GHC.StgToJS.Types: CIThunk :: CIType
+ GHC.StgToJS.Types: ClosureInfo :: Ident -> CIRegs -> FastString -> CILayout -> CIType -> CIStatic -> ClosureInfo
+ GHC.StgToJS.Types: Con :: ClosureType
+ GHC.StgToJS.Types: Died :: ThreadStatus
+ GHC.StgToJS.Types: DoubleLit :: !SaneDouble -> StaticLit
+ GHC.StgToJS.Types: DoubleType :: JSFFIType
+ GHC.StgToJS.Types: DoubleV :: VarType
+ GHC.StgToJS.Types: ExpFun :: !Bool -> [JSFFIType] -> !JSFFIType -> ExpFun
+ GHC.StgToJS.Types: ExprCont :: ExprResult
+ GHC.StgToJS.Types: ExprInline :: Maybe [JExpr] -> ExprResult
+ GHC.StgToJS.Types: ExprValData :: [JExpr] -> ExprValData
+ GHC.StgToJS.Types: Finished :: ThreadStatus
+ GHC.StgToJS.Types: ForeignJSRef :: !FastString -> !FastString -> !Safety -> !CCallConv -> ![FastString] -> !FastString -> ForeignJSRef
+ GHC.StgToJS.Types: Fun :: ClosureType
+ GHC.StgToJS.Types: GenGroupState :: [JStat] -> [ClosureInfo] -> [StaticInfo] -> [StackSlot] -> Int -> Set OtherSymb -> GlobalIdCache -> [ForeignJSRef] -> GenGroupState
+ GHC.StgToJS.Types: GenState :: !StgToJSConfig -> !Module -> {-# UNPACK #-} !FastMutInt -> !IdCache -> !UniqFM Id CgStgExpr -> GenGroupState -> [JStat] -> GenState
+ GHC.StgToJS.Types: GlobalIdCache :: UniqFM Ident (IdKey, Id) -> GlobalIdCache
+ GHC.StgToJS.Types: IdCache :: Map IdKey Ident -> IdCache
+ GHC.StgToJS.Types: IdConEntry :: IdType
+ GHC.StgToJS.Types: IdEntry :: IdType
+ GHC.StgToJS.Types: IdKey :: !Int -> !Int -> !IdType -> IdKey
+ GHC.StgToJS.Types: IdPlain :: IdType
+ GHC.StgToJS.Types: Int16Type :: JSFFIType
+ GHC.StgToJS.Types: Int32Type :: JSFFIType
+ GHC.StgToJS.Types: Int64Type :: JSFFIType
+ GHC.StgToJS.Types: Int8Type :: JSFFIType
+ GHC.StgToJS.Types: IntLit :: !Integer -> StaticLit
+ GHC.StgToJS.Types: IntV :: VarType
+ GHC.StgToJS.Types: LabelLit :: !Bool -> !FastString -> StaticLit
+ GHC.StgToJS.Types: LinkableUnit :: ObjUnit -> [Id] -> [FastString] -> [Id] -> [Unique] -> [OtherSymb] -> Bool -> [ForeignJSRef] -> LinkableUnit
+ GHC.StgToJS.Types: LongV :: VarType
+ GHC.StgToJS.Types: NullLit :: StaticLit
+ GHC.StgToJS.Types: ObjUnit :: ![FastString] -> ![ClosureInfo] -> ![StaticInfo] -> JStat -> !ByteString -> ![ExpFun] -> ![ForeignJSRef] -> ObjUnit
+ GHC.StgToJS.Types: ObjV :: VarType
+ GHC.StgToJS.Types: OtherSymb :: !Module -> !FastString -> OtherSymb
+ GHC.StgToJS.Types: PRPrimCall :: JStat -> PrimRes
+ GHC.StgToJS.Types: Pap :: ClosureType
+ GHC.StgToJS.Types: PrimInline :: JStat -> PrimRes
+ GHC.StgToJS.Types: PtrType :: JSFFIType
+ GHC.StgToJS.Types: PtrV :: VarType
+ GHC.StgToJS.Types: RefType :: JSFFIType
+ GHC.StgToJS.Types: RtsObjV :: VarType
+ GHC.StgToJS.Types: Running :: ThreadStatus
+ GHC.StgToJS.Types: SlotId :: !Id -> !Int -> StackSlot
+ GHC.StgToJS.Types: SlotUnknown :: StackSlot
+ GHC.StgToJS.Types: StackFrame :: ClosureType
+ GHC.StgToJS.Types: StaticConArg :: !FastString -> [StaticArg] -> StaticArg
+ GHC.StgToJS.Types: StaticData :: !FastString -> [StaticArg] -> StaticVal
+ GHC.StgToJS.Types: StaticFun :: !FastString -> [StaticArg] -> StaticVal
+ GHC.StgToJS.Types: StaticInfo :: !FastString -> !StaticVal -> !Maybe Ident -> StaticInfo
+ GHC.StgToJS.Types: StaticList :: [StaticArg] -> Maybe FastString -> StaticVal
+ GHC.StgToJS.Types: StaticLitArg :: !StaticLit -> StaticArg
+ GHC.StgToJS.Types: StaticObjArg :: !FastString -> StaticArg
+ GHC.StgToJS.Types: StaticThunk :: !Maybe (FastString, [StaticArg]) -> StaticVal
+ GHC.StgToJS.Types: StaticUnboxed :: !StaticUnboxed -> StaticVal
+ GHC.StgToJS.Types: StaticUnboxedBool :: !Bool -> StaticUnboxed
+ GHC.StgToJS.Types: StaticUnboxedDouble :: !SaneDouble -> StaticUnboxed
+ GHC.StgToJS.Types: StaticUnboxedInt :: !Integer -> StaticUnboxed
+ GHC.StgToJS.Types: StaticUnboxedString :: !ByteString -> StaticUnboxed
+ GHC.StgToJS.Types: StaticUnboxedStringOffset :: !ByteString -> StaticUnboxed
+ GHC.StgToJS.Types: StgToJSConfig :: !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !SDocContext -> StgToJSConfig
+ GHC.StgToJS.Types: StringLit :: !FastString -> StaticLit
+ GHC.StgToJS.Types: Thunk :: ClosureType
+ GHC.StgToJS.Types: TypedExpr :: !PrimRep -> [JExpr] -> TypedExpr
+ GHC.StgToJS.Types: VoidV :: VarType
+ GHC.StgToJS.Types: Word16Type :: JSFFIType
+ GHC.StgToJS.Types: Word32Type :: JSFFIType
+ GHC.StgToJS.Types: Word64Type :: JSFFIType
+ GHC.StgToJS.Types: Word8Type :: JSFFIType
+ GHC.StgToJS.Types: [args] :: ExpFun -> [JSFFIType]
+ GHC.StgToJS.Types: [ciLayout] :: ClosureInfo -> CILayout
+ GHC.StgToJS.Types: [ciName] :: ClosureInfo -> FastString
+ GHC.StgToJS.Types: [ciRegsSkip] :: CIRegs -> Int
+ GHC.StgToJS.Types: [ciRegsTypes] :: CIRegs -> [VarType]
+ GHC.StgToJS.Types: [ciRegs] :: ClosureInfo -> CIRegs
+ GHC.StgToJS.Types: [ciStatic] :: ClosureInfo -> CIStatic
+ GHC.StgToJS.Types: [ciType] :: ClosureInfo -> CIType
+ GHC.StgToJS.Types: [ciVar] :: ClosureInfo -> Ident
+ GHC.StgToJS.Types: [citArity] :: CIType -> !Int
+ GHC.StgToJS.Types: [citConstructor] :: CIType -> !Int
+ GHC.StgToJS.Types: [citRegs] :: CIType -> !Int
+ GHC.StgToJS.Types: [csAssertRts] :: StgToJSConfig -> !Bool
+ GHC.StgToJS.Types: [csBoundsCheck] :: StgToJSConfig -> !Bool
+ GHC.StgToJS.Types: [csContext] :: StgToJSConfig -> !SDocContext
+ GHC.StgToJS.Types: [csDebugAlloc] :: StgToJSConfig -> !Bool
+ GHC.StgToJS.Types: [csInlineAlloc] :: StgToJSConfig -> !Bool
+ GHC.StgToJS.Types: [csInlineBlackhole] :: StgToJSConfig -> !Bool
+ GHC.StgToJS.Types: [csInlineEnter] :: StgToJSConfig -> !Bool
+ GHC.StgToJS.Types: [csInlineLoadRegs] :: StgToJSConfig -> !Bool
+ GHC.StgToJS.Types: [csInlinePush] :: StgToJSConfig -> !Bool
+ GHC.StgToJS.Types: [csProf] :: StgToJSConfig -> !Bool
+ GHC.StgToJS.Types: [csRuntimeAssert] :: StgToJSConfig -> !Bool
+ GHC.StgToJS.Types: [csTraceForeign] :: StgToJSConfig -> !Bool
+ GHC.StgToJS.Types: [csTraceRts] :: StgToJSConfig -> !Bool
+ GHC.StgToJS.Types: [foreignRefArgs] :: ForeignJSRef -> ![FastString]
+ GHC.StgToJS.Types: [foreignRefCConv] :: ForeignJSRef -> !CCallConv
+ GHC.StgToJS.Types: [foreignRefPattern] :: ForeignJSRef -> !FastString
+ GHC.StgToJS.Types: [foreignRefResult] :: ForeignJSRef -> !FastString
+ GHC.StgToJS.Types: [foreignRefSafety] :: ForeignJSRef -> !Safety
+ GHC.StgToJS.Types: [foreignRefSrcSpan] :: ForeignJSRef -> !FastString
+ GHC.StgToJS.Types: [ggsClosureInfo] :: GenGroupState -> [ClosureInfo]
+ GHC.StgToJS.Types: [ggsExtraDeps] :: GenGroupState -> Set OtherSymb
+ GHC.StgToJS.Types: [ggsForeignRefs] :: GenGroupState -> [ForeignJSRef]
+ GHC.StgToJS.Types: [ggsGlobalIdCache] :: GenGroupState -> GlobalIdCache
+ GHC.StgToJS.Types: [ggsStackDepth] :: GenGroupState -> Int
+ GHC.StgToJS.Types: [ggsStack] :: GenGroupState -> [StackSlot]
+ GHC.StgToJS.Types: [ggsStatic] :: GenGroupState -> [StaticInfo]
+ GHC.StgToJS.Types: [ggsToplevelStats] :: GenGroupState -> [JStat]
+ GHC.StgToJS.Types: [gsGlobal] :: GenState -> [JStat]
+ GHC.StgToJS.Types: [gsGroup] :: GenState -> GenGroupState
+ GHC.StgToJS.Types: [gsId] :: GenState -> {-# UNPACK #-} !FastMutInt
+ GHC.StgToJS.Types: [gsIdents] :: GenState -> !IdCache
+ GHC.StgToJS.Types: [gsModule] :: GenState -> !Module
+ GHC.StgToJS.Types: [gsSettings] :: GenState -> !StgToJSConfig
+ GHC.StgToJS.Types: [gsUnfloated] :: GenState -> !UniqFM Id CgStgExpr
+ GHC.StgToJS.Types: [isIO] :: ExpFun -> !Bool
+ GHC.StgToJS.Types: [layoutSize] :: CILayout -> !Int
+ GHC.StgToJS.Types: [layout] :: CILayout -> [VarType]
+ GHC.StgToJS.Types: [luForeignRefs] :: LinkableUnit -> [ForeignJSRef]
+ GHC.StgToJS.Types: [luIdDeps] :: LinkableUnit -> [Id]
+ GHC.StgToJS.Types: [luIdExports] :: LinkableUnit -> [Id]
+ GHC.StgToJS.Types: [luObjUnit] :: LinkableUnit -> ObjUnit
+ GHC.StgToJS.Types: [luOtherDeps] :: LinkableUnit -> [OtherSymb]
+ GHC.StgToJS.Types: [luOtherExports] :: LinkableUnit -> [FastString]
+ GHC.StgToJS.Types: [luPseudoIdDeps] :: LinkableUnit -> [Unique]
+ GHC.StgToJS.Types: [luRequired] :: LinkableUnit -> Bool
+ GHC.StgToJS.Types: [oiClInfo] :: ObjUnit -> ![ClosureInfo]
+ GHC.StgToJS.Types: [oiFExports] :: ObjUnit -> ![ExpFun]
+ GHC.StgToJS.Types: [oiFImports] :: ObjUnit -> ![ForeignJSRef]
+ GHC.StgToJS.Types: [oiRaw] :: ObjUnit -> !ByteString
+ GHC.StgToJS.Types: [oiStat] :: ObjUnit -> JStat
+ GHC.StgToJS.Types: [oiStatic] :: ObjUnit -> ![StaticInfo]
+ GHC.StgToJS.Types: [oiSymbols] :: ObjUnit -> ![FastString]
+ GHC.StgToJS.Types: [result] :: ExpFun -> !JSFFIType
+ GHC.StgToJS.Types: [siCC] :: StaticInfo -> !Maybe Ident
+ GHC.StgToJS.Types: [siVal] :: StaticInfo -> !StaticVal
+ GHC.StgToJS.Types: [siVar] :: StaticInfo -> !FastString
+ GHC.StgToJS.Types: [staticRefs] :: CIStatic -> [FastString]
+ GHC.StgToJS.Types: [typex_expr] :: TypedExpr -> [JExpr]
+ GHC.StgToJS.Types: [typex_typ] :: TypedExpr -> !PrimRep
+ GHC.StgToJS.Types: ctJsName :: ClosureType -> String
+ GHC.StgToJS.Types: ctNum :: ClosureType -> Int
+ GHC.StgToJS.Types: data CILayout
+ GHC.StgToJS.Types: data CIRegs
+ GHC.StgToJS.Types: data CIType
+ GHC.StgToJS.Types: data ClosureInfo
+ GHC.StgToJS.Types: data ClosureType
+ GHC.StgToJS.Types: data ExpFun
+ GHC.StgToJS.Types: data ExprResult
+ GHC.StgToJS.Types: data ForeignJSRef
+ GHC.StgToJS.Types: data GenGroupState
+ GHC.StgToJS.Types: data GenState
+ GHC.StgToJS.Types: data IdKey
+ GHC.StgToJS.Types: data IdType
+ GHC.StgToJS.Types: data JSFFIType
+ GHC.StgToJS.Types: data LinkableUnit
+ GHC.StgToJS.Types: data ObjUnit
+ GHC.StgToJS.Types: data OtherSymb
+ GHC.StgToJS.Types: data PrimRes
+ GHC.StgToJS.Types: data StackSlot
+ GHC.StgToJS.Types: data StaticArg
+ GHC.StgToJS.Types: data StaticInfo
+ GHC.StgToJS.Types: data StaticLit
+ GHC.StgToJS.Types: data StaticUnboxed
+ GHC.StgToJS.Types: data StaticVal
+ GHC.StgToJS.Types: data StgToJSConfig
+ GHC.StgToJS.Types: data ThreadStatus
+ GHC.StgToJS.Types: data TypedExpr
+ GHC.StgToJS.Types: data VarType
+ GHC.StgToJS.Types: instance Control.DeepSeq.NFData GHC.StgToJS.Types.CILayout
+ GHC.StgToJS.Types: instance Control.DeepSeq.NFData GHC.StgToJS.Types.CIRegs
+ GHC.StgToJS.Types: instance Control.DeepSeq.NFData GHC.StgToJS.Types.CIType
+ GHC.StgToJS.Types: instance Control.DeepSeq.NFData GHC.StgToJS.Types.StaticUnboxed
+ GHC.StgToJS.Types: instance Control.DeepSeq.NFData GHC.StgToJS.Types.VarType
+ GHC.StgToJS.Types: instance GHC.Base.Monoid GHC.StgToJS.Types.CIStatic
+ GHC.StgToJS.Types: instance GHC.Base.Semigroup GHC.StgToJS.Types.CIStatic
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.CILayout
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.CIRegs
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.CIStatic
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.CIType
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.ClosureInfo
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.ClosureType
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.ExpFun
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.ExprResult
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.ExprValData
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.IdKey
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.IdType
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.JSFFIType
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.OtherSymb
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.StackSlot
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.StaticArg
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.StaticInfo
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.StaticLit
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.StaticUnboxed
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.StaticVal
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.ThreadStatus
+ GHC.StgToJS.Types: instance GHC.Classes.Eq GHC.StgToJS.Types.VarType
+ GHC.StgToJS.Types: instance GHC.Classes.Ord GHC.StgToJS.Types.CILayout
+ GHC.StgToJS.Types: instance GHC.Classes.Ord GHC.StgToJS.Types.CIRegs
+ GHC.StgToJS.Types: instance GHC.Classes.Ord GHC.StgToJS.Types.CIType
+ GHC.StgToJS.Types: instance GHC.Classes.Ord GHC.StgToJS.Types.ClosureType
+ GHC.StgToJS.Types: instance GHC.Classes.Ord GHC.StgToJS.Types.ExpFun
+ GHC.StgToJS.Types: instance GHC.Classes.Ord GHC.StgToJS.Types.IdKey
+ GHC.StgToJS.Types: instance GHC.Classes.Ord GHC.StgToJS.Types.IdType
+ GHC.StgToJS.Types: instance GHC.Classes.Ord GHC.StgToJS.Types.JSFFIType
+ GHC.StgToJS.Types: instance GHC.Classes.Ord GHC.StgToJS.Types.OtherSymb
+ GHC.StgToJS.Types: instance GHC.Classes.Ord GHC.StgToJS.Types.StackSlot
+ GHC.StgToJS.Types: instance GHC.Classes.Ord GHC.StgToJS.Types.StaticUnboxed
+ GHC.StgToJS.Types: instance GHC.Classes.Ord GHC.StgToJS.Types.ThreadStatus
+ GHC.StgToJS.Types: instance GHC.Classes.Ord GHC.StgToJS.Types.VarType
+ GHC.StgToJS.Types: instance GHC.Enum.Bounded GHC.StgToJS.Types.ClosureType
+ GHC.StgToJS.Types: instance GHC.Enum.Bounded GHC.StgToJS.Types.ThreadStatus
+ GHC.StgToJS.Types: instance GHC.Enum.Bounded GHC.StgToJS.Types.VarType
+ GHC.StgToJS.Types: instance GHC.Enum.Enum GHC.StgToJS.Types.ClosureType
+ GHC.StgToJS.Types: instance GHC.Enum.Enum GHC.StgToJS.Types.IdType
+ GHC.StgToJS.Types: instance GHC.Enum.Enum GHC.StgToJS.Types.JSFFIType
+ GHC.StgToJS.Types: instance GHC.Enum.Enum GHC.StgToJS.Types.ThreadStatus
+ GHC.StgToJS.Types: instance GHC.Enum.Enum GHC.StgToJS.Types.VarType
+ GHC.StgToJS.Types: instance GHC.Generics.Generic GHC.StgToJS.Types.CILayout
+ GHC.StgToJS.Types: instance GHC.Generics.Generic GHC.StgToJS.Types.CIRegs
+ GHC.StgToJS.Types: instance GHC.Generics.Generic GHC.StgToJS.Types.CIStatic
+ GHC.StgToJS.Types: instance GHC.Generics.Generic GHC.StgToJS.Types.CIType
+ GHC.StgToJS.Types: instance GHC.Generics.Generic GHC.StgToJS.Types.ClosureInfo
+ GHC.StgToJS.Types: instance GHC.Generics.Generic GHC.StgToJS.Types.ForeignJSRef
+ GHC.StgToJS.Types: instance GHC.Generics.Generic GHC.StgToJS.Types.StaticArg
+ GHC.StgToJS.Types: instance GHC.Generics.Generic GHC.StgToJS.Types.StaticInfo
+ GHC.StgToJS.Types: instance GHC.Generics.Generic GHC.StgToJS.Types.StaticLit
+ GHC.StgToJS.Types: instance GHC.Generics.Generic GHC.StgToJS.Types.StaticUnboxed
+ GHC.StgToJS.Types: instance GHC.Generics.Generic GHC.StgToJS.Types.StaticVal
+ GHC.StgToJS.Types: instance GHC.Generics.Generic GHC.StgToJS.Types.VarType
+ GHC.StgToJS.Types: instance GHC.JS.Make.ToJExpr GHC.StgToJS.Types.CIStatic
+ GHC.StgToJS.Types: instance GHC.JS.Make.ToJExpr GHC.StgToJS.Types.ClosureType
+ GHC.StgToJS.Types: instance GHC.JS.Make.ToJExpr GHC.StgToJS.Types.StaticLit
+ GHC.StgToJS.Types: instance GHC.JS.Make.ToJExpr GHC.StgToJS.Types.VarType
+ GHC.StgToJS.Types: instance GHC.Show.Show GHC.StgToJS.Types.CILayout
+ GHC.StgToJS.Types: instance GHC.Show.Show GHC.StgToJS.Types.CIRegs
+ GHC.StgToJS.Types: instance GHC.Show.Show GHC.StgToJS.Types.CIStatic
+ GHC.StgToJS.Types: instance GHC.Show.Show GHC.StgToJS.Types.CIType
+ GHC.StgToJS.Types: instance GHC.Show.Show GHC.StgToJS.Types.ClosureInfo
+ GHC.StgToJS.Types: instance GHC.Show.Show GHC.StgToJS.Types.ClosureType
+ GHC.StgToJS.Types: instance GHC.Show.Show GHC.StgToJS.Types.ExpFun
+ GHC.StgToJS.Types: instance GHC.Show.Show GHC.StgToJS.Types.JSFFIType
+ GHC.StgToJS.Types: instance GHC.Show.Show GHC.StgToJS.Types.StaticArg
+ GHC.StgToJS.Types: instance GHC.Show.Show GHC.StgToJS.Types.StaticInfo
+ GHC.StgToJS.Types: instance GHC.Show.Show GHC.StgToJS.Types.StaticLit
+ GHC.StgToJS.Types: instance GHC.Show.Show GHC.StgToJS.Types.StaticUnboxed
+ GHC.StgToJS.Types: instance GHC.Show.Show GHC.StgToJS.Types.StaticVal
+ GHC.StgToJS.Types: instance GHC.Show.Show GHC.StgToJS.Types.ThreadStatus
+ GHC.StgToJS.Types: instance GHC.Show.Show GHC.StgToJS.Types.VarType
+ GHC.StgToJS.Types: instance GHC.Utils.Outputable.Outputable GHC.StgToJS.Types.StaticArg
+ GHC.StgToJS.Types: instance GHC.Utils.Outputable.Outputable GHC.StgToJS.Types.StaticLit
+ GHC.StgToJS.Types: instance GHC.Utils.Outputable.Outputable GHC.StgToJS.Types.TypedExpr
+ GHC.StgToJS.Types: newtype CIStatic
+ GHC.StgToJS.Types: newtype ExprValData
+ GHC.StgToJS.Types: newtype GlobalIdCache
+ GHC.StgToJS.Types: newtype IdCache
+ GHC.StgToJS.Types: threadStatusJsName :: ThreadStatus -> String
+ GHC.StgToJS.Types: threadStatusNum :: ThreadStatus -> Int
+ GHC.StgToJS.Types: type G = StateT GenState IO
+ GHC.StgToJS.Utils: assignCoerce1 :: HasDebugCallStack => [TypedExpr] -> [TypedExpr] -> JStat
+ GHC.StgToJS.Utils: assignToExprCtx :: HasDebugCallStack => ExprCtx -> [JExpr] -> JStat
+ GHC.StgToJS.Utils: assignToTypedExprs :: HasDebugCallStack => [TypedExpr] -> [JExpr] -> JStat
+ GHC.SysTools.Cpp: CppOpts :: !Bool -> !Bool -> CppOpts
+ GHC.SysTools.Cpp: [cppLinePragmas] :: CppOpts -> !Bool
+ GHC.SysTools.Cpp: [cppUseCc] :: CppOpts -> !Bool
+ GHC.SysTools.Cpp: applyCDefs :: DefunctionalizedCDefs -> Logger -> DynFlags -> IO [String]
+ GHC.SysTools.Cpp: data CppOpts
+ GHC.SysTools.Cpp: doCpp :: Logger -> TmpFs -> DynFlags -> UnitEnv -> CppOpts -> FilePath -> FilePath -> IO ()
+ GHC.SysTools.Cpp: getGhcVersionPathName :: DynFlags -> UnitEnv -> IO FilePath
+ GHC.SysTools.Cpp: offsetIncludePaths :: DynFlags -> IncludeSpecs -> IncludeSpecs
+ GHC.SysTools.Tasks: runEmscripten :: Logger -> DynFlags -> [Option] -> IO ()
+ GHC.Tc.Gen.Head: [eva_at] :: HsExprArg (p :: TcPass) -> !LHsToken "@" GhcRn
+ GHC.Tc.Gen.Head: addHeadCtxt :: AppCtxt -> TcM a -> TcM a
+ GHC.Tc.Gen.Splice: tcTypedSplice :: Name -> LHsExpr GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)
+ GHC.Tc.Plugin: Found :: ModLocation -> Module -> FindResult
+ GHC.Tc.Plugin: FoundMultiple :: [(Module, ModuleOrigin)] -> FindResult
+ GHC.Tc.Plugin: NoPackage :: Unit -> FindResult
+ GHC.Tc.Plugin: NotFound :: [FilePath] -> Maybe Unit -> [Unit] -> [Unit] -> [(Unit, UnusableUnitReason)] -> [ModuleSuggestion] -> FindResult
+ GHC.Tc.Plugin: [fr_mods_hidden] :: FindResult -> [Unit]
+ GHC.Tc.Plugin: [fr_paths] :: FindResult -> [FilePath]
+ GHC.Tc.Plugin: [fr_pkg] :: FindResult -> Maybe Unit
+ GHC.Tc.Plugin: [fr_pkgs_hidden] :: FindResult -> [Unit]
+ GHC.Tc.Plugin: [fr_suggestions] :: FindResult -> [ModuleSuggestion]
+ GHC.Tc.Plugin: [fr_unusables] :: FindResult -> [(Unit, UnusableUnitReason)]
+ GHC.Tc.Plugin: data FindResult
+ GHC.Tc.Plugin: data TcPluginM a
+ GHC.Tc.Plugin: findImportedModule :: ModuleName -> PkgQual -> TcPluginM FindResult
+ GHC.Tc.Plugin: getEnvs :: TcPluginM (TcGblEnv, TcLclEnv)
+ GHC.Tc.Plugin: getFamInstEnvs :: TcPluginM (FamInstEnv, FamInstEnv)
+ GHC.Tc.Plugin: getInstEnvs :: TcPluginM InstEnvs
+ GHC.Tc.Plugin: getTargetPlatform :: TcPluginM Platform
+ GHC.Tc.Plugin: getTopEnv :: TcPluginM HscEnv
+ GHC.Tc.Plugin: isTouchableTcPluginM :: TcTyVar -> TcPluginM Bool
+ GHC.Tc.Plugin: lookupOrig :: Module -> OccName -> TcPluginM Name
+ GHC.Tc.Plugin: matchFam :: TyCon -> [Type] -> TcPluginM (Maybe Reduction)
+ GHC.Tc.Plugin: newCoercionHole :: PredType -> TcPluginM CoercionHole
+ GHC.Tc.Plugin: newEvVar :: PredType -> TcPluginM EvVar
+ GHC.Tc.Plugin: newFlexiTyVar :: Kind -> TcPluginM TcTyVar
+ GHC.Tc.Plugin: newGiven :: EvBindsVar -> CtLoc -> PredType -> EvExpr -> TcPluginM CtEvidence
+ GHC.Tc.Plugin: newUnique :: TcPluginM Unique
+ GHC.Tc.Plugin: newWanted :: CtLoc -> PredType -> TcPluginM CtEvidence
+ GHC.Tc.Plugin: setEvBind :: EvBindsVar -> EvBind -> TcPluginM ()
+ GHC.Tc.Plugin: tcLookup :: Name -> TcPluginM TcTyThing
+ GHC.Tc.Plugin: tcLookupClass :: Name -> TcPluginM Class
+ GHC.Tc.Plugin: tcLookupDataCon :: Name -> TcPluginM DataCon
+ GHC.Tc.Plugin: tcLookupGlobal :: Name -> TcPluginM TyThing
+ GHC.Tc.Plugin: tcLookupId :: Name -> TcPluginM Id
+ GHC.Tc.Plugin: tcLookupTyCon :: Name -> TcPluginM TyCon
+ GHC.Tc.Plugin: tcPluginIO :: IO a -> TcPluginM a
+ GHC.Tc.Plugin: tcPluginTrace :: String -> SDoc -> TcPluginM ()
+ GHC.Tc.Plugin: unsafeTcPluginTcM :: TcM a -> TcPluginM a
+ GHC.Tc.Plugin: zonkCt :: Ct -> TcPluginM Ct
+ GHC.Tc.Plugin: zonkTcType :: TcType -> TcPluginM TcType
+ GHC.Tc.Solver.Monad: ctLocWarnTcS :: CtLoc -> TcRnMessage -> TcS ()
+ GHC.Tc.Solver.Monad: instance Control.Monad.Fix.MonadFix GHC.Tc.Solver.Monad.TcS
+ GHC.Tc.Solver.Monad: recordUsedGREs :: Bag GlobalRdrElt -> TcS ()
+ GHC.Tc.Solver.Rewrite: rewriteForErrors :: CtEvidence -> TcType -> TcS (Reduction, RewriterSet)
+ GHC.Tc.Utils.Monad: getNamePprCtx :: TcRn NamePprCtx
+ GHC.Tc.Utils.Monad: mkDetailedMessage :: ErrInfo -> TcRnMessage -> TcRnMessageDetailed
+ GHC.Tc.Utils.Unify: tcSubTypeDS :: HsExpr GhcRn -> TcRhoType -> ExpRhoType -> TcM HsWrapper
+ GHC.ThToHs: instance GHC.Base.Applicative (GHC.ThToHs.CvtM' err)
+ GHC.ThToHs: instance GHC.Base.Functor (GHC.ThToHs.CvtM' err)
+ GHC.ThToHs: instance GHC.Base.Monad (GHC.ThToHs.CvtM' err)
+ GHC.Wasm.ControlFlow: (<>) :: forall s e pre mid post. WasmControl s e pre mid -> WasmControl s e mid post -> WasmControl s e pre post
+ GHC.Wasm.ControlFlow: BrTableInterval :: Integer -> Integer -> BrTableInterval
+ GHC.Wasm.ControlFlow: WasmFunctionType :: TypeList pre -> TypeList post -> WasmFunctionType pre post
+ GHC.Wasm.ControlFlow: [TagF32] :: WasmTypeTag 'F32
+ GHC.Wasm.ControlFlow: [TagF64] :: WasmTypeTag 'F64
+ GHC.Wasm.ControlFlow: [TagI32] :: WasmTypeTag 'I32
+ GHC.Wasm.ControlFlow: [TagI64] :: WasmTypeTag 'I64
+ GHC.Wasm.ControlFlow: [TypeListCons] :: WasmTypeTag t -> TypeList ts -> TypeList (t : ts)
+ GHC.Wasm.ControlFlow: [TypeListNil] :: TypeList '[]
+ GHC.Wasm.ControlFlow: [WasmActions] :: s -> WasmControl s e stack stack
+ GHC.Wasm.ControlFlow: [WasmBlock] :: WasmFunctionType pre post -> WasmControl s e pre post -> WasmControl s e pre post
+ GHC.Wasm.ControlFlow: [WasmBrTable] :: e -> BrTableInterval -> [Int] -> Int -> WasmControl s e dropped destination
+ GHC.Wasm.ControlFlow: [WasmBr] :: Int -> WasmControl s e dropped destination
+ GHC.Wasm.ControlFlow: [WasmFallthrough] :: WasmControl s e dropped destination
+ GHC.Wasm.ControlFlow: [WasmIfTop] :: WasmFunctionType pre post -> WasmControl s e pre post -> WasmControl s e pre post -> WasmControl s e ('I32 : pre) post
+ GHC.Wasm.ControlFlow: [WasmLoop] :: WasmFunctionType pre post -> WasmControl s e pre post -> WasmControl s e pre post
+ GHC.Wasm.ControlFlow: [WasmPush] :: WasmTypeTag t -> e -> WasmControl s e stack (t : stack)
+ GHC.Wasm.ControlFlow: [WasmSeq] :: WasmControl s e pre mid -> WasmControl s e mid post -> WasmControl s e pre post
+ GHC.Wasm.ControlFlow: [WasmTailCall] :: e -> WasmControl s e t1star t2star
+ GHC.Wasm.ControlFlow: [bti_count] :: BrTableInterval -> Integer
+ GHC.Wasm.ControlFlow: [bti_lo] :: BrTableInterval -> Integer
+ GHC.Wasm.ControlFlow: [ft_pops] :: WasmFunctionType pre post -> TypeList pre
+ GHC.Wasm.ControlFlow: [ft_pushes] :: WasmFunctionType pre post -> TypeList post
+ GHC.Wasm.ControlFlow: data BrTableInterval
+ GHC.Wasm.ControlFlow: data TypeList :: [WasmType] -> Type
+ GHC.Wasm.ControlFlow: data WasmControl :: Type -> Type -> [WasmType] -> [WasmType] -> Type
+ GHC.Wasm.ControlFlow: data WasmFunctionType pre post
+ GHC.Wasm.ControlFlow: data WasmType
+ GHC.Wasm.ControlFlow: data WasmTypeTag :: WasmType -> Type
+ GHC.Wasm.ControlFlow: inclusiveInterval :: Integer -> Integer -> BrTableInterval
+ GHC.Wasm.ControlFlow: pattern WasmIf :: WasmFunctionType pre post -> e -> WasmControl s e pre post -> WasmControl s e pre post -> WasmControl s e pre post
+ GHC.Wasm.ControlFlow.FromCmm: instance GHC.Utils.Outputable.Outputable GHC.Wasm.ControlFlow.FromCmm.ContainingSyntax
+ GHC.Wasm.ControlFlow.FromCmm: instance GHC.Utils.Outputable.Outputable GHC.Wasm.ControlFlow.FromCmm.Context
+ GHC.Wasm.ControlFlow.FromCmm: structuredControl :: forall expr stmt m. Applicative m => Platform -> (Label -> CmmExpr -> m expr) -> (Label -> CmmActions -> m stmt) -> CmmGraph -> m (WasmControl stmt expr '[] '[ 'I32])
+ GHCi.CreateBCO: createBCOs :: [ResolvedBCO] -> IO [HValueRef]
+ GHCi.InfoTable: mkConInfoTable :: Bool -> Int -> Int -> Int -> Int -> ByteString -> IO (Ptr StgInfoTable)
+ GHCi.ObjLink: DontRetainCAFs :: ShouldRetainCAFs
+ GHCi.ObjLink: RetainCAFs :: ShouldRetainCAFs
+ GHCi.ObjLink: addLibrarySearchPath :: String -> IO (Ptr ())
+ GHCi.ObjLink: data ShouldRetainCAFs
+ GHCi.ObjLink: findSystemLibrary :: String -> IO (Maybe String)
+ GHCi.ObjLink: initObjLinker :: ShouldRetainCAFs -> IO ()
+ GHCi.ObjLink: loadArchive :: String -> IO ()
+ GHCi.ObjLink: loadDLL :: String -> IO (Maybe String)
+ GHCi.ObjLink: loadObj :: String -> IO ()
+ GHCi.ObjLink: lookupClosure :: String -> IO (Maybe HValueRef)
+ GHCi.ObjLink: lookupSymbol :: String -> IO (Maybe (Ptr a))
+ GHCi.ObjLink: purgeObj :: String -> IO ()
+ GHCi.ObjLink: removeLibrarySearchPath :: Ptr () -> IO Bool
+ GHCi.ObjLink: resolveObjs :: IO Bool
+ GHCi.ObjLink: unloadObj :: String -> IO ()
+ GHCi.Run: redirectInterrupts :: ThreadId -> IO a -> IO a
+ GHCi.Run: run :: Message a -> IO a
+ GHCi.Signals: installSignalHandlers :: IO ()
+ GHCi.StaticPtrTable: sptAddEntry :: Fingerprint -> HValue -> IO ()
+ GHCi.TH: GHCiQException :: QState -> String -> GHCiQException
+ GHCi.TH: data GHCiQException
+ GHCi.TH: instance Control.Monad.Fail.MonadFail GHCi.TH.GHCiQ
+ GHCi.TH: instance Control.Monad.IO.Class.MonadIO GHCi.TH.GHCiQ
+ GHCi.TH: instance GHC.Base.Applicative GHCi.TH.GHCiQ
+ GHCi.TH: instance GHC.Base.Functor GHCi.TH.GHCiQ
+ GHCi.TH: instance GHC.Base.Monad GHCi.TH.GHCiQ
+ GHCi.TH: instance GHC.Exception.Type.Exception GHCi.TH.GHCiQException
+ GHCi.TH: instance GHC.Show.Show GHCi.TH.GHCiQException
+ GHCi.TH: instance Language.Haskell.TH.Syntax.Quasi GHCi.TH.GHCiQ
+ GHCi.TH: runModFinalizerRefs :: Pipe -> RemoteRef (IORef QState) -> [RemoteRef (Q ())] -> IO ()
+ GHCi.TH: runTH :: Pipe -> RemoteRef (IORef QState) -> HValueRef -> THResultType -> Maybe Loc -> IO ByteString
+ GHCi.TH: startTH :: IO (RemoteRef (IORef QState))
+ Language.Haskell.TH.CodeDo: (>>) :: Monad m => m a -> Code m b -> Code m b
+ Language.Haskell.TH.CodeDo: (>>=) :: Monad m => m a -> (a -> Code m b) -> Code m b
+ Language.Haskell.TH.Quote: QuasiQuoter :: (String -> Q Exp) -> (String -> Q Pat) -> (String -> Q Type) -> (String -> Q [Dec]) -> QuasiQuoter
+ Language.Haskell.TH.Quote: [quoteDec] :: QuasiQuoter -> String -> Q [Dec]
+ Language.Haskell.TH.Quote: [quoteExp] :: QuasiQuoter -> String -> Q Exp
+ Language.Haskell.TH.Quote: [quotePat] :: QuasiQuoter -> String -> Q Pat
+ Language.Haskell.TH.Quote: [quoteType] :: QuasiQuoter -> String -> Q Type
+ Language.Haskell.TH.Quote: data QuasiQuoter
+ Language.Haskell.TH.Quote: dataToExpQ :: (Quote m, Data a) => (forall b. Data b => b -> Maybe (m Exp)) -> a -> m Exp
+ Language.Haskell.TH.Quote: dataToPatQ :: (Quote m, Data a) => (forall b. Data b => b -> Maybe (m Pat)) -> a -> m Pat
+ Language.Haskell.TH.Quote: dataToQa :: (Quote m, Data a) => (Name -> k) -> (Lit -> m q) -> (k -> [m q] -> m q) -> (forall b. Data b => b -> Maybe (m q)) -> a -> m q
+ Language.Haskell.TH.Quote: quoteFile :: QuasiQuoter -> QuasiQuoter
- GHC: DynFlags :: GhcMode -> GhcLink -> !Backend -> {-# UNPACK #-} !GhcNameVersion -> {-# UNPACK #-} !FileSettings -> Platform -> {-# UNPACK #-} !ToolSettings -> {-# UNPACK #-} !PlatformMisc -> [(String, String)] -> TempDir -> LlvmConfig -> Int -> Int -> Int -> Int -> Int -> Maybe String -> [Int] -> Maybe Int -> Bool -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> Int -> Int -> Int -> !Int -> Maybe Int -> Maybe Int -> Int -> Maybe Word -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> Bool -> Maybe Int -> Int -> [FilePath] -> ModuleName -> Maybe String -> IntWithInf -> IntWithInf -> UnitId -> Maybe UnitId -> [(ModuleName, Module)] -> Maybe FilePath -> Maybe String -> Set ModuleName -> Set ModuleName -> Ways -> Maybe (String, Int) -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> String -> String -> String -> String -> String -> String -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> DynLibLoader -> !Bool -> FilePath -> Maybe FilePath -> [Option] -> IncludeSpecs -> [String] -> [String] -> [String] -> Maybe String -> RtsOptsEnabled -> Bool -> String -> [ModuleName] -> [(ModuleName, String)] -> [String] -> FilePath -> Bool -> Bool -> [ModuleName] -> [String] -> [PackageDBFlag] -> [IgnorePackageFlag] -> [PackageFlag] -> [PackageFlag] -> [TrustFlag] -> Maybe FilePath -> EnumSet DumpFlag -> EnumSet GeneralFlag -> EnumSet WarningFlag -> EnumSet WarningFlag -> Maybe Language -> SafeHaskellMode -> Bool -> Bool -> SrcSpan -> SrcSpan -> SrcSpan -> SrcSpan -> SrcSpan -> SrcSpan -> SrcSpan -> SrcSpan -> SrcSpan -> [OnOff Extension] -> EnumSet Extension -> !UnfoldingOpts -> Int -> Int -> FlushOut -> Maybe FilePath -> Maybe String -> [String] -> Int -> Int -> Bool -> OverridingBool -> Bool -> Scheme -> ProfAuto -> [CallerCcFilter] -> Maybe String -> Maybe SseVersion -> Maybe BmiVersion -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> IORef (Maybe LinkerInfo) -> IORef (Maybe CompilerInfo) -> IORef (Maybe CompilerInfo) -> Int -> Int -> Int -> Bool -> Maybe Int -> Word -> Int -> Weights -> DynFlags
+ GHC: DynFlags :: GhcMode -> GhcLink -> !Backend -> {-# UNPACK #-} !GhcNameVersion -> {-# UNPACK #-} !FileSettings -> Platform -> {-# UNPACK #-} !ToolSettings -> {-# UNPACK #-} !PlatformMisc -> [(String, String)] -> TempDir -> Int -> Int -> Int -> Int -> Int -> Maybe String -> [Int] -> Maybe Int -> Bool -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> Int -> Int -> Int -> !Int -> Maybe Int -> Maybe Int -> Int -> Maybe Word -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe Int -> Bool -> Maybe Int -> Int -> [FilePath] -> ModuleName -> Maybe String -> IntWithInf -> IntWithInf -> UnitId -> Maybe UnitId -> [(ModuleName, Module)] -> Maybe FilePath -> Maybe String -> Set ModuleName -> Set ModuleName -> Ways -> Maybe (String, Int) -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> String -> String -> String -> String -> String -> String -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> DynLibLoader -> !Bool -> FilePath -> Maybe FilePath -> [Option] -> IncludeSpecs -> [String] -> [String] -> [String] -> Maybe String -> RtsOptsEnabled -> Bool -> String -> [ModuleName] -> [(ModuleName, String)] -> [String] -> [ExternalPluginSpec] -> FilePath -> Bool -> Bool -> [ModuleName] -> [String] -> [PackageDBFlag] -> [IgnorePackageFlag] -> [PackageFlag] -> [PackageFlag] -> [TrustFlag] -> Maybe FilePath -> EnumSet DumpFlag -> EnumSet GeneralFlag -> EnumSet WarningFlag -> EnumSet WarningFlag -> Maybe Language -> SafeHaskellMode -> Bool -> Bool -> SrcSpan -> SrcSpan -> SrcSpan -> SrcSpan -> SrcSpan -> SrcSpan -> SrcSpan -> SrcSpan -> SrcSpan -> [OnOff Extension] -> EnumSet Extension -> !UnfoldingOpts -> Int -> Int -> FlushOut -> Maybe FilePath -> Maybe String -> [String] -> Int -> Int -> Bool -> OverridingBool -> Bool -> Scheme -> ProfAuto -> [CallerCcFilter] -> Maybe String -> Maybe SseVersion -> Maybe BmiVersion -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> IORef (Maybe LinkerInfo) -> IORef (Maybe CompilerInfo) -> IORef (Maybe CompilerInfo) -> Int -> Int -> Int -> Bool -> Maybe Int -> Word -> Int -> Weights -> DynFlags
- GHC: ModIface :: !Module -> !Maybe Module -> !HscSource -> Dependencies -> [Usage] -> ![IfaceExport] -> !Bool -> [(OccName, Fixity)] -> Warnings GhcRn -> [IfaceAnnotation] -> [IfaceDeclExts phase] -> !Maybe GlobalRdrEnv -> [IfaceClsInst] -> [IfaceFamInst] -> [IfaceRule] -> !AnyHpcUsage -> !IfaceTrustInfo -> !Bool -> ![IfaceCompleteMatch] -> Maybe Docs -> !IfaceBackendExts phase -> !ExtensibleFields -> !Fingerprint -> ModIface_ (phase :: ModIfacePhase)
+ GHC: ModIface :: !Module -> !Maybe Module -> !HscSource -> Dependencies -> [Usage] -> ![IfaceExport] -> !Bool -> [(OccName, Fixity)] -> Warnings GhcRn -> [IfaceAnnotation] -> [IfaceDeclExts phase] -> Maybe [IfaceBindingX IfaceMaybeRhs IfaceTopBndrInfo] -> !Maybe GlobalRdrEnv -> [IfaceClsInst] -> [IfaceFamInst] -> [IfaceRule] -> !AnyHpcUsage -> !IfaceTrustInfo -> !Bool -> ![IfaceCompleteMatch] -> !Maybe Docs -> !IfaceBackendExts phase -> !ExtensibleFields -> !Fingerprint -> ModIface_ (phase :: ModIfacePhase)
- GHC: [mi_docs] :: ModIface_ (phase :: ModIfacePhase) -> Maybe Docs
+ GHC: [mi_docs] :: ModIface_ (phase :: ModIfacePhase) -> !Maybe Docs
- GHC: alwaysQualify :: PrintUnqualified
+ GHC: alwaysQualify :: NamePprCtx
- GHC: parseName :: GhcMonad m => String -> m [Name]
+ GHC: parseName :: GhcMonad m => String -> m (NonEmpty Name)
- GHC: parser :: String -> DynFlags -> FilePath -> (WarningMessages, Either ErrorMessages (Located HsModule))
+ GHC: parser :: String -> DynFlags -> FilePath -> (WarningMessages, Either ErrorMessages (Located (HsModule GhcPs)))
- GHC: pprForAll :: [TyCoVarBinder] -> SDoc
+ GHC: pprForAll :: [ForAllTyBinder] -> SDoc
- GHC: pprModule :: Module -> SDoc
+ GHC: pprModule :: IsLine doc => Module -> doc
- GHC: type ParsedSource = Located HsModule
+ GHC: type ParsedSource = Located (HsModule GhcPs)
- GHC.ByteCode.Instr: PUSH_ALTS :: ProtoBCO Name -> ArgRep -> BCInstr
+ GHC.ByteCode.Instr: PUSH_ALTS :: ProtoBCO Name -> BCInstr
- GHC.ByteCode.Instr: RETURN :: ArgRep -> BCInstr
+ GHC.ByteCode.Instr: RETURN :: BCInstr
- GHC.Cmm.Config: CmmConfig :: !Profile -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> CmmConfig
+ GHC.Cmm.Config: CmmConfig :: !Profile -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> CmmConfig
- GHC.Cmm.ContFlowOpt: removeUnreachableBlocksProc :: CmmDecl -> CmmDecl
+ GHC.Cmm.ContFlowOpt: removeUnreachableBlocksProc :: Platform -> CmmDecl -> CmmDecl
- GHC.Cmm.Info.Build: doSRTs :: CmmConfig -> ModuleSRTInfo -> [(CAFEnv, [CmmDecl])] -> [(CAFSet, CmmDecl)] -> IO (ModuleSRTInfo, [CmmDeclSRTs])
+ GHC.Cmm.Info.Build: doSRTs :: CmmConfig -> ModuleSRTInfo -> [(CAFEnv, [CmmDecl])] -> [(CAFSet, CmmDataDecl)] -> IO (ModuleSRTInfo, [CmmDeclSRTs])
- GHC.Cmm.Parser: parseCmmFile :: DynFlags -> Module -> HomeUnit -> FilePath -> IO (Messages PsMessage, Messages PsMessage, Maybe (CmmGroup, [InfoProvEnt]))
+ GHC.Cmm.Parser: parseCmmFile :: CmmParserConfig -> Module -> HomeUnit -> FilePath -> IO (Messages PsMessage, Messages PsMessage, Maybe (CmmGroup, [InfoProvEnt]))
- GHC.Cmm.Parser.Monad: PD :: (DynFlags -> HomeUnit -> PState -> ParseResult a) -> PD a
+ GHC.Cmm.Parser.Monad: PD :: (PDConfig -> HomeUnit -> PState -> ParseResult a) -> PD a
- GHC.Cmm.Parser.Monad: [unPD] :: PD a -> DynFlags -> HomeUnit -> PState -> ParseResult a
+ GHC.Cmm.Parser.Monad: [unPD] :: PD a -> PDConfig -> HomeUnit -> PState -> ParseResult a
- GHC.Cmm.Pipeline: cmmPipeline :: HscEnv -> ModuleSRTInfo -> CmmGroup -> IO (ModuleSRTInfo, CmmGroupSRTs)
+ GHC.Cmm.Pipeline: cmmPipeline :: Logger -> CmmConfig -> ModuleSRTInfo -> CmmGroup -> IO (ModuleSRTInfo, CmmGroupSRTs)
- GHC.Cmm.Utils: mkFileEmbedLit :: CLabel -> FilePath -> (CmmLit, GenCmmDecl (GenCmmStatics raw) info stmt)
+ GHC.Cmm.Utils: mkFileEmbedLit :: CLabel -> FilePath -> Int -> (CmmLit, GenCmmDecl (GenCmmStatics raw) info stmt)
- GHC.CmmToAsm: NcgImpl :: !NCGConfig -> (RawCmmDecl -> NatM [NatCmmDecl statics instr]) -> (instr -> Maybe (NatCmmDecl statics instr)) -> (jumpDest -> Maybe BlockId) -> (instr -> Maybe jumpDest) -> ((BlockId -> Maybe jumpDest) -> statics -> statics) -> ((BlockId -> Maybe jumpDest) -> instr -> instr) -> (NatCmmDecl statics instr -> SDoc) -> Int -> [RealReg] -> (Int -> NatCmmDecl statics instr -> UniqSM (NatCmmDecl statics instr, [(BlockId, BlockId)])) -> (Platform -> LabelMap RawCmmStatics -> [NatBasicBlock instr] -> UniqSM [NatBasicBlock instr]) -> ([instr] -> [UnwindPoint]) -> (Maybe CFG -> LabelMap RawCmmStatics -> [NatBasicBlock instr] -> [NatBasicBlock instr]) -> NcgImpl statics instr jumpDest
+ GHC.CmmToAsm: NcgImpl :: !NCGConfig -> (RawCmmDecl -> NatM [NatCmmDecl statics instr]) -> (instr -> Maybe (NatCmmDecl statics instr)) -> (jumpDest -> Maybe BlockId) -> (instr -> Maybe jumpDest) -> ((BlockId -> Maybe jumpDest) -> statics -> statics) -> ((BlockId -> Maybe jumpDest) -> instr -> instr) -> (NatCmmDecl statics instr -> SDoc) -> (NatCmmDecl statics instr -> HDoc) -> Int -> [RealReg] -> (Int -> NatCmmDecl statics instr -> UniqSM (NatCmmDecl statics instr, [(BlockId, BlockId)])) -> (LabelMap RawCmmStatics -> [NatBasicBlock instr] -> [NatBasicBlock instr]) -> ([instr] -> [UnwindPoint]) -> (Maybe CFG -> LabelMap RawCmmStatics -> [NatBasicBlock instr] -> [NatBasicBlock instr]) -> NcgImpl statics instr jumpDest
- GHC.CmmToAsm: [ncgMakeFarBranches] :: NcgImpl statics instr jumpDest -> Platform -> LabelMap RawCmmStatics -> [NatBasicBlock instr] -> UniqSM [NatBasicBlock instr]
+ GHC.CmmToAsm: [ncgMakeFarBranches] :: NcgImpl statics instr jumpDest -> LabelMap RawCmmStatics -> [NatBasicBlock instr] -> [NatBasicBlock instr]
- GHC.CmmToAsm: cmmNativeGen :: forall statics instr jumpDest. (Instruction instr, OutputableP Platform statics, Outputable jumpDest) => Logger -> ModLocation -> NcgImpl statics instr jumpDest -> UniqSupply -> DwarfFiles -> LabelMap DebugBlock -> RawCmmDecl -> Int -> IO (UniqSupply, DwarfFiles, [NatCmmDecl statics instr], [CLabel], Maybe [RegAllocStats statics instr], Maybe [RegAllocStats], LabelMap [UnwindPoint])
+ GHC.CmmToAsm: cmmNativeGen :: forall statics instr jumpDest. (Instruction instr, OutputableP Platform statics, Outputable jumpDest) => Logger -> NcgImpl statics instr jumpDest -> UniqSupply -> DwarfFiles -> LabelMap DebugBlock -> RawCmmDecl -> Int -> IO (UniqSupply, DwarfFiles, [NatCmmDecl statics instr], [CLabel], Maybe [RegAllocStats statics instr], Maybe [RegAllocStats], LabelMap [UnwindPoint])
- GHC.CmmToAsm: nativeCodeGen :: forall a. Logger -> NCGConfig -> ModLocation -> Handle -> UniqSupply -> Stream IO RawCmmGroup a -> IO a
+ GHC.CmmToAsm: nativeCodeGen :: forall a. Logger -> ToolSettings -> NCGConfig -> ModLocation -> Handle -> UniqSupply -> Stream IO RawCmmGroup a -> IO a
- GHC.CmmToAsm.AArch64.Ppr: pprInstr :: Platform -> Instr -> SDoc
+ GHC.CmmToAsm.AArch64.Ppr: pprInstr :: IsDoc doc => Platform -> Instr -> doc
- GHC.CmmToAsm.AArch64.Ppr: pprNatCmmDecl :: NCGConfig -> NatCmmDecl RawCmmStatics Instr -> SDoc
+ GHC.CmmToAsm.AArch64.Ppr: pprNatCmmDecl :: IsDoc doc => NCGConfig -> NatCmmDecl RawCmmStatics Instr -> doc
- GHC.CmmToAsm.AArch64.Regs: ImmLit :: SDoc -> Imm
+ GHC.CmmToAsm.AArch64.Regs: ImmLit :: FastString -> Imm
- GHC.CmmToAsm.AArch64.Regs: strImmLit :: String -> Imm
+ GHC.CmmToAsm.AArch64.Regs: strImmLit :: FastString -> Imm
- GHC.CmmToAsm.BlockLayout: sequenceTop :: Instruction instr => NcgImpl statics instr jumpDest -> Maybe CFG -> NatCmmDecl statics instr -> UniqSM (NatCmmDecl statics instr)
+ GHC.CmmToAsm.BlockLayout: sequenceTop :: Instruction instr => NcgImpl statics instr jumpDest -> Maybe CFG -> NatCmmDecl statics instr -> NatCmmDecl statics instr
- GHC.CmmToAsm.Dwarf: dwarfGen :: NCGConfig -> ModLocation -> UniqSupply -> [DebugBlock] -> IO (SDoc, UniqSupply)
+ GHC.CmmToAsm.Dwarf: dwarfGen :: IsDoc doc => String -> NCGConfig -> ModLocation -> UniqSupply -> [DebugBlock] -> (doc, UniqSupply)
- GHC.CmmToAsm.Dwarf.Constants: dwarfARangesSection :: Platform -> SDoc
+ GHC.CmmToAsm.Dwarf.Constants: dwarfARangesSection :: IsDoc doc => Platform -> doc
- GHC.CmmToAsm.Dwarf.Constants: dwarfAbbrevLabel :: SDoc
+ GHC.CmmToAsm.Dwarf.Constants: dwarfAbbrevLabel :: IsLine doc => doc
- GHC.CmmToAsm.Dwarf.Constants: dwarfAbbrevSection :: Platform -> SDoc
+ GHC.CmmToAsm.Dwarf.Constants: dwarfAbbrevSection :: IsDoc doc => Platform -> doc
- GHC.CmmToAsm.Dwarf.Constants: dwarfFrameLabel :: SDoc
+ GHC.CmmToAsm.Dwarf.Constants: dwarfFrameLabel :: IsLine doc => doc
- GHC.CmmToAsm.Dwarf.Constants: dwarfFrameSection :: Platform -> SDoc
+ GHC.CmmToAsm.Dwarf.Constants: dwarfFrameSection :: IsDoc doc => Platform -> doc
- GHC.CmmToAsm.Dwarf.Constants: dwarfGhcSection :: Platform -> SDoc
+ GHC.CmmToAsm.Dwarf.Constants: dwarfGhcSection :: IsDoc doc => Platform -> doc
- GHC.CmmToAsm.Dwarf.Constants: dwarfInfoLabel :: SDoc
+ GHC.CmmToAsm.Dwarf.Constants: dwarfInfoLabel :: IsLine doc => doc
- GHC.CmmToAsm.Dwarf.Constants: dwarfInfoSection :: Platform -> SDoc
+ GHC.CmmToAsm.Dwarf.Constants: dwarfInfoSection :: IsDoc doc => Platform -> doc
- GHC.CmmToAsm.Dwarf.Constants: dwarfLineLabel :: SDoc
+ GHC.CmmToAsm.Dwarf.Constants: dwarfLineLabel :: IsLine doc => doc
- GHC.CmmToAsm.Dwarf.Constants: dwarfLineSection :: Platform -> SDoc
+ GHC.CmmToAsm.Dwarf.Constants: dwarfLineSection :: IsDoc doc => Platform -> doc
- GHC.CmmToAsm.Dwarf.Constants: dwarfSection :: Platform -> String -> SDoc
+ GHC.CmmToAsm.Dwarf.Constants: dwarfSection :: IsDoc doc => Platform -> String -> doc
- GHC.CmmToAsm.Dwarf.Types: DwarfCompileUnit :: [DwarfInfo] -> String -> String -> String -> SDoc -> SDoc -> SDoc -> DwarfInfo
+ GHC.CmmToAsm.Dwarf.Types: DwarfCompileUnit :: [DwarfInfo] -> String -> String -> String -> CLabel -> CLabel -> DwarfInfo
- GHC.CmmToAsm.Dwarf.Types: [dwHighLabel] :: DwarfInfo -> SDoc
+ GHC.CmmToAsm.Dwarf.Types: [dwHighLabel] :: DwarfInfo -> CLabel
- GHC.CmmToAsm.Dwarf.Types: [dwLowLabel] :: DwarfInfo -> SDoc
+ GHC.CmmToAsm.Dwarf.Types: [dwLowLabel] :: DwarfInfo -> CLabel
- GHC.CmmToAsm.Dwarf.Types: pprAbbrevDecls :: Platform -> Bool -> SDoc
+ GHC.CmmToAsm.Dwarf.Types: pprAbbrevDecls :: IsDoc doc => Platform -> Bool -> doc
- GHC.CmmToAsm.Dwarf.Types: pprByte :: Word8 -> SDoc
+ GHC.CmmToAsm.Dwarf.Types: pprByte :: IsDoc doc => Word8 -> doc
- GHC.CmmToAsm.Dwarf.Types: pprData4' :: SDoc -> SDoc
+ GHC.CmmToAsm.Dwarf.Types: pprData4' :: IsDoc doc => Line doc -> doc
- GHC.CmmToAsm.Dwarf.Types: pprDwWord :: SDoc -> SDoc
+ GHC.CmmToAsm.Dwarf.Types: pprDwWord :: IsDoc doc => Line doc -> doc
- GHC.CmmToAsm.Dwarf.Types: pprDwarfARanges :: Platform -> [DwarfARange] -> Unique -> SDoc
+ GHC.CmmToAsm.Dwarf.Types: pprDwarfARanges :: IsDoc doc => Platform -> [DwarfARange] -> Unique -> doc
- GHC.CmmToAsm.Dwarf.Types: pprDwarfFrame :: Platform -> DwarfFrame -> SDoc
+ GHC.CmmToAsm.Dwarf.Types: pprDwarfFrame :: forall doc. IsDoc doc => Platform -> DwarfFrame -> doc
- GHC.CmmToAsm.Dwarf.Types: pprDwarfInfo :: Platform -> Bool -> DwarfInfo -> SDoc
+ GHC.CmmToAsm.Dwarf.Types: pprDwarfInfo :: IsDoc doc => Platform -> Bool -> DwarfInfo -> doc
- GHC.CmmToAsm.Dwarf.Types: pprHalf :: Word16 -> SDoc
+ GHC.CmmToAsm.Dwarf.Types: pprHalf :: IsDoc doc => Word16 -> doc
- GHC.CmmToAsm.Dwarf.Types: pprLEBInt :: Int -> SDoc
+ GHC.CmmToAsm.Dwarf.Types: pprLEBInt :: IsDoc doc => Int -> doc
- GHC.CmmToAsm.Dwarf.Types: pprLEBWord :: Word -> SDoc
+ GHC.CmmToAsm.Dwarf.Types: pprLEBWord :: IsDoc doc => Word -> doc
- GHC.CmmToAsm.Dwarf.Types: pprWord :: Platform -> SDoc -> SDoc
+ GHC.CmmToAsm.Dwarf.Types: pprWord :: IsDoc doc => Platform -> Line doc -> doc
- GHC.CmmToAsm.Dwarf.Types: sectionOffset :: Platform -> SDoc -> SDoc -> SDoc
+ GHC.CmmToAsm.Dwarf.Types: sectionOffset :: IsDoc doc => Platform -> Line doc -> Line doc -> doc
- GHC.CmmToAsm.Dwarf.Types: wordAlign :: Platform -> SDoc
+ GHC.CmmToAsm.Dwarf.Types: wordAlign :: IsDoc doc => Platform -> doc
- GHC.CmmToAsm.Instr: mkComment :: Instruction instr => SDoc -> [instr]
+ GHC.CmmToAsm.Instr: mkComment :: Instruction instr => FastString -> [instr]
- GHC.CmmToAsm.Monad: NatM_State :: UniqSupply -> Int -> [CLabel] -> Maybe Reg -> NCGConfig -> ModLocation -> DwarfFiles -> LabelMap DebugBlock -> CFG -> NatM_State
+ GHC.CmmToAsm.Monad: NatM_State :: UniqSupply -> Int -> [CLabel] -> Maybe Reg -> NCGConfig -> DwarfFiles -> LabelMap DebugBlock -> CFG -> NatM_State
- GHC.CmmToAsm.Monad: NcgImpl :: !NCGConfig -> (RawCmmDecl -> NatM [NatCmmDecl statics instr]) -> (instr -> Maybe (NatCmmDecl statics instr)) -> (jumpDest -> Maybe BlockId) -> (instr -> Maybe jumpDest) -> ((BlockId -> Maybe jumpDest) -> statics -> statics) -> ((BlockId -> Maybe jumpDest) -> instr -> instr) -> (NatCmmDecl statics instr -> SDoc) -> Int -> [RealReg] -> (Int -> NatCmmDecl statics instr -> UniqSM (NatCmmDecl statics instr, [(BlockId, BlockId)])) -> (Platform -> LabelMap RawCmmStatics -> [NatBasicBlock instr] -> UniqSM [NatBasicBlock instr]) -> ([instr] -> [UnwindPoint]) -> (Maybe CFG -> LabelMap RawCmmStatics -> [NatBasicBlock instr] -> [NatBasicBlock instr]) -> NcgImpl statics instr jumpDest
+ GHC.CmmToAsm.Monad: NcgImpl :: !NCGConfig -> (RawCmmDecl -> NatM [NatCmmDecl statics instr]) -> (instr -> Maybe (NatCmmDecl statics instr)) -> (jumpDest -> Maybe BlockId) -> (instr -> Maybe jumpDest) -> ((BlockId -> Maybe jumpDest) -> statics -> statics) -> ((BlockId -> Maybe jumpDest) -> instr -> instr) -> (NatCmmDecl statics instr -> SDoc) -> (NatCmmDecl statics instr -> HDoc) -> Int -> [RealReg] -> (Int -> NatCmmDecl statics instr -> UniqSM (NatCmmDecl statics instr, [(BlockId, BlockId)])) -> (LabelMap RawCmmStatics -> [NatBasicBlock instr] -> [NatBasicBlock instr]) -> ([instr] -> [UnwindPoint]) -> (Maybe CFG -> LabelMap RawCmmStatics -> [NatBasicBlock instr] -> [NatBasicBlock instr]) -> NcgImpl statics instr jumpDest
- GHC.CmmToAsm.Monad: [ncgMakeFarBranches] :: NcgImpl statics instr jumpDest -> Platform -> LabelMap RawCmmStatics -> [NatBasicBlock instr] -> UniqSM [NatBasicBlock instr]
+ GHC.CmmToAsm.Monad: [ncgMakeFarBranches] :: NcgImpl statics instr jumpDest -> LabelMap RawCmmStatics -> [NatBasicBlock instr] -> [NatBasicBlock instr]
- GHC.CmmToAsm.Monad: data NatM result
+ GHC.CmmToAsm.Monad: data NatM a
- GHC.CmmToAsm.Monad: mkNatM_State :: UniqSupply -> Int -> NCGConfig -> ModLocation -> DwarfFiles -> LabelMap DebugBlock -> CFG -> NatM_State
+ GHC.CmmToAsm.Monad: mkNatM_State :: UniqSupply -> Int -> NCGConfig -> DwarfFiles -> LabelMap DebugBlock -> CFG -> NatM_State
- GHC.CmmToAsm.PIC: pprGotDeclaration :: NCGConfig -> SDoc
+ GHC.CmmToAsm.PIC: pprGotDeclaration :: NCGConfig -> HDoc
- GHC.CmmToAsm.PIC: pprImportedSymbol :: NCGConfig -> CLabel -> SDoc
+ GHC.CmmToAsm.PIC: pprImportedSymbol :: NCGConfig -> CLabel -> HDoc
- GHC.CmmToAsm.PPC.Instr: COMMENT :: SDoc -> Instr
+ GHC.CmmToAsm.PPC.Instr: COMMENT :: FastString -> Instr
- GHC.CmmToAsm.PPC.Instr: makeFarBranches :: Platform -> LabelMap RawCmmStatics -> [NatBasicBlock Instr] -> UniqSM [NatBasicBlock Instr]
+ GHC.CmmToAsm.PPC.Instr: makeFarBranches :: LabelMap RawCmmStatics -> [NatBasicBlock Instr] -> [NatBasicBlock Instr]
- GHC.CmmToAsm.PPC.Ppr: pprInstr :: Platform -> Instr -> SDoc
+ GHC.CmmToAsm.PPC.Ppr: pprInstr :: IsDoc doc => Platform -> Instr -> doc
- GHC.CmmToAsm.PPC.Ppr: pprNatCmmDecl :: NCGConfig -> NatCmmDecl RawCmmStatics Instr -> SDoc
+ GHC.CmmToAsm.PPC.Ppr: pprNatCmmDecl :: IsDoc doc => NCGConfig -> NatCmmDecl RawCmmStatics Instr -> doc
- GHC.CmmToAsm.PPC.Regs: ImmLit :: SDoc -> Imm
+ GHC.CmmToAsm.PPC.Regs: ImmLit :: FastString -> Imm
- GHC.CmmToAsm.PPC.Regs: strImmLit :: String -> Imm
+ GHC.CmmToAsm.PPC.Regs: strImmLit :: FastString -> Imm
- GHC.CmmToAsm.Ppr: pprASCII :: ByteString -> SDoc
+ GHC.CmmToAsm.Ppr: pprASCII :: forall doc. IsLine doc => ByteString -> doc
- GHC.CmmToAsm.Ppr: pprFileEmbed :: FilePath -> SDoc
+ GHC.CmmToAsm.Ppr: pprFileEmbed :: IsLine doc => FilePath -> doc
- GHC.CmmToAsm.Ppr: pprSectionHeader :: NCGConfig -> Section -> SDoc
+ GHC.CmmToAsm.Ppr: pprSectionHeader :: IsLine doc => NCGConfig -> Section -> doc
- GHC.CmmToAsm.Ppr: pprString :: ByteString -> SDoc
+ GHC.CmmToAsm.Ppr: pprString :: IsLine doc => ByteString -> doc
- GHC.CmmToAsm.X86.Instr: COMMENT :: SDoc -> Instr
+ GHC.CmmToAsm.X86.Instr: COMMENT :: FastString -> Instr
- GHC.CmmToAsm.X86.Ppr: pprInstr :: Platform -> Instr -> SDoc
+ GHC.CmmToAsm.X86.Ppr: pprInstr :: forall doc. IsDoc doc => Platform -> Instr -> doc
- GHC.CmmToAsm.X86.Ppr: pprNatCmmDecl :: NCGConfig -> NatCmmDecl (Alignment, RawCmmStatics) Instr -> SDoc
+ GHC.CmmToAsm.X86.Ppr: pprNatCmmDecl :: IsDoc doc => NCGConfig -> NatCmmDecl (Alignment, RawCmmStatics) Instr -> doc
- GHC.CmmToAsm.X86.Regs: ImmLit :: SDoc -> Imm
+ GHC.CmmToAsm.X86.Regs: ImmLit :: FastString -> Imm
- GHC.CmmToAsm.X86.Regs: strImmLit :: String -> Imm
+ GHC.CmmToAsm.X86.Regs: strImmLit :: FastString -> Imm
- GHC.Core.Opt.DmdAnal: DmdAnalOpts :: !Bool -> !Int -> !Int -> DmdAnalOpts
+ GHC.Core.Opt.DmdAnal: DmdAnalOpts :: !Bool -> !Bool -> !Int -> !Int -> DmdAnalOpts
- GHC.Core.Opt.LiberateCase: liberateCase :: DynFlags -> CoreProgram -> CoreProgram
+ GHC.Core.Opt.LiberateCase: liberateCase :: LibCaseOpts -> CoreProgram -> CoreProgram
- GHC.Core.Opt.Pipeline: simplifyExpr :: HscEnv -> CoreExpr -> IO CoreExpr
+ GHC.Core.Opt.Pipeline: simplifyExpr :: Logger -> ExternalUnitCache -> SimplifyExprOpts -> CoreExpr -> IO CoreExpr
- GHC.Core.Opt.WorkWrap: wwTopBinds :: Module -> DynFlags -> FamInstEnvs -> UniqSupply -> CoreProgram -> CoreProgram
+ GHC.Core.Opt.WorkWrap: wwTopBinds :: WwOpts -> UniqSupply -> CoreProgram -> CoreProgram
- GHC.Core.Opt.WorkWrap.Utils: DataConPatContext :: !DataCon -> ![Type] -> !Coercion -> DataConPatContext
+ GHC.Core.Opt.WorkWrap.Utils: DataConPatContext :: !DataCon -> ![Type] -> !Coercion -> ![s] -> DataConPatContext s
- GHC.Core.Opt.WorkWrap.Utils: DropAbsent :: UnboxingDecision s
+ GHC.Core.Opt.WorkWrap.Utils: DropAbsent :: UnboxingDecision unboxing_info
- GHC.Core.Opt.WorkWrap.Utils: [dcpc_co] :: DataConPatContext -> !Coercion
+ GHC.Core.Opt.WorkWrap.Utils: [dcpc_co] :: DataConPatContext s -> !Coercion
- GHC.Core.Opt.WorkWrap.Utils: [dcpc_dc] :: DataConPatContext -> !DataCon
+ GHC.Core.Opt.WorkWrap.Utils: [dcpc_dc] :: DataConPatContext s -> !DataCon
- GHC.Core.Opt.WorkWrap.Utils: [dcpc_tc_args] :: DataConPatContext -> ![Type]
+ GHC.Core.Opt.WorkWrap.Utils: [dcpc_tc_args] :: DataConPatContext s -> ![Type]
- GHC.Core.Opt.WorkWrap.Utils: data DataConPatContext
+ GHC.Core.Opt.WorkWrap.Utils: data DataConPatContext s
- GHC.Core.Opt.WorkWrap.Utils: data UnboxingDecision s
+ GHC.Core.Opt.WorkWrap.Utils: data UnboxingDecision unboxing_info
- GHC.Core.Opt.WorkWrap.Utils: mkWWstr :: WwOpts -> [Var] -> [StrictnessMark] -> UniqSM (Bool, [(Var, StrictnessMark)], CoreExpr -> CoreExpr, [CoreExpr])
+ GHC.Core.Opt.WorkWrap.Utils: mkWWstr :: WwOpts -> [Var] -> [StrictnessMark] -> UniqSM (WwUse, [(Var, StrictnessMark)], CoreExpr -> CoreExpr, [CoreExpr])
- GHC.Core.Opt.WorkWrap.Utils: mkWWstr_one :: WwOpts -> Var -> StrictnessMark -> UniqSM (Bool, [(Var, StrictnessMark)], CoreExpr -> CoreExpr, CoreExpr)
+ GHC.Core.Opt.WorkWrap.Utils: mkWWstr_one :: WwOpts -> Var -> StrictnessMark -> UniqSM (WwUse, [(Var, StrictnessMark)], CoreExpr -> CoreExpr, CoreExpr)
- GHC.CoreToStg: coreToStg :: DynFlags -> Module -> ModLocation -> CoreProgram -> ([StgTopBinding], InfoTableProvMap, CollectedCCs)
+ GHC.CoreToStg: coreToStg :: CoreToStgOpts -> Module -> ModLocation -> CoreProgram -> ([StgTopBinding], InfoTableProvMap, CollectedCCs)
- GHC.CoreToStg.Prep: corePrepExpr :: HscEnv -> CoreExpr -> IO CoreExpr
+ GHC.CoreToStg.Prep: corePrepExpr :: Logger -> CorePrepConfig -> CoreExpr -> IO CoreExpr
- GHC.CoreToStg.Prep: corePrepPgm :: HscEnv -> Module -> ModLocation -> CoreProgram -> [TyCon] -> IO CoreProgram
+ GHC.CoreToStg.Prep: corePrepPgm :: Logger -> CorePrepConfig -> CorePrepPgmConfig -> Module -> ModLocation -> CoreProgram -> [TyCon] -> IO CoreProgram
- GHC.CoreToStg.Prep: mkConvertNumLiteral :: HscEnv -> IO (LitNumType -> Integer -> Maybe CoreExpr)
+ GHC.CoreToStg.Prep: mkConvertNumLiteral :: Platform -> HomeUnit -> (Name -> IO TyThing) -> IO (LitNumType -> Integer -> Maybe CoreExpr)
- GHC.Driver.CodeOutput: codeOutput :: forall a. Logger -> TmpFs -> DynFlags -> UnitState -> Module -> FilePath -> ModLocation -> (a -> ForeignStubs) -> [(ForeignSrcLang, FilePath)] -> Set UnitId -> Stream IO RawCmmGroup a -> IO (FilePath, (Bool, Maybe FilePath), [(ForeignSrcLang, FilePath)], a)
+ GHC.Driver.CodeOutput: codeOutput :: forall a. Logger -> TmpFs -> LlvmConfigCache -> DynFlags -> UnitState -> Module -> FilePath -> ModLocation -> (a -> ForeignStubs) -> [(ForeignSrcLang, FilePath)] -> Set UnitId -> Stream IO RawCmmGroup a -> IO (FilePath, (Bool, Maybe FilePath), [(ForeignSrcLang, FilePath)], a)
- GHC.Driver.Config.CmmToLlvm: initLlvmCgConfig :: Logger -> DynFlags -> IO LlvmCgConfig
+ GHC.Driver.Config.CmmToLlvm: initLlvmCgConfig :: Logger -> LlvmConfigCache -> DynFlags -> IO LlvmCgConfig
- GHC.Driver.Main: HscUpToDate :: ModIface -> Maybe Linkable -> HscRecompStatus
+ GHC.Driver.Main: HscUpToDate :: ModIface -> HomeModLinkable -> HscRecompStatus
- GHC.Driver.Main: hscInteractive :: HscEnv -> CgGuts -> ModLocation -> IO (Maybe FilePath, CompiledByteCode, [SptEntry])
+ GHC.Driver.Main: hscInteractive :: HscEnv -> CgInteractiveGuts -> ModLocation -> IO (Maybe FilePath, CompiledByteCode, [SptEntry])
- GHC.Driver.Main: hscParseModuleWithLocation :: HscEnv -> String -> Int -> String -> IO HsModule
+ GHC.Driver.Main: hscParseModuleWithLocation :: HscEnv -> String -> Int -> String -> IO (HsModule GhcPs)
- GHC.Driver.Main: hscRecompStatus :: Maybe Messager -> HscEnv -> ModSummary -> Maybe ModIface -> Maybe Linkable -> (Int, Int) -> IO HscRecompStatus
+ GHC.Driver.Main: hscRecompStatus :: Maybe Messager -> HscEnv -> ModSummary -> Maybe ModIface -> HomeModLinkable -> (Int, Int) -> IO HscRecompStatus
- GHC.Driver.Main: hscSimpleIface' :: TcGblEnv -> ModSummary -> Hsc (ModIface, ModDetails)
+ GHC.Driver.Main: hscSimpleIface' :: Maybe CoreProgram -> TcGblEnv -> ModSummary -> Hsc (ModIface, ModDetails)
- GHC.Driver.Main: hscTcRnLookupRdrName :: HscEnv -> LocatedN RdrName -> IO [Name]
+ GHC.Driver.Main: hscTcRnLookupRdrName :: HscEnv -> LocatedN RdrName -> IO (NonEmpty Name)
- GHC.Driver.Main: initModDetails :: HscEnv -> ModSummary -> ModIface -> IO ModDetails
+ GHC.Driver.Main: initModDetails :: HscEnv -> ModIface -> IO ModDetails
- GHC.Driver.Main: newHscEnv :: DynFlags -> IO HscEnv
+ GHC.Driver.Main: newHscEnv :: FilePath -> DynFlags -> IO HscEnv
- GHC.Driver.Main: newHscEnvWithHUG :: DynFlags -> UnitId -> HomeUnitGraph -> IO HscEnv
+ GHC.Driver.Main: newHscEnvWithHUG :: FilePath -> DynFlags -> UnitId -> HomeUnitGraph -> IO HscEnv
- GHC.Driver.Pipeline: [T_Cc] :: Phase -> PipeEnv -> HscEnv -> FilePath -> TPhase [Char]
+ GHC.Driver.Pipeline: [T_Cc] :: Phase -> PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> TPhase [Char]
- GHC.Driver.Pipeline: [T_HscBackend] :: PipeEnv -> HscEnv -> ModuleName -> HscSource -> ModLocation -> HscBackendAction -> TPhase ([FilePath], ModIface, Maybe Linkable, FilePath)
+ GHC.Driver.Pipeline: [T_HscBackend] :: PipeEnv -> HscEnv -> ModuleName -> HscSource -> ModLocation -> HscBackendAction -> TPhase ([FilePath], ModIface, HomeModLinkable, FilePath)
- GHC.Driver.Pipeline: cmmCppPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> m (Maybe FilePath)
+ GHC.Driver.Pipeline: cmmCppPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> m FilePath
- GHC.Driver.Pipeline: cmmPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> m (Maybe FilePath)
+ GHC.Driver.Pipeline: cmmPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> m FilePath
- GHC.Driver.Pipeline: compileOne :: HscEnv -> ModSummary -> Int -> Int -> Maybe ModIface -> Maybe Linkable -> IO HomeModInfo
+ GHC.Driver.Pipeline: compileOne :: HscEnv -> ModSummary -> Int -> Int -> Maybe ModIface -> HomeModLinkable -> IO HomeModInfo
- GHC.Driver.Pipeline: compileOne' :: Maybe Messager -> HscEnv -> ModSummary -> Int -> Int -> Maybe ModIface -> Maybe Linkable -> IO HomeModInfo
+ GHC.Driver.Pipeline: compileOne' :: Maybe Messager -> HscEnv -> ModSummary -> Int -> Int -> Maybe ModIface -> HomeModLinkable -> IO HomeModInfo
- GHC.Driver.Pipeline: fullPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> HscSource -> m (ModIface, Maybe Linkable)
+ GHC.Driver.Pipeline: fullPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> HscSource -> m (ModIface, HomeModLinkable)
- GHC.Driver.Pipeline: hscBackendPipeline :: P m => PipeEnv -> HscEnv -> ModSummary -> HscBackendAction -> m (ModIface, Maybe Linkable)
+ GHC.Driver.Pipeline: hscBackendPipeline :: P m => PipeEnv -> HscEnv -> ModSummary -> HscBackendAction -> m (ModIface, HomeModLinkable)
- GHC.Driver.Pipeline: hscGenBackendPipeline :: P m => PipeEnv -> HscEnv -> ModSummary -> HscBackendAction -> m (ModIface, Maybe Linkable)
+ GHC.Driver.Pipeline: hscGenBackendPipeline :: P m => PipeEnv -> HscEnv -> ModSummary -> HscBackendAction -> m (ModIface, HomeModLinkable)
- GHC.Driver.Pipeline: hscPipeline :: P m => PipeEnv -> (HscEnv, ModSummary, HscRecompStatus) -> m (ModIface, Maybe Linkable)
+ GHC.Driver.Pipeline: hscPipeline :: P m => PipeEnv -> (HscEnv, ModSummary, HscRecompStatus) -> m (ModIface, HomeModLinkable)
- GHC.Driver.Pipeline.Execute: llvmOptions :: DynFlags -> [(String, String)]
+ GHC.Driver.Pipeline.Execute: llvmOptions :: LlvmConfig -> DynFlags -> [(String, String)]
- GHC.Driver.Pipeline.Execute: runCcPhase :: Phase -> PipeEnv -> HscEnv -> FilePath -> IO FilePath
+ GHC.Driver.Pipeline.Execute: runCcPhase :: Phase -> PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> IO FilePath
- GHC.Driver.Pipeline.Execute: runHscBackendPhase :: PipeEnv -> HscEnv -> ModuleName -> HscSource -> ModLocation -> HscBackendAction -> IO ([FilePath], ModIface, Maybe Linkable, FilePath)
+ GHC.Driver.Pipeline.Execute: runHscBackendPhase :: PipeEnv -> HscEnv -> ModuleName -> HscSource -> ModLocation -> HscBackendAction -> IO ([FilePath], ModIface, HomeModLinkable, FilePath)
- GHC.Hs.Stats: ppSourceStats :: Bool -> Located HsModule -> SDoc
+ GHC.Hs.Stats: ppSourceStats :: Bool -> Located (HsModule GhcPs) -> SDoc
- GHC.HsToCore.Binds: decomposeRuleLhs :: DynFlags -> [Var] -> CoreExpr -> Either DsMessage ([Var], Id, [CoreExpr])
+ GHC.HsToCore.Binds: decomposeRuleLhs :: DynFlags -> [Var] -> CoreExpr -> VarSet -> Either DsMessage ([Var], Id, [CoreExpr])
- GHC.HsToCore.Quote: dsBracket :: Maybe QuoteWrapper -> HsQuote GhcRn -> [PendingTcSplice] -> DsM CoreExpr
+ GHC.HsToCore.Quote: dsBracket :: HsBracketTc -> DsM CoreExpr
- GHC.HsToCore.Types: DsGblEnv :: Module -> FamInstEnv -> GlobalRdrEnv -> PrintUnqualified -> IORef (Messages DsMessage) -> (IfGblEnv, IfLclEnv) -> CompleteMatches -> IORef CostCentreState -> IORef (ModuleEnv Int) -> DsGblEnv
+ GHC.HsToCore.Types: DsGblEnv :: Module -> FamInstEnv -> GlobalRdrEnv -> NamePprCtx -> IORef (Messages DsMessage) -> (IfGblEnv, IfLclEnv) -> CompleteMatches -> IORef CostCentreState -> IORef (ModuleEnv Int) -> DsGblEnv
- GHC.HsToCore.Usage: mkUsageInfo :: HscEnv -> Module -> ImportedMods -> NameSet -> [FilePath] -> [(Module, Fingerprint)] -> [Linkable] -> PkgsLoaded -> IO [Usage]
+ GHC.HsToCore.Usage: mkUsageInfo :: UsageConfig -> Plugins -> FinderCache -> UnitEnv -> Module -> ImportedMods -> NameSet -> [FilePath] -> [(Module, Fingerprint)] -> [Linkable] -> PkgsLoaded -> IfG [Usage]
- GHC.Iface.Binary: putName :: BinDictionary -> BinSymbolTable -> BinHandle -> Name -> IO ()
+ GHC.Iface.Binary: putName :: FSTable -> BinSymbolTable -> BinHandle -> Name -> IO ()
- GHC.Iface.Ext.Types: HForAllTy :: ((Name, a), ArgFlag) -> a -> HieType a
+ GHC.Iface.Ext.Types: HForAllTy :: ((Name, a), ForAllTyFlag) -> a -> HieType a
- GHC.Iface.Make: mkIfaceTc :: HscEnv -> SafeHaskellMode -> ModDetails -> ModSummary -> TcGblEnv -> IO ModIface
+ GHC.Iface.Make: mkIfaceTc :: HscEnv -> SafeHaskellMode -> ModDetails -> ModSummary -> Maybe CoreProgram -> TcGblEnv -> IO ModIface
- GHC.Iface.Make: mkPartialIface :: HscEnv -> ModDetails -> ModSummary -> ModGuts -> PartialModIface
+ GHC.Iface.Make: mkPartialIface :: HscEnv -> CoreProgram -> ModDetails -> ModSummary -> ModGuts -> PartialModIface
- GHC.Iface.Recomp: ModulePackageChanged :: String -> RecompReason
+ GHC.Iface.Recomp: ModulePackageChanged :: FastString -> RecompReason
- GHC.Linker.MacOS: getFrameworkOpts :: DynFlags -> Platform -> [String]
+ GHC.Linker.MacOS: getFrameworkOpts :: FrameworkOpts -> Platform -> [String]
- GHC.Linker.MacOS: runInjectRPaths :: Logger -> DynFlags -> [FilePath] -> FilePath -> IO ()
+ GHC.Linker.MacOS: runInjectRPaths :: Logger -> ToolSettings -> [FilePath] -> FilePath -> IO ()
- GHC.Linker.Unit: collectArchives :: DynFlags -> UnitInfo -> IO [FilePath]
+ GHC.Linker.Unit: collectArchives :: GhcNameVersion -> Ways -> UnitInfo -> IO [FilePath]
- GHC.Linker.Unit: collectLinkOpts :: DynFlags -> [UnitInfo] -> ([String], [String], [String])
+ GHC.Linker.Unit: collectLinkOpts :: GhcNameVersion -> Ways -> [UnitInfo] -> ([String], [String], [String])
- GHC.Linker.Unit: getLibs :: DynFlags -> UnitEnv -> [UnitId] -> IO [(String, String)]
+ GHC.Linker.Unit: getLibs :: GhcNameVersion -> Ways -> UnitEnv -> [UnitId] -> IO [(String, String)]
- GHC.Linker.Unit: getUnitLinkOpts :: DynFlags -> UnitEnv -> [UnitId] -> IO ([String], [String], [String])
+ GHC.Linker.Unit: getUnitLinkOpts :: GhcNameVersion -> Ways -> UnitEnv -> [UnitId] -> IO ([String], [String], [String])
- GHC.Plugins: Invisible :: Specificity -> ArgFlag
+ GHC.Plugins: Invisible :: Specificity -> ForAllTyFlag
- GHC.Plugins: LC :: TCvSubst -> LiftCoEnv -> LiftingContext
+ GHC.Plugins: LC :: Subst -> LiftCoEnv -> LiftingContext
- GHC.Plugins: Required :: ArgFlag
+ GHC.Plugins: Required :: ForAllTyFlag
- GHC.Plugins: TyCoFolder :: (Type -> Maybe Type) -> (env -> TyVar -> a) -> (env -> CoVar -> a) -> (env -> CoercionHole -> a) -> (env -> TyCoVar -> ArgFlag -> env) -> TyCoFolder env a
+ GHC.Plugins: TyCoFolder :: (Type -> Maybe Type) -> (env -> TyVar -> a) -> (env -> CoVar -> a) -> (env -> CoercionHole -> a) -> (env -> TyCoVar -> ForAllTyFlag -> env) -> TyCoFolder env a
- GHC.Plugins: TyCoMapper :: (env -> TyVar -> m Type) -> (env -> CoVar -> m Coercion) -> (env -> CoercionHole -> m Coercion) -> (env -> TyCoVar -> ArgFlag -> m (env, TyCoVar)) -> (TyCon -> m TyCon) -> TyCoMapper env (m :: Type -> Type)
+ GHC.Plugins: TyCoMapper :: (env -> TyVar -> m Type) -> (env -> CoVar -> m Coercion) -> (env -> CoercionHole -> m Coercion) -> (env -> TyCoVar -> ForAllTyFlag -> m (env, TyCoVar)) -> (TyCon -> m TyCon) -> TyCoMapper env (m :: Type -> Type)
- GHC.Plugins: [tcf_tycobinder] :: TyCoFolder env a -> env -> TyCoVar -> ArgFlag -> env
+ GHC.Plugins: [tcf_tycobinder] :: TyCoFolder env a -> env -> TyCoVar -> ForAllTyFlag -> env
- GHC.Plugins: [tcm_tycobinder] :: TyCoMapper env (m :: Type -> Type) -> env -> TyCoVar -> ArgFlag -> m (env, TyCoVar)
+ GHC.Plugins: [tcm_tycobinder] :: TyCoMapper env (m :: Type -> Type) -> env -> TyCoVar -> ForAllTyFlag -> m (env, TyCoVar)
- GHC.Plugins: cloneTyVarBndr :: TCvSubst -> TyVar -> Unique -> (TCvSubst, TyVar)
+ GHC.Plugins: cloneTyVarBndr :: Subst -> TyVar -> Unique -> (Subst, TyVar)
- GHC.Plugins: cloneTyVarBndrs :: TCvSubst -> [TyVar] -> UniqSupply -> (TCvSubst, [TyVar])
+ GHC.Plugins: cloneTyVarBndrs :: Subst -> [TyVar] -> UniqSupply -> (Subst, [TyVar])
- GHC.Plugins: composeTCvSubst :: TCvSubst -> TCvSubst -> TCvSubst
+ GHC.Plugins: composeTCvSubst :: Subst -> Subst -> Subst
- GHC.Plugins: decomposeCo :: Arity -> Coercion -> [Role] -> [Coercion]
+ GHC.Plugins: decomposeCo :: Arity -> Coercion -> Infinite Role -> [Coercion]
- GHC.Plugins: decomposeFunCo :: HasDebugCallStack => Role -> Coercion -> (CoercionN, Coercion, Coercion)
+ GHC.Plugins: decomposeFunCo :: HasDebugCallStack => Coercion -> (CoercionN, Coercion, Coercion)
- GHC.Plugins: extendCvSubst :: TCvSubst -> CoVar -> Coercion -> TCvSubst
+ GHC.Plugins: extendCvSubst :: Subst -> CoVar -> Coercion -> Subst
- GHC.Plugins: extendTCvSubst :: TCvSubst -> TyCoVar -> Type -> TCvSubst
+ GHC.Plugins: extendTCvSubst :: Subst -> TyCoVar -> Type -> Subst
- GHC.Plugins: extendTCvSubstList :: TCvSubst -> [Var] -> [Type] -> TCvSubst
+ GHC.Plugins: extendTCvSubstList :: Subst -> [Var] -> [Type] -> Subst
- GHC.Plugins: extendTCvSubstWithClone :: TCvSubst -> TyCoVar -> TyCoVar -> TCvSubst
+ GHC.Plugins: extendTCvSubstWithClone :: Subst -> TyCoVar -> TyCoVar -> Subst
- GHC.Plugins: extendTvSubstAndInScope :: TCvSubst -> TyVar -> Type -> TCvSubst
+ GHC.Plugins: extendTvSubstAndInScope :: Subst -> TyVar -> Type -> Subst
- GHC.Plugins: extendTvSubstBinderAndInScope :: TCvSubst -> TyCoBinder -> Type -> TCvSubst
+ GHC.Plugins: extendTvSubstBinderAndInScope :: Subst -> PiTyBinder -> Type -> Subst
- GHC.Plugins: extendTvSubstWithClone :: TCvSubst -> TyVar -> TyVar -> TCvSubst
+ GHC.Plugins: extendTvSubstWithClone :: Subst -> TyVar -> TyVar -> Subst
- GHC.Plugins: getCvSubstEnv :: TCvSubst -> CvSubstEnv
+ GHC.Plugins: getCvSubstEnv :: Subst -> CvSubstEnv
- GHC.Plugins: getRuntimeArgTys :: Type -> [(Type, AnonArgFlag)]
+ GHC.Plugins: getRuntimeArgTys :: Type -> [(Scaled Type, FunTyFlag)]
- GHC.Plugins: getRuntimeRep :: HasDebugCallStack => Type -> Type
+ GHC.Plugins: getRuntimeRep :: HasDebugCallStack => Type -> RuntimeRepType
- GHC.Plugins: getTvSubstEnv :: TCvSubst -> TvSubstEnv
+ GHC.Plugins: getTvSubstEnv :: Subst -> TvSubstEnv
- GHC.Plugins: getTyVar :: String -> Type -> TyVar
+ GHC.Plugins: getTyVar :: HasDebugCallStack => Type -> TyVar
- GHC.Plugins: idUnfolding :: Id -> Unfolding
+ GHC.Plugins: idUnfolding :: IdUnfoldingFun
- GHC.Plugins: isBoxedRuntimeRep :: Type -> Bool
+ GHC.Plugins: isBoxedRuntimeRep :: RuntimeRepType -> Bool
- GHC.Plugins: isEmptyTCvSubst :: TCvSubst -> Bool
+ GHC.Plugins: isEmptyTCvSubst :: Subst -> Bool
- GHC.Plugins: isLiftedRuntimeRep :: Type -> Bool
+ GHC.Plugins: isLiftedRuntimeRep :: RuntimeRepType -> Bool
- GHC.Plugins: isUnliftedRuntimeRep :: Type -> Bool
+ GHC.Plugins: isUnliftedRuntimeRep :: RuntimeRepType -> Bool
- GHC.Plugins: kindRep :: HasDebugCallStack => Kind -> Type
+ GHC.Plugins: kindRep :: HasDebugCallStack => Kind -> RuntimeRepType
- GHC.Plugins: kindRep_maybe :: HasDebugCallStack => Kind -> Maybe Type
+ GHC.Plugins: kindRep_maybe :: HasDebugCallStack => Kind -> Maybe RuntimeRepType
- GHC.Plugins: lcSubstLeft :: LiftingContext -> TCvSubst
+ GHC.Plugins: lcSubstLeft :: LiftingContext -> Subst
- GHC.Plugins: lcSubstRight :: LiftingContext -> TCvSubst
+ GHC.Plugins: lcSubstRight :: LiftingContext -> Subst
- GHC.Plugins: liftEnvSubstLeft :: TCvSubst -> LiftCoEnv -> TCvSubst
+ GHC.Plugins: liftEnvSubstLeft :: Subst -> LiftCoEnv -> Subst
- GHC.Plugins: liftEnvSubstRight :: TCvSubst -> LiftCoEnv -> TCvSubst
+ GHC.Plugins: liftEnvSubstRight :: Subst -> LiftCoEnv -> Subst
- GHC.Plugins: lookupCoVar :: TCvSubst -> Var -> Maybe Coercion
+ GHC.Plugins: lookupCoVar :: Subst -> Var -> Maybe Coercion
- GHC.Plugins: lookupTyVar :: TCvSubst -> TyVar -> Maybe Type
+ GHC.Plugins: lookupTyVar :: Subst -> TyVar -> Maybe Type
- GHC.Plugins: mkFCallName :: Unique -> String -> Name
+ GHC.Plugins: mkFCallName :: Unique -> FastString -> Name
- GHC.Plugins: mkForAllTy :: TyCoVar -> ArgFlag -> Type -> Type
+ GHC.Plugins: mkForAllTy :: ForAllTyBinder -> Type -> Type
- GHC.Plugins: mkForAllTys :: [TyCoVarBinder] -> Type -> Type
+ GHC.Plugins: mkForAllTys :: [ForAllTyBinder] -> Type -> Type
- GHC.Plugins: mkFunResCo :: Role -> Scaled Type -> Coercion -> Coercion
+ GHC.Plugins: mkFunResCo :: Role -> Id -> Coercion -> Coercion
- GHC.Plugins: mkFunResMCo :: Scaled Type -> MCoercionR -> MCoercionR
+ GHC.Plugins: mkFunResMCo :: Id -> MCoercionR -> MCoercionR
- GHC.Plugins: mkFunTy :: AnonArgFlag -> Mult -> Type -> Type -> Type
+ GHC.Plugins: mkFunTy :: HasDebugCallStack => FunTyFlag -> Mult -> Type -> Type -> Type
- GHC.Plugins: mkInstCo :: Coercion -> Coercion -> Coercion
+ GHC.Plugins: mkInstCo :: Coercion -> CoercionN -> Coercion
- GHC.Plugins: mkInvisFunTy :: Mult -> Type -> Type -> Type
+ GHC.Plugins: mkInvisFunTy :: HasDebugCallStack => Type -> Type -> Type
- GHC.Plugins: mkPiTy :: TyCoBinder -> Type -> Type
+ GHC.Plugins: mkPiTy :: PiTyBinder -> Type -> Type
- GHC.Plugins: mkPiTys :: [TyCoBinder] -> Type -> Type
+ GHC.Plugins: mkPiTys :: [PiTyBinder] -> Type -> Type
- GHC.Plugins: mkRecFldSelOcc :: String -> OccName
+ GHC.Plugins: mkRecFldSelOcc :: FastString -> OccName
- GHC.Plugins: mkSubstLiftingContext :: TCvSubst -> LiftingContext
+ GHC.Plugins: mkSubstLiftingContext :: Subst -> LiftingContext
- GHC.Plugins: mkTYPEapp :: Type -> Type
+ GHC.Plugins: mkTYPEapp :: RuntimeRepType -> Type
- GHC.Plugins: mkTvSubstPrs :: [(TyVar, Type)] -> TCvSubst
+ GHC.Plugins: mkTvSubstPrs :: [(TyVar, Type)] -> Subst
- GHC.Plugins: mkVisFunTy :: Mult -> Type -> Type -> Type
+ GHC.Plugins: mkVisFunTy :: HasDebugCallStack => Mult -> Type -> Type -> Type
- GHC.Plugins: mkVisFunTyMany :: Type -> Type -> Type
+ GHC.Plugins: mkVisFunTyMany :: HasDebugCallStack => Type -> Type -> Type
- GHC.Plugins: partitionInvisibles :: [(a, ArgFlag)] -> ([a], [a])
+ GHC.Plugins: partitionInvisibles :: [(a, ForAllTyFlag)] -> ([a], [a])
- GHC.Plugins: pattern Inferred :: ArgFlag
+ GHC.Plugins: pattern Inferred :: ForAllTyFlag
- GHC.Plugins: pattern Specified :: ArgFlag
+ GHC.Plugins: pattern Specified :: ForAllTyFlag
- GHC.Plugins: pprModulePrefix :: PprStyle -> Module -> OccName -> SDoc
+ GHC.Plugins: pprModulePrefix :: IsLine doc => PprStyle -> Module -> OccName -> doc
- GHC.Plugins: pprNameSpaceBrief :: NameSpace -> SDoc
+ GHC.Plugins: pprNameSpaceBrief :: IsLine doc => NameSpace -> doc
- GHC.Plugins: pprOccName :: OccName -> SDoc
+ GHC.Plugins: pprOccName :: IsLine doc => OccName -> doc
- GHC.Plugins: runtimeRepLevity_maybe :: Type -> Maybe Levity
+ GHC.Plugins: runtimeRepLevity_maybe :: RuntimeRepType -> Maybe Levity
- GHC.Plugins: splitForAllCoVar_maybe :: Type -> Maybe (TyCoVar, Type)
+ GHC.Plugins: splitForAllCoVar_maybe :: Type -> Maybe (CoVar, Type)
- GHC.Plugins: splitForAllTyVar_maybe :: Type -> Maybe (TyCoVar, Type)
+ GHC.Plugins: splitForAllTyVar_maybe :: Type -> Maybe (TyVar, Type)
- GHC.Plugins: splitFunTy_maybe :: Type -> Maybe (Mult, Type, Type)
+ GHC.Plugins: splitFunTy_maybe :: Type -> Maybe (FunTyFlag, Mult, Type, Type)
- GHC.Plugins: splitInvisPiTys :: Type -> ([TyCoBinder], Type)
+ GHC.Plugins: splitInvisPiTys :: Type -> ([PiTyBinder], Type)
- GHC.Plugins: splitInvisPiTysN :: Int -> Type -> ([TyCoBinder], Type)
+ GHC.Plugins: splitInvisPiTysN :: Int -> Type -> ([PiTyBinder], Type)
- GHC.Plugins: splitPiTy :: Type -> (TyCoBinder, Type)
+ GHC.Plugins: splitPiTy :: Type -> (PiTyBinder, Type)
- GHC.Plugins: splitPiTy_maybe :: Type -> Maybe (TyCoBinder, Type)
+ GHC.Plugins: splitPiTy_maybe :: Type -> Maybe (PiTyBinder, Type)
- GHC.Plugins: splitPiTys :: Type -> ([TyCoBinder], Type)
+ GHC.Plugins: splitPiTys :: Type -> ([PiTyBinder], Type)
- GHC.Plugins: substBndrs :: Subst -> [Var] -> (Subst, [Var])
+ GHC.Plugins: substBndrs :: Traversable f => Subst -> f Var -> (Subst, f Var)
- GHC.Plugins: substCo :: HasCallStack => Subst -> Coercion -> Coercion
+ GHC.Plugins: substCo :: HasDebugCallStack => Subst -> Coercion -> Coercion
- GHC.Plugins: substCoUnchecked :: TCvSubst -> Coercion -> Coercion
+ GHC.Plugins: substCoUnchecked :: Subst -> Coercion -> Coercion
- GHC.Plugins: substCoVar :: TCvSubst -> CoVar -> Coercion
+ GHC.Plugins: substCoVar :: Subst -> CoVar -> Coercion
- GHC.Plugins: substCoVarBndr :: HasDebugCallStack => TCvSubst -> CoVar -> (TCvSubst, CoVar)
+ GHC.Plugins: substCoVarBndr :: HasDebugCallStack => Subst -> CoVar -> (Subst, CoVar)
- GHC.Plugins: substCoVars :: TCvSubst -> [CoVar] -> [Coercion]
+ GHC.Plugins: substCoVars :: Subst -> [CoVar] -> [Coercion]
- GHC.Plugins: substCos :: HasDebugCallStack => TCvSubst -> [Coercion] -> [Coercion]
+ GHC.Plugins: substCos :: HasDebugCallStack => Subst -> [Coercion] -> [Coercion]
- GHC.Plugins: substRecBndrs :: Subst -> [Id] -> (Subst, [Id])
+ GHC.Plugins: substRecBndrs :: Traversable f => Subst -> f Id -> (Subst, f Id)
- GHC.Plugins: substScaledTy :: HasDebugCallStack => TCvSubst -> Scaled Type -> Scaled Type
+ GHC.Plugins: substScaledTy :: HasDebugCallStack => Subst -> Scaled Type -> Scaled Type
- GHC.Plugins: substScaledTyUnchecked :: HasDebugCallStack => TCvSubst -> Scaled Type -> Scaled Type
+ GHC.Plugins: substScaledTyUnchecked :: HasDebugCallStack => Subst -> Scaled Type -> Scaled Type
- GHC.Plugins: substScaledTys :: HasDebugCallStack => TCvSubst -> [Scaled Type] -> [Scaled Type]
+ GHC.Plugins: substScaledTys :: HasDebugCallStack => Subst -> [Scaled Type] -> [Scaled Type]
- GHC.Plugins: substScaledTysUnchecked :: TCvSubst -> [Scaled Type] -> [Scaled Type]
+ GHC.Plugins: substScaledTysUnchecked :: Subst -> [Scaled Type] -> [Scaled Type]
- GHC.Plugins: substTheta :: HasDebugCallStack => TCvSubst -> ThetaType -> ThetaType
+ GHC.Plugins: substTheta :: HasDebugCallStack => Subst -> ThetaType -> ThetaType
- GHC.Plugins: substThetaUnchecked :: TCvSubst -> ThetaType -> ThetaType
+ GHC.Plugins: substThetaUnchecked :: Subst -> ThetaType -> ThetaType
- GHC.Plugins: substTyAddInScope :: TCvSubst -> Type -> Type
+ GHC.Plugins: substTyAddInScope :: Subst -> Type -> Type
- GHC.Plugins: substTyCoBndr :: TCvSubst -> TyCoBinder -> (TCvSubst, TyCoBinder)
+ GHC.Plugins: substTyCoBndr :: Subst -> PiTyBinder -> (Subst, PiTyBinder)
- GHC.Plugins: substTyUnchecked :: TCvSubst -> Type -> Type
+ GHC.Plugins: substTyUnchecked :: Subst -> Type -> Type
- GHC.Plugins: substTyVar :: TCvSubst -> TyVar -> Type
+ GHC.Plugins: substTyVar :: Subst -> TyVar -> Type
- GHC.Plugins: substTyVarBndr :: HasDebugCallStack => TCvSubst -> TyVar -> (TCvSubst, TyVar)
+ GHC.Plugins: substTyVarBndr :: HasDebugCallStack => Subst -> TyVar -> (Subst, TyVar)
- GHC.Plugins: substTyVarBndrs :: HasDebugCallStack => TCvSubst -> [TyVar] -> (TCvSubst, [TyVar])
+ GHC.Plugins: substTyVarBndrs :: HasDebugCallStack => Subst -> [TyVar] -> (Subst, [TyVar])
- GHC.Plugins: substTyVars :: TCvSubst -> [TyVar] -> [Type]
+ GHC.Plugins: substTyVars :: Subst -> [TyVar] -> [Type]
- GHC.Plugins: substTys :: HasDebugCallStack => TCvSubst -> [Type] -> [Type]
+ GHC.Plugins: substTys :: HasDebugCallStack => Subst -> [Type] -> [Type]
- GHC.Plugins: substTysUnchecked :: TCvSubst -> [Type] -> [Type]
+ GHC.Plugins: substTysUnchecked :: Subst -> [Type] -> [Type]
- GHC.Plugins: substVarBndr :: HasDebugCallStack => TCvSubst -> TyCoVar -> (TCvSubst, TyCoVar)
+ GHC.Plugins: substVarBndr :: HasDebugCallStack => Subst -> TyCoVar -> (Subst, TyCoVar)
- GHC.Plugins: substVarBndrs :: HasDebugCallStack => TCvSubst -> [TyCoVar] -> (TCvSubst, [TyCoVar])
+ GHC.Plugins: substVarBndrs :: HasDebugCallStack => Subst -> [TyCoVar] -> (Subst, [TyCoVar])
- GHC.Plugins: tyCoFVsBndr :: TyCoVarBinder -> FV -> FV
+ GHC.Plugins: tyCoFVsBndr :: ForAllTyBinder -> FV -> FV
- GHC.Plugins: tyConRolesRepresentational :: TyCon -> [Role]
+ GHC.Plugins: tyConRolesRepresentational :: TyCon -> Infinite Role
- GHC.Plugins: tyConRolesX :: Role -> TyCon -> [Role]
+ GHC.Plugins: tyConRolesX :: Role -> TyCon -> Infinite Role
- GHC.Plugins: tyVarSpecToBinders :: [VarBndr a Specificity] -> [VarBndr a ArgFlag]
+ GHC.Plugins: tyVarSpecToBinders :: [VarBndr a Specificity] -> [VarBndr a ForAllTyFlag]
- GHC.Plugins: type TyVarBinder = VarBndr TyVar ArgFlag
+ GHC.Plugins: type TyVarBinder = VarBndr TyVar ForAllTyFlag
- GHC.Plugins: typeHasFixedRuntimeRep :: Type -> Bool
+ GHC.Plugins: typeHasFixedRuntimeRep :: HasDebugCallStack => Type -> Bool
- GHC.Plugins: zipTCvSubst :: HasDebugCallStack => [TyCoVar] -> [Type] -> TCvSubst
+ GHC.Plugins: zipTCvSubst :: HasDebugCallStack => [TyCoVar] -> [Type] -> Subst
- GHC.Plugins: zipTvSubst :: HasDebugCallStack => [TyVar] -> [Type] -> TCvSubst
+ GHC.Plugins: zipTvSubst :: HasDebugCallStack => [TyVar] -> [Type] -> Subst
- GHC.Rename.Expr: type AnnoBody body = (Outputable (body GhcPs), Anno (StmtLR GhcPs GhcPs (LocatedA (body GhcPs))) ~ SrcSpanAnnA, Anno (StmtLR GhcRn GhcPs (LocatedA (body GhcPs))) ~ SrcSpanAnnA, Anno (StmtLR GhcRn GhcRn (LocatedA (body GhcRn))) ~ SrcSpanAnnA)
+ GHC.Rename.Expr: type AnnoBody body = (Outputable (body GhcPs))
- GHC.Rename.Pat: rnOverLit :: HsOverLit t -> RnM ((HsOverLit GhcRn, Maybe (HsExpr GhcRn)), FreeVars)
+ GHC.Rename.Pat: rnOverLit :: XXOverLit t ~ DataConCantHappen => HsOverLit t -> RnM ((HsOverLit GhcRn, Maybe (HsExpr GhcRn)), FreeVars)
- GHC.Rename.Pat: rnPats :: HsMatchContext GhcRn -> [LPat GhcPs] -> ([LPat GhcRn] -> RnM (a, FreeVars)) -> RnM (a, FreeVars)
+ GHC.Rename.Pat: rnPats :: Traversable f => HsMatchContext GhcRn -> f (LPat GhcPs) -> (f (LPat GhcRn) -> RnM (a, FreeVars)) -> RnM (a, FreeVars)
- GHC.Rename.Splice: rnSplicePat :: HsSplice GhcPs -> RnM (Either (Pat GhcPs) (Pat GhcRn), FreeVars)
+ GHC.Rename.Splice: rnSplicePat :: HsUntypedSplice GhcPs -> RnM ((HsUntypedSplice GhcRn, HsUntypedSpliceResult (LPat GhcPs)), FreeVars)
- GHC.Rename.Splice: rnSpliceType :: HsSplice GhcPs -> RnM (HsType GhcRn, FreeVars)
+ GHC.Rename.Splice: rnSpliceType :: HsUntypedSplice GhcPs -> RnM (HsType GhcRn, FreeVars)
- GHC.Rename.Splice: rnTopSpliceDecls :: HsSplice GhcPs -> RnM ([LHsDecl GhcPs], FreeVars)
+ GHC.Rename.Splice: rnTopSpliceDecls :: HsUntypedSplice GhcPs -> RnM ([LHsDecl GhcPs], FreeVars)
- GHC.Rename.Utils: mapFvRn :: (a -> RnM (b, FreeVars)) -> [a] -> RnM ([b], FreeVars)
+ GHC.Rename.Utils: mapFvRn :: Traversable f => (a -> RnM (b, FreeVars)) -> f a -> RnM (f b, FreeVars)
- GHC.Rename.Utils: noNestedForallsContextsErr :: SDoc -> LHsType GhcRn -> Maybe (SrcSpan, SDoc)
+ GHC.Rename.Utils: noNestedForallsContextsErr :: SDoc -> LHsType GhcRn -> Maybe (SrcSpan, TcRnMessage)
- GHC.Runtime.Eval: parseName :: GhcMonad m => String -> m [Name]
+ GHC.Runtime.Eval: parseName :: GhcMonad m => String -> m (NonEmpty Name)
- GHC.Runtime.Heap.Inspect: improveRTTIType :: HscEnv -> RttiType -> RttiType -> Maybe TCvSubst
+ GHC.Runtime.Heap.Inspect: improveRTTIType :: HscEnv -> RttiType -> RttiType -> Maybe Subst
- GHC.Stg.Lint: lintStgTopBindings :: forall a. (OutputablePass a, BinderP a ~ Id) => Platform -> Logger -> DiagOpts -> StgPprOpts -> InteractiveContext -> Module -> Bool -> String -> [GenStgTopBinding a] -> IO ()
+ GHC.Stg.Lint: lintStgTopBindings :: forall a. (OutputablePass a, BinderP a ~ Id) => Platform -> Logger -> DiagOpts -> StgPprOpts -> [Var] -> Module -> Bool -> String -> [GenStgTopBinding a] -> IO ()
- GHC.Stg.Pipeline: stg2stg :: Logger -> InteractiveContext -> StgPipelineOpts -> Module -> [StgTopBinding] -> IO ([CgStgTopBinding], StgCgInfos)
+ GHC.Stg.Pipeline: stg2stg :: Logger -> [Var] -> StgPipelineOpts -> Module -> [StgTopBinding] -> IO ([CgStgTopBinding], StgCgInfos)
- GHC.StgToCmm.Heap: mkStaticClosure :: Profile -> CLabel -> CostCentreStack -> [CmmLit] -> [CmmLit] -> [CmmLit] -> [CmmLit] -> [CmmLit]
+ GHC.StgToCmm.Heap: mkStaticClosure :: Profile -> CLabel -> CostCentreStack -> [CmmLit] -> [CmmLit] -> [CmmLit] -> [CmmLit] -> [CmmLit] -> [CmmLit]
- GHC.StgToCmm.Heap: mkStaticClosureFields :: Profile -> CmmInfoTable -> CostCentreStack -> CafInfo -> [CmmLit] -> [CmmLit]
+ GHC.StgToCmm.Heap: mkStaticClosureFields :: Profile -> CmmInfoTable -> CostCentreStack -> CafInfo -> [CmmLit] -> [CmmLit] -> [CmmLit]
- GHC.StgToCmm.Monad: StgToCmmConfig :: !Profile -> Module -> !TempDir -> !SDocContext -> !Int -> !Maybe Word -> !Int -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> Maybe String -> !Bool -> !Bool -> !Bool -> StgToCmmConfig
+ GHC.StgToCmm.Monad: StgToCmmConfig :: !Profile -> Module -> !TempDir -> !SDocContext -> !Bool -> !Maybe Word -> !Int -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> !Bool -> Maybe String -> !Bool -> !Bool -> !Bool -> StgToCmmConfig
- GHC.SysTools.Tasks: askOtool :: Logger -> DynFlags -> Maybe FilePath -> [Option] -> IO String
+ GHC.SysTools.Tasks: askOtool :: Logger -> ToolSettings -> Maybe FilePath -> [Option] -> IO String
- GHC.SysTools.Tasks: runInstallNameTool :: Logger -> DynFlags -> [Option] -> IO ()
+ GHC.SysTools.Tasks: runInstallNameTool :: Logger -> ToolSettings -> [Option] -> IO ()
- GHC.Tc.Deriv.Generate: error_Expr :: String -> LHsExpr GhcPs
+ GHC.Tc.Deriv.Generate: error_Expr :: FastString -> LHsExpr GhcPs
- GHC.Tc.Deriv.Generate: genAuxBinds :: DynFlags -> SrcSpan -> Bag AuxBindSpec -> Bag (LHsBind GhcPs, LSig GhcPs)
+ GHC.Tc.Deriv.Generate: genAuxBinds :: SrcSpan -> Bag AuxBindSpec -> Bag (LHsBind GhcPs, LSig GhcPs)
- GHC.Tc.Deriv.Generate: substDerivInstTys :: TCvSubst -> DerivInstTys -> DerivInstTys
+ GHC.Tc.Deriv.Generate: substDerivInstTys :: Subst -> DerivInstTys -> DerivInstTys
- GHC.Tc.Deriv.Utils: substPredSpec :: HasCallStack => TCvSubst -> PredSpec -> PredSpec
+ GHC.Tc.Deriv.Utils: substPredSpec :: HasCallStack => Subst -> PredSpec -> PredSpec
- GHC.Tc.Gen.Foreign: checkCTarget :: ForeignImport -> CCallTarget -> TcM ()
+ GHC.Tc.Gen.Foreign: checkCTarget :: ForeignImport GhcRn -> CCallTarget -> TcM ()
- GHC.Tc.Gen.Foreign: tcCheckFEType :: Type -> ForeignExport -> TcM ForeignExport
+ GHC.Tc.Gen.Foreign: tcCheckFEType :: Type -> ForeignExport GhcRn -> TcM (ForeignExport GhcTc)
- GHC.Tc.Gen.Foreign: tcCheckFIType :: [Scaled Type] -> Type -> ForeignImport -> TcM ForeignImport
+ GHC.Tc.Gen.Foreign: tcCheckFIType :: [Scaled Type] -> Type -> ForeignImport GhcRn -> TcM (ForeignImport GhcTc)
- GHC.Tc.Gen.Head: ETypeArg :: AppCtxt -> LHsWcType GhcRn -> !XETAType p -> HsExprArg (p :: TcPass)
+ GHC.Tc.Gen.Head: ETypeArg :: AppCtxt -> !LHsToken "@" GhcRn -> LHsWcType GhcRn -> !XETAType p -> HsExprArg (p :: TcPass)
- GHC.Tc.Gen.Head: nonBidirectionalErr :: Outputable name => name -> TcRnMessage
+ GHC.Tc.Gen.Head: nonBidirectionalErr :: Name -> TcRnMessage
- GHC.Tc.Gen.Head: rebuildHsApps :: HsExpr GhcTc -> AppCtxt -> [HsExprArg 'TcpTc] -> HsExpr GhcTc
+ GHC.Tc.Gen.Head: rebuildHsApps :: HsExpr GhcTc -> AppCtxt -> [HsExprArg 'TcpTc] -> TcRhoType -> TcM (HsExpr GhcTc)
- GHC.Tc.Gen.Splice: runMeta' :: Bool -> (hs_syn -> SDoc) -> (SrcSpan -> ForeignHValue -> TcM (Either SDoc hs_syn)) -> LHsExpr GhcTc -> TcM hs_syn
+ GHC.Tc.Gen.Splice: runMeta' :: Bool -> (hs_syn -> SDoc) -> (SrcSpan -> ForeignHValue -> TcM (Either RunSpliceFailReason hs_syn)) -> LHsExpr GhcTc -> TcM hs_syn
- GHC.Tc.Solver: promoteTyVarSet :: HasDebugCallStack => TcTyVarSet -> TcM Bool
+ GHC.Tc.Solver: promoteTyVarSet :: TcTyVarSet -> TcM Bool
- GHC.Tc.Solver.Monad: instFlexiX :: TCvSubst -> [TKVar] -> TcS TCvSubst
+ GHC.Tc.Solver.Monad: instFlexiX :: Subst -> [TKVar] -> TcS Subst
- GHC.Tc.Solver.Monad: tcInstSkolTyVarsX :: SkolemInfo -> TCvSubst -> [TyVar] -> TcS (TCvSubst, [TcTyVar])
+ GHC.Tc.Solver.Monad: tcInstSkolTyVarsX :: SkolemInfo -> Subst -> [TyVar] -> TcS (Subst, [TcTyVar])
- GHC.Tc.Solver.Monad: touchabilityTest :: CtFlavour -> TcTyVar -> TcType -> TcS (TouchabilityTestResult, TcType)
+ GHC.Tc.Solver.Monad: touchabilityTest :: CtFlavour -> TcTyVar -> TcType -> TcS TouchabilityTestResult
- GHC.Tc.TyCl: dataDeclChecks :: Name -> NewOrData -> Maybe (LHsContext GhcRn) -> [LConDecl GhcRn] -> TcM Bool
+ GHC.Tc.TyCl: dataDeclChecks :: Name -> Maybe (LHsContext GhcRn) -> DataDefnCons (LConDecl GhcRn) -> TcM Bool
- GHC.Tc.TyCl: kcConDecls :: NewOrData -> TcKind -> [LConDecl GhcRn] -> TcM ()
+ GHC.Tc.TyCl: kcConDecls :: Foldable f => NewOrData -> TcKind -> f (LConDecl GhcRn) -> TcM ()
- GHC.Tc.TyCl: tcConDecls :: NewOrData -> DataDeclInfo -> KnotTied TyCon -> [TcTyConBinder] -> TcKind -> [LConDecl GhcRn] -> TcM [DataCon]
+ GHC.Tc.TyCl: tcConDecls :: DataDeclInfo -> KnotTied TyCon -> [TcTyConBinder] -> TcKind -> DataDefnCons (LConDecl GhcRn) -> TcM (DataDefnCons DataCon)
- GHC.Tc.TyCl.Class: tcATDefault :: SrcSpan -> TCvSubst -> NameSet -> ClassATItem -> TcM [FamInst]
+ GHC.Tc.TyCl.Class: tcATDefault :: SrcSpan -> Subst -> NameSet -> ClassATItem -> TcM [FamInst]
- GHC.Tc.Utils.Instantiate: freshenCoVarBndrsX :: TCvSubst -> [CoVar] -> TcM (TCvSubst, [CoVar])
+ GHC.Tc.Utils.Instantiate: freshenCoVarBndrsX :: Subst -> [CoVar] -> TcM (Subst, [CoVar])
- GHC.Tc.Utils.Instantiate: freshenTyVarBndrs :: [TyVar] -> TcM (TCvSubst, [TyVar])
+ GHC.Tc.Utils.Instantiate: freshenTyVarBndrs :: [TyVar] -> TcM (Subst, [TyVar])
- GHC.Tc.Utils.Instantiate: instTyVarsWith :: CtOrigin -> [TyVar] -> [TcType] -> TcM TCvSubst
+ GHC.Tc.Utils.Instantiate: instTyVarsWith :: CtOrigin -> [TyVar] -> [TcType] -> TcM Subst
- GHC.Tc.Utils.Instantiate: tcInstInvisibleTyBinder :: TCvSubst -> TyBinder -> TcM (TCvSubst, TcType)
+ GHC.Tc.Utils.Instantiate: tcInstInvisibleTyBinder :: Subst -> PiTyVarBinder -> TcM (Subst, TcType)
- GHC.Tc.Utils.Instantiate: tcInstSkolTyVars :: SkolemInfo -> [TyVar] -> TcM (TCvSubst, [TcTyVar])
+ GHC.Tc.Utils.Instantiate: tcInstSkolTyVars :: SkolemInfo -> [TyVar] -> TcM (Subst, [TcTyVar])
- GHC.Tc.Utils.Instantiate: tcInstSkolTyVarsX :: SkolemInfo -> TCvSubst -> [TyVar] -> TcM (TCvSubst, [TcTyVar])
+ GHC.Tc.Utils.Instantiate: tcInstSkolTyVarsX :: SkolemInfo -> Subst -> [TyVar] -> TcM (Subst, [TcTyVar])
- GHC.Tc.Utils.Instantiate: tcInstSuperSkolTyVarsX :: SkolemInfo -> TCvSubst -> [TyVar] -> TcM (TCvSubst, [TcTyVar])
+ GHC.Tc.Utils.Instantiate: tcInstSuperSkolTyVarsX :: SkolemInfo -> Subst -> [TyVar] -> TcM (Subst, [TcTyVar])
- GHC.Tc.Utils.Instantiate: tcInstType :: ([TyVar] -> TcM (TCvSubst, [TcTyVar])) -> Id -> TcM ([(Name, TcTyVar)], TcThetaType, TcType)
+ GHC.Tc.Utils.Instantiate: tcInstType :: ([TyVar] -> TcM (Subst, [TcTyVar])) -> Id -> TcM ([(Name, TcTyVar)], TcThetaType, TcType)
- GHC.Tc.Utils.Instantiate: tcInstTypeBndrs :: Id -> TcM ([(Name, InvisTVBinder)], TcThetaType, TcType)
+ GHC.Tc.Utils.Instantiate: tcInstTypeBndrs :: Type -> TcM ([(Name, InvisTVBinder)], TcThetaType, TcType)
- GHC.Tc.Utils.Instantiate: tcSkolDFunType :: SkolemInfo -> DFunId -> TcM ([TcTyVar], TcThetaType, TcType)
+ GHC.Tc.Utils.Instantiate: tcSkolDFunType :: Type -> TcM (SkolemInfoAnon, [TcTyVar], TcThetaType, Class, [TcType])
- GHC.Tc.Utils.Instantiate: tcSuperSkolTyVars :: TcLevel -> SkolemInfo -> [TyVar] -> (TCvSubst, [TcTyVar])
+ GHC.Tc.Utils.Instantiate: tcSuperSkolTyVars :: TcLevel -> SkolemInfo -> [TyVar] -> (Subst, [TcTyVar])
- GHC.Tc.Utils.TcMType: emitNewExprHole :: OccName -> Type -> TcM HoleExprRef
+ GHC.Tc.Utils.TcMType: emitNewExprHole :: RdrName -> Type -> TcM HoleExprRef
- GHC.Tc.Utils.TcMType: newMetaTyVarTyVarX :: TCvSubst -> TyVar -> TcM (TCvSubst, TcTyVar)
+ GHC.Tc.Utils.TcMType: newMetaTyVarTyVarX :: Subst -> TyVar -> TcM (Subst, TcTyVar)
- GHC.Tc.Utils.TcMType: newMetaTyVarX :: TCvSubst -> TyVar -> TcM (TCvSubst, TcTyVar)
+ GHC.Tc.Utils.TcMType: newMetaTyVarX :: Subst -> TyVar -> TcM (Subst, TcTyVar)
- GHC.Tc.Utils.TcMType: newMetaTyVars :: [TyVar] -> TcM (TCvSubst, [TcTyVar])
+ GHC.Tc.Utils.TcMType: newMetaTyVars :: [TyVar] -> TcM (Subst, [TcTyVar])
- GHC.Tc.Utils.TcMType: newMetaTyVarsX :: TCvSubst -> [TyVar] -> TcM (TCvSubst, [TcTyVar])
+ GHC.Tc.Utils.TcMType: newMetaTyVarsX :: Subst -> [TyVar] -> TcM (Subst, [TcTyVar])
- GHC.Tc.Utils.TcMType: newWildCardX :: TCvSubst -> TyVar -> TcM (TCvSubst, TcTyVar)
+ GHC.Tc.Utils.TcMType: newWildCardX :: Subst -> TyVar -> TcM (Subst, TcTyVar)
- GHC.Tc.Utils.TcMType: promoteMetaTyVarTo :: HasDebugCallStack => TcLevel -> TcTyVar -> TcM Bool
+ GHC.Tc.Utils.TcMType: promoteMetaTyVarTo :: TcLevel -> TcTyVar -> TcM Bool
- GHC.Tc.Utils.TcMType: promoteTyVarSet :: HasDebugCallStack => TcTyVarSet -> TcM Bool
+ GHC.Tc.Utils.TcMType: promoteTyVarSet :: TcTyVarSet -> TcM Bool
- GHC.Tc.Utils.TcMType: writeMetaTyVar :: HasDebugCallStack => TcTyVar -> TcType -> TcM ()
+ GHC.Tc.Utils.TcMType: writeMetaTyVar :: TcTyVar -> TcType -> TcM ()
- GHC.Tc.Utils.TcMType: writeMetaTyVarRef :: HasDebugCallStack => TcTyVar -> TcRef MetaDetails -> TcType -> TcM ()
+ GHC.Tc.Utils.TcMType: writeMetaTyVarRef :: TcTyVar -> TcRef MetaDetails -> TcType -> TcM ()
- GHC.Tc.Utils.Unify: startSolvingByUnification :: MetaInfo -> TcType -> TcM (Maybe TcType)
+ GHC.Tc.Utils.Unify: startSolvingByUnification :: MetaInfo -> TcType -> TcM Bool
- GHC.Tc.Utils.Zonk: zonkTyVarOcc :: HasDebugCallStack => ZonkEnv -> TcTyVar -> TcM Type
+ GHC.Tc.Utils.Zonk: zonkTyVarOcc :: ZonkEnv -> TcTyVar -> TcM Type
- GHC.Tc.Validity: validDerivPred :: TyVarSet -> PredType -> Bool
+ GHC.Tc.Validity: validDerivPred :: PatersonSize -> PredType -> Bool
- GHC.ThToHs: convertToHsDecls :: Origin -> SrcSpan -> [Dec] -> Either SDoc [LHsDecl GhcPs]
+ GHC.ThToHs: convertToHsDecls :: Origin -> SrcSpan -> [Dec] -> Either RunSpliceFailReason [LHsDecl GhcPs]
- GHC.ThToHs: convertToHsExpr :: Origin -> SrcSpan -> Exp -> Either SDoc (LHsExpr GhcPs)
+ GHC.ThToHs: convertToHsExpr :: Origin -> SrcSpan -> Exp -> Either RunSpliceFailReason (LHsExpr GhcPs)
- GHC.ThToHs: convertToHsType :: Origin -> SrcSpan -> Type -> Either SDoc (LHsType GhcPs)
+ GHC.ThToHs: convertToHsType :: Origin -> SrcSpan -> Type -> Either RunSpliceFailReason (LHsType GhcPs)
- GHC.ThToHs: convertToPat :: Origin -> SrcSpan -> Pat -> Either SDoc (LPat GhcPs)
+ GHC.ThToHs: convertToPat :: Origin -> SrcSpan -> Pat -> Either RunSpliceFailReason (LPat GhcPs)
- GHC.Utils.Asm: sectionType :: Platform -> String -> SDoc
+ GHC.Utils.Asm: sectionType :: IsLine doc => Platform -> String -> doc
Files
- compiler/Bytecodes.h +2/−0
- compiler/ClosureTypes.h +2/−1
- compiler/CodeGen.Platform.h +72/−1
- compiler/GHC.hs +38/−77
- compiler/GHC/Builtin/Names/TH.hs +13/−8
- compiler/GHC/Builtin/Types/Literals.hs +2/−2
- compiler/GHC/ByteCode/Asm.hs +18/−14
- compiler/GHC/ByteCode/Instr.hs +20/−19
- compiler/GHC/ByteCode/Linker.hs +2/−1
- compiler/GHC/Cmm/CallConv.hs +0/−1
- compiler/GHC/Cmm/CommonBlockElim.hs +14/−26
- compiler/GHC/Cmm/Config.hs +1/−0
- compiler/GHC/Cmm/ContFlowOpt.hs +9/−7
- compiler/GHC/Cmm/Dataflow.hs +1/−1
- compiler/GHC/Cmm/DebugBlock.hs +24/−13
- compiler/GHC/Cmm/Dominators.hs +213/−0
- compiler/GHC/Cmm/Info/Build.hs +95/−58
- compiler/GHC/Cmm/Lint.hs +11/−16
- compiler/GHC/Cmm/Liveness.hs +1/−2
- compiler/GHC/Cmm/Parser.y +22/−30
- compiler/GHC/Cmm/Parser/Config.hs +24/−0
- compiler/GHC/Cmm/Parser/Monad.hs +8/−5
- compiler/GHC/Cmm/Pipeline.hs +26/−18
- compiler/GHC/Cmm/Ppr.hs +0/−319
- compiler/GHC/Cmm/Ppr/Decl.hs +0/−172
- compiler/GHC/Cmm/Ppr/Expr.hs +0/−299
- compiler/GHC/Cmm/ProcPoint.hs +0/−3
- compiler/GHC/Cmm/Reducibility.hs +224/−0
- compiler/GHC/Cmm/Sink.hs +4/−4
- compiler/GHC/Cmm/ThreadSanitizer.hs +285/−0
- compiler/GHC/Cmm/Utils.hs +5/−50
- compiler/GHC/CmmToAsm.hs +64/−63
- compiler/GHC/CmmToAsm/AArch64.hs +7/−5
- compiler/GHC/CmmToAsm/AArch64/CodeGen.hs +61/−246
- compiler/GHC/CmmToAsm/AArch64/Cond.hs +2/−8
- compiler/GHC/CmmToAsm/AArch64/Instr.hs +11/−4
- compiler/GHC/CmmToAsm/AArch64/Ppr.hs +184/−175
- compiler/GHC/CmmToAsm/AArch64/Regs.hs +4/−9
- compiler/GHC/CmmToAsm/BlockLayout.hs +38/−40
- compiler/GHC/CmmToAsm/CFG.hs +9/−14
- compiler/GHC/CmmToAsm/CFG/Dominators.hs +3/−3
- compiler/GHC/CmmToAsm/CPrim.hs +1/−1
- compiler/GHC/CmmToAsm/Dwarf.hs +32/−33
- compiler/GHC/CmmToAsm/Dwarf/Constants.hs +27/−4
- compiler/GHC/CmmToAsm/Dwarf/Types.hs +108/−83
- compiler/GHC/CmmToAsm/Instr.hs +2/−1
- compiler/GHC/CmmToAsm/Monad.hs +54/−53
- compiler/GHC/CmmToAsm/PIC.hs +19/−17
- compiler/GHC/CmmToAsm/PPC.hs +2/−1
- compiler/GHC/CmmToAsm/PPC/CodeGen.hs +5/−6
- compiler/GHC/CmmToAsm/PPC/Instr.hs +13/−11
- compiler/GHC/CmmToAsm/PPC/Ppr.hs +138/−142
- compiler/GHC/CmmToAsm/PPC/Regs.hs +4/−3
- compiler/GHC/CmmToAsm/Ppr.hs +32/−30
- compiler/GHC/CmmToAsm/Reg/Graph.hs +2/−2
- compiler/GHC/CmmToAsm/Reg/Graph/Base.hs +164/−0
- compiler/GHC/CmmToAsm/Reg/Graph/Coalesce.hs +99/−0
- compiler/GHC/CmmToAsm/Reg/Graph/Spill.hs +1/−2
- compiler/GHC/CmmToAsm/Reg/Graph/SpillClean.hs +1/−2
- compiler/GHC/CmmToAsm/Reg/Graph/SpillCost.hs +2/−2
- compiler/GHC/CmmToAsm/Reg/Graph/TrivColorable.hs +7/−1
- compiler/GHC/CmmToAsm/Reg/Graph/X86.hs +161/−0
- compiler/GHC/CmmToAsm/Reg/Linear.hs +4/−2
- compiler/GHC/CmmToAsm/Reg/Linear/AArch64.hs +1/−1
- compiler/GHC/CmmToAsm/Reg/Linear/FreeRegs.hs +2/−0
- compiler/GHC/CmmToAsm/Reg/Linear/JoinToTargets.hs +4/−3
- compiler/GHC/CmmToAsm/Reg/Liveness.hs +6/−8
- compiler/GHC/CmmToAsm/Reg/Target.hs +10/−0
- compiler/GHC/CmmToAsm/Wasm.hs +56/−0
- compiler/GHC/CmmToAsm/Wasm/Asm.hs +538/−0
- compiler/GHC/CmmToAsm/Wasm/FromCmm.hs +1686/−0
- compiler/GHC/CmmToAsm/Wasm/Types.hs +475/−0
- compiler/GHC/CmmToAsm/Wasm/Utils.hs +29/−0
- compiler/GHC/CmmToAsm/X86.hs +3/−2
- compiler/GHC/CmmToAsm/X86/CodeGen.hs +9/−24
- compiler/GHC/CmmToAsm/X86/Instr.hs +4/−3
- compiler/GHC/CmmToAsm/X86/Ppr.hs +127/−151
- compiler/GHC/CmmToAsm/X86/Regs.hs +4/−4
- compiler/GHC/CmmToC.hs +60/−67
- compiler/GHC/CmmToLlvm.hs +5/−5
- compiler/GHC/CmmToLlvm/Base.hs +15/−67
- compiler/GHC/CmmToLlvm/CodeGen.hs +11/−22
- compiler/GHC/CmmToLlvm/Config.hs +0/−31
- compiler/GHC/CmmToLlvm/Data.hs +0/−3
- compiler/GHC/CmmToLlvm/Ppr.hs +1/−1
- compiler/GHC/Core/LateCC.hs +5/−5
- compiler/GHC/Core/Opt/CSE.hs +50/−46
- compiler/GHC/Core/Opt/CallArity.hs +7/−8
- compiler/GHC/Core/Opt/CprAnal.hs +6/−3
- compiler/GHC/Core/Opt/DmdAnal.hs +2572/−2252
- compiler/GHC/Core/Opt/Exitify.hs +6/−6
- compiler/GHC/Core/Opt/FloatIn.hs +13/−24
- compiler/GHC/Core/Opt/FloatOut.hs +6/−12
- compiler/GHC/Core/Opt/LiberateCase.hs +34/−16
- compiler/GHC/Core/Opt/Pipeline.hs +65/−577
- compiler/GHC/Core/Opt/SetLevels.hs +119/−141
- compiler/GHC/Core/Opt/Simplify.hs +0/−4317
- compiler/GHC/Core/Opt/Simplify/Env.hs +0/−1069
- compiler/GHC/Core/Opt/Simplify/Monad.hs +0/−297
- compiler/GHC/Core/Opt/Simplify/Utils.hs +0/−2600
- compiler/GHC/Core/Opt/SpecConstr.hs +586/−381
- compiler/GHC/Core/Opt/Specialise.hs +3450/−3104
- compiler/GHC/Core/Opt/StaticArgs.hs +7/−4
- compiler/GHC/Core/Opt/WorkWrap.hs +67/−49
- compiler/GHC/Core/Opt/WorkWrap/Utils.hs +261/−185
- compiler/GHC/CoreToStg.hs +88/−85
- compiler/GHC/CoreToStg/Prep.hs +121/−145
- compiler/GHC/Data/Graph/Collapse.hs +264/−0
- compiler/GHC/Data/Graph/Color.hs +2/−3
- compiler/GHC/Data/Graph/Inductive/Graph.hs +643/−0
- compiler/GHC/Data/Graph/Inductive/PatriciaTree.hs +346/−0
- compiler/GHC/Data/Graph/Ops.hs +4/−4
- compiler/GHC/Driver/Backpack.hs +944/−0
- compiler/GHC/Driver/CodeOutput.hs +44/−31
- compiler/GHC/Driver/Config/Cmm.hs +4/−3
- compiler/GHC/Driver/Config/Cmm/Parser.hs +25/−0
- compiler/GHC/Driver/Config/CmmToAsm.hs +1/−1
- compiler/GHC/Driver/Config/CmmToLlvm.hs +9/−5
- compiler/GHC/Driver/Config/Core/Lint/Interactive.hs +35/−0
- compiler/GHC/Driver/Config/Core/Opt/Arity.hs +15/−0
- compiler/GHC/Driver/Config/Core/Opt/LiberateCase.hs +15/−0
- compiler/GHC/Driver/Config/Core/Opt/Simplify.hs +91/−0
- compiler/GHC/Driver/Config/Core/Opt/WorkWrap.hs +21/−0
- compiler/GHC/Driver/Config/Core/Rules.hs +23/−0
- compiler/GHC/Driver/Config/CoreToStg.hs +16/−0
- compiler/GHC/Driver/Config/CoreToStg/Prep.hs +34/−0
- compiler/GHC/Driver/Config/HsToCore/Ticks.hs +28/−0
- compiler/GHC/Driver/Config/HsToCore/Usage.hs +14/−0
- compiler/GHC/Driver/Config/Linker.hs +13/−0
- compiler/GHC/Driver/Config/StgToCmm.hs +13/−12
- compiler/GHC/Driver/Config/StgToJS.hs +32/−0
- compiler/GHC/Driver/Config/Tidy.hs +1/−5
- compiler/GHC/Driver/GenerateCgIPEStub.hs +23/−22
- compiler/GHC/Driver/Main.hs +419/−161
- compiler/GHC/Driver/Make.hs +382/−139
- compiler/GHC/Driver/MakeFile.hs +454/−0
- compiler/GHC/Driver/Pipeline.hs +165/−113
- compiler/GHC/Driver/Pipeline.hs-boot +2/−1
- compiler/GHC/Driver/Pipeline/Execute.hs +222/−283
- compiler/GHC/Hs/Stats.hs +4/−4
- compiler/GHC/Hs/Syn/Type.hs +16/−19
- compiler/GHC/HsToCore.hs +77/−52
- compiler/GHC/HsToCore/Arrows.hs +54/−47
- compiler/GHC/HsToCore/Binds.hs +128/−86
- compiler/GHC/HsToCore/Breakpoints.hs +54/−0
- compiler/GHC/HsToCore/Coverage.hs +129/−1358
- compiler/GHC/HsToCore/Docs.hs +8/−7
- compiler/GHC/HsToCore/Expr.hs +67/−300
- compiler/GHC/HsToCore/Foreign/C.hs +632/−0
- compiler/GHC/HsToCore/Foreign/Call.hs +9/−12
- compiler/GHC/HsToCore/Foreign/Decl.hs +24/−674
- compiler/GHC/HsToCore/Foreign/JavaScript.hs +683/−0
- compiler/GHC/HsToCore/Foreign/Prim.hs +45/−0
- compiler/GHC/HsToCore/Foreign/Utils.hs +105/−0
- compiler/GHC/HsToCore/GuardedRHSs.hs +1/−1
- compiler/GHC/HsToCore/ListComp.hs +22/−25
- compiler/GHC/HsToCore/Match.hs +22/−17
- compiler/GHC/HsToCore/Match/Constructor.hs +10/−11
- compiler/GHC/HsToCore/Match/Literal.hs +18/−11
- compiler/GHC/HsToCore/Monad.hs +16/−13
- compiler/GHC/HsToCore/Pmc.hs +1/−1
- compiler/GHC/HsToCore/Pmc/Desugar.hs +37/−6
- compiler/GHC/HsToCore/Pmc/Solver.hs +135/−31
- compiler/GHC/HsToCore/Pmc/Utils.hs +3/−3
- compiler/GHC/HsToCore/Quote.hs +208/−161
- compiler/GHC/HsToCore/Ticks.hs +1249/−0
- compiler/GHC/HsToCore/Types.hs +1/−1
- compiler/GHC/HsToCore/Usage.hs +51/−41
- compiler/GHC/HsToCore/Utils.hs +24/−25
- compiler/GHC/Iface/Binary.hs +70/−106
- compiler/GHC/Iface/Errors.hs +1/−1
- compiler/GHC/Iface/Ext/Ast.hs +88/−70
- compiler/GHC/Iface/Ext/Binary.hs +5/−6
- compiler/GHC/Iface/Ext/Types.hs +25/−25
- compiler/GHC/Iface/Ext/Utils.hs +10/−9
- compiler/GHC/Iface/Load.hs +17/−9
- compiler/GHC/Iface/Make.hs +38/−26
- compiler/GHC/Iface/Recomp.hs +35/−22
- compiler/GHC/Iface/Rename.hs +6/−14
- compiler/GHC/Iface/Tidy.hs +171/−68
- compiler/GHC/Iface/Tidy/StaticPtrTable.hs +12/−6
- compiler/GHC/IfaceToCore.hs +108/−49
- compiler/GHC/JS/Make.hs +715/−0
- compiler/GHC/JS/Ppr.hs +294/−0
- compiler/GHC/JS/Syntax.hs +392/−0
- compiler/GHC/JS/Transform.hs +264/−0
- compiler/GHC/Linker.hs +36/−0
- compiler/GHC/Linker/Config.hs +13/−0
- compiler/GHC/Linker/Dynamic.hs +10/−3
- compiler/GHC/Linker/ExtraObj.hs +7/−7
- compiler/GHC/Linker/Loader.hs +45/−11
- compiler/GHC/Linker/MacOS.hs +13/−12
- compiler/GHC/Linker/Static.hs +26/−27
- compiler/GHC/Linker/Unit.hs +15/−15
- compiler/GHC/Llvm/Ppr.hs +2/−2
- compiler/GHC/Llvm/Types.hs +1/−1
- compiler/GHC/Plugins.hs +5/−1
- compiler/GHC/Rename/Bind.hs +73/−45
- compiler/GHC/Rename/Env.hs +25/−47
- compiler/GHC/Rename/Expr.hs +158/−121
- compiler/GHC/Rename/Expr.hs-boot +1/−9
- compiler/GHC/Rename/Fixity.hs +1/−1
- compiler/GHC/Rename/HsType.hs +46/−32
- compiler/GHC/Rename/Module.hs +263/−57
- compiler/GHC/Rename/Names.hs +102/−90
- compiler/GHC/Rename/Pat.hs +37/−34
- compiler/GHC/Rename/Splice.hs +166/−148
- compiler/GHC/Rename/Splice.hs-boot +4/−5
- compiler/GHC/Rename/Unbound.hs +3/−6
- compiler/GHC/Rename/Utils.hs +66/−26
- compiler/GHC/Runtime/Debugger.hs +271/−0
- compiler/GHC/Runtime/Eval.hs +25/−22
- compiler/GHC/Runtime/Heap/Inspect.hs +29/−21
- compiler/GHC/Runtime/Loader.hs +46/−12
- compiler/GHC/Settings/IO.hs +0/−4
- compiler/GHC/Stg/BcPrep.hs +1/−1
- compiler/GHC/Stg/CSE.hs +11/−4
- compiler/GHC/Stg/Debug.hs +1/−1
- compiler/GHC/Stg/InferTags.hs +3/−3
- compiler/GHC/Stg/InferTags/Rewrite.hs +73/−56
- compiler/GHC/Stg/Lift/Analysis.hs +1/−1
- compiler/GHC/Stg/Lift/Monad.hs +2/−2
- compiler/GHC/Stg/Lint.hs +21/−23
- compiler/GHC/Stg/Pipeline.hs +9/−8
- compiler/GHC/Stg/Subst.hs +7/−1
- compiler/GHC/Stg/Unarise.hs +17/−21
- compiler/GHC/Stg/Utils.hs +2/−3
- compiler/GHC/StgToByteCode.hs +101/−118
- compiler/GHC/StgToCmm.hs +4/−3
- compiler/GHC/StgToCmm/Bind.hs +45/−6
- compiler/GHC/StgToCmm/CgUtils.hs +4/−7
- compiler/GHC/StgToCmm/Closure.hs +10/−112
- compiler/GHC/StgToCmm/Env.hs +4/−3
- compiler/GHC/StgToCmm/Expr.hs +22/−15
- compiler/GHC/StgToCmm/Foreign.hs +7/−23
- compiler/GHC/StgToCmm/Heap.hs +15/−19
- compiler/GHC/StgToCmm/InfoTableProv.hs +20/−7
- compiler/GHC/StgToCmm/Layout.hs +2/−2
- compiler/GHC/StgToCmm/Lit.hs +2/−2
- compiler/GHC/StgToCmm/Monad.hs +6/−8
- compiler/GHC/StgToCmm/Prim.hs +121/−220
- compiler/GHC/StgToCmm/Prof.hs +1/−1
- compiler/GHC/StgToCmm/Sequel.hs +0/−1
- compiler/GHC/StgToCmm/TagCheck.hs +6/−7
- compiler/GHC/StgToCmm/Ticky.hs +2/−3
- compiler/GHC/StgToCmm/Utils.hs +6/−6
- compiler/GHC/StgToJS.hs +216/−0
- compiler/GHC/StgToJS/Apply.hs +1152/−0
- compiler/GHC/StgToJS/Arg.hs +285/−0
- compiler/GHC/StgToJS/Closure.hs +156/−0
- compiler/GHC/StgToJS/CodeGen.hs +367/−0
- compiler/GHC/StgToJS/CoreUtils.hs +282/−0
- compiler/GHC/StgToJS/DataCon.hs +124/−0
- compiler/GHC/StgToJS/Deps.hs +191/−0
- compiler/GHC/StgToJS/Expr.hs +1045/−0
- compiler/GHC/StgToJS/ExprCtx.hs +172/−0
- compiler/GHC/StgToJS/FFI.hs +352/−0
- compiler/GHC/StgToJS/Heap.hs +155/−0
- compiler/GHC/StgToJS/Ids.hs +238/−0
- compiler/GHC/StgToJS/Linker/Linker.hs +945/−0
- compiler/GHC/StgToJS/Linker/Types.hs +101/−0
- compiler/GHC/StgToJS/Linker/Utils.hs +308/−0
- compiler/GHC/StgToJS/Literal.hs +109/−0
- compiler/GHC/StgToJS/Monad.hs +183/−0
- compiler/GHC/StgToJS/Object.hs +622/−0
- compiler/GHC/StgToJS/Prim.hs +1502/−0
- compiler/GHC/StgToJS/Printer.hs +218/−0
- compiler/GHC/StgToJS/Profiling.hs +178/−0
- compiler/GHC/StgToJS/Regs.hs +142/−0
- compiler/GHC/StgToJS/Rts/Rts.hs +661/−0
- compiler/GHC/StgToJS/Rts/Types.hs +78/−0
- compiler/GHC/StgToJS/Sinker.hs +180/−0
- compiler/GHC/StgToJS/Stack.hs +373/−0
- compiler/GHC/StgToJS/StaticPtr.hs +28/−0
- compiler/GHC/StgToJS/StgUtils.hs +266/−0
- compiler/GHC/StgToJS/Symbols.hs +89/−0
- compiler/GHC/StgToJS/Types.hs +430/−0
- compiler/GHC/StgToJS/Utils.hs +57/−0
- compiler/GHC/SysTools.hs +0/−45
- compiler/GHC/SysTools/Ar.hs +9/−8
- compiler/GHC/SysTools/Cpp.hs +233/−0
- compiler/GHC/SysTools/Elf.hs +1/−1
- compiler/GHC/SysTools/Info.hs +1/−1
- compiler/GHC/SysTools/Tasks.hs +35/−45
- compiler/GHC/Tc/Deriv.hs +25/−20
- compiler/GHC/Tc/Deriv/Functor.hs +5/−5
- compiler/GHC/Tc/Deriv/Generate.hs +36/−37
- compiler/GHC/Tc/Deriv/Generics.hs +22/−13
- compiler/GHC/Tc/Deriv/Infer.hs +53/−61
- compiler/GHC/Tc/Deriv/Utils.hs +24/−18
- compiler/GHC/Tc/Errors.hs +198/−198
- compiler/GHC/Tc/Errors/Hole.hs +14/−18
- compiler/GHC/Tc/Gen/Annotation.hs +1/−1
- compiler/GHC/Tc/Gen/App.hs +111/−318
- compiler/GHC/Tc/Gen/Arrow.hs +9/−8
- compiler/GHC/Tc/Gen/Bind.hs +187/−151
- compiler/GHC/Tc/Gen/Default.hs +8/−7
- compiler/GHC/Tc/Gen/Export.hs +16/−16
- compiler/GHC/Tc/Gen/Expr.hs +582/−378
- compiler/GHC/Tc/Gen/Foreign.hs +75/−69
- compiler/GHC/Tc/Gen/Head.hs +471/−165
- compiler/GHC/Tc/Gen/HsType.hs +161/−233
- compiler/GHC/Tc/Gen/Match.hs +20/−24
- compiler/GHC/Tc/Gen/Pat.hs +256/−160
- compiler/GHC/Tc/Gen/Rule.hs +15/−13
- compiler/GHC/Tc/Gen/Sig.hs +33/−47
- compiler/GHC/Tc/Gen/Splice.hs +2833/−2628
- compiler/GHC/Tc/Gen/Splice.hs-boot +5/−5
- compiler/GHC/Tc/Instance/Class.hs +129/−80
- compiler/GHC/Tc/Instance/Family.hs +7/−9
- compiler/GHC/Tc/Instance/FunDeps.hs +84/−67
- compiler/GHC/Tc/Instance/Typeable.hs +29/−23
- compiler/GHC/Tc/Module.hs +88/−67
- compiler/GHC/Tc/Plugin.hs +194/−0
- compiler/GHC/Tc/Solver.hs +76/−24
- compiler/GHC/Tc/Solver/Canonical.hs +3417/−3141
- compiler/GHC/Tc/Solver/Interact.hs +517/−277
- compiler/GHC/Tc/Solver/Monad.hs +101/−80
- compiler/GHC/Tc/Solver/Rewrite.hs +53/−33
- compiler/GHC/Tc/TyCl.hs +283/−252
- compiler/GHC/Tc/TyCl/Build.hs +19/−18
- compiler/GHC/Tc/TyCl/Class.hs +24/−112
- compiler/GHC/Tc/TyCl/Instance.hs +170/−165
- compiler/GHC/Tc/TyCl/PatSyn.hs +56/−65
- compiler/GHC/Tc/TyCl/Utils.hs +76/−48
- compiler/GHC/Tc/Utils/Backpack.hs +37/−35
- compiler/GHC/Tc/Utils/Concrete.hs +7/−7
- compiler/GHC/Tc/Utils/Env.hs +14/−17
- compiler/GHC/Tc/Utils/Instantiate.hs +89/−76
- compiler/GHC/Tc/Utils/Monad.hs +36/−30
- compiler/GHC/Tc/Utils/TcMType.hs +64/−110
- compiler/GHC/Tc/Utils/Unify.hs +121/−99
- compiler/GHC/Tc/Utils/Zonk.hs +24/−66
- compiler/GHC/Tc/Validity.hs +310/−314
- compiler/GHC/ThToHs.hs +206/−196
- compiler/GHC/Types/TyThing/Ppr.hs +4/−3
- compiler/GHC/Utils/Asm.hs +3/−2
- compiler/GHC/Wasm/ControlFlow.hs +49/−0
- compiler/GHC/Wasm/ControlFlow/FromCmm.hs +360/−0
- compiler/MachRegs.h +86/−0
- compiler/ghc-llvm-version.h +2/−2
- ghc-lib.cabal +133/−19
- ghc-lib/stage0/compiler/build/primop-can-fail.hs-incl +3/−0
- ghc-lib/stage0/compiler/build/primop-code-size.hs-incl +3/−0
- ghc-lib/stage0/compiler/build/primop-data-decl.hs-incl +8/−0
- ghc-lib/stage0/compiler/build/primop-docs.hs-incl +122/−109
- ghc-lib/stage0/compiler/build/primop-has-side-effects.hs-incl +4/−0
- ghc-lib/stage0/compiler/build/primop-list.hs-incl +8/−0
- ghc-lib/stage0/compiler/build/primop-out-of-line.hs-incl +8/−0
- ghc-lib/stage0/compiler/build/primop-primop-info.hs-incl +10/−2
- ghc-lib/stage0/compiler/build/primop-strictness.hs-incl +5/−0
- ghc-lib/stage0/compiler/build/primop-tag.hs-incl +1314/−1306
- ghc-lib/stage0/lib/llvm-passes +3/−3
- ghc-lib/stage0/lib/llvm-targets +39/−35
- ghc-lib/stage0/lib/settings +1/−3
- ghc-lib/stage0/libraries/ghc-boot/build/GHC/Platform/Host.hs +12/−0
- ghc-lib/stage0/rts/build/include/GhclibDerivedConstants.h +34/−13
- ghc-lib/stage0/rts/build/include/ghcautoconf.h +18/−12
- libraries/ghc-boot/GHC/HandleEncoding.hs +32/−0
- libraries/ghc-boot/GHC/Settings/Utils.hs +0/−72
- libraries/ghci/GHCi/CreateBCO.hs +211/−0
- libraries/ghci/GHCi/InfoTable.hsc +419/−0
- libraries/ghci/GHCi/ObjLink.hs +195/−0
- libraries/ghci/GHCi/Run.hs +398/−0
- libraries/ghci/GHCi/Signals.hs +47/−0
- libraries/ghci/GHCi/StaticPtrTable.hs +25/−0
- libraries/ghci/GHCi/TH.hs +273/−0
- libraries/template-haskell/Language/Haskell/TH/CodeDo.hs +22/−0
- libraries/template-haskell/Language/Haskell/TH/Quote.hs +57/−0
compiler/Bytecodes.h view
@@ -34,6 +34,7 @@ #define bci_PUSH16_W 9 #define bci_PUSH32_W 10 #define bci_PUSH_G 11+#define bci_PUSH_ALTS 12 #define bci_PUSH_ALTS_P 13 #define bci_PUSH_ALTS_N 14 #define bci_PUSH_ALTS_F 15@@ -80,6 +81,7 @@ #define bci_CCALL 56 #define bci_SWIZZLE 57 #define bci_ENTER 58+#define bci_RETURN 59 #define bci_RETURN_P 60 #define bci_RETURN_N 61 #define bci_RETURN_F 62
compiler/ClosureTypes.h view
@@ -87,4 +87,5 @@ #define SMALL_MUT_ARR_PTRS_FROZEN_DIRTY 61 #define SMALL_MUT_ARR_PTRS_FROZEN_CLEAN 62 #define COMPACT_NFDATA 63-#define N_CLOSURE_TYPES 64+#define CONTINUATION 64+#define N_CLOSURE_TYPES 65
compiler/CodeGen.Platform.h view
@@ -377,6 +377,74 @@ # define ft10 62 # define ft11 63 +#elif defined(MACHREGS_loongarch64)++# define zero 0+# define ra 1+# define tp 2+# define sp 3+# define a0 4+# define a1 5+# define a2 6+# define a3 7+# define a4 8+# define a5 9+# define a6 10+# define a7 11+# define t0 12+# define t1 13+# define t2 14+# define t3 15+# define t4 16+# define t5 17+# define t6 18+# define t7 19+# define t8 20+# define u0 21+# define fp 22+# define s0 23+# define s1 24+# define s2 25+# define s3 26+# define s4 27+# define s5 28+# define s6 29+# define s7 30+# define s8 31++# define fa0 32+# define fa1 33+# define fa2 34+# define fa3 35+# define fa4 36+# define fa5 37+# define fa6 38+# define fa7 39+# define ft0 40+# define ft1 41+# define ft2 42+# define ft3 43+# define ft4 44+# define ft5 45+# define ft6 46+# define ft7 47+# define ft8 48+# define ft9 49+# define ft10 50+# define ft11 51+# define ft12 52+# define ft13 53+# define ft14 54+# define ft15 55+# define fs0 56+# define fs1 57+# define fs2 58+# define fs3 59+# define fs4 60+# define fs5 61+# define fs6 62+# define fs7 63+ #endif callerSaves :: GlobalReg -> Bool@@ -664,7 +732,10 @@ #if defined(MACHREGS_i386) || defined(MACHREGS_x86_64) \ || defined(MACHREGS_powerpc) \ || defined(MACHREGS_arm) || defined(MACHREGS_aarch64) \- || defined(MACHREGS_s390x) || defined(MACHREGS_riscv64)+ || defined(MACHREGS_s390x) || defined(MACHREGS_riscv64) \+ || defined(MACHREGS_wasm32) \+ || defined(MACHREGS_loongarch64)+ # if defined(REG_Base) globalRegMaybe BaseReg = Just (RealRegSingle REG_Base) # endif
compiler/GHC.hs view
@@ -27,7 +27,8 @@ handleSourceError, -- * Flags and settings- DynFlags(..), GeneralFlag(..), Severity(..), Backend(..), gopt,+ DynFlags(..), GeneralFlag(..), Severity(..), Backend, gopt,+ ncgBackend, llvmBackend, viaCBackend, interpreterBackend, noBackend, GhcMode(..), GhcLink(..), parseDynamicFlags, parseTargetFiles, getSessionDynFlags,@@ -96,12 +97,12 @@ modInfoSafe, lookupGlobalName, findGlobalAnns,- mkPrintUnqualifiedForModule,+ mkNamePprCtxForModule, ModIface, ModIface_(..), SafeHaskellMode(..), -- * Printing- PrintUnqualified, alwaysQualify,+ NamePprCtx, alwaysQualify, -- * Interactive evaluation @@ -118,7 +119,7 @@ setGHCiMonad, getGHCiMonad, -- ** Inspecting the current context- getBindings, getInsts, getPrintUnqual,+ getBindings, getInsts, getNamePprCtx, findModule, lookupModule, findQualifiedModule, lookupQualifiedModule, renamePkgQualM, renameRawPkgQualM,@@ -175,7 +176,6 @@ -- ** Modules Module, mkModule, pprModule, moduleName, moduleUnit,- ModuleName, mkModuleName, moduleNameString, -- ** Names Name,@@ -197,7 +197,7 @@ TyCon, tyConTyVars, tyConDataCons, tyConArity, isClassTyCon, isTypeSynonymTyCon, isTypeFamilyTyCon, isNewTyCon,- isPrimTyCon, isFunTyCon,+ isPrimTyCon, isFamilyTyCon, isOpenFamilyTyCon, isOpenTypeFamilyTyCon, tyConClass_maybe, synTyConRhs_maybe, synTyConDefn_maybe, tyConKind,@@ -352,9 +352,6 @@ import GHC.Tc.Instance.Family import GHC.Utils.TmpFs-import GHC.SysTools-import GHC.SysTools.BaseDir- import GHC.Utils.Error import GHC.Utils.Monad import GHC.Utils.Misc@@ -370,8 +367,7 @@ import GHC.Core.TyCo.Ppr ( pprForAll ) import GHC.Core.Class import GHC.Core.DataCon-import GHC.Core.FVs ( orphNamesOfFamInst )-import GHC.Core.FamInstEnv ( FamInst, famInstEnvElts )+import GHC.Core.FamInstEnv ( FamInst, famInstEnvElts, orphNamesOfFamInst ) import GHC.Core.InstEnv import GHC.Core @@ -561,57 +557,16 @@ -- <http://hackage.haskell.org/package/ghc-paths>. initGhcMonad :: GhcMonad m => Maybe FilePath -> m ()-initGhcMonad mb_top_dir- = do { -- The call to c_keepCAFsForGHCi must not be optimized away. Even in non-debug builds.- -- So we can't use assertM here.- -- See Note [keepCAFsForGHCi] in keepCAFsForGHCi.c for details about why.- !keep_cafs <- liftIO $ c_keepCAFsForGHCi- ; massert keep_cafs- ; env <- liftIO $- do { top_dir <- findTopDir mb_top_dir- ; mySettings <- initSysTools top_dir- ; myLlvmConfig <- lazyInitLlvmConfig top_dir- ; dflags <- initDynFlags (defaultDynFlags mySettings myLlvmConfig)- ; hsc_env <- newHscEnv dflags- ; checkBrokenTablesNextToCode (hsc_logger hsc_env) dflags- ; setUnsafeGlobalDynFlags dflags- -- c.f. DynFlags.parseDynamicFlagsFull, which- -- creates DynFlags and sets the UnsafeGlobalDynFlags- ; return hsc_env }- ; setSession env }---- | The binutils linker on ARM emits unnecessary R_ARM_COPY relocations which--- breaks tables-next-to-code in dynamically linked modules. This--- check should be more selective but there is currently no released--- version where this bug is fixed.--- See https://sourceware.org/bugzilla/show_bug.cgi?id=16177 and--- https://gitlab.haskell.org/ghc/ghc/issues/4210#note_78333-checkBrokenTablesNextToCode :: MonadIO m => Logger -> DynFlags -> m ()-checkBrokenTablesNextToCode logger dflags- = do { broken <- checkBrokenTablesNextToCode' logger dflags- ; when broken- $ do { _ <- liftIO $ throwIO $ mkApiErr dflags invalidLdErr- ; liftIO $ fail "unsupported linker"- }- }- where- invalidLdErr = text "Tables-next-to-code not supported on ARM" <+>- text "when using binutils ld (please see:" <+>- text "https://sourceware.org/bugzilla/show_bug.cgi?id=16177)"--checkBrokenTablesNextToCode' :: MonadIO m => Logger -> DynFlags -> m Bool-checkBrokenTablesNextToCode' logger dflags- | not (isARM arch) = return False- | ways dflags `hasNotWay` WayDyn = return False- | not tablesNextToCode = return False- | otherwise = do- linkerInfo <- liftIO $ getLinkerInfo logger dflags- case linkerInfo of- GnuLD _ -> return True- _ -> return False- where platform = targetPlatform dflags- arch = platformArch platform- tablesNextToCode = platformTablesNextToCode platform+initGhcMonad mb_top_dir = setSession =<< liftIO ( do+ -- The call to c_keepCAFsForGHCi must not be optimized away. Even in non-debug builds.+ -- So we can't use assertM here.+ -- See Note [keepCAFsForGHCi] in keepCAFsForGHCi.c for details about why.+-- #if MIN_VERSION_GLASGOW_HASKELL(9,7,0,0)+ !keep_cafs <- c_keepCAFsForGHCi+ massert keep_cafs+-- #endif+ initHscEnv mb_top_dir+ ) -- %************************************************************************ -- %* *@@ -640,7 +595,7 @@ -- 'setSessionDynFlags' sets both @DynFlags@, and 'getSessionDynFlags' -- retrieves the program @DynFlags@ (for backwards compatibility). --- This is a compatability function which sets dynflags for the top session+-- This is a compatibility function which sets dynflags for the top session -- as well as the unit. setSessionDynFlags :: (HasCallStack, GhcMonad m) => DynFlags -> m () setSessionDynFlags dflags0 = do@@ -651,7 +606,7 @@ case S.toList all_uids of [uid] -> do setUnitDynFlagsNoCheck uid dflags- modifySession (hscSetActiveUnitId (homeUnitId_ dflags))+ modifySession (hscUpdateLoggerFlags . hscSetActiveUnitId (homeUnitId_ dflags)) dflags' <- getDynFlags setTopSessionDynFlags dflags' [] -> panic "nohue"@@ -983,7 +938,8 @@ -- See Note [DynFlags consistency] let (dflags', warnings) = makeDynFlagsConsistent dflags let diag_opts = initDiagOpts dflags- liftIO $ handleFlagWarnings logger diag_opts (map (Warn WarningWithoutFlag) warnings)+ print_config = initPrintConfig dflags+ liftIO $ handleFlagWarnings logger print_config diag_opts (map (Warn WarningWithoutFlag) warnings) return dflags' checkNewInteractiveDynFlags :: MonadIO m => Logger -> DynFlags -> m DynFlags@@ -993,7 +949,8 @@ if xopt LangExt.StaticPointers dflags0 then do let diag_opts = initDiagOpts dflags0- liftIO $ printOrThrowDiagnostics logger diag_opts $ singleMessage+ print_config = initPrintConfig dflags0+ liftIO $ printOrThrowDiagnostics logger print_config diag_opts $ singleMessage $ fmap GhcDriverMessage $ mkPlainMsgEnvelope diag_opts interactiveSrcSpan DriverStaticPointersNotSupported return $ xopt_unset dflags0 LangExt.StaticPointers@@ -1174,7 +1131,7 @@ instance DesugaredMod DesugaredModule where coreModule m = dm_core_module m -type ParsedSource = Located HsModule+type ParsedSource = Located (HsModule GhcPs) type RenamedSource = (HsGroup GhcRn, [LImportDecl GhcRn], Maybe [(LIE GhcRn, Avails)], Maybe (LHsDoc GhcRn)) type TypecheckedSource = LHsBinds GhcTc@@ -1398,9 +1355,9 @@ let (inst_env, fam_env) = ic_instances (hsc_IC hsc_env) in return (instEnvElts inst_env, fam_env) -getPrintUnqual :: GhcMonad m => m PrintUnqualified-getPrintUnqual = withSession $ \hsc_env -> do- return $ icPrintUnqual (hsc_unit_env hsc_env) (hsc_IC hsc_env)+getNamePprCtx :: GhcMonad m => m NamePprCtx+getNamePprCtx = withSession $ \hsc_env -> do+ return $ icNamePprCtx (hsc_unit_env hsc_env) (hsc_IC hsc_env) -- | Container for information about a 'Module'. data ModuleInfo = ModuleInfo {@@ -1494,12 +1451,14 @@ modInfoIsExportedName :: ModuleInfo -> Name -> Bool modInfoIsExportedName minf name = elemNameSet name (availsToNameSet (minf_exports minf)) -mkPrintUnqualifiedForModule :: GhcMonad m =>- ModuleInfo- -> m (Maybe PrintUnqualified) -- XXX: returns a Maybe X-mkPrintUnqualifiedForModule minf = withSession $ \hsc_env -> do- let mk_print_unqual = mkPrintUnqualified (hsc_unit_env hsc_env)- return (fmap mk_print_unqual (minf_rdr_env minf))+mkNamePprCtxForModule ::+ GhcMonad m =>+ ModuleInfo ->+ m (Maybe NamePprCtx) -- XXX: returns a Maybe X+mkNamePprCtxForModule minf = withSession $ \hsc_env -> do+ let mk_name_ppr_ctx = mkNamePprCtx ptc (hsc_unit_env hsc_env)+ ptc = initPromotionTickContext (hsc_dflags hsc_env)+ return (fmap mk_name_ppr_ctx (minf_rdr_env minf)) modInfoLookupName :: GhcMonad m => ModuleInfo -> Name@@ -1835,7 +1794,7 @@ parser :: String -- ^ Haskell module source text (full Unicode is supported) -> DynFlags -- ^ the flags -> FilePath -- ^ the filename (for source locations)- -> (WarningMessages, Either ErrorMessages (Located HsModule))+ -> (WarningMessages, Either ErrorMessages (Located (HsModule GhcPs))) parser str dflags filename = let@@ -2000,5 +1959,7 @@ mkApiErr :: DynFlags -> SDoc -> GhcApiError mkApiErr dflags msg = GhcApiError (showSDoc dflags msg) +-- foreign import ccall unsafe "keepCAFsForGHCi" c_keepCAFsForGHCi :: IO Bool+
compiler/GHC/Builtin/Names/TH.hs view
@@ -10,7 +10,6 @@ import GHC.Builtin.Names( mk_known_key_name ) import GHC.Unit.Types-import GHC.Unit.Module.Name import GHC.Types.Name( Name ) import GHC.Types.Name.Occurrence( tcName, clsName, dataName, varName ) import GHC.Types.Name.Reader( RdrName, nameRdrName )@@ -18,6 +17,8 @@ import GHC.Builtin.Uniques import GHC.Data.FastString +import Language.Haskell.Syntax.Module.Name+ -- To add a name, do three things -- -- 1) Allocate a key@@ -31,7 +32,7 @@ templateHaskellNames = [ returnQName, bindQName, sequenceQName, newNameName, liftName, liftTypedName, mkNameName, mkNameG_vName, mkNameG_dName, mkNameG_tcName, mkNameLName,- mkNameSName,+ mkNameSName, mkNameQName, mkModNameName, liftStringName, unTypeName, unTypeCodeName,@@ -68,7 +69,7 @@ -- Stmt bindSName, letSName, noBindSName, parSName, recSName, -- Dec- funDName, valDName, dataDName, newtypeDName, tySynDName,+ funDName, valDName, dataDName, newtypeDName, typeDataDName, tySynDName, classDName, instanceWithOverlapDName, standaloneDerivWithStrategyDName, sigDName, kiSigDName, forImpDName, pragInlDName, pragOpaqueDName, pragSpecDName, pragSpecInlDName, pragSpecInstDName,@@ -215,7 +216,7 @@ returnQName, bindQName, sequenceQName, newNameName, liftName, mkNameName, mkNameG_vName, mkNameG_dName, mkNameG_tcName, mkNameLName, mkNameSName, liftStringName, unTypeName, unTypeCodeName,- unsafeCodeCoerceName, liftTypedName, mkModNameName :: Name+ unsafeCodeCoerceName, liftTypedName, mkModNameName, mkNameQName :: Name returnQName = thFun (fsLit "returnQ") returnQIdKey bindQName = thFun (fsLit "bindQ") bindQIdKey sequenceQName = thFun (fsLit "sequenceQ") sequenceQIdKey@@ -227,6 +228,7 @@ mkNameG_dName = thFun (fsLit "mkNameG_d") mkNameG_dIdKey mkNameG_tcName = thFun (fsLit "mkNameG_tc") mkNameG_tcIdKey mkNameLName = thFun (fsLit "mkNameL") mkNameLIdKey+mkNameQName = thFun (fsLit "mkNameQ") mkNameQIdKey mkNameSName = thFun (fsLit "mkNameS") mkNameSIdKey mkModNameName = thFun (fsLit "mkModName") mkModNameIdKey unTypeName = thFun (fsLit "unType") unTypeIdKey@@ -353,7 +355,7 @@ recSName = libFun (fsLit "recS") recSIdKey -- data Dec = ...-funDName, valDName, dataDName, newtypeDName, tySynDName, classDName,+funDName, valDName, dataDName, newtypeDName, typeDataDName, tySynDName, classDName, instanceWithOverlapDName, sigDName, kiSigDName, forImpDName, pragInlDName, pragSpecDName, pragSpecInlDName, pragSpecInstDName, pragRuleDName, pragAnnDName, standaloneDerivWithStrategyDName, defaultSigDName, defaultDName,@@ -365,6 +367,7 @@ valDName = libFun (fsLit "valD") valDIdKey dataDName = libFun (fsLit "dataD") dataDIdKey newtypeDName = libFun (fsLit "newtypeD") newtypeDIdKey+typeDataDName = libFun (fsLit "typeDataD") typeDataDIdKey tySynDName = libFun (fsLit "tySynD") tySynDIdKey classDName = libFun (fsLit "classD") classDIdKey instanceWithOverlapDName = libFun (fsLit "instanceWithOverlapD") instanceWithOverlapDIdKey@@ -740,7 +743,7 @@ returnQIdKey, bindQIdKey, sequenceQIdKey, liftIdKey, newNameIdKey, mkNameIdKey, mkNameG_vIdKey, mkNameG_dIdKey, mkNameG_tcIdKey, mkNameLIdKey, mkNameSIdKey, unTypeIdKey, unTypeCodeIdKey,- unsafeCodeCoerceIdKey, liftTypedIdKey, mkModNameIdKey :: Unique+ unsafeCodeCoerceIdKey, liftTypedIdKey, mkModNameIdKey, mkNameQIdKey :: Unique returnQIdKey = mkPreludeMiscIdUnique 200 bindQIdKey = mkPreludeMiscIdUnique 201 sequenceQIdKey = mkPreludeMiscIdUnique 202@@ -757,6 +760,7 @@ liftTypedIdKey = mkPreludeMiscIdUnique 214 mkModNameIdKey = mkPreludeMiscIdUnique 215 unsafeCodeCoerceIdKey = mkPreludeMiscIdUnique 216+mkNameQIdKey = mkPreludeMiscIdUnique 217 -- data Lit = ...@@ -887,7 +891,7 @@ newtypeInstDIdKey, tySynInstDIdKey, standaloneDerivWithStrategyDIdKey, infixLDIdKey, infixRDIdKey, infixNDIdKey, roleAnnotDIdKey, patSynDIdKey, patSynSigDIdKey, pragCompleteDIdKey, implicitParamBindDIdKey,- kiSigDIdKey, defaultDIdKey, pragOpaqueDIdKey :: Unique+ kiSigDIdKey, defaultDIdKey, pragOpaqueDIdKey, typeDataDIdKey :: Unique funDIdKey = mkPreludeMiscIdUnique 320 valDIdKey = mkPreludeMiscIdUnique 321 dataDIdKey = mkPreludeMiscIdUnique 322@@ -922,7 +926,8 @@ implicitParamBindDIdKey = mkPreludeMiscIdUnique 351 kiSigDIdKey = mkPreludeMiscIdUnique 352 defaultDIdKey = mkPreludeMiscIdUnique 353-pragOpaqueDIdKey = mkPreludeMiscIdUnique 354+pragOpaqueDIdKey = mkPreludeMiscIdUnique 354+typeDataDIdKey = mkPreludeMiscIdUnique 355 -- type Cxt = ... cxtIdKey :: Unique
compiler/GHC/Builtin/Types/Literals.hs view
@@ -29,12 +29,12 @@ import GHC.Core.Type import GHC.Data.Pair-import GHC.Tc.Utils.TcType ( TcType, tcEqType ) import GHC.Core.TyCon ( TyCon, FamTyConFlav(..), mkFamilyTyCon , Injectivity(..) ) import GHC.Core.Coercion ( Role(..) ) import GHC.Tc.Types.Constraint ( Xi ) import GHC.Core.Coercion.Axiom ( CoAxiomRule(..), BuiltInSynFamily(..), TypeEqn )+import GHC.Core.TyCo.Compare ( tcEqType ) import GHC.Types.Name ( Name, BuiltInSyntax(..) ) import GHC.Types.Unique.FM import GHC.Builtin.Types@@ -630,7 +630,7 @@ | tc1 == promotedGTDataCon -> return GT | otherwise -> Nothing -known :: (Integer -> Bool) -> TcType -> Bool+known :: (Integer -> Bool) -> Type -> Bool known p x = case isNumLitTy x of Just a -> p a Nothing -> False
compiler/GHC/ByteCode/Asm.hs view
@@ -315,7 +315,7 @@ words = concatMap expand ops expand (SmallOp w) = [w] expand (LabelOp w) = expand (Op (e w))- expand (Op w) = if largeOps then largeArg platform w else [fromIntegral w]+ expand (Op w) = if largeOps then largeArg platform (fromIntegral w) else [fromIntegral w] -- expand (LargeOp w) = largeArg platform w state $ \(st_i0,st_l0,st_p0) -> let st_i1 = addListToSS st_i0 (opcode : words)@@ -358,7 +358,7 @@ largeArgInstr :: Word16 -> Word16 largeArgInstr bci = bci_FLAG_LARGE_ARGS .|. bci -largeArg :: Platform -> Word -> [Word16]+largeArg :: Platform -> Word64 -> [Word16] largeArg platform w = case platformWordSize platform of PW8 -> [fromIntegral (w `shiftR` 48), fromIntegral (w `shiftR` 32),@@ -395,7 +395,10 @@ PUSH_BCO proto -> do let ul_bco = assembleBCO platform proto p <- ioptr (liftM BCOPtrBCO ul_bco) emit bci_PUSH_G [Op p]- PUSH_ALTS proto pk+ PUSH_ALTS proto -> do let ul_bco = assembleBCO platform proto+ p <- ioptr (liftM BCOPtrBCO ul_bco)+ emit bci_PUSH_ALTS [Op p]+ PUSH_ALTS_UNLIFTED proto pk -> do let ul_bco = assembleBCO platform proto p <- ioptr (liftM BCOPtrBCO ul_bco) emit (push_alts pk) [Op p]@@ -501,7 +504,8 @@ SWIZZLE stkoff n -> emit bci_SWIZZLE [SmallOp stkoff, SmallOp n] JMP l -> emit bci_JMP [LabelOp l] ENTER -> emit bci_ENTER []- RETURN rep -> emit (return_non_tuple rep) []+ RETURN -> emit bci_RETURN []+ RETURN_UNLIFTED rep -> emit (return_unlifted rep) [] RETURN_TUPLE -> emit bci_RETURN_T [] CCALL off m_addr i -> do np <- addr m_addr emit bci_CCALL [SmallOp off, Op np, SmallOp i]@@ -570,16 +574,16 @@ push_alts V32 = error "push_alts: vector" push_alts V64 = error "push_alts: vector" -return_non_tuple :: ArgRep -> Word16-return_non_tuple V = bci_RETURN_V-return_non_tuple P = bci_RETURN_P-return_non_tuple N = bci_RETURN_N-return_non_tuple L = bci_RETURN_L-return_non_tuple F = bci_RETURN_F-return_non_tuple D = bci_RETURN_D-return_non_tuple V16 = error "return_non_tuple: vector"-return_non_tuple V32 = error "return_non_tuple: vector"-return_non_tuple V64 = error "return_non_tuple: vector"+return_unlifted :: ArgRep -> Word16+return_unlifted V = bci_RETURN_V+return_unlifted P = bci_RETURN_P+return_unlifted N = bci_RETURN_N+return_unlifted L = bci_RETURN_L+return_unlifted F = bci_RETURN_F+return_unlifted D = bci_RETURN_D+return_unlifted V16 = error "return_unlifted: vector"+return_unlifted V32 = error "return_unlifted: vector"+return_unlifted V64 = error "return_unlifted: vector" {- we can only handle up to a fixed number of words on the stack,
compiler/GHC/ByteCode/Instr.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE LambdaCase #-} {-# OPTIONS_GHC -funbox-strict-fields #-} -- -- (c) The University of Glasgow 2002-2006@@ -71,12 +70,12 @@ | PUSH16 !Word16 | PUSH32 !Word16 - -- Push the specifiec local as a 8, 16, 32 bit value onto the stack, but the+ -- Push the specified local as a 8, 16, 32 bit value onto the stack, but the -- value will take the whole word on the stack (i.e., the stack will grow by -- a word) -- This is useful when extracting a packed constructor field for further use. -- Currently we expect all values on the stack to take full words, except for- -- the ones used for PACK (i.e., actually constracting new data types, in+ -- the ones used for PACK (i.e., actually constructing new data types, in -- which case we use PUSH{8,16,32}) | PUSH8_W !Word16 | PUSH16_W !Word16@@ -88,7 +87,8 @@ | PUSH_BCO (ProtoBCO Name) -- Push an alt continuation- | PUSH_ALTS (ProtoBCO Name) ArgRep+ | PUSH_ALTS (ProtoBCO Name)+ | PUSH_ALTS_UNLIFTED (ProtoBCO Name) ArgRep | PUSH_ALTS_TUPLE (ProtoBCO Name) -- continuation !NativeCallInfo (ProtoBCO Name) -- tuple return BCO@@ -196,10 +196,9 @@ -- To Infinity And Beyond | ENTER- | RETURN ArgRep -- return a non-tuple value, here's its rep; see- -- Note [Return convention for non-tuple values] in GHC.StgToByteCode- | RETURN_TUPLE -- return an unboxed tuple (info already on stack); see- -- Note [unboxed tuple bytecodes and tuple_BCO] in GHC.StgToByteCode+ | RETURN -- return a lifted value+ | RETURN_UNLIFTED ArgRep -- return an unlifted value, here's its rep+ | RETURN_TUPLE -- return an unboxed tuple (info already on stack) -- Breakpoints | BRK_FUN Word16 Unique (RemotePtr CostCentre)@@ -274,7 +273,8 @@ <> ppr op ppr (PUSH_BCO bco) = hang (text "PUSH_BCO") 2 (ppr bco) - ppr (PUSH_ALTS bco pk) = hang (text "PUSH_ALTS" <+> ppr pk) 2 (ppr bco)+ ppr (PUSH_ALTS bco) = hang (text "PUSH_ALTS") 2 (ppr bco)+ ppr (PUSH_ALTS_UNLIFTED bco pk) = hang (text "PUSH_ALTS_UNLIFTED" <+> ppr pk) 2 (ppr bco) ppr (PUSH_ALTS_TUPLE bco call_info tuple_bco) = hang (text "PUSH_ALTS_TUPLE" <+> ppr call_info) 2@@ -340,9 +340,9 @@ ppr (TESTEQ_P i lab) = text "TESTEQ_P" <+> ppr i <+> text "__" <> ppr lab ppr CASEFAIL = text "CASEFAIL" ppr (JMP lab) = text "JMP" <+> ppr lab- ppr (CCALL off marshall_addr flags) = text "CCALL " <+> ppr off- <+> text "marshall code at"- <+> text (show marshall_addr)+ ppr (CCALL off marshal_addr flags) = text "CCALL " <+> ppr off+ <+> text "marshal code at"+ <+> text (show marshal_addr) <+> (case flags of 0x1 -> text "(interruptible)" 0x2 -> text "(unsafe)"@@ -351,12 +351,10 @@ ppr (SWIZZLE stkoff n) = text "SWIZZLE " <+> text "stkoff" <+> ppr stkoff <+> text "by" <+> ppr n ppr ENTER = text "ENTER"- ppr (RETURN pk) = text "RETURN " <+> ppr pk+ ppr RETURN = text "RETURN"+ ppr (RETURN_UNLIFTED pk) = text "RETURN_UNLIFTED " <+> ppr pk ppr (RETURN_TUPLE) = text "RETURN_TUPLE"- ppr (BRK_FUN index uniq _cc) = text "BRK_FUN" <+> ppr index <+> mb_uniq <+> text "<cc>"- where mb_uniq = sdocOption sdocSuppressUniques $ \case- True -> text "<uniq>"- False -> ppr uniq+ ppr (BRK_FUN index uniq _cc) = text "BRK_FUN" <+> ppr index <+> ppr uniq <+> text "<cc>" @@ -387,8 +385,10 @@ bciStackUse PUSH_G{} = 1 bciStackUse PUSH_PRIMOP{} = 1 bciStackUse PUSH_BCO{} = 1-bciStackUse (PUSH_ALTS bco _) = 2 {- profiling only, restore CCCS -} ++bciStackUse (PUSH_ALTS bco) = 2 {- profiling only, restore CCCS -} + 3 + protoBCOStackUse bco+bciStackUse (PUSH_ALTS_UNLIFTED bco _) = 2 {- profiling only, restore CCCS -} ++ 4 + protoBCOStackUse bco bciStackUse (PUSH_ALTS_TUPLE bco info _) = -- (tuple_bco, call_info word, cont_bco, stg_ctoi_t) -- tuple@@ -448,7 +448,8 @@ bciStackUse CASEFAIL{} = 0 bciStackUse JMP{} = 0 bciStackUse ENTER{} = 0-bciStackUse RETURN{} = 1 -- pushes stg_ret_X for some X+bciStackUse RETURN{} = 0+bciStackUse RETURN_UNLIFTED{} = 1 -- pushes stg_ret_X for some X bciStackUse RETURN_TUPLE{} = 1 -- pushes stg_ret_t header bciStackUse CCALL{} = 0 bciStackUse PRIMCALL{} = 1 -- pushes stg_primcall
compiler/GHC/ByteCode/Linker.hs view
@@ -28,7 +28,6 @@ import GHC.Builtin.Names import GHC.Unit.Types-import GHC.Unit.Module.Name import GHC.Data.FastString import GHC.Data.SizedSeq@@ -41,6 +40,8 @@ import GHC.Types.Name import GHC.Types.Name.Env++import Language.Haskell.Syntax.Module.Name -- Standard libraries import Data.Array.Unboxed
compiler/GHC/Cmm/CallConv.hs view
@@ -12,7 +12,6 @@ import GHC.Cmm.Expr import GHC.Runtime.Heap.Layout import GHC.Cmm (Convention(..))-import GHC.Cmm.Ppr () -- For Outputable instances import GHC.Platform import GHC.Platform.Profile
compiler/GHC/Cmm/CommonBlockElim.hs view
@@ -18,16 +18,17 @@ import GHC.Cmm.Dataflow.Graph import GHC.Cmm.Dataflow.Label import GHC.Cmm.Dataflow.Collections+import Data.Functor.Classes (liftEq) import Data.Maybe (mapMaybe) import qualified Data.List as List import Data.Word import qualified Data.Map as M-import GHC.Utils.Outputable-import GHC.Utils.Panic import qualified GHC.Data.TrieMap as TM import GHC.Types.Unique.FM import GHC.Types.Unique import Control.Arrow (first, second)+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NE -- ----------------------------------------------------------------------------- -- Eliminate common blocks@@ -81,7 +82,7 @@ | mapNull new_substs = subst | otherwise = iterate subst' updated_blocks where- grouped_blocks :: [[(Key, [DistinctBlocks])]]+ grouped_blocks :: [[(Key, NonEmpty DistinctBlocks)]] grouped_blocks = map groupByLabel blocks merged_blocks :: [[(Key, DistinctBlocks)]]@@ -106,9 +107,8 @@ -- This block is not a duplicate, keep it. Nothing -> second (b:) $ go bs -mergeBlockList :: Subst -> [DistinctBlocks] -> (Subst, DistinctBlocks)-mergeBlockList _ [] = pprPanic "mergeBlockList" empty-mergeBlockList subst (b:bs) = go mapEmpty b bs+mergeBlockList :: Subst -> NonEmpty DistinctBlocks -> (Subst, DistinctBlocks)+mergeBlockList subst (b:|bs) = go mapEmpty b bs where go !new_subst1 b [] = (new_subst1, b) go !new_subst1 b1 (b2:bs) = go new_subst b bs@@ -214,7 +214,7 @@ = eqExprWith eqBid l1 l2 && eqExprWith eqBid r1 r2 eqMiddleWith eqBid (CmmUnsafeForeignCall t1 r1 a1) (CmmUnsafeForeignCall t2 r2 a2)- = t1 == t2 && r1 == r2 && eqListWith (eqExprWith eqBid) a1 a2+ = t1 == t2 && r1 == r2 && liftEq (eqExprWith eqBid) a1 a2 eqMiddleWith _ _ _ = False eqExprWith :: (BlockId -> BlockId -> Bool)@@ -225,12 +225,10 @@ CmmLoad e1 t1 a1 `eq` CmmLoad e2 t2 a2 = t1 `cmmEqType` t2 && e1 `eq` e2 && a1==a2 CmmReg r1 `eq` CmmReg r2 = r1==r2 CmmRegOff r1 i1 `eq` CmmRegOff r2 i2 = r1==r2 && i1==i2- CmmMachOp op1 es1 `eq` CmmMachOp op2 es2 = op1==op2 && es1 `eqs` es2+ CmmMachOp op1 es1 `eq` CmmMachOp op2 es2 = op1==op2 && liftEq eq es1 es2 CmmStackSlot a1 i1 `eq` CmmStackSlot a2 i2 = eqArea a1 a2 && i1==i2 _e1 `eq` _e2 = False - xs `eqs` ys = eqListWith eq xs ys- eqLit (CmmBlock id1) (CmmBlock id2) = eqBid id1 id2 eqLit l1 l2 = l1 == l2 @@ -252,7 +250,7 @@ (_,m',l') = blockSplit block' nodes' = filter (not . dont_care) (blockToList m') - equal = eqListWith (eqMiddleWith eqBid) nodes nodes' &&+ equal = liftEq (eqMiddleWith eqBid) nodes nodes' && eqLastWith eqBid l l' @@ -261,21 +259,11 @@ eqLastWith eqBid (CmmCondBranch c1 t1 f1 l1) (CmmCondBranch c2 t2 f2 l2) = c1 == c2 && l1 == l2 && eqBid t1 t2 && eqBid f1 f2 eqLastWith eqBid (CmmCall t1 c1 g1 a1 r1 u1) (CmmCall t2 c2 g2 a2 r2 u2) =- t1 == t2 && eqMaybeWith eqBid c1 c2 && a1 == a2 && r1 == r2 && u1 == u2 && g1 == g2+ t1 == t2 && liftEq eqBid c1 c2 && a1 == a2 && r1 == r2 && u1 == u2 && g1 == g2 eqLastWith eqBid (CmmSwitch e1 ids1) (CmmSwitch e2 ids2) = e1 == e2 && eqSwitchTargetWith eqBid ids1 ids2 eqLastWith _ _ _ = False -eqMaybeWith :: (a -> b -> Bool) -> Maybe a -> Maybe b -> Bool-eqMaybeWith eltEq (Just e) (Just e') = eltEq e e'-eqMaybeWith _ Nothing Nothing = True-eqMaybeWith _ _ _ = False--eqListWith :: (a -> b -> Bool) -> [a] -> [b] -> Bool-eqListWith f (a : as) (b : bs) = f a b && eqListWith f as bs-eqListWith _ [] [] = True-eqListWith _ _ _ = False- -- | Given a block map, ensure that all "target" blocks are covered by -- the same ticks as the respective "source" blocks. This not only -- means copying ticks, but also adjusting tick scopes where@@ -301,15 +289,15 @@ -- Group by [Label] -- See Note [Compressed TrieMap] in GHC.Core.Map.Expr about the usage of GenMap.-groupByLabel :: [(Key, DistinctBlocks)] -> [(Key, [DistinctBlocks])]+groupByLabel :: [(Key, DistinctBlocks)] -> [(Key, NonEmpty DistinctBlocks)] groupByLabel =- go (TM.emptyTM :: TM.ListMap (TM.GenMap LabelMap) (Key, [DistinctBlocks]))+ go (TM.emptyTM :: TM.ListMap (TM.GenMap LabelMap) (Key, NonEmpty DistinctBlocks)) where go !m [] = TM.foldTM (:) m [] go !m ((k,v) : entries) = go (TM.alterTM k adjust m) entries where --k' = map (getKey . getUnique) k- adjust Nothing = Just (k,[v])- adjust (Just (_,vs)) = Just (k,v:vs)+ adjust Nothing = Just (k, pure v)+ adjust (Just (_,vs)) = Just (k, v NE.<| vs) groupByInt :: (a -> Int) -> [a] -> [[a]] groupByInt f xs = nonDetEltsUFM $ List.foldl' go emptyUFM xs
compiler/GHC/Cmm/Config.hs view
@@ -19,6 +19,7 @@ , cmmDoLinting :: !Bool -- ^ Do Cmm Linting Optimization or not , cmmOptElimCommonBlks :: !Bool -- ^ Eliminate common blocks or not , cmmOptSink :: !Bool -- ^ Perform sink after stack layout or not+ , cmmOptThreadSanitizer :: !Bool -- ^ Instrument memory accesses for ThreadSanitizer , cmmGenStackUnwindInstr :: !Bool -- ^ Generate stack unwinding instructions (for debugging) , cmmExternalDynamicRefs :: !Bool -- ^ Generate code to link against dynamic libraries , cmmDoCmmSwitchPlans :: !Bool -- ^ Should the Cmm pass replace Stg switch statements
compiler/GHC/Cmm/ContFlowOpt.hs view
@@ -1,7 +1,5 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE BangPatterns #-}-{-# OPTIONS_GHC -fno-warn-incomplete-patterns #-}-{-# OPTIONS_GHC -Wno-incomplete-record-updates #-} module GHC.Cmm.ContFlowOpt ( cmmCfgOpts , cmmCfgOptsProc@@ -21,8 +19,10 @@ import GHC.Cmm.Utils import GHC.Cmm.Switch (mapSwitchTargets, switchTargetsToList) import GHC.Data.Maybe-import GHC.Utils.Panic+import GHC.Platform import GHC.Utils.Misc+import GHC.Utils.Outputable+import GHC.Utils.Panic import Control.Monad @@ -334,7 +334,7 @@ In this case we could assume that we will end up with a jump for BOTH branches. In this case it might be best to put the likely path in the true branch especially if there are large numbers of predecessors as this saves- us the jump thats not taken. However I haven't tested this and as of early+ us the jump that's not taken. However I haven't tested this and as of early 2018 we almost never generate cmm where this would apply. -} @@ -422,9 +422,9 @@ add_preds block env = foldr add env (successors block) where add lbl env = mapInsertWith (+) lbl 1 env --- Removing unreachable blocks-removeUnreachableBlocksProc :: CmmDecl -> CmmDecl-removeUnreachableBlocksProc proc@(CmmProc info lbl live g)+-- Remove unreachable blocks from procs+removeUnreachableBlocksProc :: Platform -> CmmDecl -> CmmDecl+removeUnreachableBlocksProc _ proc@(CmmProc info lbl live g) | used_blocks `lengthLessThan` mapSize (toBlockMap g) = CmmProc info' lbl live g' | otherwise@@ -446,3 +446,5 @@ used_lbls :: LabelSet used_lbls = setFromList $ map entryLabel used_blocks+removeUnreachableBlocksProc platform data'@(CmmData _ _) =+ pprPanic "removeUnreachableBlocksProc: passed data declaration instead of procedure" (pdoc platform data')
compiler/GHC/Cmm/Dataflow.hs view
@@ -87,7 +87,7 @@ Block n C C -> FactBase f -> FactBase f --- | Function for rewrtiting and analysis combined. To be used with+-- | Function for rewriting and analysis combined. To be used with -- @rewriteCmm@. -- -- Currently set to work with @UniqSM@ monad, but we could probably abstract
compiler/GHC/Cmm/DebugBlock.hs view
@@ -29,7 +29,8 @@ -- * Unwinding information UnwindTable, UnwindPoint(..),- UnwindExpr(..), toUnwindExpr+ UnwindExpr(..), toUnwindExpr,+ pprUnwindTable ) where import GHC.Prelude@@ -38,12 +39,12 @@ import GHC.Cmm.BlockId import GHC.Cmm.CLabel import GHC.Cmm+import GHC.Cmm.Reg ( pprGlobalReg ) import GHC.Cmm.Utils import GHC.Data.FastString ( nilFS, mkFastString ) import GHC.Unit.Module import GHC.Utils.Outputable import GHC.Utils.Panic-import GHC.Cmm.Ppr.Expr ( pprExpr ) import GHC.Types.SrcLoc import GHC.Types.Tickish import GHC.Utils.Misc ( seqList )@@ -78,7 +79,7 @@ , dblBlocks :: ![DebugBlock] -- ^ Nested blocks } -instance OutputableP env CLabel => OutputableP env DebugBlock where+instance OutputableP Platform DebugBlock where pdoc env blk = (if | dblProcedure blk == dblLabel blk -> text "proc"@@ -86,7 +87,7 @@ -> text "pp-blk" | otherwise -> text "blk") <+>- ppr (dblLabel blk) <+> parens (pdoc env (dblCLabel blk)) <+>+ ppr (dblLabel blk) <+> parens (pprAsmLabel env (dblCLabel blk)) <+> (maybe empty ppr (dblSourceTick blk)) <+> (maybe (text "removed") ((text "pos " <>) . ppr) (dblPosition blk)) <+>@@ -122,7 +123,7 @@ | otherwise = panic "findP impossible" #endif - scopeMap = foldr (uncurry insertMulti) Map.empty childScopes+ scopeMap = foldl' (\acc (key, scope) -> insertMulti key scope acc) Map.empty childScopes -- This allows us to recover ticks that we lost by flattening -- the graph. Basically, if the parent is A but the child is@@ -496,9 +497,9 @@ -- | A label associated with an 'UnwindTable' data UnwindPoint = UnwindPoint !CLabel !UnwindTable -instance OutputableP env CLabel => OutputableP env UnwindPoint where+instance OutputableP Platform UnwindPoint where pdoc env (UnwindPoint lbl uws) =- braces $ pdoc env lbl <> colon+ braces $ pprAsmLabel env lbl <> colon <+> hsep (punctuate comma $ map pprUw $ Map.toList uws) where pprUw (g, expr) = ppr g <> char '=' <> pdoc env expr@@ -520,16 +521,24 @@ | UwTimes UnwindExpr UnwindExpr deriving (Eq) -instance OutputableP env CLabel => OutputableP env UnwindExpr where+instance OutputableP Platform UnwindExpr where pdoc = pprUnwindExpr 0 -pprUnwindExpr :: OutputableP env CLabel => Rational -> env -> UnwindExpr -> SDoc+pprUnwindTable :: IsLine doc => Platform -> UnwindTable -> doc+pprUnwindTable platform u = brackets (fsep (punctuate comma (map print_entry (Map.toList u))))+ where print_entry (reg, Nothing) =+ parens (sep [pprGlobalReg reg, text "Nothing"])+ print_entry (reg, Just x) =+ parens (sep [pprGlobalReg reg, text "Just" <+> pprUnwindExpr 0 platform x])+ -- Follow instance Outputable (Map.Map GlobalReg (Maybe UnwindExpr))++pprUnwindExpr :: IsLine doc => Rational -> Platform -> UnwindExpr -> doc pprUnwindExpr p env = \case- UwConst i -> ppr i- UwReg g 0 -> ppr g+ UwConst i -> int i+ UwReg g 0 -> pprGlobalReg g UwReg g x -> pprUnwindExpr p env (UwPlus (UwReg g 0) (UwConst x)) UwDeref e -> char '*' <> pprUnwindExpr 3 env e- UwLabel l -> pdoc env l+ UwLabel l -> pprAsmLabel env l UwPlus e0 e1 | p <= 0 -> pprUnwindExpr 0 env e0 <> char '+' <> pprUnwindExpr 0 env e1 UwMinus e0 e1@@ -537,6 +546,8 @@ UwTimes e0 e1 | p <= 1 -> pprUnwindExpr 2 env e0 <> char '*' <> pprUnwindExpr 2 env e1 other -> parens (pprUnwindExpr 0 env other)+{-# SPECIALIZE pprUnwindExpr :: Rational -> Platform -> UnwindExpr -> SDoc #-}+{-# SPECIALIZE pprUnwindExpr :: Rational -> Platform -> UnwindExpr -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Conversion of Cmm expressions to unwind expressions. We check for -- unsupported operator usages and simplify the expression as far as@@ -559,6 +570,6 @@ (MO_Sub{}, u1, u2 ) -> UwMinus u1 u2 (MO_Mul{}, u1, u2 ) -> UwTimes u1 u2 _otherwise -> pprPanic "Unsupported operator in unwind expression!"- (pprExpr platform e)+ (pdoc platform e) toUnwindExpr platform e = pprPanic "Unsupported unwind expression!" (pdoc platform e)
+ compiler/GHC/Cmm/Dominators.hs view
@@ -0,0 +1,213 @@+{-# LANGUAGE GADTs #-}++module GHC.Cmm.Dominators+ (+ -- * Dominator analysis and representation of results+ DominatorSet(..)+ , GraphWithDominators(..)+ , RPNum+ , graphWithDominators++ -- * Utility functions on graphs or graphs-with-dominators+ , graphMap+ , gwdRPNumber+ , gwdDominatorsOf+ , gwdDominatorTree++ -- * Utility functions on dominator sets+ , dominatorsMember+ , intersectDominators+ )+where++import GHC.Prelude++import Data.Array.IArray+import Data.Foldable()+import qualified Data.Tree as Tree++import qualified Data.IntMap.Strict as IM+import qualified Data.IntSet as IS++import qualified GHC.CmmToAsm.CFG.Dominators as LT++import GHC.Cmm.Dataflow+import GHC.Cmm.Dataflow.Block+import GHC.Cmm.Dataflow.Collections+import GHC.Cmm.Dataflow.Graph+import GHC.Cmm.Dataflow.Label+import GHC.Cmm++import GHC.Utils.Outputable( Outputable(..), text, int, hcat, (<+>))+import GHC.Utils.Misc+import GHC.Utils.Panic+++-- | =Dominator sets+--+-- Node X dominates node Y if and only if every path from the entry to+-- Y includes X. Node Y technically dominates itself, but it is+-- never included in the *representation* of its dominator set.+--+-- A dominator set is represented as a linked list in which each node+-- points to its *immediate* dominator, which is its parent in the+-- dominator tree. In many circumstances the immediate dominator+-- will be the only dominator of interest.++data DominatorSet = ImmediateDominator { ds_label :: Label -- ^ Label of the immediate dominator.+ , ds_parent :: DominatorSet -- ^ Set of nodes dominating the immediate dominator.+ }+ | EntryNode+ deriving (Eq)++instance Outputable DominatorSet where+ ppr EntryNode = text "entry"+ ppr (ImmediateDominator l parent) = ppr l <+> text "->" <+> ppr parent++++-- | Reverse postorder number of a node in a CFG+newtype RPNum = RPNum Int+ deriving (Eq, Ord)+-- in reverse postorder, nodes closer to the entry have smaller numbers++instance Show RPNum where+ show (RPNum i) = "RP" ++ show i++instance Outputable RPNum where+ ppr (RPNum i) = hcat [text "RP", int i]+ -- using `(<>)` would conflict with Semigroup++++dominatorsMember :: Label -> DominatorSet -> Bool+-- ^ Use to tell if the given label is in the given+-- dominator set. Which is to say, does the bloc+-- with with given label _properly_ and _non-vacuously_+-- dominate the node whose dominator set this is?+--+-- Takes linear time in the height of the dominator tree,+-- but uses space efficiently.+dominatorsMember lbl (ImmediateDominator l p) = l == lbl || dominatorsMember lbl p+dominatorsMember _ EntryNode = False+++-- | Intersect two dominator sets to produce a third dominator set.+-- This function takes time linear in the size of the sets.+-- As such it is inefficient and should be used only for things+-- like visualizations or linters.+intersectDominators :: DominatorSet -> DominatorSet -> DominatorSet+intersectDominators ds ds' = commonPrefix (revDoms ds []) (revDoms ds' []) EntryNode+ where revDoms EntryNode prev = prev+ revDoms (ImmediateDominator lbl doms) prev = revDoms doms (lbl:prev)+ commonPrefix (a:as) (b:bs) doms+ | a == b = commonPrefix as bs (ImmediateDominator a doms)+ commonPrefix _ _ doms = doms+++-- | The result of dominator analysis. Also includes a reverse+-- postorder numbering, which is needed for dominator analysis+-- and for other (downstream) analyses.+--+-- Invariant: Dominators, graph, and RP numberings include only *reachable* blocks.+data GraphWithDominators node =+ GraphWithDominators { gwd_graph :: GenCmmGraph node+ , gwd_dominators :: LabelMap DominatorSet+ , gwd_rpnumbering :: LabelMap RPNum+ }+++-- | Call this function with a `CmmGraph` to get back the results of a+-- dominator analysis of that graph (as well as a reverse postorder+-- numbering). The result also includes the subgraph of the original+-- graph that contains only the reachable blocks.+graphWithDominators :: forall node .+ (NonLocal node, HasDebugCallStack)+ => GenCmmGraph node+ -> GraphWithDominators node++-- The implementation uses the Lengauer-Tarjan algorithm from the x86+-- back end.++graphWithDominators g = GraphWithDominators (reachable rpblocks g) dmap rpmap+ where rpblocks = revPostorderFrom (graphMap g) (g_entry g)+ rplabels' = map entryLabel rpblocks+ rplabels :: Array Int Label+ rplabels = listArray bounds rplabels'++ rpmap :: LabelMap RPNum+ rpmap = mapFromList $ zipWith kvpair rpblocks [0..]+ where kvpair block i = (entryLabel block, RPNum i)++ labelIndex :: Label -> Int+ labelIndex = flip findLabelIn imap+ where imap :: LabelMap Int+ imap = mapFromList $ zip rplabels' [0..]+ blockIndex = labelIndex . entryLabel++ bounds = (0, length rpblocks - 1)++ ltGraph :: [Block node C C] -> LT.Graph+ ltGraph [] = IM.empty+ ltGraph (block:blocks) =+ IM.insert+ (blockIndex block)+ (IS.fromList $ map labelIndex $ successors block)+ (ltGraph blocks)++ idom_array :: Array Int LT.Node+ idom_array = array bounds $ LT.idom (0, ltGraph rpblocks)++ domSet 0 = EntryNode+ domSet i = ImmediateDominator (rplabels ! d) (doms ! d)+ where d = idom_array ! i+ doms = tabulate bounds domSet++ dmap = mapFromList $ zipWith (\lbl i -> (lbl, domSet i)) rplabels' [0..]++reachable :: NonLocal node => [Block node C C] -> GenCmmGraph node -> GenCmmGraph node+reachable blocks g = g { g_graph = GMany NothingO blockmap NothingO }+ where blockmap = mapFromList [(entryLabel b, b) | b <- blocks]+++-- | =Utility functions++-- | Call `graphMap` to get the mapping from `Label` to `Block` that+-- is embedded in every `CmmGraph`.+graphMap :: GenCmmGraph n -> LabelMap (Block n C C)+graphMap (CmmGraph { g_graph = GMany NothingO blockmap NothingO }) = blockmap++-- | Use `gwdRPNumber` on the result of the dominator analysis to get+-- a mapping from the `Label` of each reachable block to the reverse+-- postorder number of that block.+gwdRPNumber :: HasDebugCallStack => GraphWithDominators node -> Label -> RPNum+gwdRPNumber g l = findLabelIn l (gwd_rpnumbering g)++findLabelIn :: HasDebugCallStack => Label -> LabelMap a -> a+findLabelIn lbl = mapFindWithDefault failed lbl+ where failed =+ pprPanic "label not found in result of analysis" (ppr lbl)++-- | Use `gwdDominatorsOf` on the result of the dominator analysis to get+-- a mapping from the `Label` of each reachable block to the dominator+-- set (and the immediate dominator) of that block. The+-- implementation is space-efficient: intersecting dominator+-- sets share the representation of their intersection.++gwdDominatorsOf :: HasDebugCallStack => GraphWithDominators node -> Label -> DominatorSet+gwdDominatorsOf g lbl = findLabelIn lbl (gwd_dominators g)++gwdDominatorTree :: GraphWithDominators node -> Tree.Tree Label+gwdDominatorTree gwd = subtreeAt (g_entry (gwd_graph gwd))+ where subtreeAt label = Tree.Node label $ map subtreeAt $ children label+ children l = mapFindWithDefault [] l child_map+ child_map :: LabelMap [Label]+ child_map = mapFoldlWithKey addParent mapEmpty $ gwd_dominators gwd+ where addParent cm _ EntryNode = cm+ addParent cm lbl (ImmediateDominator p _) =+ mapInsertWith (++) p [lbl] cm+++-- | Turn a function into an array. Inspired by SML's `Array.tabulate`+tabulate :: (Ix i) => (i, i) -> (i -> e) -> Array i e+tabulate b f = listArray b $ map f $ range b
compiler/GHC/Cmm/Info/Build.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE GADTs, BangPatterns, RecordWildCards, GeneralizedNewtypeDeriving, NondecreasingIndentation, TupleSections,- ScopedTypeVariables, OverloadedStrings, LambdaCase #-}+ ScopedTypeVariables, OverloadedStrings, LambdaCase, EmptyCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE StandaloneDeriving #-}@@ -113,55 +113,70 @@ - Static thunks (THUNK), ie. CAFs - Continuations (RET_SMALL, etc.) -In each case, the info table points to the SRT. There are three ways which we-may encode the location of the SRT in the info table, described below.+In each case, the info table points to the SRT, if there is one. -USE_SRT_OFFSET----------------In this case we use the info->srt to encode whether or not there is an-SRT and if so encode the offset to its location in info->f.srt_offset:+- info->srt is 0 if there's no SRT+- otherwise, there are three ways which we may encode the location of the SRT in+ the info table, described below. -- info->srt is 0 if there's no SRT, otherwise,-- info->srt == 1 and info->f.srt_offset points to the SRT+USE_SRT_POINTER+---------------+Most general implementation. Can always be used, but other ways are more efficient. -e.g. for a FUN with an SRT:+- info->srt is a pointer -StgFunInfoTable +------+- info->f.srt_offset | ------------> offset to SRT object-StgStdInfoTable +------++We encode an **absolute pointer** to the SRT in info->srt. e.g. for a FUN+with an SRT:++StgInfoTable +------+ info->layout.ptrs | ... | info->layout.nptrs | ... |- info->srt | 1 |+ info->srt | ------------> pointer to SRT object info->type | ... | |------| -USE_SRT_POINTER-----------------When tables-next-to-code isn't in use we rather encode an absolute pointer to-the SRT in info->srt. e.g. for a FUN with an SRT:+USE_SRT_OFFSET+--------------+Requires:+ - tables-next-to-code enabled +In this case we use the info->srt to encode whether or not there is an SRT and+if so encode the offset to its location in info->f.srt_offset:++- info->srt is a half-word+- info->f.srt_offset is a 32-bit int+- info->srt is 0 if there's no SRT, otherwise,+- info->srt == 1 and info->f.srt_offset is a offset to the SRT, relative to the+field address itself++e.g. for a FUN with an SRT:+ StgFunInfoTable +------+- info->f.srt_offset | ------------> pointer to SRT object-StgStdInfoTable +------++ info->f.srt_offset | ------------> offset to SRT object+StgInfoTable +------+ info->layout.ptrs | ... | info->layout.nptrs | ... | info->srt | 1 | info->type | ... | |------|+ USE_INLINE_SRT_FIELD ---------------------When using TNTC on x86_64 (and other platforms using the small memory model),-we optimise the info table representation further. The offset to the SRT can+Requires:+ - tables-next-to-code enabled+ - 64-bit architecture+ - small memory model++We optimise the info table representation further. The offset to the SRT can be stored in 32 bits (all code lives within a 2GB region in x86_64's small memory model), so we can save a word in the info table by storing the srt_offset in the srt field, which is half a word. -On x86_64 with TABLES_NEXT_TO_CODE (except on MachO, due to #15169):--- info->srt is zero if there's no SRT, otherwise:+- info->srt is a half-word+- info->srt is 0 if there's no SRT, otherwise: - info->srt is an offset from the info pointer to the SRT object -StgStdInfoTable +------++StgInfoTable +------+ info->layout.ptrs | | info->layout.nptrs | | info->srt | ------------> offset to SRT object@@ -561,7 +576,7 @@ -> CAFSet cafAnalData platform st = case st of CmmStaticsRaw _lbl _data -> Set.empty- CmmStatics _lbl _itbl _ccs payload ->+ CmmStatics _lbl _itbl _ccs payload _extras -> foldl' analyzeStatic Set.empty payload where analyzeStatic s lit =@@ -726,7 +741,9 @@ getLabelledBlocks :: Platform -> CmmDecl -> [(SomeLabel, CAFfyLabel)] getLabelledBlocks platform decl = case decl of CmmData _ (CmmStaticsRaw _ _) -> []- CmmData _ (CmmStatics lbl _ _ _) -> [ (DeclLabel lbl, mkCAFfyLabel platform lbl) ]+ CmmData _ (CmmStatics lbl info _ _ _) -> [ (DeclLabel lbl, mkCAFfyLabel platform lbl)+ | not (isThunkRep (cit_rep info))+ ] CmmProc top_info _ _ _ -> [ (BlockLabel blockId, caf_lbl) | (blockId, info) <- mapToList (info_tbls top_info) , let rep = cit_rep info@@ -771,29 +788,49 @@ graph :: [SCC (SomeLabel, CAFfyLabel, Set CAFfyLabel)] graph = stronglyConnCompFromEdgedVerticesOrd nodes --- | Get @(Label, CAFfyLabel, Set CAFfyLabel)@ for each CAF block.--- The @Set CafLabel@ represents the set of CAFfy things which this CAF's code+-- | Get @(Maybe Label, CAFfyLabel, Set CAFfyLabel)@ for each CAF block.+-- The @Set CAFfyLabel@ represents the set of CAFfy things which this CAF's code -- depends upon. ----- CAFs are treated differently from other labelled blocks:+-- - The 'Label' represents the entry code of the closure. This may be+-- 'Nothing' if it is a standard closure type (e.g. @stg_unpack_cstring@; see+-- Note [unpack_cstring closures] in StgStdThunks.cmm).+-- - The 'CAFLabel' is the label of the CAF closure.+-- - The @Set CAFLabel@ is the set of CAFfy closures which should be included+-- in the closure's SRT. --+-- Note that CAFs are treated differently from other labelled blocks:+-- -- - we never shortcut a reference to a CAF to the contents of its -- SRT, since the point of SRTs is to keep CAFs alive. -- -- - CAFs therefore don't take part in the dependency analysis in depAnalSRTs. -- instead we generate their SRTs after everything else.----getCAFs :: Platform -> CAFEnv -> [CmmDecl] -> [(Label, CAFfyLabel, Set CAFfyLabel)]-getCAFs platform cafEnv decls =- [ (g_entry g, mkCAFfyLabel platform topLbl, cafs)- | CmmProc top_info topLbl _ g <- decls- , Just info <- [mapLookup (g_entry g) (info_tbls top_info)]- , let rep = cit_rep info- , isStaticRep rep && isThunkRep rep- , Just cafs <- [mapLookup (g_entry g) cafEnv]- ]+getCAFs :: Platform -> CAFEnv -> [CmmDecl] -> [(Maybe Label, CAFfyLabel, Set CAFfyLabel)]+getCAFs platform cafEnv = mapMaybe getCAFLabel+ where+ getCAFLabel :: CmmDecl -> Maybe (Maybe Label, CAFfyLabel, Set CAFfyLabel) + getCAFLabel (CmmProc top_info top_lbl _ g)+ | Just info <- mapLookup (g_entry g) (info_tbls top_info)+ , let rep = cit_rep info+ , isStaticRep rep && isThunkRep rep+ , Just cafs <- mapLookup (g_entry g) cafEnv+ = Just (Just (g_entry g), mkCAFfyLabel platform top_lbl, cafs) + | otherwise+ = Nothing++ getCAFLabel (CmmData _ (CmmStatics top_lbl info _ccs _payload _extras))+ | isThunkRep (cit_rep info)+ = Just (Nothing, mkCAFfyLabel platform top_lbl, Set.empty)++ | otherwise+ = Nothing++ getCAFLabel (CmmData _ (CmmStaticsRaw _lbl _payload))+ = Nothing+ -- | Get the list of blocks that correspond to the entry points for -- @FUN_STATIC@ closures. These are the blocks for which if we have an -- SRT we can merge it with the static closure. [FUN]@@ -847,7 +884,7 @@ :: CmmConfig -> ModuleSRTInfo -> [(CAFEnv, [CmmDecl])] -- ^ 'CAFEnv's and 'CmmDecl's for code blocks- -> [(CAFSet, CmmDecl)] -- ^ static data decls and their 'CAFSet's+ -> [(CAFSet, CmmDataDecl)] -- ^ static data decls and their 'CAFSet's -> IO (ModuleSRTInfo, [CmmDeclSRTs]) doSRTs cfg moduleSRTInfo procs data_ = do@@ -863,16 +900,15 @@ flip map data_ $ \(set, decl) -> case decl of- CmmProc{} ->- pprPanic "doSRTs" (text "Proc in static data list:" <+> pdoc platform decl)+ CmmProc void _ _ _ -> case void of CmmData _ static -> case static of- CmmStatics lbl _ _ _ -> (lbl, set)+ CmmStatics lbl _ _ _ _ -> (lbl, set) CmmStaticsRaw lbl _ -> (lbl, set) (proc_envs, procss) = unzip procs cafEnv = mapUnions proc_envs- decls = map snd data_ ++ concat procss+ decls = map (cmmDataDeclCmmDecl . snd) data_ ++ concat procss staticFuns = mapFromList (getStaticFuns decls) platform = cmmPlatform cfg@@ -887,7 +923,7 @@ sccs :: [SCC (SomeLabel, CAFfyLabel, Set CAFfyLabel)] sccs = {-# SCC depAnalSRTs #-} depAnalSRTs platform cafEnv static_data_env decls - cafsWithSRTs :: [(Label, CAFfyLabel, Set CAFfyLabel)]+ cafsWithSRTs :: [(Maybe Label, CAFfyLabel, Set CAFfyLabel)] cafsWithSRTs = getCAFs platform cafEnv decls srtTraceM "doSRTs" (text "data:" <+> pdoc platform data_ $$@@ -910,7 +946,7 @@ flip runStateT moduleSRTInfo $ do nonCAFs <- mapM (doSCC cfg staticFuns static_data_env) sccs cAFs <- forM cafsWithSRTs $ \(l, cafLbl, cafs) ->- oneSRT cfg staticFuns [BlockLabel l] [cafLbl]+ oneSRT cfg staticFuns (map BlockLabel (maybeToList l)) [cafLbl] True{-is a CAF-} cafs static_data_env return (nonCAFs ++ cAFs) @@ -943,8 +979,7 @@ | otherwise -> -- Not an IdLabel, ignore srtMap- CmmProc{} ->- pprPanic "doSRTs" (text "Found Proc in static data list:" <+> pdoc platform decl))+ CmmProc void _ _ _ -> case void of) (moduleSRTMap moduleSRTInfo') data_ return (moduleSRTInfo'{ moduleSRTMap = srtMap_w_raws }, srt_decls ++ decls')@@ -1137,7 +1172,8 @@ not (labelDynamic this_mod platform (cmmExternalDynamicRefs cfg) lbl) -- MachO relocations can't express offsets between compilation units at- -- all, so we are always forced to build a singleton SRT in this case.+ -- all, so we are always forced to build a singleton SRT in this case+ -- (cf #15169) && (not (osMachOTarget $ platformOS $ profilePlatform profile) || isLocalCLabel this_mod lbl) -> do @@ -1205,15 +1241,15 @@ ( [CmmDeclSRTs] -- The SRT object(s) , SRTEntry -- label to use in the info table )-buildSRTChain _ [] = panic "buildSRT: empty" buildSRTChain profile cafSet = case splitAt mAX_SRT_SIZE cafSet of+ ([], _) -> panic "buildSRT: empty" (these, []) -> do (decl,lbl) <- buildSRT profile these return ([decl], lbl)- (these,those) -> do- (rest, rest_lbl) <- buildSRTChain profile (head these : those)- (decl,lbl) <- buildSRT profile (rest_lbl : tail these)+ (this:these,those) -> do+ (rest, rest_lbl) <- buildSRTChain profile (this : those)+ (decl,lbl) <- buildSRT profile (rest_lbl : these) return (decl:rest, lbl) where mAX_SRT_SIZE = 16@@ -1232,6 +1268,7 @@ [] -- no padding [mkIntCLit platform 0] -- link field [] -- no saved info+ [] -- no extras return (mkDataLits (Section Data lbl) lbl fields, SRTEntry lbl) -- | Update info tables with references to their SRTs. Also generate@@ -1247,10 +1284,10 @@ updInfoSRTs _ _ _ _ (CmmData s (CmmStaticsRaw lbl statics)) = [CmmData s (CmmStaticsRaw lbl statics)] -updInfoSRTs profile _ _ caffy (CmmData s (CmmStatics lbl itbl ccs payload))+updInfoSRTs profile _ _ caffy (CmmData s (CmmStatics lbl itbl ccs payload extras)) = [CmmData s (CmmStaticsRaw lbl (map CmmStaticLit field_lits))] where- field_lits = mkStaticClosureFields profile itbl ccs caffy payload+ field_lits = mkStaticClosureFields profile itbl ccs caffy payload extras updInfoSRTs profile srt_env funSRTEnv caffy (CmmProc top_info top_l live g) | Just (_,closure) <- maybeStaticClosure = [ proc, closure ]@@ -1280,7 +1317,7 @@ Just srtEntries -> srtTrace "maybeStaticFun" (pdoc (profilePlatform profile) res) (info_tbl { cit_rep = new_rep }, res) where res = [ CmmLabel lbl | SRTEntry lbl <- srtEntries ]- fields = mkStaticClosureFields profile info_tbl ccs caffy srtEntries+ fields = mkStaticClosureFields profile info_tbl ccs caffy srtEntries [] new_rep = case cit_rep of HeapRep sta ptrs nptrs ty -> HeapRep sta (ptrs + length srtEntries) nptrs ty
compiler/GHC/Cmm/Lint.hs view
@@ -6,6 +6,7 @@ -- ----------------------------------------------------------------------------- {-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DerivingVia #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} module GHC.Cmm.Lint (@@ -21,13 +22,15 @@ import GHC.Cmm.Dataflow.Graph import GHC.Cmm.Dataflow.Label import GHC.Cmm-import GHC.Cmm.Utils import GHC.Cmm.Liveness import GHC.Cmm.Switch (switchTargetsToList)-import GHC.Cmm.Ppr () -- For Outputable instances+import GHC.Cmm.CLabel (pprDebugCLabel) import GHC.Utils.Outputable -import Control.Monad (ap, unless)+import Control.Monad (unless)+import Control.Monad.Trans.Except (ExceptT (..), Except)+import Control.Monad.Trans.Reader (ReaderT (..))+import Data.Functor.Identity (Identity (..)) -- Things to check: -- - invariant on CmmBlock in GHC.Cmm.Expr (see comment there)@@ -47,7 +50,8 @@ runCmmLint :: OutputableP Platform a => Platform -> (a -> CmmLint b) -> a -> Maybe SDoc runCmmLint platform l p = case unCL (l p) platform of- Left err -> Just (vcat [text "Cmm lint error:",+ Left err -> Just (withPprStyle defaultDumpStyle $ vcat+ [text "Cmm lint error:", nest 2 err, text "Program was:", nest 2 (pdoc platform p)])@@ -57,7 +61,7 @@ lintCmmDecl (CmmProc _ lbl _ g) = do platform <- getPlatform- addLintInfo (text "in proc " <> pdoc platform lbl) $ lintCmmGraph g+ addLintInfo (text "in proc " <> pprDebugCLabel platform lbl) $ lintCmmGraph g lintCmmDecl (CmmData {}) = return () @@ -274,17 +278,8 @@ -- just a basic error monad: newtype CmmLint a = CmmLint { unCL :: Platform -> Either SDoc a }- deriving (Functor)--instance Applicative CmmLint where- pure a = CmmLint (\_ -> Right a)- (<*>) = ap--instance Monad CmmLint where- CmmLint m >>= k = CmmLint $ \platform ->- case m platform of- Left e -> Left e- Right a -> unCL (k a) platform+ deriving stock (Functor)+ deriving (Applicative, Monad) via ReaderT Platform (Except SDoc) getPlatform :: CmmLint Platform getPlatform = CmmLint $ \platform -> Right platform
compiler/GHC/Cmm/Liveness.hs view
@@ -20,7 +20,6 @@ import GHC.Platform import GHC.Cmm.BlockId import GHC.Cmm-import GHC.Cmm.Ppr.Expr () -- For Outputable instances import GHC.Cmm.Dataflow.Block import GHC.Cmm.Dataflow.Collections import GHC.Cmm.Dataflow@@ -104,7 +103,7 @@ ----------------------------------------------------------------------------- -- | Specialization that only retains the keys for local variables. ----- Local variablas are mostly glorified Ints, and some parts of the compiler+-- Local variables are mostly glorified Ints, and some parts of the compiler -- really don't care about anything but the Int part. So we can avoid some -- overhead by computing a IntSet instead of a Set LocalReg which (unsurprisingly) -- is quite a bit faster.
compiler/GHC/Cmm/Parser.y view
@@ -221,16 +221,11 @@ { {-# LANGUAGE TupleSections #-} -module GHC.Cmm.Parser ( parseCmmFile ) where+module GHC.Cmm.Parser ( parseCmmFile, CmmParserConfig(..) ) where import GHC.Prelude import qualified Prelude -- for happy-generated code -import GHC.Driver.Session-import GHC.Driver.Ppr-import GHC.Driver.Config.Parser (initParserOpts)-import GHC.Driver.Config.StgToCmm- import GHC.Platform import GHC.Platform.Profile @@ -261,6 +256,7 @@ import GHC.Cmm.BlockId import GHC.Cmm.Lexer import GHC.Cmm.CLabel+import GHC.Cmm.Parser.Config import GHC.Cmm.Parser.Monad hiding (getPlatform, getProfile) import qualified GHC.Cmm.Parser.Monad as PD import GHC.Cmm.CallConv@@ -464,7 +460,7 @@ mkStaticClosure profile (mkForeignLabel $3 Nothing ForeignLabelInExternalPackage IsData) -- mkForeignLabel because these are only used -- for CHARLIKE and INTLIKE closures in the RTS.- dontCareCCS (map getLit lits) [] [] [] } }+ dontCareCCS (map getLit lits) [] [] [] [] } } -- arrays of closures required for the CHARLIKE & INTLIKE arrays lits :: { [CmmParse CmmExpr] }@@ -479,7 +475,7 @@ platform <- getPlatform; ctx <- getContext; formals <- sequence (fromMaybe [] $3);- withName (renderWithContext ctx (pdoc platform entry_ret_label))+ withName (showSDocOneLine ctx (pprCLabel platform entry_ret_label)) $4; return (entry_ret_label, info, stk_formals, formals) } let do_layout = isJust $3@@ -522,15 +518,14 @@ , cit_prof = prof, cit_srt = Nothing, cit_clo = Nothing }, []) } - | 'INFO_TABLE_FUN' '(' NAME ',' INT ',' INT ',' INT ',' STRING ',' STRING ',' INT ')'- -- ptrs, nptrs, closure type, description, type, fun type+ | 'INFO_TABLE_FUN' '(' NAME ',' INT ',' INT ',' INT ',' STRING ',' STRING ',' INT ',' INT ')'+ -- ptrs, nptrs, closure type, description, type, arity, fun type {% do home_unit_id <- getHomeUnitId liftP $ pure $ do profile <- getProfile let prof = profilingInfo profile $11 $13- ty = Fun 0 (ArgSpec (fromIntegral $15))- -- Arity zero, arg_type $15+ ty = Fun (fromIntegral $15) (ArgSpec (fromIntegral $17)) rep = mkRTSRep (fromIntegral $9) $ mkHeapRep profile False (fromIntegral $5) (fromIntegral $7) ty@@ -540,7 +535,7 @@ , cit_prof = prof, cit_srt = Nothing, cit_clo = Nothing }, []) } -- we leave most of the fields zero here. This is only used- -- to generate the BCO info table in the RTS at the moment.+ -- to generate the BCO and CONTINUATION info tables in the RTS at the moment. | 'INFO_TABLE_CONSTR' '(' NAME ',' INT ',' INT ',' INT ',' INT ',' STRING ',' STRING ')' -- ptrs, nptrs, tag, closure type, description, type@@ -978,8 +973,9 @@ exprOp :: FastString -> [CmmParse CmmExpr] -> PD (CmmParse CmmExpr) exprOp name args_code = do- profile <- PD.getProfile- align_check <- gopt Opt_AlignmentSanitisation <$> getDynFlags+ pdc <- PD.getPDConfig+ let profile = PD.pdProfile pdc+ let align_check = PD.pdSanitizeAlignment pdc case lookupUFM (exprMacros profile align_check) name of Just f -> return $ do args <- sequence args_code@@ -1014,7 +1010,6 @@ ( "ne", MO_Ne ), ( "mul", MO_Mul ), ( "mulmayoflo", MO_S_MulMayOflo ),- ( "mulmayoflou", MO_U_MulMayOflo ), ( "neg", MO_S_Neg ), ( "quot", MO_S_Quot ), ( "rem", MO_S_Rem ),@@ -1296,7 +1291,7 @@ staticClosure :: UnitId -> FastString -> FastString -> [CmmLit] -> CmmParse () staticClosure pkg cl_label info payload = do profile <- getProfile- let lits = mkStaticClosure profile (mkCmmInfoLabel pkg info) dontCareCCS payload [] [] []+ let lits = mkStaticClosure profile (mkCmmInfoLabel pkg info) dontCareCCS payload [] [] [] [] code $ emitDataLits (mkCmmDataLabel pkg (NeedExternDecl True) cl_label) lits foreignCall@@ -1547,43 +1542,40 @@ ] where platform = profilePlatform profile --parseCmmFile :: DynFlags+parseCmmFile :: CmmParserConfig -> Module -> HomeUnit -> FilePath -> IO (Messages PsMessage, Messages PsMessage, Maybe (CmmGroup, [InfoProvEnt]))-parseCmmFile dflags this_mod home_unit filename = do+parseCmmFile cmmpConfig this_mod home_unit filename = do buf <- hGetStringBuffer filename let init_loc = mkRealSrcLoc (mkFastString filename) 1 1- opts = initParserOpts dflags- init_state = (initParserState opts buf init_loc) { lex_state = [0] }+ init_state = (initParserState (cmmpParserOpts cmmpConfig) buf init_loc) { lex_state = [0] } -- reset the lex_state: the Lexer monad leaves some stuff -- in there we don't want.- case unPD cmmParse dflags home_unit init_state of+ pdConfig = cmmpPDConfig cmmpConfig+ case unPD cmmParse pdConfig home_unit init_state of PFailed pst -> do let (warnings,errors) = getPsMessages pst return (warnings, errors, Nothing) POk pst code -> do st <- initC- let fstate = F.initFCodeState (profilePlatform $ targetProfile dflags)- let config = initStgToCmmConfig dflags no_module+ let fstate = F.initFCodeState (profilePlatform $ pdProfile pdConfig) let fcode = do- ((), cmm) <- getCmm $ unEC code "global" (initEnv (targetProfile dflags)) [] >> return ()+ ((), cmm) <- getCmm $ unEC code "global" (initEnv (pdProfile pdConfig)) [] >> return () -- See Note [Mapping Info Tables to Source Positions] (IPE Maps) let used_info | do_ipe = map (cmmInfoTableToInfoProvEnt this_mod) (mapMaybe topInfoTable cmm) | otherwise = [] where- do_ipe = stgToCmmInfoTableMap config+ do_ipe = stgToCmmInfoTableMap $ cmmpStgToCmmConfig cmmpConfig ((), cmm2) <- getCmm $ emitIpeBufferListNode this_mod used_info return (cmm ++ cmm2, used_info)- (cmm, _) = runC config fstate st fcode+ (cmm, _) = runC (cmmpStgToCmmConfig cmmpConfig) fstate st fcode (warnings,errors) = getPsMessages pst if not (isEmptyMessages errors) then return (warnings, errors, Nothing) else return (warnings, errors, Just cmm)- where- no_module = panic "parseCmmFile: no module"+ }
+ compiler/GHC/Cmm/Parser/Config.hs view
@@ -0,0 +1,24 @@+module GHC.Cmm.Parser.Config (+ PDConfig(..)+ , CmmParserConfig(..)+) where++import GHC.Prelude++import GHC.Platform.Profile++import GHC.StgToCmm.Config++import GHC.Parser.Lexer+++data PDConfig = PDConfig+ { pdProfile :: !Profile+ , pdSanitizeAlignment :: !Bool -- ^ Insert alignment checks (cf @-falignment-sanitisation@)+ }++data CmmParserConfig = CmmParserConfig+ { cmmpParserOpts :: !ParserOpts+ , cmmpPDConfig :: !PDConfig+ , cmmpStgToCmmConfig :: !StgToCmmConfig+ }
compiler/GHC/Cmm/Parser/Monad.hs view
@@ -11,19 +11,22 @@ PD(..) , liftP , failMsgPD+ , getPDConfig , getProfile , getPlatform , getHomeUnitId+ , PDConfig(..) ) where import GHC.Prelude +import GHC.Cmm.Parser.Config+ import GHC.Platform import GHC.Platform.Profile import Control.Monad -import GHC.Driver.Session import GHC.Parser.Lexer import GHC.Parser.Errors.Types import GHC.Types.Error ( MsgEnvelope )@@ -31,7 +34,7 @@ import GHC.Unit.Types import GHC.Unit.Home -newtype PD a = PD { unPD :: DynFlags -> HomeUnit -> PState -> ParseResult a }+newtype PD a = PD { unPD :: PDConfig -> HomeUnit -> PState -> ParseResult a } instance Functor PD where fmap = liftM@@ -58,11 +61,11 @@ POk s1 a -> unPD (k a) d hu s1 PFailed s1 -> PFailed s1 -instance HasDynFlags PD where- getDynFlags = PD $ \d _ s -> POk s d+getPDConfig :: PD PDConfig+getPDConfig = PD $ \pdc _ s -> POk s pdc getProfile :: PD Profile-getProfile = targetProfile <$> getDynFlags+getProfile = PD $ \pdc _ s -> POk s (pdProfile pdc) getPlatform :: PD Platform getPlatform = profilePlatform <$> getProfile
compiler/GHC/Cmm/Pipeline.hs view
@@ -1,14 +1,13 @@ {-# LANGUAGE BangPatterns #-} module GHC.Cmm.Pipeline (- -- | Converts C-- with an implicit stack and native C-- calls into- -- optimized, CPS converted and native-call-less C--. The latter- -- C-- can be used to generate assembly. cmmPipeline ) where import GHC.Prelude +import GHC.Driver.Flags+ import GHC.Cmm import GHC.Cmm.Config import GHC.Cmm.ContFlowOpt@@ -20,39 +19,41 @@ import GHC.Cmm.ProcPoint import GHC.Cmm.Sink import GHC.Cmm.Switch.Implement+import GHC.Cmm.ThreadSanitizer import GHC.Types.Unique.Supply-import GHC.Driver.Session-import GHC.Driver.Config.Cmm+ import GHC.Utils.Error import GHC.Utils.Logger-import GHC.Driver.Env-import Control.Monad import GHC.Utils.Outputable+ import GHC.Platform++import Control.Monad import Data.Either (partitionEithers) ----------------------------------------------------------------------------- -- | Top level driver for C-- pipeline ----------------------------------------------------------------------------- +-- | Converts C-- with an implicit stack and native C-- calls into+-- optimized, CPS converted and native-call-less C--. The latter+-- C-- can be used to generate assembly. cmmPipeline- :: HscEnv -- Compilation env including- -- dynamic flags: -dcmm-lint -ddump-cmm-cps+ :: Logger+ -> CmmConfig -> ModuleSRTInfo -- Info about SRTs generated so far -> CmmGroup -- Input C-- with Procedures -> IO (ModuleSRTInfo, CmmGroupSRTs) -- Output CPS transformed C-- -cmmPipeline hsc_env srtInfo prog = do- let logger = hsc_logger hsc_env- let cmmConfig = initCmmConfig (hsc_dflags hsc_env)+cmmPipeline logger cmm_config srtInfo prog = do let forceRes (info, group) = info `seq` foldr seq () group- let platform = cmmPlatform cmmConfig+ let platform = cmmPlatform cmm_config withTimingSilent logger (text "Cmm pipeline") forceRes $ do- tops <- {-# SCC "tops" #-} mapM (cpsTop logger platform cmmConfig) prog+ tops <- {-# SCC "tops" #-} mapM (cpsTop logger platform cmm_config) prog let (procs, data_) = partitionEithers tops- (srtInfo, cmms) <- {-# SCC "doSRTs" #-} doSRTs cmmConfig srtInfo procs data_+ (srtInfo, cmms) <- {-# SCC "doSRTs" #-} doSRTs cmm_config srtInfo procs data_ dumpWith logger Opt_D_dump_cmm_cps "Post CPS Cmm" FormatCMM (pdoc platform cmms) return (srtInfo, cmms)@@ -66,8 +67,8 @@ -- [SRTs]. -- -- - in the case of a `CmmData`, the unmodified 'CmmDecl' and a 'CAFSet' containing-cpsTop :: Logger -> Platform -> CmmConfig -> CmmDecl -> IO (Either (CAFEnv, [CmmDecl]) (CAFSet, CmmDecl))-cpsTop _logger platform _ p@(CmmData _ statics) = return (Right (cafAnalData platform statics, p))+cpsTop :: Logger -> Platform -> CmmConfig -> CmmDecl -> IO (Either (CAFEnv, [CmmDecl]) (CAFSet, CmmDataDecl))+cpsTop _logger platform _ (CmmData section statics) = return (Right (cafAnalData platform statics, CmmData section statics)) cpsTop logger platform cfg proc = do ----------- Control-flow optimisations ----------------------------------@@ -98,6 +99,13 @@ else pure g dump Opt_D_dump_cmm_switch "Post switch plan" g + ----------- ThreadSanitizer instrumentation -----------------------------+ g <- {-# SCC "annotateTSAN" #-}+ if cmmOptThreadSanitizer cfg+ then runUniqSM $ annotateTSAN platform g+ else return g+ dump Opt_D_dump_cmm_thread_sanitizer "ThreadSanitizer instrumentation" g+ ----------- Proc points ------------------------------------------------- let call_pps :: ProcPointSet -- LabelMap@@ -156,7 +164,7 @@ return $ if cmmOptControlFlow cfg then map (cmmCfgOptsProc splitting_proc_points) g else g- g <- return (map removeUnreachableBlocksProc g)+ g <- return $ map (removeUnreachableBlocksProc platform) g -- See Note [unreachable blocks] dumps Opt_D_dump_cmm_cfg "Post control-flow optimisations" g
− compiler/GHC/Cmm/Ppr.hs
@@ -1,319 +0,0 @@-{-# LANGUAGE GADTs #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE LambdaCase #-}--{-# OPTIONS_GHC -fno-warn-orphans #-}------------------------------------------------------------------------------------ Pretty-printing of Cmm as (a superset of) C-------- (c) The University of Glasgow 2004-2006--------------------------------------------------------------------------------------- This is where we walk over CmmNode emitting an external representation,--- suitable for parsing, in a syntax strongly reminiscent of C--. This--- is the "External Core" for the Cmm layer.------ As such, this should be a well-defined syntax: we want it to look nice.--- Thus, we try wherever possible to use syntax defined in [1],--- "The C-- Reference Manual", http://www.cs.tufts.edu/~nr/c--/index.html. We--- differ slightly, in some cases. For one, we use I8 .. I64 for types, rather--- than C--'s bits8 .. bits64.------ We try to ensure that all information available in the abstract--- syntax is reproduced, or reproducible, in the concrete syntax.--- Data that is not in printed out can be reconstructed according to--- conventions used in the pretty printer. There are at least two such--- cases:--- 1) if a value has wordRep type, the type is not appended in the--- output.--- 2) MachOps that operate over wordRep type are printed in a--- C-style, rather than as their internal MachRep name.------ These conventions produce much more readable Cmm output.------ A useful example pass over Cmm is in nativeGen/MachCodeGen.hs--module GHC.Cmm.Ppr- ( module GHC.Cmm.Ppr.Decl- , module GHC.Cmm.Ppr.Expr- )-where--import GHC.Prelude hiding (succ)--import GHC.Platform-import GHC.Cmm.CLabel-import GHC.Cmm-import GHC.Cmm.Utils-import GHC.Cmm.Switch-import GHC.Data.FastString-import GHC.Utils.Outputable-import GHC.Cmm.Ppr.Decl-import GHC.Cmm.Ppr.Expr-import GHC.Utils.Constants (debugIsOn)--import GHC.Types.Basic-import GHC.Cmm.Dataflow.Block-import GHC.Cmm.Dataflow.Graph------------------------------------------------------ Outputable instances-instance OutputableP Platform InfoProvEnt where- pdoc platform (InfoProvEnt clabel _ _ _ _) = pdoc platform clabel--instance Outputable CmmStackInfo where- ppr = pprStackInfo--instance OutputableP Platform CmmTopInfo where- pdoc = pprTopInfo---instance OutputableP Platform (CmmNode e x) where- pdoc = pprNode--instance Outputable Convention where- ppr = pprConvention--instance Outputable ForeignConvention where- ppr = pprForeignConvention--instance OutputableP Platform ForeignTarget where- pdoc = pprForeignTarget--instance Outputable CmmReturnInfo where- ppr = pprReturnInfo--instance OutputableP Platform (Block CmmNode C C) where- pdoc = pprBlock-instance OutputableP Platform (Block CmmNode C O) where- pdoc = pprBlock-instance OutputableP Platform (Block CmmNode O C) where- pdoc = pprBlock-instance OutputableP Platform (Block CmmNode O O) where- pdoc = pprBlock--instance OutputableP Platform (Graph CmmNode e x) where- pdoc = pprGraph--instance OutputableP Platform CmmGraph where- pdoc = pprCmmGraph--------------------------------------------------------------- Outputting types Cmm contains--pprStackInfo :: CmmStackInfo -> SDoc-pprStackInfo (StackInfo {arg_space=arg_space}) =- text "arg_space: " <> ppr arg_space--pprTopInfo :: Platform -> CmmTopInfo -> SDoc-pprTopInfo platform (TopInfo {info_tbls=info_tbl, stack_info=stack_info}) =- vcat [text "info_tbls: " <> pdoc platform info_tbl,- text "stack_info: " <> ppr stack_info]--------------------------------------------------------------- Outputting blocks and graphs--pprBlock :: IndexedCO x SDoc SDoc ~ SDoc- => Platform -> Block CmmNode e x -> IndexedCO e SDoc SDoc-pprBlock platform block- = foldBlockNodesB3 ( ($$) . pdoc platform- , ($$) . (nest 4) . pdoc platform- , ($$) . (nest 4) . pdoc platform- )- block- empty--pprGraph :: Platform -> Graph CmmNode e x -> SDoc-pprGraph platform = \case- GNil -> empty- GUnit block -> pdoc platform block- GMany entry body exit ->- text "{"- $$ nest 2 (pprMaybeO entry $$ (vcat $ map (pdoc platform) $ bodyToBlockList body) $$ pprMaybeO exit)- $$ text "}"- where pprMaybeO :: OutputableP Platform (Block CmmNode e x)- => MaybeO ex (Block CmmNode e x) -> SDoc- pprMaybeO NothingO = empty- pprMaybeO (JustO block) = pdoc platform block--pprCmmGraph :: Platform -> CmmGraph -> SDoc-pprCmmGraph platform g- = text "{" <> text "offset"- $$ nest 2 (vcat $ map (pdoc platform) blocks)- $$ text "}"- where blocks = revPostorder g- -- revPostorder has the side-effect of discarding unreachable code,- -- so pretty-printed Cmm will omit any unreachable blocks. This can- -- sometimes be confusing.-------------------------------------------------- Outputting CmmNode and types which it contains--pprConvention :: Convention -> SDoc-pprConvention (NativeNodeCall {}) = text "<native-node-call-convention>"-pprConvention (NativeDirectCall {}) = text "<native-direct-call-convention>"-pprConvention (NativeReturn {}) = text "<native-ret-convention>"-pprConvention Slow = text "<slow-convention>"-pprConvention GC = text "<gc-convention>"--pprForeignConvention :: ForeignConvention -> SDoc-pprForeignConvention (ForeignConvention c args res ret) =- doubleQuotes (ppr c) <+> text "arg hints: " <+> ppr args <+> text " result hints: " <+> ppr res <+> ppr ret--pprReturnInfo :: CmmReturnInfo -> SDoc-pprReturnInfo CmmMayReturn = empty-pprReturnInfo CmmNeverReturns = text "never returns"--pprForeignTarget :: Platform -> ForeignTarget -> SDoc-pprForeignTarget platform (ForeignTarget fn c) = ppr c <+> ppr_target fn- where- ppr_target :: CmmExpr -> SDoc- ppr_target t@(CmmLit _) = pdoc platform t- ppr_target fn' = parens (pdoc platform fn')--pprForeignTarget platform (PrimTarget op)- -- HACK: We're just using a ForeignLabel to get this printed, the label- -- might not really be foreign.- = pdoc platform- (CmmLabel (mkForeignLabel- (mkFastString (show op))- Nothing ForeignLabelInThisPackage IsFunction))--pprNode :: Platform -> CmmNode e x -> SDoc-pprNode platform node = pp_node <+> pp_debug- where- pp_node :: SDoc- pp_node = case node of- -- label:- CmmEntry id tscope ->- (sdocOption sdocSuppressUniques $ \case- True -> text "_lbl_"- False -> ppr id- )- <> colon- <+> ppUnlessOption sdocSuppressTicks (text "//" <+> ppr tscope)-- -- // text- CmmComment s -> text "//" <+> ftext s-- -- //tick bla<...>- CmmTick t -> ppUnlessOption sdocSuppressTicks- (text "//tick" <+> ppr t)-- -- unwind reg = expr;- CmmUnwind regs ->- text "unwind "- <> commafy (map (\(r,e) -> ppr r <+> char '=' <+> pdoc platform e) regs) <> semi-- -- reg = expr;- CmmAssign reg expr -> ppr reg <+> equals <+> pdoc platform expr <> semi-- -- rep[lv] = expr;- CmmStore lv expr align -> rep <> align_mark <> brackets (pdoc platform lv) <+> equals <+> pdoc platform expr <> semi- where- align_mark = case align of- Unaligned -> text "^"- NaturallyAligned -> empty- rep = ppr ( cmmExprType platform expr )-- -- call "ccall" foo(x, y)[r1, r2];- -- ToDo ppr volatile- CmmUnsafeForeignCall target results args ->- hsep [ ppUnless (null results) $- parens (commafy $ map ppr results) <+> equals,- text "call",- pdoc platform target <> parens (commafy $ map (pdoc platform) args) <> semi]-- -- goto label;- CmmBranch ident -> text "goto" <+> ppr ident <> semi-- -- if (expr) goto t; else goto f;- CmmCondBranch expr t f l ->- hsep [ text "if"- , parens (pdoc platform expr)- , case l of- Nothing -> empty- Just b -> parens (text "likely:" <+> ppr b)- , text "goto"- , ppr t <> semi- , text "else goto"- , ppr f <> semi- ]-- CmmSwitch expr ids ->- hang (hsep [ text "switch"- , range- , if isTrivialCmmExpr expr- then pdoc platform expr- else parens (pdoc platform expr)- , text "{"- ])- 4 (vcat (map ppCase cases) $$ def) $$ rbrace- where- (cases, mbdef) = switchTargetsFallThrough ids- ppCase (is,l) = hsep- [ text "case"- , commafy $ map integer is- , text ": goto"- , ppr l <> semi- ]- def | Just l <- mbdef = hsep- [ text "default:"- , braces (text "goto" <+> ppr l <> semi)- ]- | otherwise = empty-- range = brackets $ hsep [integer lo, text "..", integer hi]- where (lo,hi) = switchTargetsRange ids-- CmmCall tgt k regs out res updfr_off ->- hcat [ text "call", space- , pprFun tgt, parens (interpp'SP regs), space- , returns <+>- text "args: " <> ppr out <> comma <+>- text "res: " <> ppr res <> comma <+>- text "upd: " <> ppr updfr_off- , semi ]- where pprFun f@(CmmLit _) = pdoc platform f- pprFun f = parens (pdoc platform f)-- returns- | Just r <- k = text "returns to" <+> ppr r <> comma- | otherwise = empty-- CmmForeignCall {tgt=t, res=rs, args=as, succ=s, ret_args=a, ret_off=u, intrbl=i} ->- hcat $ if i then [text "interruptible", space] else [] ++- [ text "foreign call", space- , pdoc platform t, text "(...)", space- , text "returns to" <+> ppr s- <+> text "args:" <+> parens (pdoc platform as)- <+> text "ress:" <+> parens (ppr rs)- , text "ret_args:" <+> ppr a- , text "ret_off:" <+> ppr u- , semi ]-- pp_debug :: SDoc- pp_debug =- if not debugIsOn then empty- else case node of- CmmEntry {} -> empty -- Looks terrible with text " // CmmEntry"- CmmComment {} -> empty -- Looks also terrible with text " // CmmComment"- CmmTick {} -> empty- CmmUnwind {} -> text " // CmmUnwind"- CmmAssign {} -> text " // CmmAssign"- CmmStore {} -> text " // CmmStore"- CmmUnsafeForeignCall {} -> text " // CmmUnsafeForeignCall"- CmmBranch {} -> text " // CmmBranch"- CmmCondBranch {} -> text " // CmmCondBranch"- CmmSwitch {} -> text " // CmmSwitch"- CmmCall {} -> text " // CmmCall"- CmmForeignCall {} -> text " // CmmForeignCall"-- commafy :: [SDoc] -> SDoc- commafy xs = hsep $ punctuate comma xs
− compiler/GHC/Cmm/Ppr/Decl.hs
@@ -1,172 +0,0 @@-{-# LANGUAGE GADTs #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FlexibleContexts #-}------------------------------------------------------------------------------------- Pretty-printing of common Cmm types------ (c) The University of Glasgow 2004-2006---------------------------------------------------------------------------------------- This is where we walk over Cmm emitting an external representation,--- suitable for parsing, in a syntax strongly reminiscent of C--. This--- is the "External Core" for the Cmm layer.------ As such, this should be a well-defined syntax: we want it to look nice.--- Thus, we try wherever possible to use syntax defined in [1],--- "The C-- Reference Manual", http://www.cs.tufts.edu/~nr/c--/index.html. We--- differ slightly, in some cases. For one, we use I8 .. I64 for types, rather--- than C--'s bits8 .. bits64.------ We try to ensure that all information available in the abstract--- syntax is reproduced, or reproducible, in the concrete syntax.--- Data that is not in printed out can be reconstructed according to--- conventions used in the pretty printer. There are at least two such--- cases:--- 1) if a value has wordRep type, the type is not appended in the--- output.--- 2) MachOps that operate over wordRep type are printed in a--- C-style, rather than as their internal MachRep name.------ These conventions produce much more readable Cmm output.------ A useful example pass over Cmm is in nativeGen/MachCodeGen.hs-----{-# OPTIONS_GHC -fno-warn-orphans #-}-module GHC.Cmm.Ppr.Decl- ( pprCmms, pprCmmGroup, pprSection, pprStatic- )-where--import GHC.Prelude--import GHC.Platform-import GHC.Cmm.Ppr.Expr-import GHC.Cmm--import GHC.Utils.Outputable--import Data.List (intersperse)--import qualified Data.ByteString as BS---pprCmms :: (OutputableP Platform info, OutputableP Platform g)- => Platform -> [GenCmmGroup RawCmmStatics info g] -> SDoc-pprCmms platform cmms = pprCode CStyle (vcat (intersperse separator $ map (pdoc platform) cmms))- where- separator = space $$ text "-------------------" $$ space---------------------------------------------------------------------------------instance (OutputableP Platform d, OutputableP Platform info, OutputableP Platform i)- => OutputableP Platform (GenCmmDecl d info i) where- pdoc = pprTop--instance OutputableP Platform (GenCmmStatics a) where- pdoc = pprStatics--instance OutputableP Platform CmmStatic where- pdoc = pprStatic--instance OutputableP Platform CmmInfoTable where- pdoc = pprInfoTable----------------------------------------------------------------------------------pprCmmGroup :: (OutputableP Platform d, OutputableP Platform info, OutputableP Platform g)- => Platform -> GenCmmGroup d info g -> SDoc-pprCmmGroup platform tops- = vcat $ intersperse blankLine $ map (pprTop platform) tops---- ----------------------------------------------------------------------------- Top level `procedure' blocks.----pprTop :: (OutputableP Platform d, OutputableP Platform info, OutputableP Platform i)- => Platform -> GenCmmDecl d info i -> SDoc--pprTop platform (CmmProc info lbl live graph)-- = vcat [ pdoc platform lbl <> lparen <> rparen <+> lbrace <+> text "// " <+> ppr live- , nest 8 $ lbrace <+> pdoc platform info $$ rbrace- , nest 4 $ pdoc platform graph- , rbrace ]---- ----------------------------------------------------------------------------- We follow [1], 4.5------ section "data" { ... }----pprTop platform (CmmData section ds) =- (hang (pprSection platform section <+> lbrace) 4 (pdoc platform ds))- $$ rbrace---- ----------------------------------------------------------------------------- Info tables.--pprInfoTable :: Platform -> CmmInfoTable -> SDoc-pprInfoTable platform (CmmInfoTable { cit_lbl = lbl, cit_rep = rep- , cit_prof = prof_info- , cit_srt = srt })- = vcat [ text "label: " <> pdoc platform lbl- , text "rep: " <> ppr rep- , case prof_info of- NoProfilingInfo -> empty- ProfilingInfo ct cd ->- vcat [ text "type: " <> text (show (BS.unpack ct))- , text "desc: " <> text (show (BS.unpack cd)) ]- , text "srt: " <> pdoc platform srt ]--instance Outputable ForeignHint where- ppr NoHint = empty- ppr SignedHint = quotes(text "signed")--- ppr AddrHint = quotes(text "address")--- Temp Jan08- ppr AddrHint = (text "PtrHint")---- ----------------------------------------------------------------------------- Static data.--- Strings are printed as C strings, and we print them as I8[],--- following C-------pprStatics :: Platform -> GenCmmStatics a -> SDoc-pprStatics platform (CmmStatics lbl itbl ccs payload) =- pdoc platform lbl <> colon <+> pdoc platform itbl <+> ppr ccs <+> pdoc platform payload-pprStatics platform (CmmStaticsRaw lbl ds) = vcat ((pdoc platform lbl <> colon) : map (pprStatic platform) ds)--pprStatic :: Platform -> CmmStatic -> SDoc-pprStatic platform s = case s of- CmmStaticLit lit -> nest 4 $ text "const" <+> pprLit platform lit <> semi- CmmUninitialised i -> nest 4 $ text "I8" <> brackets (int i)- CmmString s' -> nest 4 $ text "I8[]" <+> text (show s')- CmmFileEmbed path -> nest 4 $ text "incbin " <+> text (show path)---- ----------------------------------------------------------------------------- data sections----pprSection :: Platform -> Section -> SDoc-pprSection platform (Section t suffix) =- section <+> doubleQuotes (pprSectionType t <+> char '.' <+> pdoc platform suffix)- where- section = text "section"--pprSectionType :: SectionType -> SDoc-pprSectionType s = doubleQuotes $ case s of- Text -> text "text"- Data -> text "data"- ReadOnlyData -> text "readonly"- ReadOnlyData16 -> text "readonly16"- RelocatableReadOnlyData -> text "relreadonly"- UninitialisedData -> text "uninitialised"- InitArray -> text "initarray"- FiniArray -> text "finiarray"- CString -> text "cstring"- OtherSection s' -> text s'
− compiler/GHC/Cmm/Ppr/Expr.hs
@@ -1,299 +0,0 @@----------------------------------------------------------------------------------- Pretty-printing of common Cmm types------ (c) The University of Glasgow 2004-2006---------------------------------------------------------------------------------------- This is where we walk over Cmm emitting an external representation,--- suitable for parsing, in a syntax strongly reminiscent of C--. This--- is the "External Core" for the Cmm layer.------ As such, this should be a well-defined syntax: we want it to look nice.--- Thus, we try wherever possible to use syntax defined in [1],--- "The C-- Reference Manual", http://www.cs.tufts.edu/~nr/c--/index.html. We--- differ slightly, in some cases. For one, we use I8 .. I64 for types, rather--- than C--'s bits8 .. bits64.------ We try to ensure that all information available in the abstract--- syntax is reproduced, or reproducible, in the concrete syntax.--- Data that is not in printed out can be reconstructed according to--- conventions used in the pretty printer. There are at least two such--- cases:--- 1) if a value has wordRep type, the type is not appended in the--- output.--- 2) MachOps that operate over wordRep type are printed in a--- C-style, rather than as their internal MachRep name.------ These conventions produce much more readable Cmm output.------ A useful example pass over Cmm is in nativeGen/MachCodeGen.hs----{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE FlexibleInstances #-}--{-# OPTIONS_GHC -fno-warn-orphans #-}--module GHC.Cmm.Ppr.Expr- ( pprExpr, pprLit- )-where--import GHC.Prelude--import GHC.Platform-import GHC.Cmm.Expr--import GHC.Utils.Outputable-import GHC.Utils.Trace--import Data.Maybe-import Numeric ( fromRat )---------------------------------------------------------------------------------instance OutputableP Platform CmmExpr where- pdoc = pprExpr--instance Outputable CmmReg where- ppr e = pprReg e--instance OutputableP Platform CmmLit where- pdoc = pprLit--instance Outputable LocalReg where- ppr e = pprLocalReg e--instance Outputable Area where- ppr e = pprArea e--instance Outputable GlobalReg where- ppr e = pprGlobalReg e--instance OutputableP env GlobalReg where- pdoc _ = ppr---- ----------------------------------------------------------------------------- Expressions-----pprExpr :: Platform -> CmmExpr -> SDoc-pprExpr platform e- = case e of- CmmRegOff reg i ->- pprExpr platform (CmmMachOp (MO_Add rep)- [CmmReg reg, CmmLit (CmmInt (fromIntegral i) rep)])- where rep = typeWidth (cmmRegType platform reg)- CmmLit lit -> pprLit platform lit- _other -> pprExpr1 platform e---- Here's the precedence table from GHC.Cmm.Parser:--- %nonassoc '>=' '>' '<=' '<' '!=' '=='--- %left '|'--- %left '^'--- %left '&'--- %left '>>' '<<'--- %left '-' '+'--- %left '/' '*' '%'--- %right '~'---- We just cope with the common operators for now, the rest will get--- a default conservative behaviour.---- %nonassoc '>=' '>' '<=' '<' '!=' '=='-pprExpr1, pprExpr7, pprExpr8 :: Platform -> CmmExpr -> SDoc-pprExpr1 platform (CmmMachOp op [x,y])- | Just doc <- infixMachOp1 op- = pprExpr7 platform x <+> doc <+> pprExpr7 platform y-pprExpr1 platform e = pprExpr7 platform e--infixMachOp1, infixMachOp7, infixMachOp8 :: MachOp -> Maybe SDoc--infixMachOp1 (MO_Eq _) = Just (text "==")-infixMachOp1 (MO_Ne _) = Just (text "!=")-infixMachOp1 (MO_Shl _) = Just (text "<<")-infixMachOp1 (MO_U_Shr _) = Just (text ">>")-infixMachOp1 (MO_U_Ge _) = Just (text ">=")-infixMachOp1 (MO_U_Le _) = Just (text "<=")-infixMachOp1 (MO_U_Gt _) = Just (char '>')-infixMachOp1 (MO_U_Lt _) = Just (char '<')-infixMachOp1 _ = Nothing---- %left '-' '+'-pprExpr7 platform (CmmMachOp (MO_Add rep1) [x, CmmLit (CmmInt i rep2)]) | i < 0- = pprExpr7 platform (CmmMachOp (MO_Sub rep1) [x, CmmLit (CmmInt (negate i) rep2)])-pprExpr7 platform (CmmMachOp op [x,y])- | Just doc <- infixMachOp7 op- = pprExpr7 platform x <+> doc <+> pprExpr8 platform y-pprExpr7 platform e = pprExpr8 platform e--infixMachOp7 (MO_Add _) = Just (char '+')-infixMachOp7 (MO_Sub _) = Just (char '-')-infixMachOp7 _ = Nothing---- %left '/' '*' '%'-pprExpr8 platform (CmmMachOp op [x,y])- | Just doc <- infixMachOp8 op- = pprExpr8 platform x <+> doc <+> pprExpr9 platform y-pprExpr8 platform e = pprExpr9 platform e--infixMachOp8 (MO_U_Quot _) = Just (char '/')-infixMachOp8 (MO_Mul _) = Just (char '*')-infixMachOp8 (MO_U_Rem _) = Just (char '%')-infixMachOp8 _ = Nothing--pprExpr9 :: Platform -> CmmExpr -> SDoc-pprExpr9 platform e =- case e of- CmmLit lit -> pprLit1 platform lit- CmmLoad expr rep align- -> let align_mark =- case align of- NaturallyAligned -> empty- Unaligned -> text "^"- in ppr rep <> align_mark <> brackets (pdoc platform expr)- CmmReg reg -> ppr reg- CmmRegOff reg off -> parens (ppr reg <+> char '+' <+> int off)- CmmStackSlot a off -> parens (ppr a <+> char '+' <+> int off)- CmmMachOp mop args -> genMachOp platform mop args--genMachOp :: Platform -> MachOp -> [CmmExpr] -> SDoc-genMachOp platform mop args- | Just doc <- infixMachOp mop = case args of- -- dyadic- [x,y] -> pprExpr9 platform x <+> doc <+> pprExpr9 platform y-- -- unary- [x] -> doc <> pprExpr9 platform x-- _ -> pprTrace "GHC.Cmm.Ppr.Expr.genMachOp: machop with strange number of args"- (pprMachOp mop <+>- parens (hcat $ punctuate comma (map (pprExpr platform) args)))- empty-- | isJust (infixMachOp1 mop)- || isJust (infixMachOp7 mop)- || isJust (infixMachOp8 mop) = parens (pprExpr platform (CmmMachOp mop args))-- | otherwise = char '%' <> ppr_op <> parens (commafy (map (pprExpr platform) args))- where ppr_op = text (map (\c -> if c == ' ' then '_' else c)- (show mop))- -- replace spaces in (show mop) with underscores,------- Unsigned ops on the word size of the machine get nice symbols.--- All else get dumped in their ugly format.----infixMachOp :: MachOp -> Maybe SDoc-infixMachOp mop- = case mop of- MO_And _ -> Just $ char '&'- MO_Or _ -> Just $ char '|'- MO_Xor _ -> Just $ char '^'- MO_Not _ -> Just $ char '~'- MO_S_Neg _ -> Just $ char '-' -- there is no unsigned neg :)- _ -> Nothing---- ----------------------------------------------------------------------------- Literals.--- To minimise line noise we adopt the convention that if the literal--- has the natural machine word size, we do not append the type----pprLit :: Platform -> CmmLit -> SDoc-pprLit platform lit = case lit of- CmmInt i rep ->- hcat [ (if i < 0 then parens else id)(integer i)- , ppUnless (rep == wordWidth platform) $- space <> dcolon <+> ppr rep ]-- CmmFloat f rep -> hsep [ double (fromRat f), dcolon, ppr rep ]- CmmVec lits -> char '<' <> commafy (map (pprLit platform) lits) <> char '>'- CmmLabel clbl -> pdoc platform clbl- CmmLabelOff clbl i -> pdoc platform clbl <> ppr_offset i- CmmLabelDiffOff clbl1 clbl2 i _ -> pdoc platform clbl1 <> char '-'- <> pdoc platform clbl2 <> ppr_offset i- CmmBlock id -> ppr id- CmmHighStackMark -> text "<highSp>"--pprLit1 :: Platform -> CmmLit -> SDoc-pprLit1 platform lit@(CmmLabelOff {}) = parens (pprLit platform lit)-pprLit1 platform lit = pprLit platform lit--ppr_offset :: Int -> SDoc-ppr_offset i- | i==0 = empty- | i>=0 = char '+' <> int i- | otherwise = char '-' <> int (-i)---- ----------------------------------------------------------------------------- Registers, whether local (temps) or global----pprReg :: CmmReg -> SDoc-pprReg r- = case r of- CmmLocal local -> pprLocalReg local- CmmGlobal global -> pprGlobalReg global------- We only print the type of the local reg if it isn't wordRep----pprLocalReg :: LocalReg -> SDoc-pprLocalReg (LocalReg uniq rep) =--- = ppr rep <> char '_' <> ppr uniq--- Temp Jan08- char '_' <> pprUnique uniq <>- (if isWord32 rep -- && not (isGcPtrType rep) -- Temp Jan08 -- sigh- then dcolon <> ptr <> ppr rep- else dcolon <> ptr <> ppr rep)- where- pprUnique unique = sdocOption sdocSuppressUniques $ \case- True -> text "_locVar_"- False -> ppr unique- ptr = empty- --if isGcPtrType rep- -- then doubleQuotes (text "ptr")- -- else empty---- Stack areas-pprArea :: Area -> SDoc-pprArea Old = text "old"-pprArea (Young id) = hcat [ text "young<", ppr id, text ">" ]---- needs to be kept in syn with 'GHC.Cmm.Expr.GlobalReg'----pprGlobalReg :: GlobalReg -> SDoc-pprGlobalReg gr- = case gr of- VanillaReg n _ -> char 'R' <> int n--- Temp Jan08--- VanillaReg n VNonGcPtr -> char 'R' <> int n--- VanillaReg n VGcPtr -> char 'P' <> int n- FloatReg n -> char 'F' <> int n- DoubleReg n -> char 'D' <> int n- LongReg n -> char 'L' <> int n- XmmReg n -> text "XMM" <> int n- YmmReg n -> text "YMM" <> int n- ZmmReg n -> text "ZMM" <> int n- Sp -> text "Sp"- SpLim -> text "SpLim"- Hp -> text "Hp"- HpLim -> text "HpLim"- MachSp -> text "MachSp"- UnwindReturnReg-> text "UnwindReturnReg"- CCCS -> text "CCCS"- CurrentTSO -> text "CurrentTSO"- CurrentNursery -> text "CurrentNursery"- HpAlloc -> text "HpAlloc"- EagerBlackholeInfo -> text "stg_EAGER_BLACKHOLE_info"- GCEnter1 -> text "stg_gc_enter_1"- GCFun -> text "stg_gc_fun"- BaseReg -> text "BaseReg"- PicBaseReg -> text "PicBaseReg"---------------------------------------------------------------------------------commafy :: [SDoc] -> SDoc-commafy xs = fsep $ punctuate comma xs
compiler/GHC/Cmm/ProcPoint.hs view
@@ -2,8 +2,6 @@ {-# LANGUAGE DisambiguateRecordFields #-} {-# LANGUAGE GADTs #-} -{-# OPTIONS_GHC -Wno-incomplete-record-updates #-}- module GHC.Cmm.ProcPoint ( ProcPointSet, Status(..) , callProcPoints, minimalProcPointSet@@ -17,7 +15,6 @@ import GHC.Cmm.BlockId import GHC.Cmm.CLabel import GHC.Cmm-import GHC.Cmm.Ppr () -- For Outputable instances import GHC.Cmm.Utils import GHC.Cmm.Info import GHC.Cmm.Liveness
+ compiler/GHC/Cmm/Reducibility.hs view
@@ -0,0 +1,224 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE MultiParamTypeClasses #-}++{-|+Module : GHC.Cmm.Reducibility+Description : Tell if a `CmmGraph` is reducible, or make it so++Test a Cmm control-flow graph for reducibility. And provide a+function that, when given an arbitrary control-flow graph, returns an+equivalent, reducible control-flow graph. The equivalent graph is+obtained by "splitting" (copying) nodes of the original graph.+The resulting equivalent graph has the same dynamic behavior as the+original, but it is larger.++Documentation uses the language of control-flow analysis, in which a+basic block is called a "node." These "nodes" are `CmmBlock`s or+equivalent; they have nothing to do with a `CmmNode`.++For more on reducibility and related analyses and algorithms, see+Note [Reducibility resources]+-}++module GHC.Cmm.Reducibility+ ( Reducibility(..)+ , reducibility++ , asReducible+ )+where++import GHC.Prelude hiding (splitAt, succ)++import Control.Monad+import Data.List (nub)+import Data.Maybe+import Data.Semigroup+import qualified Data.Sequence as Seq++import GHC.Cmm+import GHC.Cmm.BlockId+import GHC.Cmm.Dataflow+import GHC.Cmm.Dataflow.Collections+import GHC.Cmm.Dataflow.Block+import GHC.Cmm.Dominators+import GHC.Cmm.Dataflow.Graph hiding (addBlock)+import GHC.Cmm.Dataflow.Label+import GHC.Data.Graph.Collapse+import GHC.Data.Graph.Inductive.Graph+import GHC.Data.Graph.Inductive.PatriciaTree+import GHC.Types.Unique.Supply+import GHC.Utils.Panic++-- | Represents the result of a reducibility analysis.+data Reducibility = Reducible | Irreducible+ deriving (Eq, Show)++-- | Given a graph, say whether the graph is reducible. The graph must+-- be bundled with a dominator analysis and a reverse postorder+-- numbering, as these results are needed to perform the test.++reducibility :: NonLocal node+ => GraphWithDominators node+ -> Reducibility+reducibility gwd =+ if all goodBlock blockmap then Reducible else Irreducible+ where goodBlock b = all (goodEdge (entryLabel b)) (successors b)+ goodEdge from to = rpnum to > rpnum from || to `dominates` from+ rpnum = gwdRPNumber gwd+ blockmap = graphMap $ gwd_graph gwd+ dominators = gwdDominatorsOf gwd+ dominates lbl blockname =+ lbl == blockname || dominatorsMember lbl (dominators blockname)++-- | Given a graph, return an equivalent reducible graph, by+-- "splitting" (copying) nodes if necessary. The input+-- graph must be bundled with a dominator analysis and a reverse+-- postorder numbering. The computation is monadic because when a+-- node is split, the new copy needs a fresh label.+--+-- Use this function whenever a downstream algorithm needs a reducible+-- control-flow graph.++asReducible :: GraphWithDominators CmmNode+ -> UniqSM (GraphWithDominators CmmNode)+asReducible gwd = case reducibility gwd of+ Reducible -> return gwd+ Irreducible -> assertReducible <$> nodeSplit gwd++assertReducible :: GraphWithDominators CmmNode -> GraphWithDominators CmmNode+assertReducible gwd = case reducibility gwd of+ Reducible -> gwd+ Irreducible -> panic "result not reducible"++----------------------------------------------------------------++-- | Split one or more nodes of the given graph, which must be+-- irreducible.++nodeSplit :: GraphWithDominators CmmNode+ -> UniqSM (GraphWithDominators CmmNode)+nodeSplit gwd =+ graphWithDominators <$> inflate (g_entry g) <$> runNullCollapse collapsed+ where g = gwd_graph gwd+ collapsed :: NullCollapseViz (Gr CmmSuper ())+ collapsed = collapseInductiveGraph (cgraphOfCmm g)++type CGraph = Gr CmmSuper ()++-- | Turn a collapsed supernode back into a control-flow graph+inflate :: Label -> CGraph -> CmmGraph+inflate entry cg = CmmGraph entry graph+ where graph = GMany NothingO body NothingO+ body :: LabelMap CmmBlock+ body = foldl (\map block -> mapInsert (entryLabel block) block map) mapEmpty $+ blocks super+ super = case labNodes cg of+ [(_, s)] -> s+ _ -> panic "graph given to `inflate` is not singleton"+++-- | Convert a `CmmGraph` into an inductive graph.+-- (The function coalesces duplicate edges into a single edge.)+cgraphOfCmm :: CmmGraph -> CGraph+cgraphOfCmm g = foldl' addSuccEdges (mkGraph cnodes []) blocks+ where blocks = zip [0..] $ revPostorderFrom (graphMap g) (g_entry g)+ cnodes = [(k, super block) | (k, block) <- blocks]+ where super block = Nodes (entryLabel block) (Seq.singleton block)+ labelNumber = \lbl -> fromJust $ mapLookup lbl numbers+ where numbers :: LabelMap Int+ numbers = mapFromList $ map swap blocks+ swap (k, block) = (entryLabel block, k)+ addSuccEdges :: CGraph -> (Node, CmmBlock) -> CGraph+ addSuccEdges graph (k, block) =+ insEdges [(k, labelNumber lbl, ()) | lbl <- nub $ successors block] graph+{-+Note [Reducibility resources]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++*Flow Analysis of Computer Programs.* Matthew S. Hecht North Holland, 1977.+Available to borrow from archive.org.++Matthew S. Hecht and Jeffrey D. Ullman (1972).+Flow Graph Reducibility. SIAM J. Comput., 1(2), 188–202.+https://doi.org/10.1137/0201014++Johan Janssen and Henk Corporaal. 1997. Making graphs reducible with+controlled node splitting. ACM TOPLAS 19, 6 (Nov. 1997),+1031–1052. DOI:https://doi.org/10.1145/267959.269971++Sebastian Unger and Frank Mueller. 2002. Handling irreducible loops:+optimized node splitting versus DJ-graphs. ACM TOPLAS 24, 4 (July+2002), 299–333. https://doi.org/10.1145/567097.567098. (This one+contains the most detailed account of how the Hecht/Ullman algorithm+is used to modify an actual control-flow graph. But still not much detail.)++https://rgrig.blogspot.com/2009/10/dtfloatleftclearleft-summary-of-some.html+ (Nice summary of useful facts)++-}++++type Seq = Seq.Seq++-- | A "supernode" contains a single-entry, multiple-exit, reducible subgraph.+-- The entry point is the given label, and the block with that label+-- dominates all the other blocks in the supernode. When an entire+-- graph is collapsed into a single supernode, the graph is reducible.+-- More detail can be found in "GHC.Data.Graph.Collapse".++data CmmSuper+ = Nodes { label :: Label+ , blocks :: Seq CmmBlock+ }++instance Semigroup CmmSuper where+ s <> s' = Nodes (label s) (blocks s <> blocks s')++instance PureSupernode CmmSuper where+ superLabel = label+ mapLabels = changeLabels++instance Supernode CmmSuper NullCollapseViz where+ freshen s = liftUniqSM $ relabel s+++-- | Return all labels defined within a supernode.+definedLabels :: CmmSuper -> Seq Label+definedLabels = fmap entryLabel . blocks++++-- | Map the given function over every use and definition of a label+-- in the given supernode.+changeLabels :: (Label -> Label) -> (CmmSuper -> CmmSuper)+changeLabels f (Nodes l blocks) = Nodes (f l) (fmap (changeBlockLabels f) blocks)++-- | Map the given function over every use and definition of a label+-- in the given block.+changeBlockLabels :: (Label -> Label) -> CmmBlock -> CmmBlock+changeBlockLabels f block = blockJoin entry' middle exit'+ where (entry, middle, exit) = blockSplit block+ entry' = let CmmEntry l scope = entry+ in CmmEntry (f l) scope+ exit' = case exit of+ -- unclear why mapSuccessors doesn't touch these+ CmmCall { cml_cont = Just l } -> exit { cml_cont = Just (f l) }+ CmmForeignCall { succ = l } -> exit { succ = f l }+ _ -> mapSuccessors f exit+++-- | Within the given supernode, replace every defined label (and all+-- of its uses) with a fresh label.++relabel :: CmmSuper -> UniqSM CmmSuper+relabel node = do+ finite_map <- foldM addPair mapEmpty $ definedLabels node+ return $ changeLabels (labelChanger finite_map) node+ where addPair :: LabelMap Label -> Label -> UniqSM (LabelMap Label)+ addPair map old = do new <- newBlockId+ return $ mapInsert old new map+ labelChanger :: LabelMap Label -> (Label -> Label)+ labelChanger mapping = \lbl -> mapFindWithDefault lbl lbl mapping
compiler/GHC/Cmm/Sink.hs view
@@ -268,7 +268,7 @@ all_succs = concatMap successors blocks succ_counts :: LabelMap Int- succ_counts = foldr (\l -> mapInsertWith (+) l 1) mapEmpty all_succs+ succ_counts = foldl' (\acc l -> mapInsertWith (+) l 1 acc) mapEmpty all_succs -- -- filter the list of assignments to remove any assignments that@@ -360,7 +360,7 @@ * The assignment `_c3 = [R1 + 1]`; (R1 already inlined on pickup) conflicts with R1 = R1, because it reads `R1` and the node writes to R1- * This is clearly no-sensical because `R1 = R1` doesn't affect R1's value.+ * This is clearly nonsensical because `R1 = R1` doesn't affect R1's value. The solutions is to check if we can discard nodes before and *after* simplifying them. We could only do it after as well, but I assume doing it early might save@@ -490,7 +490,7 @@ where (final_node, rest') = go usages live' node' (insertLRegSet l skipped) rest -- Avoid discarding of assignments to vars on the rhs.- -- See Note [Keeping assignemnts mentioned in skipped RHSs]+ -- See Note [Keeping assignments mentioned in skipped RHSs] -- usages' = foldLocalRegsUsed platform (\m r -> addToUFM m r 2) -- usages rhs live' = inline foldLocalRegsUsed platform (\m r -> insertLRegSet r m)@@ -518,7 +518,7 @@ inl_exp (CmmMachOp op args) = cmmMachOpFold platform op args inl_exp other = other -{- Note [Keeping assignemnts mentioned in skipped RHSs]+{- Note [Keeping assignments mentioned in skipped RHSs] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If we have to assignments: [z = y, y = e1] and we skip z we *must* retain the assignment y = e1. This is because
+ compiler/GHC/Cmm/ThreadSanitizer.hs view
@@ -0,0 +1,285 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE RankNTypes #-}++-- | Annotate a CmmGraph with ThreadSanitizer instrumentation calls.+module GHC.Cmm.ThreadSanitizer (annotateTSAN) where++import GHC.Prelude++import GHC.StgToCmm.Utils (get_GlobalReg_addr)+import GHC.Platform+import GHC.Platform.Regs (activeStgRegs, callerSaves)+import GHC.Cmm+import GHC.Cmm.Utils+import GHC.Cmm.CLabel+import GHC.Cmm.Dataflow+import GHC.Cmm.Dataflow.Block+import GHC.Cmm.Dataflow.Graph+import GHC.Data.FastString+import GHC.Types.Basic+import GHC.Types.ForeignCall+import GHC.Types.Unique+import GHC.Types.Unique.Supply++import Data.Maybe (fromMaybe)++data Env = Env { platform :: Platform+ , uniques :: [Unique]+ }++annotateTSAN :: Platform -> CmmGraph -> UniqSM CmmGraph+annotateTSAN platform graph = do+ env <- Env platform <$> getUniquesM+ return $ modifyGraph (mapGraphBlocks (annotateBlock env)) graph++mapBlockList :: (forall e' x'. n e' x' -> Block n e' x')+ -> Block n e x -> Block n e x+mapBlockList f (BlockCO n rest ) = f n `blockAppend` mapBlockList f rest+mapBlockList f (BlockCC n rest m) = f n `blockAppend` mapBlockList f rest `blockAppend` f m+mapBlockList f (BlockOC rest m) = mapBlockList f rest `blockAppend` f m+mapBlockList _ BNil = BNil+mapBlockList f (BMiddle blk) = f blk+mapBlockList f (BCat a b) = mapBlockList f a `blockAppend` mapBlockList f b+mapBlockList f (BSnoc a n) = mapBlockList f a `blockAppend` f n+mapBlockList f (BCons n a) = f n `blockAppend` mapBlockList f a++annotateBlock :: Env -> Block CmmNode e x -> Block CmmNode e x+annotateBlock env = mapBlockList (annotateNode env)++annotateNode :: Env -> CmmNode e x -> Block CmmNode e x+annotateNode env node =+ case node of+ CmmEntry{} -> BlockCO node BNil+ CmmComment{} -> BMiddle node+ CmmTick{} -> BMiddle node+ CmmUnwind{} -> BMiddle node+ CmmAssign{} -> annotateNodeOO env node+ CmmStore lhs rhs align ->+ let ty = cmmExprType (platform env) rhs+ rhs_nodes = annotateLoads env (collectExprLoads rhs)+ lhs_nodes = annotateLoads env (collectExprLoads lhs)+ st = tsanStore env align ty lhs+ in rhs_nodes `blockAppend` lhs_nodes `blockAppend` st `blockSnoc` node+ CmmUnsafeForeignCall (PrimTarget op) formals args ->+ let node' = fromMaybe (BMiddle node) (annotatePrim env op formals args)+ arg_nodes = blockConcat $ map (annotateExpr env) args+ in arg_nodes `blockAppend` node'+ CmmUnsafeForeignCall{} -> annotateNodeOO env node+ CmmBranch{} -> annotateNodeOC env node+ CmmCondBranch{} -> annotateNodeOC env node+ CmmSwitch{} -> annotateNodeOC env node+ CmmCall{} -> annotateNodeOC env node+ CmmForeignCall{} -> annotateNodeOC env node++annotateNodeOO :: Env -> CmmNode O O -> Block CmmNode O O+annotateNodeOO env node =+ annotateLoads env (collectLoadsNode node) `blockSnoc` node++annotateNodeOC :: Env -> CmmNode O C -> Block CmmNode O C+annotateNodeOC env node =+ annotateLoads env (collectLoadsNode node) `blockJoinTail` node++annotateExpr :: Env -> CmmExpr -> Block CmmNode O O+annotateExpr env expr =+ annotateLoads env (collectExprLoads expr)++data Load = Load CmmType AlignmentSpec CmmExpr++annotateLoads :: Env -> [Load] -> Block CmmNode O O+annotateLoads env loads =+ blockConcat+ [ tsanLoad env align ty addr+ | Load ty align addr <- loads+ ]++collectLoadsNode :: CmmNode e x -> [Load]+collectLoadsNode node =+ foldExp (\exp rest -> collectExprLoads exp ++ rest) node []++-- | Collect all of the memory locations loaded from by a 'CmmExpr'.+collectExprLoads :: CmmExpr -> [Load]+collectExprLoads (CmmLit _) = []+collectExprLoads (CmmLoad e ty align) = [Load ty align e]+collectExprLoads (CmmReg _) = []+collectExprLoads (CmmMachOp _op args) = foldMap collectExprLoads args+collectExprLoads (CmmStackSlot _ _) = []+collectExprLoads (CmmRegOff _ _) = []++-- | Generate TSAN instrumentation for a 'CallishMachOp' occurrence.+annotatePrim :: Env+ -> CallishMachOp -- ^ the applied operation+ -> [CmmFormal] -- ^ results+ -> [CmmActual] -- ^ arguments+ -> Maybe (Block CmmNode O O)+ -- ^ 'Just' a block of instrumentation, if applicable+annotatePrim env (MO_AtomicRMW w aop) [dest] [addr, val] = Just $ tsanAtomicRMW env MemOrderSeqCst aop w addr val dest+annotatePrim env (MO_AtomicRead w mord) [dest] [addr] = Just $ tsanAtomicLoad env mord w addr dest+annotatePrim env (MO_AtomicWrite w mord) [] [addr, val] = Just $ tsanAtomicStore env mord w val addr+annotatePrim env (MO_Xchg w) [dest] [addr, val] = Just $ tsanAtomicExchange env MemOrderSeqCst w val addr dest+annotatePrim env (MO_Cmpxchg w) [dest] [addr, expected, new]+ = Just $ tsanAtomicCas env MemOrderSeqCst MemOrderSeqCst w addr expected new dest+annotatePrim _ _ _ _ = Nothing++mkUnsafeCall :: Env+ -> ForeignTarget -- ^ function+ -> [CmmFormal] -- ^ results+ -> [CmmActual] -- ^ arguments+ -> Block CmmNode O O+mkUnsafeCall env ftgt formals args =+ save `blockAppend` -- save global registers+ bind_args `blockSnoc` -- bind arguments to local registers+ call `blockAppend` -- perform call+ restore -- restore global registers+ where+ -- We are rather conservative here and just save/restore all GlobalRegs.+ (save, restore) = saveRestoreCallerRegs (platform env)++ -- We also must be careful not to mention caller-saved registers in+ -- arguments as Cmm-Lint checks this. To accomplish this we instead bind+ -- the arguments to local registers.+ arg_regs :: [CmmReg]+ arg_regs = zipWith arg_reg (uniques env) args+ where+ arg_reg :: Unique -> CmmExpr -> CmmReg+ arg_reg u expr = CmmLocal $ LocalReg u (cmmExprType (platform env) expr)++ bind_args :: Block CmmNode O O+ bind_args = blockConcat $ zipWith (\r e -> BMiddle $ CmmAssign r e) arg_regs args++ call = CmmUnsafeForeignCall ftgt formals (map CmmReg arg_regs)++saveRestoreCallerRegs :: Platform+ -> (Block CmmNode O O, Block CmmNode O O)+saveRestoreCallerRegs platform =+ (save, restore)+ where+ regs = filter (callerSaves platform) (activeStgRegs platform)++ save = blockFromList (map saveReg regs)+ saveReg reg =+ CmmStore (get_GlobalReg_addr platform reg)+ (CmmReg (CmmGlobal reg))+ NaturallyAligned++ restore = blockFromList (map restoreReg regs)+ restoreReg reg =+ CmmAssign (CmmGlobal reg)+ (CmmLoad (get_GlobalReg_addr platform reg)+ (globalRegType platform reg)+ NaturallyAligned)++-- | Mirrors __tsan_memory_order+-- <https://github.com/llvm-mirror/compiler-rt/blob/master/include/sanitizer/tsan_interface_atomic.h#L32>+memoryOrderToTsanMemoryOrder :: Env -> MemoryOrdering -> CmmExpr+memoryOrderToTsanMemoryOrder env mord =+ mkIntExpr (platform env) n+ where+ n = case mord of+ MemOrderRelaxed -> 0+ MemOrderAcquire -> 2+ MemOrderRelease -> 3+ MemOrderSeqCst -> 5++tsanTarget :: FastString -- ^ function name+ -> [ForeignHint] -- ^ formals+ -> [ForeignHint] -- ^ arguments+ -> ForeignTarget+tsanTarget fn formals args =+ ForeignTarget (CmmLit (CmmLabel lbl)) conv+ where+ conv = ForeignConvention CCallConv args formals CmmMayReturn+ lbl = mkForeignLabel fn Nothing ForeignLabelInExternalPackage IsFunction++tsanStore :: Env+ -> AlignmentSpec -> CmmType -> CmmExpr+ -> Block CmmNode O O+tsanStore env align ty addr =+ mkUnsafeCall env ftarget [] [addr]+ where+ ftarget = tsanTarget fn [] [AddrHint]+ w = widthInBytes (typeWidth ty)+ fn = case align of+ Unaligned+ | w > 1 -> fsLit $ "__tsan_unaligned_write" ++ show w+ _ -> fsLit $ "__tsan_write" ++ show w++tsanLoad :: Env+ -> AlignmentSpec -> CmmType -> CmmExpr+ -> Block CmmNode O O+tsanLoad env align ty addr =+ mkUnsafeCall env ftarget [] [addr]+ where+ ftarget = tsanTarget fn [] [AddrHint]+ w = widthInBytes (typeWidth ty)+ fn = case align of+ Unaligned+ | w > 1 -> fsLit $ "__tsan_unaligned_read" ++ show w+ _ -> fsLit $ "__tsan_read" ++ show w++tsanAtomicStore :: Env+ -> MemoryOrdering -> Width -> CmmExpr -> CmmExpr+ -> Block CmmNode O O+tsanAtomicStore env mord w val addr =+ mkUnsafeCall env ftarget [] [addr, val, mord']+ where+ mord' = memoryOrderToTsanMemoryOrder env mord+ ftarget = tsanTarget fn [] [AddrHint, NoHint, NoHint]+ fn = fsLit $ "__tsan_atomic" ++ show (widthInBits w) ++ "_store"++tsanAtomicLoad :: Env+ -> MemoryOrdering -> Width -> CmmExpr -> LocalReg+ -> Block CmmNode O O+tsanAtomicLoad env mord w addr dest =+ mkUnsafeCall env ftarget [dest] [addr, mord']+ where+ mord' = memoryOrderToTsanMemoryOrder env mord+ ftarget = tsanTarget fn [NoHint] [AddrHint, NoHint]+ fn = fsLit $ "__tsan_atomic" ++ show (widthInBits w) ++ "_load"++tsanAtomicExchange :: Env+ -> MemoryOrdering -> Width -> CmmExpr -> CmmExpr -> LocalReg+ -> Block CmmNode O O+tsanAtomicExchange env mord w val addr dest =+ mkUnsafeCall env ftarget [dest] [addr, val, mord']+ where+ mord' = memoryOrderToTsanMemoryOrder env mord+ ftarget = tsanTarget fn [NoHint] [AddrHint, NoHint, NoHint]+ fn = fsLit $ "__tsan_atomic" ++ show (widthInBits w) ++ "_exchange"++-- N.B. C11 CAS returns a boolean (to avoid the ABA problem) whereas Cmm's CAS+-- returns the expected value. We use define a shim in the RTS to provide+-- Cmm's semantics using the TSAN C11 primitive.+tsanAtomicCas :: Env+ -> MemoryOrdering -- ^ success ordering+ -> MemoryOrdering -- ^ failure ordering+ -> Width+ -> CmmExpr -- ^ address+ -> CmmExpr -- ^ expected value+ -> CmmExpr -- ^ new value+ -> LocalReg -- ^ result destination+ -> Block CmmNode O O+tsanAtomicCas env mord_success mord_failure w addr expected new dest =+ mkUnsafeCall env ftarget [dest] [addr, expected, new, mord_success', mord_failure']+ where+ mord_success' = memoryOrderToTsanMemoryOrder env mord_success+ mord_failure' = memoryOrderToTsanMemoryOrder env mord_failure+ ftarget = tsanTarget fn [NoHint] [AddrHint, NoHint, NoHint, NoHint, NoHint]+ fn = fsLit $ "ghc_tsan_atomic" ++ show (widthInBits w) ++ "_compare_exchange"++tsanAtomicRMW :: Env+ -> MemoryOrdering -> AtomicMachOp -> Width -> CmmExpr -> CmmExpr -> LocalReg+ -> Block CmmNode O O+tsanAtomicRMW env mord op w addr val dest =+ mkUnsafeCall env ftarget [dest] [addr, val, mord']+ where+ mord' = memoryOrderToTsanMemoryOrder env mord+ ftarget = tsanTarget fn [NoHint] [AddrHint, NoHint, NoHint]+ op' = case op of+ AMO_Add -> "fetch_add"+ AMO_Sub -> "fetch_sub"+ AMO_And -> "fetch_and"+ AMO_Nand -> "fetch_nand"+ AMO_Or -> "fetch_or"+ AMO_Xor -> "fetch_xor"+ fn = fsLit $ "__tsan_atomic" ++ show (widthInBits w) ++ "_" ++ op'+
compiler/GHC/Cmm/Utils.hs view
@@ -42,8 +42,6 @@ cmmMkAssign, - isTrivialCmmExpr, hasNoGlobalRegs, isLit, isComparisonExpr,- baseExpr, spExpr, hpExpr, spLimExpr, hpLimExpr, currentTSOExpr, currentNurseryExpr, cccsExpr, @@ -61,9 +59,9 @@ modifyGraph, ofBlockMap, toBlockMap,- ofBlockList, toBlockList, bodyToBlockList,+ ofBlockList, toBlockList, toBlockListEntryFirst, toBlockListEntryFirstFalseFallthrough,- foldlGraphBlocks, mapGraphNodes, revPostorder, mapGraphNodes1,+ foldlGraphBlocks, mapGraphNodes, mapGraphNodes1, -- * Ticks blockTicks@@ -199,9 +197,9 @@ -- | We make a top-level decl for the embedded binary file, and return a label pointing to it mkFileEmbedLit- :: CLabel -> FilePath -> (CmmLit, GenCmmDecl (GenCmmStatics raw) info stmt)-mkFileEmbedLit lbl path- = (CmmLabel lbl, CmmData (Section ReadOnlyData lbl) (CmmStaticsRaw lbl [CmmFileEmbed path]))+ :: CLabel -> FilePath -> Int -> (CmmLit, GenCmmDecl (GenCmmStatics raw) info stmt)+mkFileEmbedLit lbl path len+ = (CmmLabel lbl, CmmData (Section ReadOnlyData lbl) (CmmStaticsRaw lbl [CmmFileEmbed path len])) -- | Build a data-segment data block@@ -401,36 +399,6 @@ --------------------------------------------------- ----- CmmExpr predicates---------------------------------------------------------isTrivialCmmExpr :: CmmExpr -> Bool-isTrivialCmmExpr (CmmLoad _ _ _) = False-isTrivialCmmExpr (CmmMachOp _ _) = False-isTrivialCmmExpr (CmmLit _) = True-isTrivialCmmExpr (CmmReg _) = True-isTrivialCmmExpr (CmmRegOff _ _) = True-isTrivialCmmExpr (CmmStackSlot _ _) = panic "isTrivialCmmExpr CmmStackSlot"--hasNoGlobalRegs :: CmmExpr -> Bool-hasNoGlobalRegs (CmmLoad e _ _) = hasNoGlobalRegs e-hasNoGlobalRegs (CmmMachOp _ es) = all hasNoGlobalRegs es-hasNoGlobalRegs (CmmLit _) = True-hasNoGlobalRegs (CmmReg (CmmLocal _)) = True-hasNoGlobalRegs (CmmRegOff (CmmLocal _) _) = True-hasNoGlobalRegs _ = False--isLit :: CmmExpr -> Bool-isLit (CmmLit _) = True-isLit _ = False--isComparisonExpr :: CmmExpr -> Bool-isComparisonExpr (CmmMachOp op _) = isComparisonMachOp op-isComparisonExpr _ = False-------------------------------------------------------- -- Tagging -- ---------------------------------------------------@@ -526,15 +494,9 @@ modifyGraph :: (Graph n C C -> Graph n' C C) -> GenCmmGraph n -> GenCmmGraph n' modifyGraph f g = CmmGraph {g_entry=g_entry g, g_graph=f (g_graph g)} -toBlockMap :: CmmGraph -> LabelMap CmmBlock-toBlockMap (CmmGraph {g_graph=GMany NothingO body NothingO}) = body- ofBlockMap :: BlockId -> LabelMap CmmBlock -> CmmGraph ofBlockMap entry bodyMap = CmmGraph {g_entry=entry, g_graph=GMany NothingO bodyMap NothingO} -toBlockList :: CmmGraph -> [CmmBlock]-toBlockList g = mapElems $ toBlockMap g- -- | like 'toBlockList', but the entry block always comes first toBlockListEntryFirst :: CmmGraph -> [CmmBlock] toBlockListEntryFirst g@@ -579,9 +541,6 @@ , g_graph = GMany NothingO body NothingO } where body = foldr addBlock emptyBody blocks -bodyToBlockList :: Body CmmNode -> [CmmBlock]-bodyToBlockList body = mapElems body- mapGraphNodes :: ( CmmNode C O -> CmmNode C O , CmmNode O O -> CmmNode O O , CmmNode O C -> CmmNode O C)@@ -596,10 +555,6 @@ foldlGraphBlocks :: (a -> CmmBlock -> a) -> a -> CmmGraph -> a foldlGraphBlocks k z g = mapFoldl k z $ toBlockMap g--revPostorder :: CmmGraph -> [CmmBlock]-revPostorder g = {-# SCC "revPostorder" #-}- revPostorderFrom (toBlockMap g) (g_entry g) ------------------------------------------------- -- Tick utilities
compiler/GHC/CmmToAsm.hs view
@@ -15,8 +15,6 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE UnboxedTuples #-} -{-# OPTIONS_GHC -Wno-incomplete-record-updates #-}- -- | Native code generator -- -- The native-code generator has machine-independent and@@ -78,11 +76,12 @@ ) where -import GHC.Prelude+import GHC.Prelude hiding (head) import qualified GHC.CmmToAsm.X86 as X86 import qualified GHC.CmmToAsm.PPC as PPC import qualified GHC.CmmToAsm.AArch64 as AArch64+import qualified GHC.CmmToAsm.Wasm as Wasm32 import GHC.CmmToAsm.Reg.Liveness import qualified GHC.CmmToAsm.Reg.Linear as Linear@@ -116,7 +115,6 @@ import GHC.Cmm.Dataflow.Label import GHC.Cmm.Dataflow.Block import GHC.Cmm.Opt ( cmmMachOpFold )-import GHC.Cmm.Ppr import GHC.Cmm.CLabel import GHC.Types.Unique.FM@@ -126,11 +124,9 @@ import GHC.Utils.Misc import GHC.Utils.Logger -import qualified GHC.Utils.Ppr as Pretty import GHC.Utils.BufHandle import GHC.Utils.Outputable as Outputable import GHC.Utils.Panic-import GHC.Utils.Panic.Plain ( massert ) import GHC.Utils.Error import GHC.Utils.Exception (evaluate) import GHC.Utils.Constants (debugIsOn)@@ -140,18 +136,21 @@ import GHC.Unit import GHC.Data.Stream (Stream) import qualified GHC.Data.Stream as Stream+import GHC.Settings -import Data.List (sortBy, groupBy)+import Data.List (sortBy)+import Data.List.NonEmpty (groupAllWith, head) import Data.Maybe import Data.Ord ( comparing ) import Control.Monad import System.IO+import System.Directory ( getCurrentDirectory ) ---------------------nativeCodeGen :: forall a . Logger -> NCGConfig -> ModLocation -> Handle -> UniqSupply+nativeCodeGen :: forall a . Logger -> ToolSettings -> NCGConfig -> ModLocation -> Handle -> UniqSupply -> Stream IO RawCmmGroup a -> IO a-nativeCodeGen logger config modLoc h us cmms+nativeCodeGen logger ts config modLoc h us cmms = let platform = ncgPlatform config nCG' :: ( OutputableP Platform statics, Outputable jumpDest, Instruction instr) => NcgImpl statics instr jumpDest -> IO a@@ -168,8 +167,10 @@ ArchMipseb -> panic "nativeCodeGen: No NCG for mipseb" ArchMipsel -> panic "nativeCodeGen: No NCG for mipsel" ArchRISCV64 -> panic "nativeCodeGen: No NCG for RISCV64"+ ArchLoongArch64->panic "nativeCodeGen: No NCG for LoongArch64" ArchUnknown -> panic "nativeCodeGen: No NCG for unknown arch" ArchJavaScript-> panic "nativeCodeGen: No NCG for JavaScript"+ ArchWasm32 -> Wasm32.ncgWasm platform ts us modLoc h cmms -- | Data accumulated during code generation. Mostly about statistics, -- but also collects debug data for DWARF generation.@@ -243,16 +244,17 @@ -> UniqSupply -> NativeGenAcc statics instr -> IO UniqSupply-finishNativeGen logger config modLoc bufh@(BufHandle _ _ h) us ngs+finishNativeGen logger config modLoc bufh us ngs = withTimingSilent logger (text "NCG") (`seq` ()) $ do -- Write debug data and finish us' <- if not (ncgDwarfEnabled config) then return us else do- (dwarf, us') <- dwarfGen config modLoc us (ngs_debug ngs)- emitNativeCode logger config bufh dwarf+ compPath <- getCurrentDirectory+ let (dwarf_h, us') = dwarfGen compPath config modLoc us (ngs_debug ngs)+ (dwarf_s, _) = dwarfGen compPath config modLoc us (ngs_debug ngs)+ emitNativeCode logger config bufh dwarf_h dwarf_s return us'- bFlush bufh -- dump global NCG stats for graph coloring allocator let stats = concat (ngs_colorStats ngs)@@ -285,8 +287,9 @@ -- write out the imports let ctx = ncgAsmContext config- printSDocLn ctx Pretty.LeftMode h- $ makeImportsDoc config (concat (ngs_imports ngs))+ bPutHDoc bufh ctx $ makeImportsDoc config (concat (ngs_imports ngs))+ bFlush bufh+ return us' where dump_stats = logDumpFile logger (mkDumpStyle alwaysQualify)@@ -333,7 +336,7 @@ dbgMap = debugToMap ndbgs -- Generate native code- (ngs',us') <- cmmNativeGens logger config modLoc ncgImpl h+ (ngs',us') <- cmmNativeGens logger config ncgImpl h dbgMap us cmms ngs 0 -- Link native code information into debug blocks@@ -357,7 +360,6 @@ (OutputableP Platform statics, Outputable jumpDest, Instruction instr) => Logger -> NCGConfig- -> ModLocation -> NcgImpl statics instr jumpDest -> BufHandle -> LabelMap DebugBlock@@ -367,7 +369,7 @@ -> Int -> IO (NativeGenAcc statics instr, UniqSupply) -cmmNativeGens logger config modLoc ncgImpl h dbgMap = go+cmmNativeGens logger config ncgImpl h dbgMap = go where go :: UniqSupply -> [RawCmmDecl] -> NativeGenAcc statics instr -> Int@@ -380,7 +382,7 @@ let fileIds = ngs_dwarfFiles ngs (us', fileIds', native, imports, colorStats, linearStats, unwinds) <- {-# SCC "cmmNativeGen" #-}- cmmNativeGen logger modLoc ncgImpl us fileIds dbgMap+ cmmNativeGen logger ncgImpl us fileIds dbgMap cmm count -- Generate .file directives for every new file that has been@@ -389,16 +391,21 @@ let newFileIds = sortBy (comparing snd) $ nonDetEltsUFM $ fileIds' `minusUFM` fileIds -- See Note [Unique Determinism and code generation]- pprDecl (f,n) = text "\t.file " <> ppr n <+>- pprFilePathString (unpackFS f)+ pprDecl (f,n) = line $ text "\t.file " <> int n <+>+ pprFilePathString (unpackFS f) - emitNativeCode logger config h $ vcat $- map pprDecl newFileIds ++- map (pprNatCmmDecl ncgImpl) native+ -- see Note [pprNatCmmDeclS and pprNatCmmDeclH] in GHC.CmmToAsm.Monad+ emitNativeCode logger config h+ (vcat $+ map pprDecl newFileIds +++ map (pprNatCmmDeclH ncgImpl) native)+ (vcat $+ map pprDecl newFileIds +++ map (pprNatCmmDeclS ncgImpl) native) -- force evaluation all this stuff to avoid space leaks let platform = ncgPlatform config- {-# SCC "seqString" #-} evaluate $ seqList (showSDocUnsafe $ vcat $ map (pdoc platform) imports) ()+ {-# SCC "seqString" #-} evaluate $ seqList (showSDocUnsafe $ vcat $ map (pprAsmLabel platform) imports) () let !labels' = if ncgDwarfEnabled config then cmmDebugLabels isMetaInstr native else []@@ -417,11 +424,11 @@ go us' cmms ngs' (count + 1) -emitNativeCode :: Logger -> NCGConfig -> BufHandle -> SDoc -> IO ()-emitNativeCode logger config h sdoc = do-+-- see Note [pprNatCmmDeclS and pprNatCmmDeclH] in GHC.CmmToAsm.Monad+emitNativeCode :: Logger -> NCGConfig -> BufHandle -> HDoc -> SDoc -> IO ()+emitNativeCode logger config h hdoc sdoc = do let ctx = ncgAsmContext config- {-# SCC "pprNativeCode" #-} bufLeftRenderSDoc ctx h sdoc+ {-# SCC "pprNativeCode" #-} bPutHDoc h ctx hdoc -- dump native code putDumpFileMaybe logger@@ -434,7 +441,6 @@ cmmNativeGen :: forall statics instr jumpDest. (Instruction instr, OutputableP Platform statics, Outputable jumpDest) => Logger- -> ModLocation -> NcgImpl statics instr jumpDest -> UniqSupply -> DwarfFiles@@ -450,14 +456,14 @@ , LabelMap [UnwindPoint] -- unwinding information for blocks ) -cmmNativeGen logger modLoc ncgImpl us fileIds dbgMap cmm count+cmmNativeGen logger ncgImpl us fileIds dbgMap cmm count = do let config = ncgConfig ncgImpl let platform = ncgPlatform config let weights = ncgCfgWeights config let proc_name = case cmm of- (CmmProc _ entry_label _ _) -> pdoc platform entry_label+ (CmmProc _ entry_label _ _) -> pprAsmLabel platform entry_label _ -> text "DataChunk" -- rewrite assignments to global regs@@ -480,13 +486,13 @@ -- generate native code from cmm let ((native, lastMinuteImports, fileIds', nativeCfgWeights), usGen) = {-# SCC "genMachCode" #-}- initUs us $ genMachCode config modLoc+ initUs us $ genMachCode config (cmmTopCodeGen ncgImpl) fileIds dbgMap opt_cmm cmmCfg putDumpFileMaybe logger Opt_D_dump_asm_native "Native code" FormatASM- (vcat $ map (pprNatCmmDecl ncgImpl) native)+ (vcat $ map (pprNatCmmDeclS ncgImpl) native) maybeDumpCfg logger (Just nativeCfgWeights) "CFG Weights - Native" proc_name @@ -543,7 +549,7 @@ putDumpFileMaybe logger Opt_D_dump_asm_regalloc "Registers allocated" FormatCMM- (vcat $ map (pprNatCmmDecl ncgImpl) alloced)+ (vcat $ map (pprNatCmmDeclS ncgImpl) alloced) putDumpFileMaybe logger Opt_D_dump_asm_regalloc_stages "Build/spill stages"@@ -587,7 +593,7 @@ putDumpFileMaybe logger Opt_D_dump_asm_regalloc "Registers allocated" FormatCMM- (vcat $ map (pprNatCmmDecl ncgImpl) alloced)+ (vcat $ map (pprNatCmmDeclS ncgImpl) alloced) let mPprStats = if logHasDumpFlag logger Opt_D_dump_asm_stats@@ -649,14 +655,13 @@ text "cfg not in lockstep") () ---- sequence blocks- -- sequenced :: [NatCmmDecl statics instr]- let (sequenced, us_seq) =- {-# SCC "sequenceBlocks" #-}- initUs usAlloc $ mapM (BlockLayout.sequenceTop- ncgImpl optimizedCFG)- shorted-- massert (checkLayout shorted sequenced)+ let sequenced :: [NatCmmDecl statics instr]+ sequenced =+ checkLayout shorted $+ {-# SCC "sequenceBlocks" #-}+ map (BlockLayout.sequenceTop+ ncgImpl optimizedCFG)+ shorted let branchOpt :: [NatCmmDecl statics instr] branchOpt =@@ -679,7 +684,7 @@ addUnwind acc proc = acc `mapUnion` computeUnwinding config ncgImpl proc - return ( us_seq+ return ( usAlloc , fileIds' , branchOpt , lastMinuteImports ++ imports@@ -699,10 +704,10 @@ -- | Make sure all blocks we want the layout algorithm to place have been placed. checkLayout :: [NatCmmDecl statics instr] -> [NatCmmDecl statics instr]- -> Bool+ -> [NatCmmDecl statics instr] checkLayout procsUnsequenced procsSequenced = assertPpr (setNull diff) (text "Block sequencing dropped blocks:" <> ppr diff)- True+ procsSequenced where blocks1 = foldl' (setUnion) setEmpty $ map getBlockIds procsUnsequenced :: LabelSet@@ -740,7 +745,7 @@ -- | Build a doc for all the imports. ---makeImportsDoc :: NCGConfig -> [CLabel] -> SDoc+makeImportsDoc :: NCGConfig -> [CLabel] -> HDoc makeImportsDoc config imports = dyld_stubs imports $$@@ -748,7 +753,7 @@ -- dead-stripping of code and data on a per-symbol basis. -- There's a hack to make this work in PprMach.pprNatCmmDecl. (if platformHasSubsectionsViaSymbols platform- then text ".subsections_via_symbols"+ then line $ text ".subsections_via_symbols" else Outputable.empty) $$ -- On recent GNU ELF systems one can mark an object file@@ -758,14 +763,14 @@ -- security. GHC generated code does not need an executable -- stack so add the note in: (if platformHasGnuNonexecStack platform- then text ".section .note.GNU-stack,\"\"," <> sectionType platform "progbits"+ then line $ text ".section .note.GNU-stack,\"\"," <> sectionType platform "progbits" else Outputable.empty) $$ -- And just because every other compiler does, let's stick in -- an identifier directive: .ident "GHC x.y.z" (if platformHasIdentDirective platform then let compilerIdent = text "GHC" <+> text cProjectVersion- in text ".ident" <+> doubleQuotes compilerIdent+ in line $ text ".ident" <+> doubleQuotes compilerIdent else Outputable.empty) where@@ -773,26 +778,23 @@ -- Generate "symbol stubs" for all external symbols that might -- come from a dynamic library.- dyld_stubs :: [CLabel] -> SDoc-{- dyld_stubs imps = vcat $ map pprDyldSymbolStub $- map head $ group $ sort imps-}+ dyld_stubs :: [CLabel] -> HDoc -- (Hack) sometimes two Labels pretty-print the same, but have -- different uniques; so we compare their text versions... dyld_stubs imps | needImportedSymbols config = vcat $ (pprGotDeclaration config :) $- map ( pprImportedSymbol config . fst . head) $- groupBy (\(_,a) (_,b) -> a == b) $- sortBy (\(_,a) (_,b) -> compare a b) $+ fmap (pprImportedSymbol config . fst . head) $+ groupAllWith snd $ map doPpr $ imps | otherwise = Outputable.empty - doPpr lbl = (lbl, renderWithContext+ doPpr lbl = (lbl, showSDocOneLine (ncgAsmContext config)- (pprCLabel platform AsmStyle lbl))+ (pprAsmLabel platform lbl)) -- ----------------------------------------------------------------------------- -- Generate jump tables@@ -805,7 +807,7 @@ generateJumpTables ncgImpl xs = concatMap f xs where f p@(CmmProc _ _ _ (ListGraph xs)) = p : concatMap g xs f p = [p]- g (BasicBlock _ xs) = catMaybes (map (generateJumpTableForInstr ncgImpl) xs)+ g (BasicBlock _ xs) = mapMaybe (generateJumpTableForInstr ncgImpl) xs -- ----------------------------------------------------------------------------- -- Shortcut branches@@ -920,7 +922,6 @@ genMachCode :: NCGConfig- -> ModLocation -> (RawCmmDecl -> NatM [NatCmmDecl statics instr]) -> DwarfFiles -> LabelMap DebugBlock@@ -933,10 +934,10 @@ , CFG ) -genMachCode config modLoc cmmTopCodeGen fileIds dbgMap cmm_top cmm_cfg+genMachCode config cmmTopCodeGen fileIds dbgMap cmm_top cmm_cfg = do { initial_us <- getUniqueSupplyM ; let initial_st = mkNatM_State initial_us 0 config- modLoc fileIds dbgMap cmm_cfg+ fileIds dbgMap cmm_cfg (new_tops, final_st) = initNat initial_st (cmmTopCodeGen cmm_top) final_delta = natm_delta final_st final_imports = natm_imports final_st
compiler/GHC/CmmToAsm/AArch64.hs view
@@ -1,6 +1,6 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} --- | Native code generator for x86 and x86-64 architectures+-- | Native code generator for AArch64 architectures module GHC.CmmToAsm.AArch64 ( ncgAArch64 ) where@@ -11,6 +11,7 @@ import GHC.CmmToAsm.Monad import GHC.CmmToAsm.Config import GHC.CmmToAsm.Types+import GHC.Utils.Outputable (ftext) import qualified GHC.CmmToAsm.AArch64.Instr as AArch64 import qualified GHC.CmmToAsm.AArch64.Ppr as AArch64@@ -28,13 +29,14 @@ ,canShortcut = AArch64.canShortcut ,shortcutStatics = AArch64.shortcutStatics ,shortcutJump = AArch64.shortcutJump- ,pprNatCmmDecl = AArch64.pprNatCmmDecl config+ ,pprNatCmmDeclS = AArch64.pprNatCmmDecl config+ ,pprNatCmmDeclH = AArch64.pprNatCmmDecl config ,maxSpillSlots = AArch64.maxSpillSlots config ,allocatableRegs = AArch64.allocatableRegs platform ,ncgAllocMoreStack = AArch64.allocMoreStack platform- ,ncgMakeFarBranches = AArch64.makeFarBranches+ ,ncgMakeFarBranches = const id ,extractUnwindPoints = const []- ,invertCondBranches = \_ _ blocks -> blocks+ ,invertCondBranches = \_ _ -> id } where platform = ncgPlatform config@@ -55,5 +57,5 @@ mkJumpInstr = AArch64.mkJumpInstr mkStackAllocInstr = AArch64.mkStackAllocInstr mkStackDeallocInstr = AArch64.mkStackDeallocInstr- mkComment = pure . AArch64.COMMENT+ mkComment = pure . AArch64.COMMENT . ftext pprInstr = AArch64.pprInstr
compiler/GHC/CmmToAsm/AArch64/CodeGen.hs view
@@ -3,10 +3,10 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE BinaryLiterals #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE NumericUnderscores #-} module GHC.CmmToAsm.AArch64.CodeGen ( cmmTopCodeGen , generateJumpTableForInstr- , makeFarBranches ) where@@ -43,11 +43,9 @@ import GHC.Cmm.Switch import GHC.Cmm.CLabel import GHC.Cmm.Dataflow.Block-import GHC.Cmm.Dataflow.Label import GHC.Cmm.Dataflow.Graph import GHC.Types.Tickish ( GenTickish(..) ) import GHC.Types.SrcLoc ( srcSpanFile, srcSpanStartLine, srcSpanStartCol )-import GHC.Types.Unique.Supply -- The rest: import GHC.Data.OrdList@@ -62,9 +60,7 @@ import GHC.Data.FastString import GHC.Utils.Misc import GHC.Utils.Panic-import GHC.Utils.Monad (mapAccumLM)--import GHC.Cmm.Dataflow.Collections+import GHC.Utils.Constants (debugIsOn) -- Note [General layout of an NCG] -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -140,10 +136,11 @@ id = entryLabel block stmts = blockToList nodes - header_comment_instr = unitOL $ MULTILINE_COMMENT (+ header_comment_instr | debugIsOn = unitOL $ MULTILINE_COMMENT ( text "-- --------------------------- basicBlockCodeGen --------------------------- --\n"- $+$ pdoc (ncgPlatform config) block+ $+$ withPprStyle defaultDumpStyle (pdoc (ncgPlatform config) block) )+ | otherwise = nilOL -- Generate location directive dbg <- getDebugBlock (entryLabel block) loc_instrs <- case dblSourceTick =<< dbg of@@ -164,17 +161,15 @@ let (top,other_blocks,statics) = foldrOL mkBlocks ([],[],[]) instrs + mkBlocks (NEWBLOCK id) (instrs,blocks,statics)+ = ([], BasicBlock id instrs : blocks, statics)+ mkBlocks (LDATA sec dat) (instrs,blocks,statics)+ = (instrs, blocks, CmmData sec dat:statics)+ mkBlocks instr (instrs,blocks,statics)+ = (instr:instrs, blocks, statics) return (BasicBlock id top : other_blocks, statics) -mkBlocks :: Instr- -> ([Instr], [GenBasicBlock Instr], [GenCmmDecl RawCmmStatics h g])- -> ([Instr], [GenBasicBlock Instr], [GenCmmDecl RawCmmStatics h g])-mkBlocks (NEWBLOCK id) (instrs,blocks,statics)- = ([], BasicBlock id instrs : blocks, statics)-mkBlocks (LDATA sec dat) (instrs,blocks,statics)- = (instrs, blocks, CmmData sec dat:statics)-mkBlocks instr (instrs,blocks,statics)- = (instr:instrs, blocks, statics)+ -- ----------------------------------------------------------------------------- -- | Utilities ann :: SDoc -> Instr -> Instr@@ -184,13 +179,13 @@ -- Using pprExpr will hide the AST, @ANN@ will end up in the assembly with -- -dppr-debug. The idea is that we can trivially see how a cmm expression--- ended up producing the assmebly we see. By having the verbatim AST printed--- we can simply check the patterns that were matched to arrive at the assmebly+-- ended up producing the assembly we see. By having the verbatim AST printed+-- we can simply check the patterns that were matched to arrive at the assembly -- we generated. -- -- pprExpr will hide a lot of noise of the underlying data structure and print -- the expression into something that can be easily read by a human. However--- going back to the exact CmmExpr representation can be labourous and adds+-- going back to the exact CmmExpr representation can be laborious and adds -- indirections to find the matches that lead to the assembly. -- -- An improvement oculd be to have@@ -701,16 +696,12 @@ -- 1. Compute Reg +/- n directly. -- For Add/Sub we can directly encode 12bits, or 12bits lsl #12. CmmMachOp (MO_Add w) [(CmmReg reg), CmmLit (CmmInt n _)]- | n > 0 && n < 4096- , w == W32 || w == W64 -- Work around #23749- -> return $ Any (intFormat w) (\d -> unitOL $ annExpr expr (ADD (OpReg w d) (OpReg w' r') (OpImm (ImmInteger n))))+ | n > 0 && n < 4096 -> return $ Any (intFormat w) (\d -> unitOL $ annExpr expr (ADD (OpReg w d) (OpReg w' r') (OpImm (ImmInteger n)))) -- TODO: 12bits lsl #12; e.g. lower 12 bits of n are 0; shift n >> 12, and set lsl to #12. where w' = formatToWidth (cmmTypeFormat (cmmRegType plat reg)) r' = getRegisterReg plat reg CmmMachOp (MO_Sub w) [(CmmReg reg), CmmLit (CmmInt n _)]- | n > 0 && n < 4096- , w == W32 || w == W64 -- Work around #23749- -> return $ Any (intFormat w) (\d -> unitOL $ annExpr expr (SUB (OpReg w d) (OpReg w' r') (OpImm (ImmInteger n))))+ | n > 0 && n < 4096 -> return $ Any (intFormat w) (\d -> unitOL $ annExpr expr (SUB (OpReg w d) (OpReg w' r') (OpImm (ImmInteger n)))) -- TODO: 12bits lsl #12; e.g. lower 12 bits of n are 0; shift n >> 12, and set lsl to #12. where w' = formatToWidth (cmmTypeFormat (cmmRegType plat reg)) r' = getRegisterReg plat reg@@ -782,12 +773,12 @@ return $ Any (intFormat w) (\dst -> code_x `snocOL` annExpr expr (LSR (OpReg w dst) (OpReg w reg_x) (OpImm (ImmInteger n)))) -- 3. Logic &&, ||- CmmMachOp (MO_And w) [(CmmReg reg), CmmLit (CmmInt n _)] | isAArch64Bitmask (fromIntegral n) ->+ CmmMachOp (MO_And w) [(CmmReg reg), CmmLit (CmmInt n _)] | isBitMaskImmediate (fromIntegral n) -> return $ Any (intFormat w) (\d -> unitOL $ annExpr expr (AND (OpReg w d) (OpReg w' r') (OpImm (ImmInteger n)))) where w' = formatToWidth (cmmTypeFormat (cmmRegType plat reg)) r' = getRegisterReg plat reg - CmmMachOp (MO_Or w) [(CmmReg reg), CmmLit (CmmInt n _)] | isAArch64Bitmask (fromIntegral n) ->+ CmmMachOp (MO_Or w) [(CmmReg reg), CmmLit (CmmInt n _)] | isBitMaskImmediate (fromIntegral n) -> return $ Any (intFormat w) (\d -> unitOL $ annExpr expr (ORR (OpReg w d) (OpReg w' r') (OpImm (ImmInteger n)))) where w' = formatToWidth (cmmTypeFormat (cmmRegType plat reg)) r' = getRegisterReg plat reg@@ -919,7 +910,6 @@ intOp True w (\d x y -> toOL [ SDIV t x y, MSUB d t y x ]) -- Unsigned multiply/divide- MO_U_MulMayOflo _w -> unsupportedP plat expr MO_U_Quot w -> intOp False w (\d x y -> unitOL $ UDIV d x y) MO_U_Rem w -> withTempIntReg w $ \t -> intOp False w (\d x y -> toOL [ UDIV t x y, MSUB d t y x ])@@ -949,7 +939,7 @@ -- careful with the floating point operations. -- SLE is effectively LE or unordered (NaN) -- SLT is the same. ULE, and ULT will not return true for NaN.- -- This is a bit counter intutive. Don't let yourself be fooled by+ -- This is a bit counter-intuitive. Don't let yourself be fooled by -- the S/U prefix for floats, it's only meaningful for integers. MO_F_Ge w -> floatCond w (\d x y -> toOL [ CMP x y, CSET d OGE ]) MO_F_Le w -> floatCond w (\d x y -> toOL [ CMP x y, CSET d OLE ]) -- x <= y <=> y > x@@ -971,11 +961,21 @@ -> pprPanic "getRegister' (variadic CmmMachOp): " (pdoc plat expr) where- unsupportedP :: OutputableP env a => env -> a -> b- unsupportedP platform op = pprPanic "Unsupported op:" (pdoc platform op)- isNbitEncodeable :: Int -> Integer -> Bool isNbitEncodeable n i = let shift = n - 1 in (-1 `shiftL` shift) <= i && i < (1 `shiftL` shift)+ -- This needs to check if n can be encoded as a bitmask immediate:+ --+ -- See https://stackoverflow.com/questions/30904718/range-of-immediate-values-in-armv8-a64-assembly+ --+ isBitMaskImmediate :: Integer -> Bool+ isBitMaskImmediate i = i `elem` [0b0000_0001, 0b0000_0010, 0b0000_0100, 0b0000_1000, 0b0001_0000, 0b0010_0000, 0b0100_0000, 0b1000_0000+ ,0b0000_0011, 0b0000_0110, 0b0000_1100, 0b0001_1000, 0b0011_0000, 0b0110_0000, 0b1100_0000+ ,0b0000_0111, 0b0000_1110, 0b0001_1100, 0b0011_1000, 0b0111_0000, 0b1110_0000+ ,0b0000_1111, 0b0001_1110, 0b0011_1100, 0b0111_1000, 0b1111_0000+ ,0b0001_1111, 0b0011_1110, 0b0111_1100, 0b1111_1000+ ,0b0011_1111, 0b0111_1110, 0b1111_1100+ ,0b0111_1111, 0b1111_1110+ ,0b1111_1111] -- N.B. MUL does not set the overflow flag. do_mul_may_oflo :: Width -> CmmExpr -> CmmExpr -> NatM Register@@ -1018,39 +1018,6 @@ CMP (OpReg tmp_w tmp) (OpRegExt tmp_w tmp ext_mode 0) `snocOL` CSET (OpReg w dst) NE) --- | Is a given number encodable as a bitmask immediate?------ https://stackoverflow.com/questions/30904718/range-of-immediate-values-in-armv8-a64-assembly-isAArch64Bitmask :: Integer -> Bool--- N.B. zero and ~0 are not encodable as bitmask immediates-isAArch64Bitmask 0 = False-isAArch64Bitmask n- | n == bit 64 - 1 = False-isAArch64Bitmask n =- check 64 || check 32 || check 16 || check 8- where- -- Check whether @n@ can be represented as a subpattern of the given- -- width.- check width- | hasOneRun subpat =- let n' = fromIntegral (mkPat width subpat)- in n == n'- | otherwise = False- where- subpat :: Word64- subpat = fromIntegral (n .&. (bit width - 1))-- -- Construct a bit-pattern from a repeated subpatterns the given width.- mkPat :: Int -> Word64 -> Word64- mkPat width subpat =- foldl' (.|.) 0 [ subpat `shiftL` p | p <- [0, width..63] ]-- -- Does the given number's bit representation match the regular expression- -- @0*1*0*@?- hasOneRun :: Word64 -> Bool- hasOneRun m =- 64 == popCount m + countLeadingZeros m + countTrailingZeros m- -- | Instructions to sign-extend the value in the given register from width @w@ -- up to width @w'@. signExtendReg :: Width -> Width -> Reg -> NatM (Reg, OrdList Instr)@@ -1176,7 +1143,6 @@ -- ----------------------------------------------------------------------------- -- Jumps- genJump :: CmmExpr{-the branch target-} -> NatM InstrBlock genJump expr@(CmmLit (CmmLabel lbl)) = return $ unitOL (annExpr expr (J (TLabel lbl)))@@ -1262,23 +1228,7 @@ _ -> pprPanic "AArch64.genCondJump:case mop: " (text $ show expr) _ -> pprPanic "AArch64.genCondJump: " (text $ show expr) --- A conditional jump with at least +/-128M jump range-genCondFarJump :: MonadUnique m => Cond -> Target -> m InstrBlock-genCondFarJump cond far_target = do- skip_lbl_id <- newBlockId- jmp_lbl_id <- newBlockId - -- TODO: We can improve this by inverting the condition- -- but it's not quite trivial since we don't know if we- -- need to consider float orderings.- -- So we take the hit of the additional jump in the false- -- case for now.- return $ toOL [ BCOND cond (TBlock jmp_lbl_id)- , B (TBlock skip_lbl_id)- , NEWBLOCK jmp_lbl_id- , B far_target- , NEWBLOCK skip_lbl_id]- genCondBranch :: BlockId -- the source of the jump -> BlockId -- the true branch target@@ -1582,9 +1532,34 @@ MO_BRev w -> mkCCall (bRevLabel w) -- -- Atomic read-modify-write.+ MO_AtomicRead w ord+ | [p_reg] <- arg_regs+ , [dst_reg] <- dest_regs -> do+ (p, _fmt_p, code_p) <- getSomeReg p_reg+ platform <- getPlatform+ let instr = case ord of+ MemOrderRelaxed -> LDR+ _ -> LDAR+ dst = getRegisterReg platform (CmmLocal dst_reg)+ code =+ code_p `snocOL`+ instr (intFormat w) (OpReg w dst) (OpAddr $ AddrReg p)+ return (code, Nothing)+ | otherwise -> panic "mal-formed AtomicRead"+ MO_AtomicWrite w ord+ | [p_reg, val_reg] <- arg_regs -> do+ (p, _fmt_p, code_p) <- getSomeReg p_reg+ (val, fmt_val, code_val) <- getSomeReg val_reg+ let instr = case ord of+ MemOrderRelaxed -> STR+ _ -> STLR+ code =+ code_p `appOL`+ code_val `snocOL`+ instr fmt_val (OpReg w val) (OpAddr $ AddrReg p)+ return (code, Nothing)+ | otherwise -> panic "mal-formed AtomicWrite" MO_AtomicRMW w amop -> mkCCall (atomicRMWLabel w amop)- MO_AtomicRead w _ -> mkCCall (atomicReadLabel w)- MO_AtomicWrite w _ -> mkCCall (atomicWriteLabel w) MO_Cmpxchg w -> mkCCall (cmpxchgLabel w) -- -- Should be an AtomicRMW variant eventually. -- -- Sequential consistent.@@ -1604,7 +1579,7 @@ genCCall (ForeignTarget target cconv) dest_regs arg_regs bid -- TODO: Optimize using paired stores and loads (STP, LDP). It is- -- automomatically done by the allocator for us. However it's not optimal,+ -- automatically done by the allocator for us. However it's not optimal, -- as we'd rather want to have control over -- all spill/load registers, so we can optimize with instructions like -- STP xA, xB, [sp, #-16]!@@ -1768,163 +1743,3 @@ let dst = getRegisterReg platform (CmmLocal dest_reg) let code = code_fx `appOL` op (OpReg w dst) (OpReg w reg_fx) return (code, Nothing)--{- Note [AArch64 far jumps]-~~~~~~~~~~~~~~~~~~~~~~~~~~~-AArch conditional jump instructions can only encode an offset of +/-1MB-which is usually enough but can be exceeded in edge cases. In these cases-we will replace:-- b.cond <cond> foo--with the sequence:-- b.cond <cond> <lbl_true>- b <lbl_false>- <lbl_true>:- b foo- <lbl_false>:--Note the encoding of the `b` instruction still limits jumps to-+/-128M offsets, but that seems like an acceptable limitation.--Since AArch64 instructions are all of equal length we can reasonably estimate jumps-in range by counting the instructions between a jump and its target label.--We make some simplifications in the name of performance which can result in overestimating-jump <-> label offsets:--* To avoid having to recalculate the label offsets once we replaced a jump we simply- assume all jumps will be expanded to a three instruction far jump sequence.-* For labels associated with a info table we assume the info table is 64byte large.- Most info tables are smaller than that but it means we don't have to distinguish- between multiple types of info tables.--In terms of implementation we walk the instruction stream at least once calculating-label offsets, and if we determine during this that the functions body is big enough-to potentially contain out of range jumps we walk the instructions a second time, replacing-out of range jumps with the sequence of instructions described above.---}---- See Note [AArch64 far jumps]-data BlockInRange = InRange | NotInRange Target---- See Note [AArch64 far jumps]-makeFarBranches :: Platform -> LabelMap RawCmmStatics -> [NatBasicBlock Instr]- -> UniqSM [NatBasicBlock Instr]-makeFarBranches {- only used when debugging -} _platform statics basic_blocks = do- -- All offsets/positions are counted in multiples of 4 bytes (the size of AArch64 instructions)- -- That is an offset of 1 represents a 4-byte/one instruction offset.- let (func_size, lblMap) = foldl' calc_lbl_positions (0, mapEmpty) basic_blocks- if func_size < max_jump_dist- then pure basic_blocks- else do- (_,blocks) <- mapAccumLM (replace_blk lblMap) 0 basic_blocks- pure $ concat blocks- -- pprTrace "lblMap" (ppr lblMap) $ basic_blocks-- where- -- 2^18, 19 bit immediate with one bit is reserved for the sign- max_jump_dist = 2^(18::Int) - 1 :: Int- -- Currently all inline info tables fit into 64 bytes.- max_info_size = 16 :: Int- long_bc_jump_size = 3 :: Int- long_bz_jump_size = 4 :: Int-- -- Replace out of range conditional jumps with unconditional jumps.- replace_blk :: LabelMap Int -> Int -> GenBasicBlock Instr -> UniqSM (Int, [GenBasicBlock Instr])- replace_blk !m !pos (BasicBlock lbl instrs) = do- -- Account for a potential info table before the label.- let !block_pos = pos + infoTblSize_maybe lbl- (!pos', instrs') <- mapAccumLM (replace_jump m) block_pos instrs- let instrs'' = concat instrs'- -- We might have introduced new labels, so split the instructions into basic blocks again if neccesary.- let (top, split_blocks, no_data) = foldr mkBlocks ([],[],[]) instrs''- -- There should be no data in the instruction stream at this point- massertPpr (null no_data) $ text "There should be no data in the instruction stream"-- let final_blocks = BasicBlock lbl top : split_blocks- pure (pos', final_blocks)-- replace_jump :: LabelMap Int -> Int -> Instr -> UniqSM (Int, [Instr])- replace_jump !m !pos instr = do- case instr of- ANN ann instr -> do- (idx,instr':instrs') <- replace_jump m pos instr- pure (idx, ANN ann instr':instrs')- BCOND cond t- -> case target_in_range m t pos of- InRange -> pure (pos+long_bc_jump_size,[instr])- NotInRange far_target -> do- jmp_code <- genCondFarJump cond far_target- pure (pos+long_bc_jump_size, fromOL jmp_code)- CBZ op t -> long_zero_jump op t EQ- CBNZ op t -> long_zero_jump op t NE- instr- | isMetaInstr instr -> pure (pos,[instr])- | otherwise -> pure (pos+1, [instr])-- where- -- cmp_op: EQ = CBZ, NEQ = CBNZ- long_zero_jump op t cmp_op =- case target_in_range m t pos of- InRange -> pure (pos+long_bz_jump_size,[instr])- NotInRange far_target -> do- jmp_code <- genCondFarJump cmp_op far_target- -- TODO: Fix zero reg so we can use it here- pure (pos + long_bz_jump_size, CMP op (OpImm (ImmInt 0)) : fromOL jmp_code)--- target_in_range :: LabelMap Int -> Target -> Int -> BlockInRange- target_in_range m target src =- case target of- (TReg{}) -> InRange- (TBlock bid) -> block_in_range m src bid- (TLabel clbl)- | Just bid <- maybeLocalBlockLabel clbl- -> block_in_range m src bid- | otherwise- -- Maybe we should be pessimistic here, for now just fixing intra proc jumps- -> InRange-- block_in_range :: LabelMap Int -> Int -> BlockId -> BlockInRange- block_in_range m src_pos dest_lbl =- case mapLookup dest_lbl m of- Nothing ->- -- pprTrace "not in range" (ppr dest_lbl) $- NotInRange (TBlock dest_lbl)- Just dest_pos -> if abs (dest_pos - src_pos) < max_jump_dist- then InRange- else NotInRange (TBlock dest_lbl)-- calc_lbl_positions :: (Int, LabelMap Int) -> GenBasicBlock Instr -> (Int, LabelMap Int)- calc_lbl_positions (pos, m) (BasicBlock lbl instrs)- = let !pos' = pos + infoTblSize_maybe lbl- in foldl' instr_pos (pos',mapInsert lbl pos' m) instrs-- instr_pos :: (Int, LabelMap Int) -> Instr -> (Int, LabelMap Int)- instr_pos (pos, m) instr =- case instr of- ANN _ann instr -> instr_pos (pos, m) instr- NEWBLOCK _bid -> panic "mkFarBranched - unexpected NEWBLOCK" -- At this point there should be no NEWBLOCK- -- in the instruction stream- -- (pos, mapInsert bid pos m)- COMMENT{} -> (pos, m)- instr- | Just jump_size <- is_expandable_jump instr -> (pos+jump_size, m)- | otherwise -> (pos+1, m)-- infoTblSize_maybe bid =- case mapLookup bid statics of- Nothing -> 0 :: Int- Just _info_static -> max_info_size-- -- These jumps have a 19bit immediate as offset which is quite- -- limiting so we potentially have to expand them into- -- multiple instructions.- is_expandable_jump i = case i of- CBZ{} -> Just long_bz_jump_size- CBNZ{} -> Just long_bz_jump_size- BCOND{} -> Just long_bc_jump_size- _ -> Nothing
compiler/GHC/CmmToAsm/AArch64/Cond.hs view
@@ -1,6 +1,6 @@ module GHC.CmmToAsm.AArch64.Cond where -import GHC.Prelude hiding (EQ)+import GHC.Prelude -- https://developer.arm.com/documentation/den0024/a/the-a64-instruction-set/data-processing-instructions/conditional-instructions @@ -60,13 +60,7 @@ | UOGE -- b.pl | UOGT -- b.hi -- others- -- NEVER -- b.nv- -- I removed never. According to the ARM spec:- -- > The Condition code NV exists only to provide a valid disassembly of- -- > the 0b1111 encoding, otherwise its behavior is identical to AL.- -- This can only lead to disaster. Better to not have it than someone- -- using it assuming it actually means never.-+ | NEVER -- b.nv | VS -- oVerflow set | VC -- oVerflow clear deriving Eq
compiler/GHC/CmmToAsm/AArch64/Instr.hs view
@@ -128,7 +128,9 @@ CBNZ src _ -> usage (regOp src, []) -- 7. Load and Store Instructions -------------------------------------------- STR _ src dst -> usage (regOp src ++ regOp dst, [])+ STLR _ src dst -> usage (regOp src ++ regOp dst, []) LDR _ dst src -> usage (regOp src, regOp dst)+ LDAR _ dst src -> usage (regOp src, regOp dst) -- TODO is this right? see STR, which I'm only partial about being right? STP _ src1 src2 dst -> usage (regOp src1 ++ regOp src2 ++ regOp dst, []) LDP _ dst1 dst2 src -> usage (regOp src, regOp dst1 ++ regOp dst2)@@ -265,7 +267,9 @@ CBNZ o l -> CBNZ (patchOp o) l -- 7. Load and Store Instructions ------------------------------------------ STR f o1 o2 -> STR f (patchOp o1) (patchOp o2)+ STLR f o1 o2 -> STLR f (patchOp o1) (patchOp o2) LDR f o1 o2 -> LDR f (patchOp o1) (patchOp o2)+ LDAR f o1 o2 -> LDAR f (patchOp o1) (patchOp o2) STP f o1 o2 o3 -> STP f (patchOp o1) (patchOp o2) (patchOp o3) LDP f o1 o2 o3 -> LDP f (patchOp o1) (patchOp o2) (patchOp o3) @@ -348,8 +352,8 @@ -- -- Using sp to compute the offset will violate assumptions about the stack pointer -- pointing to the top of the stack during signal handling. As we can't force--- every signal to use its own stack, we have to ensure that the stack poitner--- always poitns to the top of the stack, and we can't use it for computation.+-- every signal to use its own stack, we have to ensure that the stack pointer+-- always points to the top of the stack, and we can't use it for computation. -- -- | An instruction to spill a register into a spill slot. mkSpillInstr@@ -551,7 +555,7 @@ | PUSH_STACK_FRAME | POP_STACK_FRAME -- 1. Arithmetic Instructions ----------------------------------------------- -- | ADC Operand Operand Operang -- rd = rn + rm + C+ -- | ADC Operand Operand Operand -- rd = rn + rm + C -- | ADCS ... | ADD Operand Operand Operand -- rd = rn + rm -- | ADDS Operand Operand Operand -- rd = rn + rm@@ -618,7 +622,9 @@ -- Load and stores. -- TODO STR/LDR might want to change to STP/LDP with XZR for the second register. | STR Format Operand Operand -- str Xn, address-mode // Xn -> *addr+ | STLR Format Operand Operand -- stlr Xn, address-mode // Xn -> *addr | LDR Format Operand Operand -- ldr Xn, address-mode // Xn <- *addr+ | LDAR Format Operand Operand -- ldar Xn, address-mode // Xn <- *addr | STP Format Operand Operand Operand -- stp Xn, Xm, address-mode // Xn -> *addr, Xm -> *(addr + 8) | LDP Format Operand Operand Operand -- stp Xn, Xm, address-mode // Xn <- *addr, Xm <- *(addr + 8) @@ -693,7 +699,9 @@ ROR{} -> "ROR" TST{} -> "TST" STR{} -> "STR"+ STLR{} -> "STLR" LDR{} -> "LDR"+ LDAR{} -> "LDAR" STP{} -> "STP" LDP{} -> "LDP" CSET{} -> "CSET"@@ -713,7 +721,6 @@ = TBlock BlockId | TLabel CLabel | TReg Reg- deriving (Eq, Ord) -- Extension
compiler/GHC/CmmToAsm/AArch64/Ppr.hs view
@@ -1,7 +1,7 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE CPP #-} -module GHC.CmmToAsm.AArch64.Ppr (pprNatCmmDecl, pprInstr, pprBasicBlock) where+module GHC.CmmToAsm.AArch64.Ppr (pprNatCmmDecl, pprInstr) where import GHC.Prelude hiding (EQ) @@ -22,7 +22,6 @@ import GHC.Cmm.BlockId import GHC.Cmm.CLabel-import GHC.Cmm.Ppr.Expr () -- For Outputable instances import GHC.Types.Unique ( pprUniqueAlways, getUnique ) import GHC.Platform@@ -30,12 +29,12 @@ import GHC.Utils.Panic -pprProcAlignment :: NCGConfig -> SDoc+pprProcAlignment :: IsDoc doc => NCGConfig -> doc pprProcAlignment config = maybe empty (pprAlign platform . mkAlignment) (ncgProcAlignment config) where platform = ncgPlatform config -pprNatCmmDecl :: NCGConfig -> NatCmmDecl RawCmmStatics Instr -> SDoc+pprNatCmmDecl :: IsDoc doc => NCGConfig -> NatCmmDecl RawCmmStatics Instr -> doc pprNatCmmDecl config (CmmData section dats) = pprSectionAlign config section $$ pprDatas config dats @@ -51,45 +50,45 @@ pprLabel platform lbl $$ -- blocks guaranteed not null, so label needed vcat (map (pprBasicBlock config top_info) blocks) $$ (if ncgDwarfEnabled config- then ppr (mkAsmTempEndLabel lbl) <> char ':' else empty) $$+ then line (pprAsmLabel platform (mkAsmTempEndLabel lbl) <> char ':') else empty) $$ pprSizeDecl platform lbl Just (CmmStaticsRaw info_lbl _) -> pprSectionAlign config (Section Text info_lbl) $$ -- pprProcAlignment config $$ (if platformHasSubsectionsViaSymbols platform- then ppr (mkDeadStripPreventer info_lbl) <> char ':'+ then line (pprAsmLabel platform (mkDeadStripPreventer info_lbl) <> char ':') else empty) $$ vcat (map (pprBasicBlock config top_info) blocks) $$ -- above: Even the first block gets a label, because with branch-chain -- elimination, it might be the target of a goto. (if platformHasSubsectionsViaSymbols platform then -- See Note [Subsections Via Symbols]- text "\t.long "- <+> ppr info_lbl+ line+ $ text "\t.long "+ <+> pprAsmLabel platform info_lbl <+> char '-'- <+> ppr (mkDeadStripPreventer info_lbl)+ <+> pprAsmLabel platform (mkDeadStripPreventer info_lbl) else empty) $$ pprSizeDecl platform info_lbl+{-# SPECIALIZE pprNatCmmDecl :: NCGConfig -> NatCmmDecl RawCmmStatics Instr -> SDoc #-}+{-# SPECIALIZE pprNatCmmDecl :: NCGConfig -> NatCmmDecl RawCmmStatics Instr -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -pprLabel :: Platform -> CLabel -> SDoc+pprLabel :: IsDoc doc => Platform -> CLabel -> doc pprLabel platform lbl = pprGloblDecl platform lbl $$ pprTypeDecl platform lbl- $$ (pdoc platform lbl <> char ':')+ $$ line (pprAsmLabel platform lbl <> char ':') -pprAlign :: Platform -> Alignment -> SDoc+pprAlign :: IsDoc doc => Platform -> Alignment -> doc pprAlign _platform alignment- = text "\t.balign " <> int (alignmentBytes alignment)+ = line $ text "\t.balign " <> int (alignmentBytes alignment) -- | Print appropriate alignment for the given section type.-pprAlignForSection :: Platform -> SectionType -> SDoc+pprAlignForSection :: IsDoc doc => Platform -> SectionType -> doc pprAlignForSection _platform _seg -- .balign is stable, whereas .align is platform dependent.- = text "\t.balign 8" -- always 8--instance Outputable Instr where- ppr = pprInstr genericPlatform+ = line (text "\t.balign 8") -- always 8 -- | Print section header and appropriate alignment for that section. --@@ -98,28 +97,28 @@ -- .section .text -- .balign 8 ---pprSectionAlign :: NCGConfig -> Section -> SDoc+pprSectionAlign :: IsDoc doc => NCGConfig -> Section -> doc pprSectionAlign _config (Section (OtherSection _) _) = panic "AArch64.Ppr.pprSectionAlign: unknown section" pprSectionAlign config sec@(Section seg _) =- pprSectionHeader config sec+ line (pprSectionHeader config sec) $$ pprAlignForSection (ncgPlatform config) seg -- | Output the ELF .size directive.-pprSizeDecl :: Platform -> CLabel -> SDoc+pprSizeDecl :: IsDoc doc => Platform -> CLabel -> doc pprSizeDecl platform lbl = if osElfTarget (platformOS platform)- then text "\t.size" <+> pdoc platform lbl <> text ", .-" <> pdoc platform lbl+ then line (text "\t.size" <+> pprAsmLabel platform lbl <> text ", .-" <> pprAsmLabel platform lbl) else empty -pprBasicBlock :: NCGConfig -> LabelMap RawCmmStatics -> NatBasicBlock Instr- -> SDoc+pprBasicBlock :: IsDoc doc => NCGConfig -> LabelMap RawCmmStatics -> NatBasicBlock Instr+ -> doc pprBasicBlock config info_env (BasicBlock blockid instrs) = maybe_infotable $ pprLabel platform asmLbl $$ vcat (map (pprInstr platform) (id {-detectTrivialDeadlock-} optInstrs)) $$ (if ncgDwarfEnabled config- then ppr (mkAsmTempEndLabel asmLbl) <> char ':'+ then line (pprAsmLabel platform (mkAsmTempEndLabel asmLbl) <> char ':') else empty ) where@@ -139,7 +138,7 @@ pprLabel platform info_lbl $$ c $$ (if ncgDwarfEnabled config- then ppr (mkAsmTempEndLabel info_lbl) <> char ':'+ then line (pprAsmLabel platform (mkAsmTempEndLabel info_lbl) <> char ':') else empty) -- Make sure the info table has the right .loc for the block -- coming right after it. See Note [Info Offset]@@ -147,7 +146,7 @@ (l@LOCATION{} : _) -> pprInstr platform l _other -> empty -pprDatas :: NCGConfig -> RawCmmStatics -> SDoc+pprDatas :: IsDoc doc => NCGConfig -> RawCmmStatics -> doc -- See Note [emit-time elimination of static indirections] in "GHC.Cmm.CLabel". pprDatas config (CmmStaticsRaw alias [CmmStaticLit (CmmLabel lbl), CmmStaticLit ind, _, _]) | lbl == mkIndStaticInfoLabel@@ -157,41 +156,41 @@ , Just ind' <- labelInd ind , alias `mayRedirectTo` ind' = pprGloblDecl (ncgPlatform config) alias- $$ text ".equiv" <+> pdoc (ncgPlatform config) alias <> comma <> pdoc (ncgPlatform config) (CmmLabel ind')+ $$ line (text ".equiv" <+> pprAsmLabel (ncgPlatform config) alias <> comma <> pprAsmLabel (ncgPlatform config) ind') pprDatas config (CmmStaticsRaw lbl dats) = vcat (pprLabel platform lbl : map (pprData config) dats) where platform = ncgPlatform config -pprData :: NCGConfig -> CmmStatic -> SDoc-pprData _config (CmmString str) = pprString str-pprData _config (CmmFileEmbed path) = pprFileEmbed path+pprData :: IsDoc doc => NCGConfig -> CmmStatic -> doc+pprData _config (CmmString str) = line (pprString str)+pprData _config (CmmFileEmbed path _) = line (pprFileEmbed path) pprData config (CmmUninitialised bytes)- = let platform = ncgPlatform config- in if platformOS platform == OSDarwin- then text ".space " <> int bytes- else text ".skip " <> int bytes+ = line $ let platform = ncgPlatform config+ in if platformOS platform == OSDarwin+ then text ".space " <> int bytes+ else text ".skip " <> int bytes pprData config (CmmStaticLit lit) = pprDataItem config lit -pprGloblDecl :: Platform -> CLabel -> SDoc+pprGloblDecl :: IsDoc doc => Platform -> CLabel -> doc pprGloblDecl platform lbl | not (externallyVisibleCLabel lbl) = empty- | otherwise = text "\t.globl " <> pdoc platform lbl+ | otherwise = line (text "\t.globl " <> pprAsmLabel platform lbl) -- Note [Always use objects for info tables] -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- See discussion in X86.Ppr for why this is necessary. Essentially we need to--- ensure that we never pass function symbols when we migth want to lookup the+-- ensure that we never pass function symbols when we might want to lookup the -- info table. If we did, we could end up with procedure linking tables -- (PLT)s, and thus the lookup wouldn't point to the function, but into the -- jump table. -- -- Fun fact: The LLVMMangler exists to patch this issue su on the LLVM side as -- well.-pprLabelType' :: Platform -> CLabel -> SDoc+pprLabelType' :: IsLine doc => Platform -> CLabel -> doc pprLabelType' platform lbl = if isCFunctionLabel lbl || functionOkInfoTable then text "@function"@@ -199,18 +198,18 @@ text "@object" where functionOkInfoTable = platformTablesNextToCode platform &&- isInfoTableLabel lbl && not (isConInfoTableLabel lbl)+ isInfoTableLabel lbl && not (isCmmInfoTableLabel lbl) && not (isConInfoTableLabel lbl) -- this is called pprTypeAndSizeDecl in PPC.Ppr-pprTypeDecl :: Platform -> CLabel -> SDoc+pprTypeDecl :: IsDoc doc => Platform -> CLabel -> doc pprTypeDecl platform lbl = if osElfTarget (platformOS platform) && externallyVisibleCLabel lbl- then text ".type " <> pdoc platform lbl <> text ", " <> pprLabelType' platform lbl+ then line (text ".type " <> pprAsmLabel platform lbl <> text ", " <> pprLabelType' platform lbl) else empty -pprDataItem :: NCGConfig -> CmmLit -> SDoc+pprDataItem :: IsDoc doc => NCGConfig -> CmmLit -> doc pprDataItem config lit- = vcat (ppr_item (cmmTypeFormat $ cmmLitType platform lit) lit)+ = lines_ (ppr_item (cmmTypeFormat $ cmmLitType platform lit) lit) where platform = ncgPlatform config @@ -231,12 +230,12 @@ ppr_item _ _ = pprPanic "pprDataItem:ppr_item" (text $ show lit) -pprImm :: Platform -> Imm -> SDoc+pprImm :: IsLine doc => Platform -> Imm -> doc pprImm _ (ImmInt i) = int i pprImm _ (ImmInteger i) = integer i-pprImm p (ImmCLbl l) = pdoc p l-pprImm p (ImmIndex l i) = pdoc p l <> char '+' <> int i-pprImm _ (ImmLit s) = s+pprImm p (ImmCLbl l) = pprAsmLabel p l+pprImm p (ImmIndex l i) = pprAsmLabel p l <> char '+' <> int i+pprImm _ (ImmLit s) = ftext s -- TODO: See pprIm below for why this is a bad idea! pprImm _ (ImmFloat f)@@ -261,13 +260,13 @@ asmMultilineComment :: SDoc -> SDoc asmMultilineComment c = whenPprDebug $ text "/*" $+$ c $+$ text "*/" -pprIm :: Platform -> Imm -> SDoc+pprIm :: IsLine doc => Platform -> Imm -> doc pprIm platform im = case im of ImmInt i -> char '#' <> int i ImmInteger i -> char '#' <> integer i -- TODO: This will only work for- -- The floating point value must be expressable as ±n ÷ 16 × 2^r,+ -- The floating point value must be expressible as ±n ÷ 16 × 2^r, -- where n and r are integers such that 16 ≤ n ≤ 31 and -3 ≤ r ≤ 4. -- and 0 needs to be encoded as wzr/xzr. --@@ -277,17 +276,17 @@ -- This is something the x86 backend does. -- -- We could also just turn them into statics :-/ Which is what the- -- PowerPC backend odes.+ -- PowerPC backend does. ImmFloat f | f == 0 -> text "wzr" ImmFloat f -> char '#' <> float (fromRational f) ImmDouble d | d == 0 -> text "xzr" ImmDouble d -> char '#' <> double (fromRational d) -- =<lbl> pseudo instruction!- ImmCLbl l -> char '=' <> pdoc platform l- ImmIndex l o -> text "[=" <> pdoc platform l <> comma <+> char '#' <> int o <> char ']'+ ImmCLbl l -> char '=' <> pprAsmLabel platform l+ ImmIndex l o -> text "[=" <> pprAsmLabel platform l <> comma <+> char '#' <> int o <> char ']' _ -> panic "AArch64.pprIm" -pprExt :: ExtMode -> SDoc+pprExt :: IsLine doc => ExtMode -> doc pprExt EUXTB = text "uxtb" pprExt EUXTH = text "uxth" pprExt EUXTW = text "uxtw"@@ -297,13 +296,13 @@ pprExt ESXTW = text "sxtw" pprExt ESXTX = text "sxtx" -pprShift :: ShiftMode -> SDoc+pprShift :: IsLine doc => ShiftMode -> doc pprShift SLSL = text "lsl" pprShift SLSR = text "lsr" pprShift SASR = text "asr" pprShift SROR = text "ror" -pprOp :: Platform -> Operand -> SDoc+pprOp :: IsLine doc => Platform -> Operand -> doc pprOp plat op = case op of OpReg w r -> pprReg w r OpRegExt w r x 0 -> pprReg w r <> comma <+> pprExt x@@ -311,12 +310,12 @@ OpRegShift w r s i -> pprReg w r <> comma <+> pprShift s <+> char '#' <> int i OpImm im -> pprIm plat im OpImmShift im s i -> pprIm plat im <> comma <+> pprShift s <+> char '#' <> int i- -- TODO: Address compuation always use registers as 64bit -- is this correct?+ -- TODO: Address computation always use registers as 64bit -- is this correct? OpAddr (AddrRegReg r1 r2) -> char '[' <+> pprReg W64 r1 <> comma <+> pprReg W64 r2 <+> char ']' OpAddr (AddrRegImm r1 im) -> char '[' <+> pprReg W64 r1 <> comma <+> pprImm plat im <+> char ']' OpAddr (AddrReg r1) -> char '[' <+> pprReg W64 r1 <+> char ']' -pprReg :: Width -> Reg -> SDoc+pprReg :: forall doc. IsLine doc => Width -> Reg -> doc pprReg w r = case r of RegReal (RealRegSingle i) -> ppr_reg_no w i -- virtual regs should not show up, but this is helpful for debugging.@@ -326,7 +325,7 @@ _ -> pprPanic "AArch64.pprReg" (text $ show r) where- ppr_reg_no :: Width -> Int -> SDoc+ ppr_reg_no :: Width -> Int -> doc ppr_reg_no w 31 | w == W64 = text "sp" | w == W32 = text "wsp"@@ -355,110 +354,110 @@ isFloatOp (OpReg _ (RegVirtual (VirtualRegD _))) = True isFloatOp _ = False -pprInstr :: Platform -> Instr -> SDoc+pprInstr :: IsDoc doc => Platform -> Instr -> doc pprInstr platform instr = case instr of -- Meta Instructions ---------------------------------------------------------- COMMENT s -> asmComment s- MULTILINE_COMMENT s -> asmMultilineComment s- ANN d i -> pprInstr platform i <+> asmDoubleslashComment d- LOCATION file line col _name- -> text "\t.loc" <+> ppr file <+> ppr line <+> ppr col- DELTA d -> asmComment $ text ("\tdelta = " ++ show d)- NEWBLOCK blockid -> -- This is invalid assembly. But NEWBLOCK should never be contained- -- in the final instruction stream. But we still want to be able to- -- print it for debugging purposes.- text "BLOCK " <> pprLabel platform (blockLbl blockid)+ -- see Note [dualLine and dualDoc] in GHC.Utils.Outputable+ COMMENT s -> dualDoc (asmComment s) empty+ MULTILINE_COMMENT s -> dualDoc (asmMultilineComment s) empty+ ANN d i -> dualDoc (pprInstr platform i <+> asmDoubleslashComment d) (pprInstr platform i)++ LOCATION file line' col _name+ -> line (text "\t.loc" <+> int file <+> int line' <+> int col)+ DELTA d -> dualDoc (asmComment $ text "\tdelta = " <> int d) empty+ -- see Note [dualLine and dualDoc] in GHC.Utils.Outputable+ NEWBLOCK _ -> panic "PprInstr: NEWBLOCK" LDATA _ _ -> panic "pprInstr: LDATA" -- Pseudo Instructions ------------------------------------------------------- - PUSH_STACK_FRAME -> text "\tstp x29, x30, [sp, #-16]!"- $$ text "\tmov x29, sp"+ PUSH_STACK_FRAME -> lines_ [text "\tstp x29, x30, [sp, #-16]!",+ text "\tmov x29, sp"] - POP_STACK_FRAME -> text "\tldp x29, x30, [sp], #16"+ POP_STACK_FRAME -> line $ text "\tldp x29, x30, [sp], #16" -- =========================================================================== -- AArch64 Instruction Set -- 1. Arithmetic Instructions ------------------------------------------------ ADD o1 o2 o3- | isFloatOp o1 && isFloatOp o2 && isFloatOp o3 -> text "\tfadd" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- | otherwise -> text "\tadd" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- CMN o1 o2 -> text "\tcmn" <+> pprOp platform o1 <> comma <+> pprOp platform o2+ | isFloatOp o1 && isFloatOp o2 && isFloatOp o3 -> op3 (text "\tfadd") o1 o2 o3+ | otherwise -> op3 (text "\tadd") o1 o2 o3+ CMN o1 o2 -> op2 (text "\tcmn") o1 o2 CMP o1 o2- | isFloatOp o1 && isFloatOp o2 -> text "\tfcmp" <+> pprOp platform o1 <> comma <+> pprOp platform o2- | otherwise -> text "\tcmp" <+> pprOp platform o1 <> comma <+> pprOp platform o2- MSUB o1 o2 o3 o4 -> text "\tmsub" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3 <> comma <+> pprOp platform o4+ | isFloatOp o1 && isFloatOp o2 -> op2 (text "\tfcmp") o1 o2+ | otherwise -> op2 (text "\tcmp") o1 o2+ MSUB o1 o2 o3 o4 -> op4 (text "\tmsub") o1 o2 o3 o4 MUL o1 o2 o3- | isFloatOp o1 && isFloatOp o2 && isFloatOp o3 -> text "\tfmul" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- | otherwise -> text "\tmul" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- SMULH o1 o2 o3 -> text "\tsmulh" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- SMULL o1 o2 o3 -> text "\tsmull" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3+ | isFloatOp o1 && isFloatOp o2 && isFloatOp o3 -> op3 (text "\tfmul") o1 o2 o3+ | otherwise -> op3 (text "\tmul") o1 o2 o3+ SMULH o1 o2 o3 -> op3 (text "\tsmulh") o1 o2 o3+ SMULL o1 o2 o3 -> op3 (text "\tsmull") o1 o2 o3 NEG o1 o2- | isFloatOp o1 && isFloatOp o2 -> text "\tfneg" <+> pprOp platform o1 <> comma <+> pprOp platform o2- | otherwise -> text "\tneg" <+> pprOp platform o1 <> comma <+> pprOp platform o2+ | isFloatOp o1 && isFloatOp o2 -> op2 (text "\tfneg") o1 o2+ | otherwise -> op2 (text "\tneg") o1 o2 SDIV o1 o2 o3 | isFloatOp o1 && isFloatOp o2 && isFloatOp o3- -> text "\tfdiv" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- SDIV o1 o2 o3 -> text "\tsdiv" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3+ -> op3 (text "\tfdiv") o1 o2 o3+ SDIV o1 o2 o3 -> op3 (text "\tsdiv") o1 o2 o3 SUB o1 o2 o3- | isFloatOp o1 && isFloatOp o2 && isFloatOp o3 -> text "\tfsub" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- | otherwise -> text "\tsub" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- UDIV o1 o2 o3 -> text "\tudiv" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3+ | isFloatOp o1 && isFloatOp o2 && isFloatOp o3 -> op3 (text "\tfsub") o1 o2 o3+ | otherwise -> op3 (text "\tsub") o1 o2 o3+ UDIV o1 o2 o3 -> op3 (text "\tudiv") o1 o2 o3 -- 2. Bit Manipulation Instructions ------------------------------------------- SBFM o1 o2 o3 o4 -> text "\tsbfm" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3 <> comma <+> pprOp platform o4- UBFM o1 o2 o3 o4 -> text "\tubfm" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3 <> comma <+> pprOp platform o4+ SBFM o1 o2 o3 o4 -> op4 (text "\tsbfm") o1 o2 o3 o4+ UBFM o1 o2 o3 o4 -> op4 (text "\tubfm") o1 o2 o3 o4 -- signed and unsigned bitfield extract- SBFX o1 o2 o3 o4 -> text "\tsbfx" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3 <> comma <+> pprOp platform o4- UBFX o1 o2 o3 o4 -> text "\tubfx" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3 <> comma <+> pprOp platform o4- SXTB o1 o2 -> text "\tsxtb" <+> pprOp platform o1 <> comma <+> pprOp platform o2- UXTB o1 o2 -> text "\tuxtb" <+> pprOp platform o1 <> comma <+> pprOp platform o2- SXTH o1 o2 -> text "\tsxth" <+> pprOp platform o1 <> comma <+> pprOp platform o2- UXTH o1 o2 -> text "\tuxth" <+> pprOp platform o1 <> comma <+> pprOp platform o2+ SBFX o1 o2 o3 o4 -> op4 (text "\tsbfx") o1 o2 o3 o4+ UBFX o1 o2 o3 o4 -> op4 (text "\tubfx") o1 o2 o3 o4+ SXTB o1 o2 -> op2 (text "\tsxtb") o1 o2+ UXTB o1 o2 -> op2 (text "\tuxtb") o1 o2+ SXTH o1 o2 -> op2 (text "\tsxth") o1 o2+ UXTH o1 o2 -> op2 (text "\tuxth") o1 o2 -- 3. Logical and Move Instructions ------------------------------------------- AND o1 o2 o3 -> text "\tand" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- ANDS o1 o2 o3 -> text "\tands" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- ASR o1 o2 o3 -> text "\tasr" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- BIC o1 o2 o3 -> text "\tbic" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- BICS o1 o2 o3 -> text "\tbics" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- EON o1 o2 o3 -> text "\teon" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- EOR o1 o2 o3 -> text "\teor" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- LSL o1 o2 o3 -> text "\tlsl" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- LSR o1 o2 o3 -> text "\tlsr" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3+ AND o1 o2 o3 -> op3 (text "\tand") o1 o2 o3+ ANDS o1 o2 o3 -> op3 (text "\tands") o1 o2 o3+ ASR o1 o2 o3 -> op3 (text "\tasr") o1 o2 o3+ BIC o1 o2 o3 -> op3 (text "\tbic") o1 o2 o3+ BICS o1 o2 o3 -> op3 (text "\tbics") o1 o2 o3+ EON o1 o2 o3 -> op3 (text "\teon") o1 o2 o3+ EOR o1 o2 o3 -> op3 (text "\teor") o1 o2 o3+ LSL o1 o2 o3 -> op3 (text "\tlsl") o1 o2 o3+ LSR o1 o2 o3 -> op3 (text "\tlsr") o1 o2 o3 MOV o1 o2- | isFloatOp o1 || isFloatOp o2 -> text "\tfmov" <+> pprOp platform o1 <> comma <+> pprOp platform o2- | otherwise -> text "\tmov" <+> pprOp platform o1 <> comma <+> pprOp platform o2- MOVK o1 o2 -> text "\tmovk" <+> pprOp platform o1 <> comma <+> pprOp platform o2- MVN o1 o2 -> text "\tmvn" <+> pprOp platform o1 <> comma <+> pprOp platform o2- ORN o1 o2 o3 -> text "\torn" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- ORR o1 o2 o3 -> text "\torr" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- ROR o1 o2 o3 -> text "\tror" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- TST o1 o2 -> text "\ttst" <+> pprOp platform o1 <> comma <+> pprOp platform o2+ | isFloatOp o1 || isFloatOp o2 -> op2 (text "\tfmov") o1 o2+ | otherwise -> op2 (text "\tmov") o1 o2+ MOVK o1 o2 -> op2 (text "\tmovk") o1 o2+ MVN o1 o2 -> op2 (text "\tmvn") o1 o2+ ORN o1 o2 o3 -> op3 (text "\torn") o1 o2 o3+ ORR o1 o2 o3 -> op3 (text "\torr") o1 o2 o3+ ROR o1 o2 o3 -> op3 (text "\tror") o1 o2 o3+ TST o1 o2 -> op2 (text "\ttst") o1 o2 -- 4. Branch Instructions ---------------------------------------------------- J t -> pprInstr platform (B t)- B (TBlock bid) -> text "\tb" <+> pdoc platform (mkLocalBlockLabel (getUnique bid))- B (TLabel lbl) -> text "\tb" <+> pdoc platform lbl- B (TReg r) -> text "\tbr" <+> pprReg W64 r+ B (TBlock bid) -> line $ text "\tb" <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))+ B (TLabel lbl) -> line $ text "\tb" <+> pprAsmLabel platform lbl+ B (TReg r) -> line $ text "\tbr" <+> pprReg W64 r - BL (TBlock bid) _ _ -> text "\tbl" <+> pdoc platform (mkLocalBlockLabel (getUnique bid))- BL (TLabel lbl) _ _ -> text "\tbl" <+> pdoc platform lbl- BL (TReg r) _ _ -> text "\tblr" <+> pprReg W64 r+ BL (TBlock bid) _ _ -> line $ text "\tbl" <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))+ BL (TLabel lbl) _ _ -> line $ text "\tbl" <+> pprAsmLabel platform lbl+ BL (TReg r) _ _ -> line $ text "\tblr" <+> pprReg W64 r - BCOND c (TBlock bid) -> text "\t" <> pprBcond c <+> pdoc platform (mkLocalBlockLabel (getUnique bid))- BCOND c (TLabel lbl) -> text "\t" <> pprBcond c <+> pdoc platform lbl+ BCOND c (TBlock bid) -> line $ text "\t" <> pprBcond c <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))+ BCOND c (TLabel lbl) -> line $ text "\t" <> pprBcond c <+> pprAsmLabel platform lbl BCOND _ (TReg _) -> panic "AArch64.ppr: No conditional branching to registers!" -- 5. Atomic Instructions ---------------------------------------------------- -- 6. Conditional Instructions ------------------------------------------------ CSET o c -> text "\tcset" <+> pprOp platform o <> comma <+> pprCond c+ CSET o c -> line $ text "\tcset" <+> pprOp platform o <> comma <+> pprCond c - CBZ o (TBlock bid) -> text "\tcbz" <+> pprOp platform o <> comma <+> pdoc platform (mkLocalBlockLabel (getUnique bid))- CBZ o (TLabel lbl) -> text "\tcbz" <+> pprOp platform o <> comma <+> pdoc platform lbl+ CBZ o (TBlock bid) -> line $ text "\tcbz" <+> pprOp platform o <> comma <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))+ CBZ o (TLabel lbl) -> line $ text "\tcbz" <+> pprOp platform o <> comma <+> pprAsmLabel platform lbl CBZ _ (TReg _) -> panic "AArch64.ppr: No conditional (cbz) branching to registers!" - CBNZ o (TBlock bid) -> text "\tcbnz" <+> pprOp platform o <> comma <+> pdoc platform (mkLocalBlockLabel (getUnique bid))- CBNZ o (TLabel lbl) -> text "\tcbnz" <+> pprOp platform o <> comma <+> pdoc platform lbl+ CBNZ o (TBlock bid) -> line $ text "\tcbnz" <+> pprOp platform o <> comma <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))+ CBNZ o (TLabel lbl) -> line $ text "\tcbnz" <+> pprOp platform o <> comma <+> pprAsmLabel platform lbl CBNZ _ (TReg _) -> panic "AArch64.ppr: No conditional (cbnz) branching to registers!" -- 7. Load and Store Instructions --------------------------------------------@@ -466,88 +465,98 @@ -- address. Not observing the correct size when loading will lead -- inevitably to crashes. STR _f o1@(OpReg W8 (RegReal (RealRegSingle i))) o2 | i < 32 ->- text "\tstrb" <+> pprOp platform o1 <> comma <+> pprOp platform o2+ op2 (text "\tstrb") o1 o2 STR _f o1@(OpReg W16 (RegReal (RealRegSingle i))) o2 | i < 32 ->- text "\tstrh" <+> pprOp platform o1 <> comma <+> pprOp platform o2- STR _f o1 o2 -> text "\tstr" <+> pprOp platform o1 <> comma <+> pprOp platform o2+ op2 (text "\tstrh") o1 o2+ STR _f o1 o2 -> op2 (text "\tstr") o1 o2+ STLR _f o1 o2 -> op2 (text "\tstlr") o1 o2 #if defined(darwin_HOST_OS) LDR _f o1 (OpImm (ImmIndex lbl' off)) | Just (_info, lbl) <- dynamicLinkerLabelInfo lbl' ->- text "\tadrp" <+> pprOp platform o1 <> comma <+> pdoc platform lbl <> text "@gotpage" $$- text "\tldr" <+> pprOp platform o1 <> comma <+> text "[" <> pprOp platform o1 <> comma <+> pdoc platform lbl <> text "@gotpageoff" <> text "]" $$- text "\tadd" <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> char '#' <> int off -- TODO: check that off is in 12bits.+ op_adrp o1 (pprAsmLabel platform lbl <> text "@gotpage") $$+ op_ldr o1 (pprAsmLabel platform lbl <> text "@gotpageoff") $$+ op_add o1 (char '#' <> int off) -- TODO: check that off is in 12bits. LDR _f o1 (OpImm (ImmIndex lbl off)) | isForeignLabel lbl ->- text "\tadrp" <+> pprOp platform o1 <> comma <+> pdoc platform lbl <> text "@gotpage" $$- text "\tldr" <+> pprOp platform o1 <> comma <+> text "[" <> pprOp platform o1 <> comma <+> pdoc platform lbl <> text "@gotpageoff" <> text "]" $$- text "\tadd" <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> char '#' <> int off -- TODO: check that off is in 12bits.+ op_adrp o1 (pprAsmLabel platform lbl <> text "@gotpage") $$+ op_ldr o1 (pprAsmLabel platform lbl <> text "@gotpageoff") $$+ op_add o1 (char '#' <> int off) -- TODO: check that off is in 12bits. LDR _f o1 (OpImm (ImmIndex lbl off)) ->- text "\tadrp" <+> pprOp platform o1 <> comma <+> pdoc platform lbl <> text "@page" $$- text "\tadd" <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> pdoc platform lbl <> text "@pageoff" $$- text "\tadd" <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> char '#' <> int off -- TODO: check that off is in 12bits.+ op_adrp o1 (pprAsmLabel platform lbl <> text "@page") $$+ op_add o1 (pprAsmLabel platform lbl <> text "@pageoff") $$+ op_add o1 (char '#' <> int off) -- TODO: check that off is in 12bits. LDR _f o1 (OpImm (ImmCLbl lbl')) | Just (_info, lbl) <- dynamicLinkerLabelInfo lbl' ->- text "\tadrp" <+> pprOp platform o1 <> comma <+> pdoc platform lbl <> text "@gotpage" $$- text "\tldr" <+> pprOp platform o1 <> comma <+> text "[" <> pprOp platform o1 <> comma <+> pdoc platform lbl <> text "@gotpageoff" <> text "]"+ op_adrp o1 (pprAsmLabel platform lbl <> text "@gotpage") $$+ op_ldr o1 (pprAsmLabel platform lbl <> text "@gotpageoff") LDR _f o1 (OpImm (ImmCLbl lbl)) | isForeignLabel lbl ->- text "\tadrp" <+> pprOp platform o1 <> comma <+> pdoc platform lbl <> text "@gotpage" $$- text "\tldr" <+> pprOp platform o1 <> comma <+> text "[" <> pprOp platform o1 <> comma <+> pdoc platform lbl <> text "@gotpageoff" <> text "]"+ op_adrp o1 (pprAsmLabel platform lbl <> text "@gotpage") $$+ op_ldr o1 (pprAsmLabel platform lbl <> text "@gotpageoff") LDR _f o1 (OpImm (ImmCLbl lbl)) ->- text "\tadrp" <+> pprOp platform o1 <> comma <+> pdoc platform lbl <> text "@page" $$- text "\tadd" <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> pdoc platform lbl <> text "@pageoff"+ op_adrp o1 (pprAsmLabel platform lbl <> text "@page") $$+ op_add o1 (pprAsmLabel platform lbl <> text "@pageoff")+ #else LDR _f o1 (OpImm (ImmIndex lbl' off)) | Just (_info, lbl) <- dynamicLinkerLabelInfo lbl' ->- text "\tadrp" <+> pprOp platform o1 <> comma <+> text ":got:" <> pdoc platform lbl $$- text "\tldr" <+> pprOp platform o1 <> comma <+> text "[" <> pprOp platform o1 <> comma <+> text ":got_lo12:" <> pdoc platform lbl <> text "]" $$- text "\tadd" <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> char '#' <> int off -- TODO: check that off is in 12bits.+ op_adrp o1 (text ":got:" <> pprAsmLabel platform lbl) $$+ op_ldr o1 (text ":got_lo12:" <> pprAsmLabel platform lbl) $$+ op_add o1 (char '#' <> int off) -- TODO: check that off is in 12bits. LDR _f o1 (OpImm (ImmIndex lbl off)) | isForeignLabel lbl ->- text "\tadrp" <+> pprOp platform o1 <> comma <+> text ":got:" <> pdoc platform lbl $$- text "\tldr" <+> pprOp platform o1 <> comma <+> text "[" <> pprOp platform o1 <> comma <+> text ":got_lo12:" <> pdoc platform lbl <> text "]" $$- text "\tadd" <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> char '#' <> int off -- TODO: check that off is in 12bits.+ op_adrp o1 (text ":got:" <> pprAsmLabel platform lbl) $$+ op_ldr o1 (text ":got_lo12:" <> pprAsmLabel platform lbl) $$+ op_add o1 (char '#' <> int off) -- TODO: check that off is in 12bits. LDR _f o1 (OpImm (ImmIndex lbl off)) ->- text "\tadrp" <+> pprOp platform o1 <> comma <+> pdoc platform lbl $$- text "\tadd" <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> text ":lo12:" <> pdoc platform lbl $$- text "\tadd" <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> char '#' <> int off -- TODO: check that off is in 12bits.+ op_adrp o1 (pprAsmLabel platform lbl) $$+ op_add o1 (text ":lo12:" <> pprAsmLabel platform lbl) $$+ op_add o1 (char '#' <> int off) -- TODO: check that off is in 12bits. LDR _f o1 (OpImm (ImmCLbl lbl')) | Just (_info, lbl) <- dynamicLinkerLabelInfo lbl' ->- text "\tadrp" <+> pprOp platform o1 <> comma <+> text ":got:" <> pdoc platform lbl $$- text "\tldr" <+> pprOp platform o1 <> comma <+> text "[" <> pprOp platform o1 <> comma <+> text ":got_lo12:" <> pdoc platform lbl <> text "]"+ op_adrp o1 (text ":got:" <> pprAsmLabel platform lbl) $$+ op_ldr o1 (text ":got_lo12:" <> pprAsmLabel platform lbl) LDR _f o1 (OpImm (ImmCLbl lbl)) | isForeignLabel lbl ->- text "\tadrp" <+> pprOp platform o1 <> comma <+> text ":got:" <> pdoc platform lbl $$- text "\tldr" <+> pprOp platform o1 <> comma <+> text "[" <> pprOp platform o1 <> comma <+> text ":got_lo12:" <> pdoc platform lbl <> text "]"+ op_adrp o1 (text ":got:" <> pprAsmLabel platform lbl) $$+ op_ldr o1 (text ":got_lo12:" <> pprAsmLabel platform lbl) LDR _f o1 (OpImm (ImmCLbl lbl)) ->- text "\tadrp" <+> pprOp platform o1 <> comma <+> pdoc platform lbl $$- text "\tadd" <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> text ":lo12:" <> pdoc platform lbl+ op_adrp o1 (pprAsmLabel platform lbl) $$+ op_add o1 (text ":lo12:" <> pprAsmLabel platform lbl)+ #endif LDR _f o1@(OpReg W8 (RegReal (RealRegSingle i))) o2 | i < 32 ->- text "\tldrb" <+> pprOp platform o1 <> comma <+> pprOp platform o2+ op2 (text "\tldrb") o1 o2 LDR _f o1@(OpReg W16 (RegReal (RealRegSingle i))) o2 | i < 32 ->- text "\tldrh" <+> pprOp platform o1 <> comma <+> pprOp platform o2- LDR _f o1 o2 -> text "\tldr" <+> pprOp platform o1 <> comma <+> pprOp platform o2+ op2 (text "\tldrh") o1 o2+ LDR _f o1 o2 -> op2 (text "\tldr") o1 o2+ LDAR _f o1 o2 -> op2 (text "\tldar") o1 o2 - STP _f o1 o2 o3 -> text "\tstp" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3- LDP _f o1 o2 o3 -> text "\tldp" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3+ STP _f o1 o2 o3 -> op3 (text "\tstp") o1 o2 o3+ LDP _f o1 o2 o3 -> op3 (text "\tldp") o1 o2 o3 -- 8. Synchronization Instructions -------------------------------------------- DMBSY -> text "\tdmb sy"+ DMBSY -> line $ text "\tdmb sy" -- 9. Floating Point Instructions --------------------------------------------- FCVT o1 o2 -> text "\tfcvt" <+> pprOp platform o1 <> comma <+> pprOp platform o2- SCVTF o1 o2 -> text "\tscvtf" <+> pprOp platform o1 <> comma <+> pprOp platform o2- FCVTZS o1 o2 -> text "\tfcvtzs" <+> pprOp platform o1 <> comma <+> pprOp platform o2- FABS o1 o2 -> text "\tfabs" <+> pprOp platform o1 <> comma <+> pprOp platform o2+ FCVT o1 o2 -> op2 (text "\tfcvt") o1 o2+ SCVTF o1 o2 -> op2 (text "\tscvtf") o1 o2+ FCVTZS o1 o2 -> op2 (text "\tfcvtzs") o1 o2+ FABS o1 o2 -> op2 (text "\tfabs") o1 o2+ where op2 op o1 o2 = line $ op <+> pprOp platform o1 <> comma <+> pprOp platform o2+ op3 op o1 o2 o3 = line $ op <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3+ op4 op o1 o2 o3 o4 = line $ op <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3 <> comma <+> pprOp platform o4+ op_ldr o1 rest = line $ text "\tldr" <+> pprOp platform o1 <> comma <+> text "[" <> pprOp platform o1 <> comma <+> rest <> text "]"+ op_adrp o1 rest = line $ text "\tadrp" <+> pprOp platform o1 <> comma <+> rest+ op_add o1 rest = line $ text "\tadd" <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> rest -pprBcond :: Cond -> SDoc+pprBcond :: IsLine doc => Cond -> doc pprBcond c = text "b." <> pprCond c -pprCond :: Cond -> SDoc+pprCond :: IsLine doc => Cond -> doc pprCond c = case c of ALWAYS -> text "al" -- Always EQ -> text "eq" -- Equal@@ -563,11 +572,11 @@ UGE -> text "hs" -- Carry set/unsigned higher or same ; Greater than or equal, or unordered UGT -> text "hi" -- Unsigned higher ; Greater than, or unordered - -- NEVER -> text "nv" -- Never+ NEVER -> text "nv" -- Never VS -> text "vs" -- Overflow ; Unordered (at least one NaN operand) VC -> text "vc" -- No overflow ; Not unordered - -- Orderd variants. Respecting NaN.+ -- Ordered variants. Respecting NaN. OLT -> text "mi" OLE -> text "ls" OGE -> text "ge"
compiler/GHC/CmmToAsm/AArch64/Regs.hs view
@@ -2,6 +2,7 @@ module GHC.CmmToAsm.AArch64.Regs where import GHC.Prelude+import GHC.Data.FastString import GHC.Platform.Reg import GHC.Platform.Reg.Class@@ -59,7 +60,7 @@ = ImmInt Int | ImmInteger Integer -- Sigh. | ImmCLbl CLabel -- AbstractC Label (with baggage)- | ImmLit SDoc -- Simple string+ | ImmLit FastString | ImmIndex CLabel Int | ImmFloat Rational | ImmDouble Rational@@ -67,14 +68,8 @@ | ImmConstantDiff Imm Imm deriving (Eq, Show) -instance Show SDoc where- show = showPprUnsafe . ppr--instance Eq SDoc where- lhs == rhs = show lhs == show rhs--strImmLit :: String -> Imm-strImmLit s = ImmLit (text s)+strImmLit :: FastString -> Imm+strImmLit s = ImmLit s litToImm :: CmmLit -> Imm
compiler/GHC/CmmToAsm/BlockLayout.hs view
@@ -13,7 +13,7 @@ ( sequenceTop, backendMaintainsCfg) where -import GHC.Prelude+import GHC.Prelude hiding (head, init, last, tail) import GHC.Platform @@ -35,20 +35,21 @@ import GHC.Data.List.SetOps (removeDups) import GHC.Data.OrdList -import GHC.Utils.Trace import GHC.Utils.Outputable import GHC.Utils.Panic import GHC.Utils.Panic.Plain import GHC.Utils.Misc import Data.List (sortOn, sortBy, nub)+import qualified Data.List as Partial (head, tail)+import Data.List.NonEmpty (nonEmpty)+import qualified Data.List.NonEmpty as NE import Data.Foldable (toList) import qualified Data.Set as Set import Data.STRef import Control.Monad.ST.Strict import Control.Monad (foldM, unless) import GHC.Data.UnionFind-import GHC.Types.Unique.Supply (UniqSM) {- Note [CFG based code layout]@@ -355,7 +356,7 @@ breakChainAt :: BlockId -> BlockChain -> (BlockChain,BlockChain) breakChainAt bid (BlockChain blks)- | not (bid == head rblks)+ | not (bid == Partial.head rblks) = panic "Block not in chain" | otherwise = (BlockChain (toOL lblks),@@ -494,7 +495,7 @@ merge :: forall s. [CfgEdge] -> LabelMap (Point s BlockChain) -> ST s BlockChain merge [] chains = do chains' <- mapM find =<< (nub <$> (mapM repr $ mapElems chains)) :: ST s [BlockChain]- return $ foldl' chainConcat (head chains') (tail chains')+ return $ foldl' chainConcat (Partial.head chains') (Partial.tail chains') merge ((CfgEdge from to _):edges) chains -- | pprTrace "merge" (ppr (from,to) <> ppr chains) False -- = undefined@@ -594,8 +595,8 @@ toChain <- readSTRef toRef let newChain = chainConcat fromChain toChain ref <- newSTRef newChain- let start = head $ takeL 1 newChain- let end = head $ takeR 1 newChain+ let start = Partial.head $ takeL 1 newChain+ let end = Partial.head $ takeR 1 newChain -- chains <- sequence $ mapMap readSTRef chainStarts -- pprTraceM "pre-fuse chains:" $ ppr chains buildNext@@ -656,7 +657,7 @@ sequenceChain _info _weights [x] = [x] sequenceChain info weights blocks@((BasicBlock entry _):_) = let directEdges :: [CfgEdge]- directEdges = sortBy (flip compare) $ catMaybes . map relevantWeight $ (infoEdgeList weights)+ directEdges = sortBy (flip compare) $ mapMaybe relevantWeight (infoEdgeList weights) where -- Apply modifiers to turn edge frequencies into useable weights -- for computing code layout.@@ -759,7 +760,7 @@ -- builds if something slips through. assert (null unplaced) $ --pprTraceIt "placedBlocks" $- -- ++ [] is stil kinda expensive+ -- ++ [] is still kinda expensive if null unplaced then blockList else blockList ++ unplaced getBlock bid = expectJust "Block placement" $ mapLookup bid blockMap in@@ -773,13 +774,13 @@ dropJumps :: forall a i. Instruction i => LabelMap a -> [GenBasicBlock i] -> [GenBasicBlock i] dropJumps _ [] = []-dropJumps info ((BasicBlock lbl ins):todo)- | not . null $ ins --This can happen because of shortcutting- , [dest] <- jumpDestsOfInstr (last ins)- , ((BasicBlock nextLbl _) : _) <- todo+dropJumps info (BasicBlock lbl ins:todo)+ | Just ins <- nonEmpty ins --This can happen because of shortcutting+ , [dest] <- jumpDestsOfInstr (NE.last ins)+ , BasicBlock nextLbl _ : _ <- todo , not (mapMember dest info) , nextLbl == dest- = BasicBlock lbl (init ins) : dropJumps info todo+ = BasicBlock lbl (NE.init ins) : dropJumps info todo | otherwise = BasicBlock lbl ins : dropJumps info todo @@ -798,32 +799,29 @@ => NcgImpl statics instr jumpDest -> Maybe CFG -- ^ CFG if we have one. -> NatCmmDecl statics instr -- ^ Function to serialize- -> UniqSM (NatCmmDecl statics instr)--sequenceTop _ _ top@(CmmData _ _) = pure top-sequenceTop ncgImpl edgeWeights (CmmProc info lbl live (ListGraph blocks)) = do- let config = ncgConfig ncgImpl- platform = ncgPlatform config-- seq_blocks =- if -- Chain based algorithm- | ncgCfgBlockLayout config- , backendMaintainsCfg platform- , Just cfg <- edgeWeights- -> {-# SCC layoutBlocks #-} sequenceChain info cfg blocks+ -> NatCmmDecl statics instr - -- Old algorithm without edge weights- | ncgCfgWeightlessLayout config- || not (backendMaintainsCfg platform)- -> {-# SCC layoutBlocks #-} sequenceBlocks Nothing info blocks+sequenceTop _ _ top@(CmmData _ _) = top+sequenceTop ncgImpl edgeWeights (CmmProc info lbl live (ListGraph blocks))+ = let+ config = ncgConfig ncgImpl+ platform = ncgPlatform config - -- Old algorithm with edge weights (if any)- | otherwise- -> {-# SCC layoutBlocks #-} sequenceBlocks edgeWeights info blocks+ in CmmProc info lbl live $ ListGraph $ ncgMakeFarBranches ncgImpl info $+ if -- Chain based algorithm+ | ncgCfgBlockLayout config+ , backendMaintainsCfg platform+ , Just cfg <- edgeWeights+ -> {-# SCC layoutBlocks #-} sequenceChain info cfg blocks - far_blocks <- (ncgMakeFarBranches ncgImpl) platform info seq_blocks- pure $ CmmProc info lbl live $ ListGraph far_blocks+ -- Old algorithm without edge weights+ | ncgCfgWeightlessLayout config+ || not (backendMaintainsCfg platform)+ -> {-# SCC layoutBlocks #-} sequenceBlocks Nothing info blocks + -- Old algorithm with edge weights (if any)+ | otherwise+ -> {-# SCC layoutBlocks #-} sequenceBlocks edgeWeights info blocks -- The old algorithm: -- It is very simple (and stupid): We make a graph out of@@ -873,10 +871,10 @@ ((target,info):_) | length successors > 2 || edgeWeight info <= 0 -> [] | otherwise -> [target]- | otherwise- = case jumpDestsOfInstr (last instrs) of- [one] -> [one]- _many -> []+ | Just instr <- lastMaybe instrs+ , [one] <- jumpDestsOfInstr instr+ = [one]+ | otherwise = [] seqBlocks :: LabelMap i -> [Node BlockId (GenBasicBlock t1)]
compiler/GHC/CmmToAsm/CFG.hs view
@@ -47,7 +47,6 @@ import GHC.Cmm.BlockId import GHC.Cmm as Cmm -import GHC.Cmm.Utils import GHC.Cmm.Switch import GHC.Cmm.Dataflow.Collections import GHC.Cmm.Dataflow.Label@@ -78,7 +77,6 @@ --import GHC.Cmm.DebugBlock --import GHC.Data.OrdList --import GHC.Cmm.DebugBlock.Trace-import GHC.Cmm.Ppr () -- For Outputable instances import Data.List (sort, nub, partition) import Data.STRef.Strict@@ -221,7 +219,7 @@ -- | Check if the nodes in the cfg and the set of blocks are the same.--- In a case of a missmatch we panic and show the difference.+-- In a case of a mismatch we panic and show the difference. sanityCheckCfg :: CFG -> LabelSet -> SDoc -> Bool sanityCheckCfg m blockSet msg | blockSet == cfgNodes@@ -309,7 +307,7 @@ -- Create a unification variable for each of the nodes in a rewrite cuts_vars <- traverse (\p -> (p,) <$> fresh (Just p)) (concatMap (\(a, b) -> [a] ++ maybe [] (:[]) b) cuts_list) let cuts_map = mapFromList cuts_vars :: LabelMap (Point s (Maybe BlockId))- -- Then unify according the the rewrites in the cuts map+ -- Then unify according to the rewrites in the cuts map mapM_ (\(from, to) -> expectJust "shortcutWeightMap" (mapLookup from cuts_map) `union` expectJust "shortcutWeightMap" (maybe (Just null) (flip mapLookup cuts_map) to) ) cuts_list -- Then recover the unique representative, which is the result of following@@ -387,10 +385,7 @@ delEdge :: BlockId -> BlockId -> CFG -> CFG delEdge from to m =- mapAlter remDest from m- where- remDest Nothing = Nothing- remDest (Just wm) = Just $ mapDelete to wm+ mapAdjust (mapDelete to) from m -- | Destinations from bid ordered by weight (descending)@@ -419,11 +414,11 @@ getEdgeWeight :: CFG -> BlockId -> BlockId -> EdgeWeight getEdgeWeight cfg from to =- edgeWeight $ expectJust "Edgeweight for noexisting block" $+ edgeWeight $ expectJust "Edgeweight for nonexisting block" $ getEdgeInfo from to cfg getTransitionSource :: BlockId -> BlockId -> CFG -> TransitionSource-getTransitionSource from to cfg = transitionSource $ expectJust "Source info for noexisting block" $+getTransitionSource from to cfg = transitionSource $ expectJust "Source info for nonexisting block" $ getEdgeInfo from to cfg reverseEdges :: CFG -> CFG@@ -665,7 +660,7 @@ (CmmCall { cml_cont = Nothing }) -> [] other -> panic "Foo" $- assertPpr False (ppr "Unknown successor cause:" <>+ assertPpr False (text "Unknown successor cause:" <> (pdoc platform branch <+> text "=>" <> pdoc platform (G.successors other))) $ map (\x -> ((bid,x),mkEdgeInfo 0)) $ G.successors other where@@ -1025,11 +1020,11 @@ loops (lehPredicts) and one to predict that backedges are more likely than any other edge. -The back edge case is special as it superceeds any other heuristic if it+The back edge case is special as it supersedes any other heuristic if it applies. Do NOT rely solely on nofib results for benchmarking this. I recommend at least-comparing megaparsec and container benchmarks. Nofib does not seeem to have+comparing megaparsec and container benchmarks. Nofib does not seem to have many instances of "loopy" Cmm where these make a difference. TODO:@@ -1350,7 +1345,7 @@ vcat (map (\(k,m) -> ppr (k,m :: IM.IntMap Double)) $ IM.toList g) ) - nodeCount = IM.foldl' (\count toMap -> IM.foldlWithKey' countTargets count toMap) (IM.size graph) graph+ nodeCount = IM.foldl' (\count toMap -> IM.foldlWithKey' countTargets (count + 1) toMap) 0 graph where countTargets = (\count k _ -> countNode k + count ) countNode n = if IM.member n graph then 0 else 1
compiler/GHC/CmmToAsm/CFG/Dominators.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE Strict #-}@@ -526,10 +527,9 @@ ----------------------------------------------------------------------------- --- Nothing better than reinvinting the state monad.+-- Nothing better than reinventing the state monad. newtype S z s a = S {unS :: forall o. (a -> s -> ST z o) -> s -> ST z o}-instance Functor (S z s) where- fmap f (S g) = S (\k -> g (k . f))+ deriving (Functor) instance Monad (S z s) where return = pure S g >>= f = S (\k -> g (\a -> unS (f a) k))
compiler/GHC/CmmToAsm/CPrim.hs view
@@ -87,7 +87,7 @@ atomicRMWLabel :: Width -> AtomicMachOp -> FastString atomicRMWLabel w amop = case amop of -- lots of boring cases, but we do it this way to get shared FastString- -- literals (compared to concatening strings and allocating FastStrings at+ -- literals (compared to concatenating strings and allocating FastStrings at -- runtime) AMO_Add -> case w of W8 -> fsLit "hs_atomic_add8"
compiler/GHC/CmmToAsm/Dwarf.hs view
@@ -26,50 +26,47 @@ import Data.Ord ( comparing ) import qualified Data.Map as Map import System.FilePath-import System.Directory ( getCurrentDirectory ) import qualified GHC.Cmm.Dataflow.Label as H import qualified GHC.Cmm.Dataflow.Collections as H -- | Generate DWARF/debug information-dwarfGen :: NCGConfig -> ModLocation -> UniqSupply -> [DebugBlock]- -> IO (SDoc, UniqSupply)-dwarfGen _ _ us [] = return (empty, us)-dwarfGen config modLoc us blocks = do+dwarfGen :: IsDoc doc => String -> NCGConfig -> ModLocation -> UniqSupply -> [DebugBlock]+ -> (doc, UniqSupply)+dwarfGen _ _ _ us [] = (empty, us)+dwarfGen compPath config modLoc us blocks = let platform = ncgPlatform config - -- Convert debug data structures to DWARF info records- let procs = debugSplitProcs blocks+ -- Convert debug data structures to DWARF info records+ procs = debugSplitProcs blocks stripBlocks dbg | ncgDwarfStripBlockInfo config = dbg { dblBlocks = [] } | otherwise = dbg- compPath <- getCurrentDirectory- let lowLabel = dblCLabel $ head procs+ lowLabel = dblCLabel $ head procs highLabel = mkAsmTempProcEndLabel $ dblCLabel $ last procs dwarfUnit = DwarfCompileUnit { dwChildren = map (procToDwarf config) (map stripBlocks procs) , dwName = fromMaybe "" (ml_hs_file modLoc) , dwCompDir = addTrailingPathSeparator compPath , dwProducer = cProjectName ++ " " ++ cProjectVersion- , dwLowLabel = pdoc platform lowLabel- , dwHighLabel = pdoc platform highLabel- , dwLineLabel = dwarfLineLabel+ , dwLowLabel = lowLabel+ , dwHighLabel = highLabel } - -- Check whether we have any source code information, so we do not- -- end up writing a pointer to an empty .debug_line section- -- (dsymutil on Mac Os gets confused by this).- let haveSrcIn blk = isJust (dblSourceTick blk) && isJust (dblPosition blk)+ -- Check whether we have any source code information, so we do not+ -- end up writing a pointer to an empty .debug_line section+ -- (dsymutil on Mac Os gets confused by this).+ haveSrcIn blk = isJust (dblSourceTick blk) && isJust (dblPosition blk) || any haveSrcIn (dblBlocks blk) haveSrc = any haveSrcIn procs -- .debug_abbrev section: Declare the format we're using- let abbrevSct = pprAbbrevDecls platform haveSrc+ abbrevSct = pprAbbrevDecls platform haveSrc -- .debug_info section: Information records on procedures and blocks- let -- unique to identify start and end compilation unit .debug_inf+ -- unique to identify start and end compilation unit .debug_inf (unitU, us') = takeUniqFromSupply us- infoSct = vcat [ dwarfInfoLabel <> colon+ infoSct = vcat [ line (dwarfInfoLabel <> colon) , dwarfInfoSection platform , compileUnitHeader platform unitU , pprDwarfInfo platform haveSrc dwarfUnit@@ -78,21 +75,23 @@ -- .debug_line section: Generated mainly by the assembler, but we -- need to label it- let lineSct = dwarfLineSection platform $$- dwarfLineLabel <> colon+ lineSct = dwarfLineSection platform $$+ line (dwarfLineLabel <> colon) -- .debug_frame section: Information about the layout of the GHC stack- let (framesU, us'') = takeUniqFromSupply us'+ (framesU, us'') = takeUniqFromSupply us' frameSct = dwarfFrameSection platform $$- dwarfFrameLabel <> colon $$+ line (dwarfFrameLabel <> colon) $$ pprDwarfFrame platform (debugFrame framesU procs) -- .aranges section: Information about the bounds of compilation units- let aranges' | ncgSplitSections config = map mkDwarfARange procs+ aranges' | ncgSplitSections config = map mkDwarfARange procs | otherwise = [DwarfARange lowLabel highLabel]- let aranges = dwarfARangesSection platform $$ pprDwarfARanges platform aranges' unitU+ aranges = dwarfARangesSection platform $$ pprDwarfARanges platform aranges' unitU - return (infoSct $$ abbrevSct $$ lineSct $$ frameSct $$ aranges, us'')+ in (infoSct $$ abbrevSct $$ lineSct $$ frameSct $$ aranges, us'')+{-# SPECIALIZE dwarfGen :: String -> NCGConfig -> ModLocation -> UniqSupply -> [DebugBlock] -> (SDoc, UniqSupply) #-}+{-# SPECIALIZE dwarfGen :: String -> NCGConfig -> ModLocation -> UniqSupply -> [DebugBlock] -> (HDoc, UniqSupply) #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Build an address range entry for one proc. -- With split sections, each proc needs its own entry, since they may get@@ -106,24 +105,24 @@ -- | Header for a compilation unit, establishing global format -- parameters-compileUnitHeader :: Platform -> Unique -> SDoc+compileUnitHeader :: IsDoc doc => Platform -> Unique -> doc compileUnitHeader platform unitU = let cuLabel = mkAsmTempLabel unitU -- sits right before initialLength field- length = pdoc platform (mkAsmTempEndLabel cuLabel) <> char '-' <> pdoc platform cuLabel+ length = pprAsmLabel platform (mkAsmTempEndLabel cuLabel) <> char '-' <> pprAsmLabel platform cuLabel <> text "-4" -- length of initialLength field- in vcat [ pdoc platform cuLabel <> colon- , text "\t.long " <> length -- compilation unit size+ in vcat [ line (pprAsmLabel platform cuLabel <> colon)+ , line (text "\t.long " <> length) -- compilation unit size , pprHalf 3 -- DWARF version , sectionOffset platform dwarfAbbrevLabel dwarfAbbrevLabel -- abbrevs offset- , text "\t.byte " <> ppr (platformWordSizeInBytes platform) -- word size+ , line (text "\t.byte " <> int (platformWordSizeInBytes platform)) -- word size ] -- | Compilation unit footer, mainly establishing size of debug sections-compileUnitFooter :: Platform -> Unique -> SDoc+compileUnitFooter :: IsDoc doc => Platform -> Unique -> doc compileUnitFooter platform unitU = let cuEndLabel = mkAsmTempEndLabel $ mkAsmTempLabel unitU- in pdoc platform cuEndLabel <> colon+ in line (pprAsmLabel platform cuEndLabel <> colon) -- | Splits the blocks by procedures. In the result all nested blocks -- will come from the same procedure as the top-level block. See
compiler/GHC/CmmToAsm/Dwarf/Constants.hs view
@@ -144,17 +144,29 @@ -- * Dwarf section declarations dwarfInfoSection, dwarfAbbrevSection, dwarfLineSection,- dwarfFrameSection, dwarfGhcSection, dwarfARangesSection :: Platform -> SDoc+ dwarfFrameSection, dwarfGhcSection, dwarfARangesSection :: IsDoc doc => Platform -> doc dwarfInfoSection platform = dwarfSection platform "info" dwarfAbbrevSection platform = dwarfSection platform "abbrev" dwarfLineSection platform = dwarfSection platform "line" dwarfFrameSection platform = dwarfSection platform "frame" dwarfGhcSection platform = dwarfSection platform "ghc" dwarfARangesSection platform = dwarfSection platform "aranges"+{-# SPECIALIZE dwarfInfoSection :: Platform -> SDoc #-}+{-# SPECIALIZE dwarfInfoSection :: Platform -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable+{-# SPECIALIZE dwarfAbbrevSection :: Platform -> SDoc #-}+{-# SPECIALIZE dwarfAbbrevSection :: Platform -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable+{-# SPECIALIZE dwarfLineSection :: Platform -> SDoc #-}+{-# SPECIALIZE dwarfLineSection :: Platform -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable+{-# SPECIALIZE dwarfFrameSection :: Platform -> SDoc #-}+{-# SPECIALIZE dwarfFrameSection :: Platform -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable+{-# SPECIALIZE dwarfGhcSection :: Platform -> SDoc #-}+{-# SPECIALIZE dwarfGhcSection :: Platform -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable+{-# SPECIALIZE dwarfARangesSection :: Platform -> SDoc #-}+{-# SPECIALIZE dwarfARangesSection :: Platform -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -dwarfSection :: Platform -> String -> SDoc+dwarfSection :: IsDoc doc => Platform -> String -> doc dwarfSection platform name =- case platformOS platform of+ line $ case platformOS platform of os | osElfTarget os -> text "\t.section .debug_" <> text name <> text ",\"\"," <> sectionType platform "progbits"@@ -162,13 +174,24 @@ -> text "\t.section __DWARF,__debug_" <> text name <> text ",regular,debug" | otherwise -> text "\t.section .debug_" <> text name <> text ",\"dr\""+{-# SPECIALIZE dwarfSection :: Platform -> String -> SDoc #-}+{-# SPECIALIZE dwarfSection :: Platform -> String -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable + -- * Dwarf section labels-dwarfInfoLabel, dwarfAbbrevLabel, dwarfLineLabel, dwarfFrameLabel :: SDoc+dwarfInfoLabel, dwarfAbbrevLabel, dwarfLineLabel, dwarfFrameLabel :: IsLine doc => doc dwarfInfoLabel = text ".Lsection_info" dwarfAbbrevLabel = text ".Lsection_abbrev" dwarfLineLabel = text ".Lsection_line" dwarfFrameLabel = text ".Lsection_frame"+{-# SPECIALIZE dwarfInfoLabel :: SDoc #-}+{-# SPECIALIZE dwarfInfoLabel :: HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable+{-# SPECIALIZE dwarfAbbrevLabel :: SDoc #-}+{-# SPECIALIZE dwarfAbbrevLabel :: HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable+{-# SPECIALIZE dwarfLineLabel :: SDoc #-}+{-# SPECIALIZE dwarfLineLabel :: HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable+{-# SPECIALIZE dwarfFrameLabel :: SDoc #-}+{-# SPECIALIZE dwarfFrameLabel :: HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Mapping of registers to DWARF register numbers dwarfRegNo :: Platform -> Reg -> Word8
compiler/GHC/CmmToAsm/Dwarf/Types.hs view
@@ -59,9 +59,8 @@ , dwName :: String , dwProducer :: String , dwCompDir :: String- , dwLowLabel :: SDoc- , dwHighLabel :: SDoc- , dwLineLabel :: SDoc }+ , dwLowLabel :: CLabel+ , dwHighLabel :: CLabel } | DwarfSubprogram { dwChildren :: [DwarfInfo] , dwName :: String , dwLabel :: CLabel@@ -88,13 +87,13 @@ deriving (Eq, Enum) -- | Generate assembly for the given abbreviation code-pprAbbrev :: DwarfAbbrev -> SDoc+pprAbbrev :: IsDoc doc => DwarfAbbrev -> doc pprAbbrev = pprLEBWord . fromIntegral . fromEnum -- | Abbreviation declaration. This explains the binary encoding we -- use for representing 'DwarfInfo'. Be aware that this must be updated -- along with 'pprDwarfInfo'.-pprAbbrevDecls :: Platform -> Bool -> SDoc+pprAbbrevDecls :: IsDoc doc => Platform -> Bool -> doc pprAbbrevDecls platform haveDebugLine = let mkAbbrev abbr tag chld flds = let fld (tag, form) = pprLEBWord tag $$ pprLEBWord form@@ -111,7 +110,7 @@ , (dW_AT_frame_base, dW_FORM_block1) ] in dwarfAbbrevSection platform $$- dwarfAbbrevLabel <> colon $$+ line (dwarfAbbrevLabel <> colon) $$ mkAbbrev DwAbbrCompileUnit dW_TAG_compile_unit dW_CHILDREN_yes ([(dW_AT_name, dW_FORM_string) , (dW_AT_producer, dW_FORM_string)@@ -144,9 +143,11 @@ , (dW_AT_ghc_span_end_col, dW_FORM_data2) ] $$ pprByte 0+{-# SPECIALIZE pprAbbrevDecls :: Platform -> Bool -> SDoc #-}+{-# SPECIALIZE pprAbbrevDecls :: Platform -> Bool -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Generate assembly for DWARF data-pprDwarfInfo :: Platform -> Bool -> DwarfInfo -> SDoc+pprDwarfInfo :: IsDoc doc => Platform -> Bool -> DwarfInfo -> doc pprDwarfInfo platform haveSrc d = case d of DwarfCompileUnit {} -> hasChildren@@ -159,39 +160,41 @@ vcat (map (pprDwarfInfo platform haveSrc) (dwChildren d)) $$ pprDwarfInfoClose noChildren = pprDwarfInfoOpen platform haveSrc d+{-# SPECIALIZE pprDwarfInfo :: Platform -> Bool -> DwarfInfo -> SDoc #-}+{-# SPECIALIZE pprDwarfInfo :: Platform -> Bool -> DwarfInfo -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Print a CLabel name in a ".stringz \"LABEL\""-pprLabelString :: Platform -> CLabel -> SDoc+pprLabelString :: IsDoc doc => Platform -> CLabel -> doc pprLabelString platform label =- pprString' -- we don't need to escape the string as labels don't contain exotic characters- $ pprCLabel platform CStyle label -- pretty-print as C label (foreign labels may be printed differently in Asm)+ pprString' -- we don't need to escape the string as labels don't contain exotic characters+ $ pprCLabel platform label -- pretty-print as C label (foreign labels may be printed differently in Asm) -- | Prints assembler data corresponding to DWARF info records. Note -- that the binary format of this is parameterized in @abbrevDecls@ and -- has to be kept in synch.-pprDwarfInfoOpen :: Platform -> Bool -> DwarfInfo -> SDoc+pprDwarfInfoOpen :: IsDoc doc => Platform -> Bool -> DwarfInfo -> doc pprDwarfInfoOpen platform haveSrc (DwarfCompileUnit _ name producer compDir lowLabel- highLabel lineLbl) =+ highLabel) = pprAbbrev DwAbbrCompileUnit $$ pprString name $$ pprString producer $$ pprData4 dW_LANG_Haskell $$ pprString compDir -- Offset due to Note [Info Offset]- $$ pprWord platform (lowLabel <> text "-1")- $$ pprWord platform highLabel+ $$ pprWord platform (pprAsmLabel platform lowLabel <> text "-1")+ $$ pprWord platform (pprAsmLabel platform highLabel) $$ if haveSrc- then sectionOffset platform lineLbl dwarfLineLabel+ then sectionOffset platform dwarfLineLabel dwarfLineLabel else empty pprDwarfInfoOpen platform _ (DwarfSubprogram _ name label parent) =- pdoc platform (mkAsmTempDieLabel label) <> colon+ line (pprAsmLabel platform (mkAsmTempDieLabel label) <> colon) $$ pprAbbrev abbrev $$ pprString name $$ pprLabelString platform label $$ pprFlag (externallyVisibleCLabel label) -- Offset due to Note [Info Offset]- $$ pprWord platform (pdoc platform label <> text "-1")- $$ pprWord platform (pdoc platform $ mkAsmTempProcEndLabel label)+ $$ pprWord platform (pprAsmLabel platform label <> text "-1")+ $$ pprWord platform (pprAsmLabel platform $ mkAsmTempProcEndLabel label) $$ pprByte 1 $$ pprByte dW_OP_call_frame_cfa $$ parentValue@@ -199,17 +202,17 @@ abbrev = case parent of Nothing -> DwAbbrSubprogram Just _ -> DwAbbrSubprogramWithParent parentValue = maybe empty pprParentDie parent- pprParentDie sym = sectionOffset platform (pdoc platform sym) dwarfInfoLabel+ pprParentDie sym = sectionOffset platform (pprAsmLabel platform sym) dwarfInfoLabel pprDwarfInfoOpen platform _ (DwarfBlock _ label Nothing) =- pdoc platform (mkAsmTempDieLabel label) <> colon+ line (pprAsmLabel platform (mkAsmTempDieLabel label) <> colon) $$ pprAbbrev DwAbbrBlockWithoutCode $$ pprLabelString platform label pprDwarfInfoOpen platform _ (DwarfBlock _ label (Just marker)) =- pdoc platform (mkAsmTempDieLabel label) <> colon+ line (pprAsmLabel platform (mkAsmTempDieLabel label) <> colon) $$ pprAbbrev DwAbbrBlock $$ pprLabelString platform label- $$ pprWord platform (pdoc platform marker)- $$ pprWord platform (pdoc platform $ mkAsmTempEndLabel marker)+ $$ pprWord platform (pprAsmLabel platform marker)+ $$ pprWord platform (pprAsmLabel platform $ mkAsmTempEndLabel marker) pprDwarfInfoOpen _ _ (DwarfSrcNote ss) = pprAbbrev DwAbbrGhcSrcNote $$ pprString' (ftext $ srcSpanFile ss)@@ -219,7 +222,7 @@ $$ pprHalf (fromIntegral $ srcSpanEndCol ss) -- | Close a DWARF info record with children-pprDwarfInfoClose :: SDoc+pprDwarfInfoClose :: IsDoc doc => doc pprDwarfInfoClose = pprAbbrev DwAbbrNull -- | A DWARF address range. This is used by the debugger to quickly locate@@ -233,7 +236,7 @@ -- | Print assembler directives corresponding to a DWARF @.debug_aranges@ -- address table entry.-pprDwarfARanges :: Platform -> [DwarfARange] -> Unique -> SDoc+pprDwarfARanges :: IsDoc doc => Platform -> [DwarfARange] -> Unique -> doc pprDwarfARanges platform arngs unitU = let wordSize = platformWordSizeInBytes platform paddingSize = 4 :: Int@@ -243,9 +246,9 @@ pad n = vcat $ replicate n $ pprByte 0 -- Fix for #17428 initialLength = 8 + paddingSize + (1 + length arngs) * 2 * wordSize- in pprDwWord (ppr initialLength)+ in pprDwWord (int initialLength) $$ pprHalf 2- $$ sectionOffset platform (pdoc platform $ mkAsmTempLabel $ unitU) dwarfInfoLabel+ $$ sectionOffset platform (pprAsmLabel platform $ mkAsmTempLabel $ unitU) dwarfInfoLabel $$ pprByte (fromIntegral wordSize) $$ pprByte 0 $$ pad paddingSize@@ -254,15 +257,17 @@ -- terminus $$ pprWord platform (char '0') $$ pprWord platform (char '0')+{-# SPECIALIZE pprDwarfARanges :: Platform -> [DwarfARange] -> Unique -> SDoc #-}+{-# SPECIALIZE pprDwarfARanges :: Platform -> [DwarfARange] -> Unique -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -pprDwarfARange :: Platform -> DwarfARange -> SDoc+pprDwarfARange :: IsDoc doc => Platform -> DwarfARange -> doc pprDwarfARange platform arng = -- Offset due to Note [Info Offset].- pprWord platform (pdoc platform (dwArngStartLabel arng) <> text "-1")+ pprWord platform (pprAsmLabel platform (dwArngStartLabel arng) <> text "-1") $$ pprWord platform length where- length = pdoc platform (dwArngEndLabel arng)- <> char '-' <> pdoc platform (dwArngStartLabel arng)+ length = pprAsmLabel platform (dwArngEndLabel arng)+ <> char '-' <> pprAsmLabel platform (dwArngStartLabel arng) -- | Information about unwind instructions for a procedure. This -- corresponds to a "Common Information Entry" (CIE) in DWARF.@@ -293,21 +298,21 @@ -- in the block } -instance OutputableP env CLabel => OutputableP env DwarfFrameBlock where+instance OutputableP Platform DwarfFrameBlock where pdoc env (DwarfFrameBlock hasInfo unwinds) = braces $ ppr hasInfo <+> pdoc env unwinds -- | Header for the @.debug_frame@ section. Here we emit the "Common -- Information Entry" record that establishes general call frame -- parameters and the default stack layout.-pprDwarfFrame :: Platform -> DwarfFrame -> SDoc+pprDwarfFrame :: forall doc. IsDoc doc => Platform -> DwarfFrame -> doc pprDwarfFrame platform DwarfFrame{dwCieLabel=cieLabel,dwCieInit=cieInit,dwCieProcs=procs} = let cieStartLabel= mkAsmTempDerivedLabel cieLabel (fsLit "_start") cieEndLabel = mkAsmTempEndLabel cieLabel- length = pdoc platform cieEndLabel <> char '-' <> pdoc platform cieStartLabel+ length = pprAsmLabel platform cieEndLabel <> char '-' <> pprAsmLabel platform cieStartLabel spReg = dwarfGlobalRegNo platform Sp retReg = dwarfReturnRegNo platform wordSize = platformWordSizeInBytes platform- pprInit :: (GlobalReg, Maybe UnwindExpr) -> SDoc+ pprInit :: (GlobalReg, Maybe UnwindExpr) -> doc pprInit (g, uw) = pprSetUnwind platform g (Nothing, uw) -- Preserve C stack pointer: This necessary to override that default@@ -316,9 +321,9 @@ ArchX86 -> pprByte dW_CFA_same_value $$ pprLEBWord 4 ArchX86_64 -> pprByte dW_CFA_same_value $$ pprLEBWord 7 _ -> empty- in vcat [ pdoc platform cieLabel <> colon+ in vcat [ line (pprAsmLabel platform cieLabel <> colon) , pprData4' length -- Length of CIE- , pdoc platform cieStartLabel <> colon+ , line (pprAsmLabel platform cieStartLabel <> colon) , pprData4' (text "-1") -- Common Information Entry marker (-1 = 0xf..f) , pprByte 3 -- CIE version (we require DWARF 3)@@ -346,41 +351,43 @@ , pprLEBWord 0 ] $$ wordAlign platform $$- pdoc platform cieEndLabel <> colon $$+ line (pprAsmLabel platform cieEndLabel <> colon) $$ -- Procedure unwind tables vcat (map (pprFrameProc platform cieLabel cieInit) procs)+{-# SPECIALIZE pprDwarfFrame :: Platform -> DwarfFrame -> SDoc #-}+{-# SPECIALIZE pprDwarfFrame :: Platform -> DwarfFrame -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Writes a "Frame Description Entry" for a procedure. This consists -- mainly of referencing the CIE and writing state machine -- instructions to describe how the frame base (CFA) changes.-pprFrameProc :: Platform -> CLabel -> UnwindTable -> DwarfFrameProc -> SDoc+pprFrameProc :: IsDoc doc => Platform -> CLabel -> UnwindTable -> DwarfFrameProc -> doc pprFrameProc platform frameLbl initUw (DwarfFrameProc procLbl hasInfo blocks) = let fdeLabel = mkAsmTempDerivedLabel procLbl (fsLit "_fde") fdeEndLabel = mkAsmTempDerivedLabel procLbl (fsLit "_fde_end") procEnd = mkAsmTempProcEndLabel procLbl ifInfo str = if hasInfo then text str else empty -- see Note [Info Offset]- in vcat [ whenPprDebug $ text "# Unwinding for" <+> pdoc platform procLbl <> colon- , pprData4' (pdoc platform fdeEndLabel <> char '-' <> pdoc platform fdeLabel)- , pdoc platform fdeLabel <> colon- , pprData4' (pdoc platform frameLbl <> char '-' <> dwarfFrameLabel) -- Reference to CIE- , pprWord platform (pdoc platform procLbl <> ifInfo "-1") -- Code pointer- , pprWord platform (pdoc platform procEnd <> char '-' <>- pdoc platform procLbl <> ifInfo "+1") -- Block byte length+ in vcat [ whenPprDebug $ line $ text "# Unwinding for" <+> pprAsmLabel platform procLbl <> colon+ , pprData4' (pprAsmLabel platform fdeEndLabel <> char '-' <> pprAsmLabel platform fdeLabel)+ , line (pprAsmLabel platform fdeLabel <> colon)+ , pprData4' (pprAsmLabel platform frameLbl <> char '-' <> dwarfFrameLabel) -- Reference to CIE+ , pprWord platform (pprAsmLabel platform procLbl <> ifInfo "-1") -- Code pointer+ , pprWord platform (pprAsmLabel platform procEnd <> char '-' <>+ pprAsmLabel platform procLbl <> ifInfo "+1") -- Block byte length ] $$ vcat (S.evalState (mapM (pprFrameBlock platform) blocks) initUw) $$ wordAlign platform $$- pdoc platform fdeEndLabel <> colon+ line (pprAsmLabel platform fdeEndLabel <> colon) -- | Generates unwind information for a block. We only generate -- instructions where unwind information actually changes. This small -- optimisations saves a lot of space, as subsequent blocks often have -- the same unwind information.-pprFrameBlock :: Platform -> DwarfFrameBlock -> S.State UnwindTable SDoc+pprFrameBlock :: forall doc. IsDoc doc => Platform -> DwarfFrameBlock -> S.State UnwindTable doc pprFrameBlock platform (DwarfFrameBlock hasInfo uws0) = vcat <$> zipWithM pprFrameDecl (True : repeat False) uws0 where- pprFrameDecl :: Bool -> UnwindPoint -> S.State UnwindTable SDoc+ pprFrameDecl :: Bool -> UnwindPoint -> S.State UnwindTable doc pprFrameDecl firstDecl (UnwindPoint lbl uws) = S.state $ \oldUws -> let -- Did a register's unwind expression change? isChanged :: GlobalReg -> Maybe UnwindExpr@@ -402,7 +409,7 @@ then (empty, oldUws) else let -- see Note [Info Offset] needsOffset = firstDecl && hasInfo- lblDoc = pdoc platform lbl <>+ lblDoc = pprAsmLabel platform lbl <> if needsOffset then text "-1" else empty doc = pprByte dW_CFA_set_loc $$ pprWord platform lblDoc $$ vcat (map (uncurry $ pprSetUnwind platform) changed)@@ -450,12 +457,12 @@ -- | Generate code for setting the unwind information for a register, -- optimized using its known old value in the table. Note that "Sp" is -- special: We see it as synonym for the CFA.-pprSetUnwind :: Platform+pprSetUnwind :: IsDoc doc => Platform -> GlobalReg -- ^ the register to produce an unwinding table entry for -> (Maybe UnwindExpr, Maybe UnwindExpr) -- ^ the old and new values of the register- -> SDoc+ -> doc pprSetUnwind plat g (_, Nothing) = pprUndefUnwind plat g pprSetUnwind _ Sp (Just (UwReg s _), Just (UwReg s' o')) | s == s'@@ -495,13 +502,13 @@ -- | Print the register number of the given 'GlobalReg' as an unsigned LEB128 -- encoded number.-pprLEBRegNo :: Platform -> GlobalReg -> SDoc+pprLEBRegNo :: IsDoc doc => Platform -> GlobalReg -> doc pprLEBRegNo plat = pprLEBWord . fromIntegral . dwarfGlobalRegNo plat -- | Generates a DWARF expression for the given unwind expression. If -- @spIsCFA@ is true, we see @Sp@ as the frame base CFA where it gets -- mentioned.-pprUnwindExpr :: Platform -> Bool -> UnwindExpr -> SDoc+pprUnwindExpr :: IsDoc doc => Platform -> Bool -> UnwindExpr -> doc pprUnwindExpr platform spIsCFA expr = let pprE (UwConst i) | i >= 0 && i < 32 = pprByte (dW_OP_lit0 + fromIntegral i)@@ -513,96 +520,112 @@ pprE (UwReg g i) = pprByte (dW_OP_breg0+dwarfGlobalRegNo platform g) $$ pprLEBInt i pprE (UwDeref u) = pprE u $$ pprByte dW_OP_deref- pprE (UwLabel l) = pprByte dW_OP_addr $$ pprWord platform (pdoc platform l)+ pprE (UwLabel l) = pprByte dW_OP_addr $$ pprWord platform (pprAsmLabel platform l) pprE (UwPlus u1 u2) = pprE u1 $$ pprE u2 $$ pprByte dW_OP_plus pprE (UwMinus u1 u2) = pprE u1 $$ pprE u2 $$ pprByte dW_OP_minus pprE (UwTimes u1 u2) = pprE u1 $$ pprE u2 $$ pprByte dW_OP_mul- in text "\t.uleb128 2f-1f" $$ -- DW_FORM_block length+ in line (text "\t.uleb128 2f-1f") $$ -- DW_FORM_block length -- computed as the difference of the following local labels 2: and 1:- text "1:" $$+ line (text "1:") $$ pprE expr $$- text "2:"+ line (text "2:") -- | Generate code for re-setting the unwind information for a -- register to @undefined@-pprUndefUnwind :: Platform -> GlobalReg -> SDoc+pprUndefUnwind :: IsDoc doc => Platform -> GlobalReg -> doc pprUndefUnwind plat g = pprByte dW_CFA_undefined $$ pprLEBRegNo plat g -- | Align assembly at (machine) word boundary-wordAlign :: Platform -> SDoc+wordAlign :: IsDoc doc => Platform -> doc wordAlign plat =- text "\t.align " <> case platformOS plat of+ line $ text "\t.align " <> case platformOS plat of OSDarwin -> case platformWordSize plat of PW8 -> char '3' PW4 -> char '2'- _other -> ppr (platformWordSizeInBytes plat)+ _other -> int (platformWordSizeInBytes plat)+{-# SPECIALIZE wordAlign :: Platform -> SDoc #-}+{-# SPECIALIZE wordAlign :: Platform -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Assembly for a single byte of constant DWARF data-pprByte :: Word8 -> SDoc-pprByte x = text "\t.byte " <> ppr (fromIntegral x :: Word)+pprByte :: IsDoc doc => Word8 -> doc+pprByte x = line $ text "\t.byte " <> integer (fromIntegral x)+{-# SPECIALIZE pprByte :: Word8 -> SDoc #-}+{-# SPECIALIZE pprByte :: Word8 -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Assembly for a two-byte constant integer-pprHalf :: Word16 -> SDoc-pprHalf x = text "\t.short" <+> ppr (fromIntegral x :: Word)+pprHalf :: IsDoc doc => Word16 -> doc+pprHalf x = line $ text "\t.short" <+> integer (fromIntegral x)+{-# SPECIALIZE pprHalf :: Word16 -> SDoc #-}+{-# SPECIALIZE pprHalf :: Word16 -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Assembly for a constant DWARF flag-pprFlag :: Bool -> SDoc+pprFlag :: IsDoc doc => Bool -> doc pprFlag f = pprByte (if f then 0xff else 0x00) -- | Assembly for 4 bytes of dynamic DWARF data-pprData4' :: SDoc -> SDoc-pprData4' x = text "\t.long " <> x+pprData4' :: IsDoc doc => Line doc -> doc+pprData4' x = line (text "\t.long " <> x)+{-# SPECIALIZE pprData4' :: SDoc -> SDoc #-}+{-# SPECIALIZE pprData4' :: HLine -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Assembly for 4 bytes of constant DWARF data-pprData4 :: Word -> SDoc-pprData4 = pprData4' . ppr+pprData4 :: IsDoc doc => Word -> doc+pprData4 = pprData4' . integer . fromIntegral -- | Assembly for a DWARF word of dynamic data. This means 32 bit, as -- we are generating 32 bit DWARF.-pprDwWord :: SDoc -> SDoc+pprDwWord :: IsDoc doc => Line doc -> doc pprDwWord = pprData4'+{-# SPECIALIZE pprDwWord :: SDoc -> SDoc #-}+{-# SPECIALIZE pprDwWord :: HLine -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Assembly for a machine word of dynamic data. Depends on the -- architecture we are currently generating code for.-pprWord :: Platform -> SDoc -> SDoc+pprWord :: IsDoc doc => Platform -> Line doc -> doc pprWord plat s =- case platformWordSize plat of+ line $ case platformWordSize plat of PW4 -> text "\t.long " <> s PW8 -> text "\t.quad " <> s+{-# SPECIALIZE pprWord :: Platform -> SDoc -> SDoc #-}+{-# SPECIALIZE pprWord :: Platform -> HLine -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Prints a number in "little endian base 128" format. The idea is -- to optimize for small numbers by stopping once all further bytes -- would be 0. The highest bit in every byte signals whether there -- are further bytes to read.-pprLEBWord :: Word -> SDoc+pprLEBWord :: IsDoc doc => Word -> doc pprLEBWord x | x < 128 = pprByte (fromIntegral x) | otherwise = pprByte (fromIntegral $ 128 .|. (x .&. 127)) $$ pprLEBWord (x `shiftR` 7)+{-# SPECIALIZE pprLEBWord :: Word -> SDoc #-}+{-# SPECIALIZE pprLEBWord :: Word -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Same as @pprLEBWord@, but for a signed number-pprLEBInt :: Int -> SDoc+pprLEBInt :: IsDoc doc => Int -> doc pprLEBInt x | x >= -64 && x < 64 = pprByte (fromIntegral (x .&. 127)) | otherwise = pprByte (fromIntegral $ 128 .|. (x .&. 127)) $$ pprLEBInt (x `shiftR` 7)+{-# SPECIALIZE pprLEBInt :: Int -> SDoc #-}+{-# SPECIALIZE pprLEBInt :: Int -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Generates a dynamic null-terminated string. If required the -- caller needs to make sure that the string is escaped properly.-pprString' :: SDoc -> SDoc-pprString' str = text "\t.asciz \"" <> str <> char '"'+pprString' :: IsDoc doc => Line doc -> doc+pprString' str = line (text "\t.asciz \"" <> str <> char '"') -- | Generate a string constant. We take care to escape the string.-pprString :: String -> SDoc+pprString :: IsDoc doc => String -> doc pprString str = pprString' $ hcat $ map escapeChar $ if str `lengthIs` utf8EncodedLength str then str- else map (chr . fromIntegral) $ BS.unpack $ utf8EncodeString str+ else map (chr . fromIntegral) $ BS.unpack $ utf8EncodeByteString str -- | Escape a single non-unicode character-escapeChar :: Char -> SDoc+escapeChar :: IsLine doc => Char -> doc escapeChar '\\' = text "\\\\" escapeChar '\"' = text "\\\"" escapeChar '\n' = text "\\n"@@ -621,9 +644,11 @@ -- us to just reference the target directly, and will figure out on -- their own that we actually need an offset. Finally, Windows has -- a special directive to refer to relative offsets. Fun.-sectionOffset :: Platform -> SDoc -> SDoc -> SDoc+sectionOffset :: IsDoc doc => Platform -> Line doc -> Line doc -> doc sectionOffset plat target section = case platformOS plat of OSDarwin -> pprDwWord (target <> char '-' <> section)- OSMinGW32 -> text "\t.secrel32 " <> target+ OSMinGW32 -> line (text "\t.secrel32 " <> target) _other -> pprDwWord target+{-# SPECIALIZE sectionOffset :: Platform -> SDoc -> SDoc -> SDoc #-}+{-# SPECIALIZE sectionOffset :: Platform -> HLine -> HLine -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable
compiler/GHC/CmmToAsm/Instr.hs view
@@ -15,6 +15,7 @@ import GHC.Cmm.BlockId import GHC.CmmToAsm.Config+import GHC.Data.FastString -- | Holds a list of source and destination registers used by a -- particular instruction.@@ -160,4 +161,4 @@ pprInstr :: Platform -> instr -> SDoc -- Create a comment instruction- mkComment :: SDoc -> [instr]+ mkComment :: FastString -> [instr]
compiler/GHC/CmmToAsm/Monad.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE ViewPatterns #-} -- ----------------------------------------------------------------------------- --@@ -20,7 +22,6 @@ addImmediateSuccessorNat, updateCfgNat, getUniqueNat,- mapAccumLNat, setDeltaNat, getConfig, getPlatform,@@ -32,7 +33,6 @@ getPicBaseMaybeNat, getPicBaseNat, getCfgWeights,- getModLoc, getFileId, getDebugBlock, @@ -67,10 +67,9 @@ import GHC.Types.Unique ( Unique ) import GHC.Unit.Module -import Control.Monad ( ap )--import GHC.Utils.Outputable (SDoc, ppr)+import GHC.Utils.Outputable (SDoc, HDoc, ppr) import GHC.Utils.Panic (pprPanic)+import GHC.Utils.Monad.State.Strict (State (..), runState, state) import GHC.Utils.Misc import GHC.CmmToAsm.CFG import GHC.CmmToAsm.CFG.Weight@@ -85,15 +84,16 @@ shortcutJump :: (BlockId -> Maybe jumpDest) -> instr -> instr, -- | 'Module' is only for printing internal labels. See Note [Internal proc -- labels] in CLabel.- pprNatCmmDecl :: NatCmmDecl statics instr -> SDoc,+ pprNatCmmDeclS :: NatCmmDecl statics instr -> SDoc,+ pprNatCmmDeclH :: NatCmmDecl statics instr -> HDoc,+ -- see Note [pprNatCmmDeclS and pprNatCmmDeclH] maxSpillSlots :: Int, allocatableRegs :: [RealReg], ncgAllocMoreStack :: Int -> NatCmmDecl statics instr -> UniqSM (NatCmmDecl statics instr, [(BlockId,BlockId)]), -- ^ The list of block ids records the redirected jumps to allow us to update -- the CFG.- ncgMakeFarBranches :: Platform -> LabelMap RawCmmStatics -> [NatBasicBlock instr]- -> UniqSM [NatBasicBlock instr],+ ncgMakeFarBranches :: LabelMap RawCmmStatics -> [NatBasicBlock instr] -> [NatBasicBlock instr], extractUnwindPoints :: [instr] -> [UnwindPoint], -- ^ given the instruction sequence of a block, produce a list of -- the block's 'UnwindPoint's@@ -105,14 +105,45 @@ -- when possible. } +{- Note [pprNatCmmDeclS and pprNatCmmDeclH]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Each NcgImpl provides two implementations of its CmmDecl printer, pprNatCmmDeclS+and pprNatCmmDeclH, which are specialized to SDoc and HDoc, respectively+(see Note [SDoc versus HDoc] in GHC.Utils.Outputable). These are both internally+implemented as a single, polymorphic function, but they need to be stored using+monomorphic types to ensure the specialized versions are used, which is+essential for performance (see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable).++One might wonder why we bother with pprNatCmmDeclS and SDoc at all, since we+have a perfectly serviceable HDoc-based implementation that is more efficient.+However, it turns out we benefit from keeping both, for two (related) reasons:++ 1. Although we absolutely want to take care to use pprNatCmmDeclH for actual+ code generation (the improved performance there is why we have HDoc at+ all!), we also sometimes print assembly for debug dumps, when requested via+ -ddump-asm. In this case, it’s more convenient to produce an SDoc, which+ can be concatenated with other SDocs for consistency with the general-+ purpose dump file infrastructure.++ 2. Some debug information is sometimes useful to include in -ddump-asm that is+ neither necessary nor useful in normal code generation, and it turns out to+ be tricky to format neatly using the one-line-at-a-time model of HLine/HDoc.++Therefore, we provide both pprNatCmmDeclS and pprNatCmmDeclH, and we sometimes+include additional information in the SDoc variant using dualDoc+(see Note [dualLine and dualDoc] in GHC.Utils.Outputable). However, it is+absolutely *critical* that pprNatCmmDeclS is not actually used unless -ddump-asm+is provided, as that would rather defeat the whole point. (Fortunately, the+difference in allocations between the two implementations is so vast that such a+mistake would readily show up in performance tests). -}+ data NatM_State = NatM_State { natm_us :: UniqSupply,- natm_delta :: Int, -- ^ Stack offset for unwinding information+ natm_delta :: Int, natm_imports :: [(CLabel)], natm_pic :: Maybe Reg, natm_config :: NCGConfig,- natm_modloc :: ModLocation, natm_fileid :: DwarfFiles, natm_debug_map :: LabelMap DebugBlock, natm_cfg :: CFG@@ -123,38 +154,35 @@ type DwarfFiles = UniqFM FastString (FastString, Int) -newtype NatM result = NatM (NatM_State -> (result, NatM_State))- deriving (Functor)+newtype NatM a = NatM' (State NatM_State a)+ deriving stock (Functor)+ deriving (Applicative, Monad) via State NatM_State +pattern NatM :: (NatM_State -> (a, NatM_State)) -> NatM a+pattern NatM f <- NatM' (runState -> f)+ where NatM f = NatM' (state f)+{-# COMPLETE NatM #-}+ unNat :: NatM a -> NatM_State -> (a, NatM_State) unNat (NatM a) = a -mkNatM_State :: UniqSupply -> Int -> NCGConfig -> ModLocation ->+mkNatM_State :: UniqSupply -> Int -> NCGConfig -> DwarfFiles -> LabelMap DebugBlock -> CFG -> NatM_State mkNatM_State us delta config- = \loc dwf dbg cfg ->+ = \dwf dbg cfg -> NatM_State { natm_us = us , natm_delta = delta , natm_imports = [] , natm_pic = Nothing , natm_config = config- , natm_modloc = loc , natm_fileid = dwf , natm_debug_map = dbg , natm_cfg = cfg } initNat :: NatM_State -> NatM a -> (a, NatM_State)-initNat init_st m- = case unNat m init_st of { (r,st) -> (r,st) }--instance Applicative NatM where- pure = returnNat- (<*>) = ap--instance Monad NatM where- (>>=) = thenNat+initNat = flip unNat instance MonadUnique NatM where getUniqueSupplyM = NatM $ \st ->@@ -165,27 +193,6 @@ case takeUniqFromSupply (natm_us st) of (uniq, us') -> (uniq, st {natm_us = us'}) -thenNat :: NatM a -> (a -> NatM b) -> NatM b-thenNat expr cont- = NatM $ \st -> case unNat expr st of- (result, st') -> unNat (cont result) st'--returnNat :: a -> NatM a-returnNat result- = NatM $ \st -> (result, st)--mapAccumLNat :: (acc -> x -> NatM (acc, y))- -> acc- -> [x]- -> NatM (acc, [y])--mapAccumLNat _ b []- = return (b, [])-mapAccumLNat f b (x:xs)- = do (b__2, x__2) <- f b x- (b__3, xs__2) <- mapAccumLNat f b__2 xs- return (b__3, x__2:xs__2)- getUniqueNat :: NatM Unique getUniqueNat = NatM $ \ st -> case takeUniqFromSupply $ natm_us st of@@ -245,9 +252,7 @@ getBlockIdNat :: NatM BlockId getBlockIdNat- = do u <- getUniqueNat- return (mkBlockId u)-+ = mkBlockId <$> getUniqueNat getNewLabelNat :: NatM CLabel getNewLabelNat@@ -309,10 +314,6 @@ -> do reg <- getNewRegNat rep NatM (\state -> (reg, state { natm_pic = Just reg }))--getModLoc :: NatM ModLocation-getModLoc- = NatM $ \ st -> (natm_modloc st, st) -- | Get native code generator configuration getConfig :: NatM NCGConfig
compiler/GHC/CmmToAsm/PIC.hs view
@@ -532,11 +532,11 @@ -- -- We don't need to declare any offset tables. -- However, for PIC on x86, we need a small helper function.-pprGotDeclaration :: NCGConfig -> SDoc+pprGotDeclaration :: NCGConfig -> HDoc pprGotDeclaration config = case (arch,os) of (ArchX86, OSDarwin) | ncgPIC config- -> vcat [+ -> lines_ [ text ".section __TEXT,__textcoal_nt,coalesced,no_toc", text ".weak_definition ___i686.get_pc_thunk.ax", text ".private_extern ___i686.get_pc_thunk.ax",@@ -548,7 +548,7 @@ -- Emit XCOFF TOC section (_, OSAIX)- -> vcat $ [ text ".toc"+ -> lines_ [ text ".toc" , text ".tc ghc_toc_table[TC],.LCTOC1" , text ".csect ghc_toc_table[RW]" -- See Note [.LCTOC1 in PPC PIC code]@@ -558,16 +558,16 @@ -- PPC 64 ELF v1 needs a Table Of Contents (TOC) (ArchPPC_64 ELF_V1, _)- -> text ".section \".toc\",\"aw\""+ -> line $ text ".section \".toc\",\"aw\"" -- In ELF v2 we also need to tell the assembler that we want ABI -- version 2. This would normally be done at the top of the file -- right after a file directive, but I could not figure out how -- to do that. (ArchPPC_64 ELF_V2, _)- -> vcat [ text ".abiversion 2",- text ".section \".toc\",\"aw\""- ]+ -> lines_ [ text ".abiversion 2",+ text ".section \".toc\",\"aw\""+ ] (arch, os) | osElfTarget os@@ -577,7 +577,7 @@ | osElfTarget os , arch /= ArchPPC_64 ELF_V1 && arch /= ArchPPC_64 ELF_V2- -> vcat [+ -> lines_ [ -- See Note [.LCTOC1 in PPC PIC code] text ".section \".got2\",\"aw\"", text ".LCTOC1 = .+32768" ]@@ -595,13 +595,13 @@ -- and one for non-PIC. -- -pprImportedSymbol :: NCGConfig -> CLabel -> SDoc+pprImportedSymbol :: NCGConfig -> CLabel -> HDoc pprImportedSymbol config importedLbl = case (arch,os) of (ArchX86, OSDarwin) | Just (CodeStub, lbl) <- dynamicLinkerLabelInfo importedLbl -> if not pic then- vcat [+ lines_ [ text ".symbol_stub", text "L" <> ppr_lbl lbl <> text "$stub:", text "\t.indirect_symbol" <+> ppr_lbl lbl,@@ -614,7 +614,7 @@ text "\tjmp dyld_stub_binding_helper" ] else- vcat [+ lines_ [ text ".section __TEXT,__picsymbolstub2," <> text "symbol_stubs,pure_instructions,25", text "L" <> ppr_lbl lbl <> text "$stub:",@@ -631,7 +631,8 @@ text "\tpushl %eax", text "\tjmp dyld_stub_binding_helper" ]- $+$ vcat [ text ".section __DATA, __la_sym_ptr"+ $$ lines_ [+ text ".section __DATA, __la_sym_ptr" <> (if pic then int 2 else int 3) <> text ",lazy_symbol_pointers", text "L" <> ppr_lbl lbl <> text "$lazy_ptr:",@@ -640,7 +641,7 @@ <> text "$stub_binder"] | Just (SymbolPtr, lbl) <- dynamicLinkerLabelInfo importedLbl- -> vcat [+ -> lines_ [ text ".non_lazy_symbol_pointer", char 'L' <> ppr_lbl lbl <> text "$non_lazy_ptr:", text "\t.indirect_symbol" <+> ppr_lbl lbl,@@ -667,7 +668,7 @@ (_, OSAIX) -> case dynamicLinkerLabelInfo importedLbl of Just (SymbolPtr, lbl)- -> vcat [+ -> lines_ [ text "LC.." <> ppr_lbl lbl <> char ':', text "\t.long" <+> ppr_lbl lbl ] _ -> empty@@ -705,7 +706,7 @@ | osElfTarget os -> case dynamicLinkerLabelInfo importedLbl of Just (SymbolPtr, lbl)- -> vcat [+ -> lines_ [ text ".LC_" <> ppr_lbl lbl <> char ':', text "\t.quad" <+> ppr_lbl lbl ] _ -> empty@@ -718,7 +719,7 @@ W64 -> text "\t.quad" _ -> panic "Unknown wordRep in pprImportedSymbol" - in vcat [+ in lines_ [ text ".section \".got2\", \"aw\"", text ".LC_" <> ppr_lbl lbl <> char ':', symbolSize <+> ppr_lbl lbl ]@@ -729,7 +730,8 @@ _ -> panic "PIC.pprImportedSymbol: no match" where platform = ncgPlatform config- ppr_lbl = pprCLabel platform AsmStyle+ ppr_lbl :: CLabel -> HLine+ ppr_lbl = pprAsmLabel platform arch = platformArch platform os = platformOS platform pic = ncgPIC config
compiler/GHC/CmmToAsm/PPC.hs view
@@ -28,7 +28,8 @@ , canShortcut = PPC.canShortcut , shortcutStatics = PPC.shortcutStatics , shortcutJump = PPC.shortcutJump- , pprNatCmmDecl = PPC.pprNatCmmDecl config+ , pprNatCmmDeclH = PPC.pprNatCmmDecl config+ , pprNatCmmDeclS = PPC.pprNatCmmDecl config , maxSpillSlots = PPC.maxSpillSlots config , allocatableRegs = PPC.allocatableRegs platform , ncgAllocMoreStack = PPC.allocMoreStack platform
compiler/GHC/CmmToAsm/PPC/CodeGen.hs view
@@ -48,7 +48,6 @@ -- Our intermediate code: import GHC.Cmm.BlockId-import GHC.Cmm.Ppr ( pprExpr ) import GHC.Cmm import GHC.Cmm.Utils import GHC.Cmm.Switch@@ -163,7 +162,7 @@ config <- getConfig platform <- getPlatform case stmt of- CmmComment s -> return (unitOL (COMMENT $ ftext s))+ CmmComment s -> return (unitOL (COMMENT s)) CmmTick {} -> return nilOL CmmUnwind {} -> return nilOL @@ -395,7 +394,7 @@ iselExpr64 expr = do platform <- getPlatform- pprPanic "iselExpr64(powerpc)" (pprExpr platform expr)+ pprPanic "iselExpr64(powerpc)" (pdoc platform expr) @@ -408,7 +407,7 @@ getRegister' _ platform (CmmReg (CmmGlobal PicBaseReg)) | OSAIX <- platformOS platform = do let code dst = toOL [ LD II32 dst tocAddr ]- tocAddr = AddrRegImm toc (ImmLit (text "ghc_toc_table[TC]"))+ tocAddr = AddrRegImm toc (ImmLit (fsLit "ghc_toc_table[TC]")) return (Any II32 code) | target32Bit platform = do reg <- getPicBaseNat $ archWordFormat (target32Bit platform)@@ -689,7 +688,7 @@ `consOL` (addr_code `snocOL` LD format dst addr) return (Any format code) -getRegister' _ platform other = pprPanic "getRegister(ppc)" (pprExpr platform other)+getRegister' _ platform other = pprPanic "getRegister(ppc)" (pdoc platform other) -- extend?Rep: wrap integer expression of type `from` -- in a conversion to `to`@@ -2146,7 +2145,7 @@ -- See Note [Sub-word subtlety during jump-table indexing] in -- GHC.CmmToAsm.X86.CodeGen for why we must first offset, then widen. indexExpr0 = cmmOffset platform expr offset- -- We widen to a native-width register to santize the high bits+ -- We widen to a native-width register to sanitize the high bits indexExpr = CmmMachOp (MO_UU_Conv expr_w (platformWordWidth platform)) [indexExpr0]
compiler/GHC/CmmToAsm/PPC/Instr.hs view
@@ -33,7 +33,7 @@ ) where -import GHC.Prelude+import GHC.Prelude hiding (head, init, last, tail) import GHC.CmmToAsm.PPC.Regs import GHC.CmmToAsm.PPC.Cond@@ -52,12 +52,14 @@ import GHC.Cmm import GHC.Cmm.Info import GHC.Cmm.CLabel-import GHC.Utils.Outputable import GHC.Utils.Panic import GHC.Platform import GHC.Types.Unique.FM (listToUFM, lookupUFM) import GHC.Types.Unique.Supply +import Data.Foldable (toList)+import qualified Data.List.NonEmpty as NE+import GHC.Data.FastString (FastString) import Data.Maybe (fromMaybe) @@ -177,7 +179,7 @@ data Instr -- comment pseudo-op- = COMMENT SDoc+ = COMMENT FastString -- location pseudo-op (file, line, col, name) | LOCATION Int Int Int String@@ -675,15 +677,15 @@ -- big, we have to work around this limitation. makeFarBranches- :: Platform- -> LabelMap RawCmmStatics+ :: LabelMap RawCmmStatics -> [NatBasicBlock Instr]- -> UniqSM [NatBasicBlock Instr]-makeFarBranches _platform info_env blocks- | last blockAddresses < nearLimit = return blocks- | otherwise = return $ zipWith handleBlock blockAddresses blocks+ -> [NatBasicBlock Instr]+makeFarBranches info_env blocks+ | NE.last blockAddresses < nearLimit = blocks+ | otherwise = zipWith handleBlock blockAddressList blocks where- blockAddresses = scanl (+) 0 $ map blockLen blocks+ blockAddresses = NE.scanl (+) 0 $ map blockLen blocks+ blockAddressList = toList blockAddresses blockLen (BasicBlock _ instrs) = length instrs handleBlock addr (BasicBlock id instrs)@@ -704,4 +706,4 @@ -- to calculate things exactly nearLimit = 7000 - mapSize info_env * maxRetInfoTableSizeW - blockAddressMap = listToUFM $ zip (map blockId blocks) blockAddresses+ blockAddressMap = listToUFM $ zip (map blockId blocks) blockAddressList
compiler/GHC/CmmToAsm/PPC/Ppr.hs view
@@ -34,7 +34,6 @@ import GHC.Cmm.BlockId import GHC.Cmm.CLabel-import GHC.Cmm.Ppr.Expr () -- For Outputable instances import GHC.Types.Unique ( pprUniqueAlways, getUnique ) import GHC.Platform@@ -47,7 +46,7 @@ -- ----------------------------------------------------------------------------- -- Printing this stuff out -pprNatCmmDecl :: NCGConfig -> NatCmmDecl RawCmmStatics Instr -> SDoc+pprNatCmmDecl :: IsDoc doc => NCGConfig -> NatCmmDecl RawCmmStatics Instr -> doc pprNatCmmDecl config (CmmData section dats) = pprSectionAlign config section $$ pprDatas (ncgPlatform config) dats@@ -64,15 +63,15 @@ _ -> pprLabel platform lbl) $$ -- blocks guaranteed not null, -- so label needed vcat (map (pprBasicBlock config top_info) blocks) $$- ppWhen (ncgDwarfEnabled config) (pdoc platform (mkAsmTempEndLabel lbl)- <> char ':' $$- pprProcEndLabel platform lbl) $$+ ppWhen (ncgDwarfEnabled config) (line (pprAsmLabel platform (mkAsmTempEndLabel lbl)+ <> char ':') $$+ line (pprProcEndLabel platform lbl)) $$ pprSizeDecl platform lbl Just (CmmStaticsRaw info_lbl _) -> pprSectionAlign config (Section Text info_lbl) $$ (if platformHasSubsectionsViaSymbols platform- then pdoc platform (mkDeadStripPreventer info_lbl) <> char ':'+ then line (pprAsmLabel platform (mkDeadStripPreventer info_lbl) <> char ':') else empty) $$ vcat (map (pprBasicBlock config top_info) blocks) $$ -- above: Even the first block gets a label, because with branch-chain@@ -80,66 +79,66 @@ (if platformHasSubsectionsViaSymbols platform then -- See Note [Subsections Via Symbols] in X86/Ppr.hs- text "\t.long "- <+> pdoc platform info_lbl- <+> char '-'- <+> pdoc platform (mkDeadStripPreventer info_lbl)+ line (text "\t.long "+ <+> pprAsmLabel platform info_lbl+ <+> char '-'+ <+> pprAsmLabel platform (mkDeadStripPreventer info_lbl)) else empty) $$ pprSizeDecl platform info_lbl+{-# SPECIALIZE pprNatCmmDecl :: NCGConfig -> NatCmmDecl RawCmmStatics Instr -> SDoc #-}+{-# SPECIALIZE pprNatCmmDecl :: NCGConfig -> NatCmmDecl RawCmmStatics Instr -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Output the ELF .size directive.-pprSizeDecl :: Platform -> CLabel -> SDoc+pprSizeDecl :: IsDoc doc => Platform -> CLabel -> doc pprSizeDecl platform lbl = if osElfTarget (platformOS platform)- then text "\t.size" <+> prettyLbl <> text ", .-" <> codeLbl+ then line (text "\t.size" <+> prettyLbl <> text ", .-" <> codeLbl) else empty where- prettyLbl = pdoc platform lbl+ prettyLbl = pprAsmLabel platform lbl codeLbl | platformArch platform == ArchPPC_64 ELF_V1 = char '.' <> prettyLbl | otherwise = prettyLbl -pprFunctionDescriptor :: Platform -> CLabel -> SDoc-pprFunctionDescriptor platform lab = pprGloblDecl platform lab- $$ text "\t.section \".opd\", \"aw\""- $$ text "\t.align 3"- $$ pdoc platform lab <> char ':'- $$ text "\t.quad ."- <> pdoc platform lab- <> text ",.TOC.@tocbase,0"- $$ text "\t.previous"- $$ text "\t.type"- <+> pdoc platform lab- <> text ", @function"- $$ char '.' <> pdoc platform lab <> char ':'+pprFunctionDescriptor :: IsDoc doc => Platform -> CLabel -> doc+pprFunctionDescriptor platform lab =+ vcat [pprGloblDecl platform lab,+ line (text "\t.section \".opd\", \"aw\""),+ line (text "\t.align 3"),+ line (pprAsmLabel platform lab <> char ':'),+ line (text "\t.quad ."+ <> pprAsmLabel platform lab+ <> text ",.TOC.@tocbase,0"),+ line (text "\t.previous"),+ line (text "\t.type"+ <+> pprAsmLabel platform lab+ <> text ", @function"),+ line (char '.' <> pprAsmLabel platform lab <> char ':')] -pprFunctionPrologue :: Platform -> CLabel ->SDoc-pprFunctionPrologue platform lab = pprGloblDecl platform lab- $$ text ".type "- <> pdoc platform lab- <> text ", @function"- $$ pdoc platform lab <> char ':'- $$ text "0:\taddis\t" <> pprReg toc- <> text ",12,.TOC.-0b@ha"- $$ text "\taddi\t" <> pprReg toc- <> char ',' <> pprReg toc <> text ",.TOC.-0b@l"- $$ text "\t.localentry\t" <> pdoc platform lab- <> text ",.-" <> pdoc platform lab+pprFunctionPrologue :: IsDoc doc => Platform -> CLabel -> doc+pprFunctionPrologue platform lab =+ vcat [pprGloblDecl platform lab,+ line (text ".type " <> pprAsmLabel platform lab <> text ", @function"),+ line (pprAsmLabel platform lab <> char ':'),+ line (text "0:\taddis\t" <> pprReg toc <> text ",12,.TOC.-0b@ha"),+ line (text "\taddi\t" <> pprReg toc <> char ',' <> pprReg toc <> text ",.TOC.-0b@l"),+ line (text "\t.localentry\t" <> pprAsmLabel platform lab <>+ text ",.-" <> pprAsmLabel platform lab)] -pprProcEndLabel :: Platform -> CLabel -- ^ Procedure name- -> SDoc+pprProcEndLabel :: IsLine doc => Platform -> CLabel -- ^ Procedure name+ -> doc pprProcEndLabel platform lbl =- pdoc platform (mkAsmTempProcEndLabel lbl) <> char ':'+ pprAsmLabel platform (mkAsmTempProcEndLabel lbl) <> char ':' -pprBasicBlock :: NCGConfig -> LabelMap RawCmmStatics -> NatBasicBlock Instr- -> SDoc+pprBasicBlock :: IsDoc doc => NCGConfig -> LabelMap RawCmmStatics -> NatBasicBlock Instr+ -> doc pprBasicBlock config info_env (BasicBlock blockid instrs) = maybe_infotable $$ pprLabel platform asmLbl $$ vcat (map (pprInstr platform) instrs) $$ ppWhen (ncgDwarfEnabled config) (- pdoc platform (mkAsmTempEndLabel asmLbl) <> char ':'- <> pprProcEndLabel platform asmLbl+ line (pprAsmLabel platform (mkAsmTempEndLabel asmLbl) <> char ':'+ <> pprProcEndLabel platform asmLbl) ) where asmLbl = blockLbl blockid@@ -153,7 +152,7 @@ -pprDatas :: Platform -> RawCmmStatics -> SDoc+pprDatas :: IsDoc doc => Platform -> RawCmmStatics -> doc -- See Note [emit-time elimination of static indirections] in "GHC.Cmm.CLabel". pprDatas platform (CmmStaticsRaw alias [CmmStaticLit (CmmLabel lbl), CmmStaticLit ind, _, _]) | lbl == mkIndStaticInfoLabel@@ -163,38 +162,38 @@ , Just ind' <- labelInd ind , alias `mayRedirectTo` ind' = pprGloblDecl platform alias- $$ text ".equiv" <+> pdoc platform alias <> comma <> pdoc platform (CmmLabel ind')+ $$ line (text ".equiv" <+> pprAsmLabel platform alias <> comma <> pprAsmLabel platform ind') pprDatas platform (CmmStaticsRaw lbl dats) = vcat (pprLabel platform lbl : map (pprData platform) dats) -pprData :: Platform -> CmmStatic -> SDoc+pprData :: IsDoc doc => Platform -> CmmStatic -> doc pprData platform d = case d of- CmmString str -> pprString str- CmmFileEmbed path -> pprFileEmbed path- CmmUninitialised bytes -> text ".space " <> int bytes+ CmmString str -> line (pprString str)+ CmmFileEmbed path _ -> line (pprFileEmbed path)+ CmmUninitialised bytes -> line (text ".space " <> int bytes) CmmStaticLit lit -> pprDataItem platform lit -pprGloblDecl :: Platform -> CLabel -> SDoc+pprGloblDecl :: IsDoc doc => Platform -> CLabel -> doc pprGloblDecl platform lbl | not (externallyVisibleCLabel lbl) = empty- | otherwise = text ".globl " <> pdoc platform lbl+ | otherwise = line (text ".globl " <> pprAsmLabel platform lbl) -pprTypeAndSizeDecl :: Platform -> CLabel -> SDoc+pprTypeAndSizeDecl :: IsLine doc => Platform -> CLabel -> doc pprTypeAndSizeDecl platform lbl = if platformOS platform == OSLinux && externallyVisibleCLabel lbl then text ".type " <>- pdoc platform lbl <> text ", @object"+ pprAsmLabel platform lbl <> text ", @object" else empty -pprLabel :: Platform -> CLabel -> SDoc+pprLabel :: IsDoc doc => Platform -> CLabel -> doc pprLabel platform lbl = pprGloblDecl platform lbl- $$ pprTypeAndSizeDecl platform lbl- $$ (pdoc platform lbl <> char ':')+ $$ line (pprTypeAndSizeDecl platform lbl)+ $$ line (pprAsmLabel platform lbl <> char ':') -- ----------------------------------------------------------------------------- -- pprInstr: print an 'Instr' -pprReg :: Reg -> SDoc+pprReg :: forall doc. IsLine doc => Reg -> doc pprReg r = case r of@@ -205,7 +204,7 @@ RegVirtual (VirtualRegD u) -> text "%vD_" <> pprUniqueAlways u where- ppr_reg_no :: Int -> SDoc+ ppr_reg_no :: Int -> doc ppr_reg_no i | i <= 31 = int i -- GPRs | i <= 63 = int (i-32) -- FPRs@@ -213,7 +212,7 @@ -pprFormat :: Format -> SDoc+pprFormat :: IsLine doc => Format -> doc pprFormat x = case x of II8 -> text "b"@@ -224,7 +223,7 @@ FF64 -> text "fd" -pprCond :: Cond -> SDoc+pprCond :: IsLine doc => Cond -> doc pprCond c = case c of { ALWAYS -> text "";@@ -235,13 +234,13 @@ GU -> text "gt"; LEU -> text "le"; } -pprImm :: Platform -> Imm -> SDoc+pprImm :: IsLine doc => Platform -> Imm -> doc pprImm platform = \case ImmInt i -> int i ImmInteger i -> integer i- ImmCLbl l -> pdoc platform l- ImmIndex l i -> pdoc platform l <> char '+' <> int i- ImmLit s -> s+ ImmCLbl l -> pprAsmLabel platform l+ ImmIndex l i -> pprAsmLabel platform l <> char '+' <> int i+ ImmLit s -> ftext s ImmFloat f -> float $ fromRational f ImmDouble d -> double $ fromRational d ImmConstantSum a b -> pprImm platform a <> char '+' <> pprImm platform b@@ -265,7 +264,7 @@ HIGHESTA i -> pprImm platform i <> text "@highesta" -pprAddr :: Platform -> AddrMode -> SDoc+pprAddr :: IsLine doc => Platform -> AddrMode -> doc pprAddr platform = \case AddrRegReg r1 r2 -> pprReg r1 <> char ',' <+> pprReg r2 AddrRegImm r1 (ImmInt i) -> hcat [ int i, char '(', pprReg r1, char ')' ]@@ -273,14 +272,14 @@ AddrRegImm r1 imm -> hcat [ pprImm platform imm, char '(', pprReg r1, char ')' ] -pprSectionAlign :: NCGConfig -> Section -> SDoc+pprSectionAlign :: IsDoc doc => NCGConfig -> Section -> doc pprSectionAlign config sec@(Section seg _) =- pprSectionHeader config sec $$+ line (pprSectionHeader config sec) $$ pprAlignForSection (ncgPlatform config) seg -- | Print appropriate alignment for the given section type.-pprAlignForSection :: Platform -> SectionType -> SDoc-pprAlignForSection platform seg =+pprAlignForSection :: IsDoc doc => Platform -> SectionType -> doc+pprAlignForSection platform seg = line $ let ppc64 = not $ target32Bit platform in case seg of Text -> text ".align 2"@@ -296,7 +295,6 @@ UninitialisedData | ppc64 -> text ".align 3" | otherwise -> text ".align 2"- ReadOnlyData16 -> text ".align 4" -- TODO: This is copied from the ReadOnlyData case, but it can likely be -- made more efficient. InitArray -> text ".align 3"@@ -306,9 +304,9 @@ | otherwise -> text ".align 2" OtherSection _ -> panic "PprMach.pprSectionAlign: unknown section" -pprDataItem :: Platform -> CmmLit -> SDoc+pprDataItem :: IsDoc doc => Platform -> CmmLit -> doc pprDataItem platform lit- = vcat (ppr_item (cmmTypeFormat $ cmmLitType platform lit) lit)+ = lines_ (ppr_item (cmmTypeFormat $ cmmLitType platform lit) lit) where imm = litToImm lit archPPC_64 = not $ target32Bit platform@@ -335,21 +333,21 @@ = panic "PPC.Ppr.pprDataItem: no match" -asmComment :: SDoc -> SDoc+asmComment :: IsLine doc => doc -> doc asmComment c = whenPprDebug $ text "#" <+> c -pprInstr :: Platform -> Instr -> SDoc+pprInstr :: IsDoc doc => Platform -> Instr -> doc pprInstr platform instr = case instr of COMMENT s- -> asmComment s+ -> line (asmComment (ftext s)) - LOCATION file line col _name- -> text "\t.loc" <+> ppr file <+> ppr line <+> ppr col+ LOCATION file line' col _name+ -> line (text "\t.loc" <+> int file <+> int line' <+> int col) DELTA d- -> asmComment $ text ("\tdelta = " ++ show d)+ -> line (asmComment $ text ("\tdelta = " ++ show d)) NEWBLOCK _ -> panic "PprMach.pprInstr: NEWBLOCK"@@ -376,7 +374,7 @@ -} LD fmt reg addr- -> hcat [+ -> line $ hcat [ char '\t', text "l", (case fmt of@@ -405,7 +403,7 @@ -> panic "PPC.Ppr.pprInstr LDFAR: no match" LDR fmt reg1 addr- -> hcat [+ -> line $ hcat [ text "\tl", case fmt of II32 -> char 'w'@@ -418,7 +416,7 @@ ] LA fmt reg addr- -> hcat [+ -> line $ hcat [ char '\t', text "l", (case fmt of@@ -438,7 +436,7 @@ ] ST fmt reg addr- -> hcat [+ -> line $ hcat [ char '\t', text "st", pprFormat fmt,@@ -459,7 +457,7 @@ -> panic "PPC.Ppr.pprInstr STFAR: no match" STU fmt reg addr- -> hcat [+ -> line $ hcat [ char '\t', text "st", pprFormat fmt,@@ -473,7 +471,7 @@ ] STC fmt reg1 addr- -> hcat [+ -> line $ hcat [ text "\tst", case fmt of II32 -> char 'w'@@ -486,7 +484,7 @@ ] LIS reg imm- -> hcat [+ -> line $ hcat [ char '\t', text "lis", char '\t',@@ -496,7 +494,7 @@ ] LI reg imm- -> hcat [+ -> line $ hcat [ char '\t', text "li", char '\t',@@ -507,7 +505,7 @@ MR reg1 reg2 | reg1 == reg2 -> empty- | otherwise -> hcat [+ | otherwise -> line $ hcat [ char '\t', case targetClassOfReg platform reg1 of RcInteger -> text "mr"@@ -519,7 +517,7 @@ ] CMP fmt reg ri- -> hcat [+ -> line $ hcat [ char '\t', op, char '\t',@@ -537,7 +535,7 @@ ] CMPL fmt reg ri- -> hcat [+ -> line $ hcat [ char '\t', op, char '\t',@@ -555,13 +553,13 @@ ] BCC cond blockid prediction- -> hcat [+ -> line $ hcat [ char '\t', text "b", pprCond cond, pprPrediction prediction, char '\t',- pdoc platform lbl+ pprAsmLabel platform lbl ] where lbl = mkLocalBlockLabel (getUnique blockid) pprPrediction p = case p of@@ -570,7 +568,7 @@ Just False -> char '-' BCCFAR cond blockid prediction- -> vcat [+ -> lines_ [ hcat [ text "\tb", pprCond (condNegate cond),@@ -579,7 +577,7 @@ ], hcat [ text "\tb\t",- pdoc platform lbl+ pprAsmLabel platform lbl ] ] where lbl = mkLocalBlockLabel (getUnique blockid)@@ -592,15 +590,13 @@ -- We never jump to ForeignLabels; if we ever do, c.f. handling for "BL" | isForeignLabel lbl -> panic "PPC.Ppr.pprInstr: JMP to ForeignLabel" | otherwise ->- hcat [ -- an alias for b that takes a CLabel- char '\t',- text "b",- char '\t',- pdoc platform lbl+ line $ hcat [ -- an alias for b that takes a CLabel+ text "\tb\t",+ pprAsmLabel platform lbl ] MTCTR reg- -> hcat [+ -> line $ hcat [ char '\t', text "mtctr", char '\t',@@ -608,7 +604,7 @@ ] BCTR _ _ _- -> hcat [+ -> line $ hcat [ char '\t', text "bctr" ]@@ -625,18 +621,18 @@ -- but when profiling the codegen inserts calls via -- 'emitRtsCallGen' which are 'CmmLabel's even though -- they'd technically be more like 'ForeignLabel's.- hcat [+ line $ hcat [ text "\tbl\t.",- pdoc platform lbl+ pprAsmLabel platform lbl ] _ ->- hcat [+ line $ hcat [ text "\tbl\t",- pdoc platform lbl+ pprAsmLabel platform lbl ] BCTRL _- -> hcat [+ -> line $ hcat [ char '\t', text "bctrl" ]@@ -645,7 +641,7 @@ -> pprLogic platform (text "add") reg1 reg2 ri ADDIS reg1 reg2 imm- -> hcat [+ -> line $ hcat [ char '\t', text "addis", char '\t',@@ -675,7 +671,7 @@ -> pprLogic platform (text "subfo") reg1 reg2 (RIReg reg3) SUBFC reg1 reg2 ri- -> hcat [+ -> line $ hcat [ char '\t', text "subf", case ri of@@ -696,7 +692,7 @@ -> pprMul platform fmt reg1 reg2 ri MULLO fmt reg1 reg2 reg3- -> hcat [+ -> line $ hcat [ char '\t', text "mull", case fmt of@@ -713,13 +709,13 @@ MFOV fmt reg -> vcat [- hcat [+ line $ hcat [ char '\t', text "mfxer", char '\t', pprReg reg ],- hcat [+ line $ hcat [ char '\t', text "extr", case fmt of@@ -739,7 +735,7 @@ ] MULHU fmt reg1 reg2 reg3- -> hcat [+ -> line $ hcat [ char '\t', text "mulh", case fmt of@@ -760,7 +756,7 @@ -- for some reason, "andi" doesn't exist. -- we'll use "andi." instead. AND reg1 reg2 (RIImm imm)- -> hcat [+ -> line $ hcat [ char '\t', text "andi.", char '\t',@@ -787,7 +783,7 @@ -> pprLogic platform (text "xor") reg1 reg2 ri ORIS reg1 reg2 imm- -> hcat [+ -> line $ hcat [ char '\t', text "oris", char '\t',@@ -799,7 +795,7 @@ ] XORIS reg1 reg2 imm- -> hcat [+ -> line $ hcat [ char '\t', text "xoris", char '\t',@@ -811,7 +807,7 @@ ] EXTS fmt reg1 reg2- -> hcat [+ -> line $ hcat [ char '\t', text "exts", pprFormat fmt,@@ -822,7 +818,7 @@ ] CNTLZ fmt reg1 reg2- -> hcat [+ -> line $ hcat [ char '\t', text "cntlz", case fmt of@@ -883,7 +879,7 @@ in pprLogic platform op reg1 reg2 (limitShiftRI fmt ri) RLWINM reg1 reg2 sh mb me- -> hcat [+ -> line $ hcat [ text "\trlwinm\t", pprReg reg1, text ", ",@@ -897,7 +893,7 @@ ] CLRLI fmt reg1 reg2 n- -> hcat [+ -> line $ hcat [ text "\tclrl", pprFormat fmt, text "i ",@@ -909,7 +905,7 @@ ] CLRRI fmt reg1 reg2 n- -> hcat [+ -> line $ hcat [ text "\tclrr", pprFormat fmt, text "i ",@@ -939,7 +935,7 @@ -> pprUnary (text "fneg") reg1 reg2 FCMP reg1 reg2- -> hcat [+ -> line $ hcat [ char '\t', text "fcmpu\t0, ", -- Note: we're using fcmpu, not fcmpo@@ -967,7 +963,7 @@ -> pprUnary (text "frsp") reg1 reg2 CRNOR dst src1 src2- -> hcat [+ -> line $ hcat [ text "\tcrnor\t", int dst, text ", ",@@ -977,7 +973,7 @@ ] MFCR reg- -> hcat [+ -> line $ hcat [ char '\t', text "mfcr", char '\t',@@ -985,7 +981,7 @@ ] MFLR reg- -> hcat [+ -> line $ hcat [ char '\t', text "mflr", char '\t',@@ -993,25 +989,25 @@ ] FETCHPC reg- -> vcat [+ -> lines_ [ text "\tbcl\t20,31,1f", hcat [ text "1:\tmflr\t", pprReg reg ] ] HWSYNC- -> text "\tsync"+ -> line $ text "\tsync" ISYNC- -> text "\tisync"+ -> line $ text "\tisync" LWSYNC- -> text "\tlwsync"+ -> line $ text "\tlwsync" NOP- -> text "\tnop"+ -> line $ text "\tnop" -pprLogic :: Platform -> SDoc -> Reg -> Reg -> RI -> SDoc-pprLogic platform op reg1 reg2 ri = hcat [+pprLogic :: IsDoc doc => Platform -> Line doc -> Reg -> Reg -> RI -> doc+pprLogic platform op reg1 reg2 ri = line $ hcat [ char '\t', op, case ri of@@ -1026,8 +1022,8 @@ ] -pprMul :: Platform -> Format -> Reg -> Reg -> RI -> SDoc-pprMul platform fmt reg1 reg2 ri = hcat [+pprMul :: IsDoc doc => Platform -> Format -> Reg -> Reg -> RI -> doc+pprMul platform fmt reg1 reg2 ri = line $ hcat [ char '\t', text "mull", case ri of@@ -1045,8 +1041,8 @@ ] -pprDiv :: Format -> Bool -> Reg -> Reg -> Reg -> SDoc-pprDiv fmt sgn reg1 reg2 reg3 = hcat [+pprDiv :: IsDoc doc => Format -> Bool -> Reg -> Reg -> Reg -> doc+pprDiv fmt sgn reg1 reg2 reg3 = line $ hcat [ char '\t', text "div", case fmt of@@ -1063,8 +1059,8 @@ ] -pprUnary :: SDoc -> Reg -> Reg -> SDoc-pprUnary op reg1 reg2 = hcat [+pprUnary :: IsDoc doc => Line doc -> Reg -> Reg -> doc+pprUnary op reg1 reg2 = line $ hcat [ char '\t', op, char '\t',@@ -1074,8 +1070,8 @@ ] -pprBinaryF :: SDoc -> Format -> Reg -> Reg -> Reg -> SDoc-pprBinaryF op fmt reg1 reg2 reg3 = hcat [+pprBinaryF :: IsDoc doc => Line doc -> Format -> Reg -> Reg -> Reg -> doc+pprBinaryF op fmt reg1 reg2 reg3 = line $ hcat [ char '\t', op, pprFFormat fmt,@@ -1087,12 +1083,12 @@ pprReg reg3 ] -pprRI :: Platform -> RI -> SDoc+pprRI :: IsLine doc => Platform -> RI -> doc pprRI _ (RIReg r) = pprReg r pprRI platform (RIImm r) = pprImm platform r -pprFFormat :: Format -> SDoc+pprFFormat :: IsLine doc => Format -> doc pprFFormat FF64 = empty pprFFormat FF32 = char 's' pprFFormat _ = panic "PPC.Ppr.pprFFormat: no match"
compiler/GHC/CmmToAsm/PPC/Regs.hs view
@@ -47,6 +47,7 @@ where import GHC.Prelude+import GHC.Data.FastString import GHC.Platform.Reg import GHC.Platform.Reg.Class@@ -133,7 +134,7 @@ = ImmInt Int | ImmInteger Integer -- Sigh. | ImmCLbl CLabel -- AbstractC Label (with baggage)- | ImmLit SDoc -- Simple string+ | ImmLit FastString | ImmIndex CLabel Int | ImmFloat Rational | ImmDouble Rational@@ -146,8 +147,8 @@ | HIGHESTA Imm -strImmLit :: String -> Imm-strImmLit s = ImmLit (text s)+strImmLit :: FastString -> Imm+strImmLit s = ImmLit s litToImm :: CmmLit -> Imm
compiler/GHC/CmmToAsm/Ppr.hs view
@@ -27,7 +27,6 @@ import GHC.Cmm import GHC.CmmToAsm.Config import GHC.Utils.Outputable as SDoc-import qualified GHC.Utils.Ppr as Pretty import GHC.Utils.Panic import GHC.Platform @@ -89,7 +88,7 @@ -- Print as a string and escape non-printable characters. -- This is similar to charToC in GHC.Utils.Misc -pprASCII :: ByteString -> SDoc+pprASCII :: forall doc. IsLine doc => ByteString -> doc pprASCII str -- Transform this given literal bytestring to escaped string and construct -- the literal SDoc directly.@@ -98,19 +97,19 @@ -- -- We work with a `Doc` instead of an `SDoc` because there is no need to carry -- an `SDocContext` that we don't use. It leads to nicer (STG) code.- = docToSDoc (BS.foldr f Pretty.empty str)+ = BS.foldr f empty str where- f :: Word8 -> Pretty.Doc -> Pretty.Doc- f w s = do1 w Pretty.<> s+ f :: Word8 -> doc -> doc+ f w s = do1 w <> s - do1 :: Word8 -> Pretty.Doc- do1 w | 0x09 == w = Pretty.text "\\t"- | 0x0A == w = Pretty.text "\\n"- | 0x22 == w = Pretty.text "\\\""- | 0x5C == w = Pretty.text "\\\\"+ do1 :: Word8 -> doc+ do1 w | 0x09 == w = text "\\t"+ | 0x0A == w = text "\\n"+ | 0x22 == w = text "\\\""+ | 0x5C == w = text "\\\\" -- ASCII printable characters range- | w >= 0x20 && w <= 0x7E = Pretty.char (chr' w)- | otherwise = Pretty.sizedText 4 xs+ | w >= 0x20 && w <= 0x7E = char (chr' w)+ | otherwise = text xs where !xs = [ '\\', x0, x1, x2] -- octal !x0 = chr' (ord0 + (w `unsafeShiftR` 6) .&. 0x07)@@ -122,20 +121,25 @@ -- so we bypass the check in "chr" chr' :: Word8 -> Char chr' (W8# w#) = C# (chr# (word2Int# (word8ToWord# w#)))-+{-# SPECIALIZE pprASCII :: ByteString -> SDoc #-}+{-# SPECIALIZE pprASCII :: ByteString -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Emit a ".string" directive-pprString :: ByteString -> SDoc+pprString :: IsLine doc => ByteString -> doc pprString bs = text "\t.string " <> doubleQuotes (pprASCII bs)+{-# SPECIALIZE pprString :: ByteString -> SDoc #-}+{-# SPECIALIZE pprString :: ByteString -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Emit a ".incbin" directive -- -- A NULL byte is added after the binary data.-pprFileEmbed :: FilePath -> SDoc+pprFileEmbed :: IsLine doc => FilePath -> doc pprFileEmbed path = text "\t.incbin " <> pprFilePathString path -- proper escape (see #16389) <> text "\n\t.byte 0"+{-# SPECIALIZE pprFileEmbed :: FilePath -> SDoc #-}+{-# SPECIALIZE pprFileEmbed :: FilePath -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable {- Note [Embedding large binary blobs]@@ -193,14 +197,16 @@ -- identical strings in the linker. With -split-sections each string also gets -- a unique section to allow strings from unused code to be GC'd. -pprSectionHeader :: NCGConfig -> Section -> SDoc+pprSectionHeader :: IsLine doc => NCGConfig -> Section -> doc pprSectionHeader config (Section t suffix) = case platformOS (ncgPlatform config) of OSAIX -> pprXcoffSectionHeader t OSDarwin -> pprDarwinSectionHeader t _ -> pprGNUSectionHeader config t suffix+{-# SPECIALIZE pprSectionHeader :: NCGConfig -> Section -> SDoc #-}+{-# SPECIALIZE pprSectionHeader :: NCGConfig -> Section -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -pprGNUSectionHeader :: NCGConfig -> SectionType -> CLabel -> SDoc+pprGNUSectionHeader :: IsLine doc => NCGConfig -> SectionType -> CLabel -> doc pprGNUSectionHeader config t suffix = hcat [text ".section ", header, subsection, flags] where@@ -210,7 +216,7 @@ platform = ncgPlatform config splitSections = ncgSplitSections config subsection- | splitSections = sep <> pdoc platform suffix+ | splitSections = sep <> pprAsmLabel platform suffix | otherwise = empty header = case t of Text -> text ".text"@@ -224,9 +230,6 @@ -> text ".rdata$rel.ro" | otherwise -> text ".data.rel.ro" UninitialisedData -> text ".bss"- ReadOnlyData16 | OSMinGW32 <- platformOS platform- -> text ".rdata$cst16"- | otherwise -> text ".rodata.cst16" InitArray | OSMinGW32 <- platformOS platform -> text ".ctors"@@ -242,39 +245,38 @@ OtherSection _ -> panic "PprBase.pprGNUSectionHeader: unknown section type" flags = case t of- Text- | OSMinGW32 <- platformOS platform, splitSections- -> text ",\"xr\""- | splitSections- -> text ",\"ax\"," <> sectionType platform "progbits" CString | OSMinGW32 <- platformOS platform -> empty | otherwise -> text ",\"aMS\"," <> sectionType platform "progbits" <> text ",1" _ -> empty+{-# SPECIALIZE pprGNUSectionHeader :: NCGConfig -> SectionType -> CLabel -> SDoc #-}+{-# SPECIALIZE pprGNUSectionHeader :: NCGConfig -> SectionType -> CLabel -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- XCOFF doesn't support relocating label-differences, so we place all -- RO sections into .text[PR] sections-pprXcoffSectionHeader :: SectionType -> SDoc+pprXcoffSectionHeader :: IsLine doc => SectionType -> doc pprXcoffSectionHeader t = case t of Text -> text ".csect .text[PR]" Data -> text ".csect .data[RW]" ReadOnlyData -> text ".csect .text[PR] # ReadOnlyData" RelocatableReadOnlyData -> text ".csect .text[PR] # RelocatableReadOnlyData"- ReadOnlyData16 -> text ".csect .text[PR] # ReadOnlyData16" CString -> text ".csect .text[PR] # CString" UninitialisedData -> text ".csect .data[BS]" _ -> panic "pprXcoffSectionHeader: unknown section type"+{-# SPECIALIZE pprXcoffSectionHeader :: SectionType -> SDoc #-}+{-# SPECIALIZE pprXcoffSectionHeader :: SectionType -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -pprDarwinSectionHeader :: SectionType -> SDoc+pprDarwinSectionHeader :: IsLine doc => SectionType -> doc pprDarwinSectionHeader t = case t of Text -> text ".text" Data -> text ".data" ReadOnlyData -> text ".const" RelocatableReadOnlyData -> text ".const_data" UninitialisedData -> text ".data"- ReadOnlyData16 -> text ".const" InitArray -> text ".section\t__DATA,__mod_init_func,mod_init_funcs" FiniArray -> panic "pprDarwinSectionHeader: fini not supported" CString -> text ".section\t__TEXT,__cstring,cstring_literals" OtherSection _ -> panic "pprDarwinSectionHeader: unknown section type"+{-# SPECIALIZE pprDarwinSectionHeader :: SectionType -> SDoc #-}+{-# SPECIALIZE pprDarwinSectionHeader :: SectionType -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable
compiler/GHC/CmmToAsm/Reg/Graph.hs view
@@ -355,8 +355,8 @@ in graph2 --- | Add some coalesence edges to the graph--- Coalesences between virtual and real regs are recorded as preferences.+-- | Add some coalescence edges to the graph+-- Coalescences between virtual and real regs are recorded as preferences. graphAddCoalesce :: (Reg, Reg) -> Color.Graph VirtualReg RegClass RealReg
+ compiler/GHC/CmmToAsm/Reg/Graph/Base.hs view
@@ -0,0 +1,164 @@++-- | Utils for calculating general worst, bound, squeese and free, functions.+--+-- as per: "A Generalized Algorithm for Graph-Coloring Register Allocation"+-- Michael Smith, Normal Ramsey, Glenn Holloway.+-- PLDI 2004+--+-- These general versions are not used in GHC proper because they are too slow.+-- Instead, hand written optimised versions are provided for each architecture+-- in MachRegs*.hs+--+-- This code is here because we can test the architecture specific code against+-- it.+--+module GHC.CmmToAsm.Reg.Graph.Base (+ RegClass(..),+ Reg(..),+ RegSub(..),++ worst,+ bound,+ squeese+) where++import GHC.Prelude++import GHC.Types.Unique.Set+import GHC.Types.Unique.FM+import GHC.Types.Unique+import GHC.Builtin.Uniques+import GHC.Utils.Monad (concatMapM)+++-- Some basic register classes.+-- These aren't necessarily in 1-to-1 correspondence with the allocatable+-- RegClasses in MachRegs.hs+data RegClass+ -- general purpose regs+ = ClassG32 -- 32 bit GPRs+ | ClassG16 -- 16 bit GPRs+ | ClassG8 -- 8 bit GPRs++ -- floating point regs+ | ClassF64 -- 64 bit FPRs+ deriving (Show, Eq, Enum)+++-- | A register of some class+data Reg+ -- a register of some class+ = Reg RegClass Int++ -- a sub-component of one of the other regs+ | RegSub RegSub Reg+ deriving (Show, Eq)+++-- | so we can put regs in UniqSets+instance Uniquable Reg where+ getUnique (Reg c i)+ = mkRegSingleUnique+ $ fromEnum c * 1000 + i++ getUnique (RegSub s (Reg c i))+ = mkRegSubUnique+ $ fromEnum s * 10000 + fromEnum c * 1000 + i++ getUnique (RegSub _ (RegSub _ _))+ = error "RegArchBase.getUnique: can't have a sub-reg of a sub-reg."+++-- | A subcomponent of another register+data RegSub+ = SubL16 -- lowest 16 bits+ | SubL8 -- lowest 8 bits+ | SubL8H -- second lowest 8 bits+ deriving (Show, Enum, Ord, Eq)+++-- | Worst case displacement+--+-- a node N of classN has some number of neighbors,+-- all of which are from classC.+--+-- (worst neighbors classN classC) is the maximum number of potential+-- colors for N that can be lost by coloring its neighbors.+--+-- This should be hand coded/cached for each particular architecture,+-- because the compute time is very long..+worst :: (RegClass -> UniqSet Reg)+ -> (Reg -> UniqSet Reg)+ -> Int -> RegClass -> RegClass -> Int++worst regsOfClass regAlias neighbors classN classC+ = let regAliasS regs = unionManyUniqSets+ $ map regAlias+ $ nonDetEltsUniqSet regs+ -- This is non-deterministic but we do not+ -- currently support deterministic code-generation.+ -- See Note [Unique Determinism and code generation]++ -- all the regs in classes N, C+ regsN = regsOfClass classN+ regsC = regsOfClass classC++ -- all the possible subsets of c which have size < m+ regsS = filter (\s -> not (isEmptyUniqSet s)+ && sizeUniqSet s <= neighbors)+ $ powersetLS regsC++ -- for each of the subsets of C, the regs which conflict+ -- with possibilities for N+ regsS_conflict+ = map (\s -> intersectUniqSets regsN (regAliasS s)) regsS++ in maximum $ map sizeUniqSet $ regsS_conflict+++-- | For a node N of classN and neighbors of classesC+-- (bound classN classesC) is the maximum number of potential+-- colors for N that can be lost by coloring its neighbors.+bound :: (RegClass -> UniqSet Reg)+ -> (Reg -> UniqSet Reg)+ -> RegClass -> [RegClass] -> Int++bound regsOfClass regAlias classN classesC+ = let regAliasS regs = unionManyUniqSets+ $ map regAlias+ $ nonDetEltsUFM regs+ -- See Note [Unique Determinism and code generation]++ regsC_aliases+ = unionManyUniqSets+ $ map (regAliasS . getUniqSet . regsOfClass) classesC++ overlap = intersectUniqSets (regsOfClass classN) regsC_aliases++ in sizeUniqSet overlap+++-- | The total squeese on a particular node with a list of neighbors.+--+-- A version of this should be constructed for each particular architecture,+-- possibly including uses of bound, so that aliased registers don't get+-- counted twice, as per the paper.+squeese :: (RegClass -> UniqSet Reg)+ -> (Reg -> UniqSet Reg)+ -> RegClass -> [(Int, RegClass)] -> Int++squeese regsOfClass regAlias classN countCs+ = sum+ $ map (\(i, classC) -> worst regsOfClass regAlias i classN classC)+ $ countCs+++-- | powerset (for lists)+powersetL :: [a] -> [[a]]+powersetL = concatMapM (\x -> [[],[x]])+++-- | powersetLS (list of sets)+powersetLS :: Uniquable a => UniqSet a -> [UniqSet a]+powersetLS s = map mkUniqSet $ powersetL $ nonDetEltsUniqSet s+ -- See Note [Unique Determinism and code generation]
+ compiler/GHC/CmmToAsm/Reg/Graph/Coalesce.hs view
@@ -0,0 +1,99 @@+-- | Register coalescing.+module GHC.CmmToAsm.Reg.Graph.Coalesce (+ regCoalesce,+ slurpJoinMovs+) where+import GHC.Prelude++import GHC.CmmToAsm.Reg.Liveness+import GHC.CmmToAsm.Instr+import GHC.Platform.Reg++import GHC.Cmm+import GHC.Data.Bag+import GHC.Data.Graph.Directed+import GHC.Types.Unique.FM+import GHC.Types.Unique.Set+import GHC.Types.Unique.Supply+++-- | Do register coalescing on this top level thing+--+-- For Reg -> Reg moves, if the first reg dies at the same time the+-- second reg is born then the mov only serves to join live ranges.+-- The two regs can be renamed to be the same and the move instruction+-- safely erased.+regCoalesce+ :: Instruction instr+ => [LiveCmmDecl statics instr]+ -> UniqSM [LiveCmmDecl statics instr]++regCoalesce code+ = do+ let joins = foldl' unionBags emptyBag+ $ map slurpJoinMovs code++ let alloc = foldl' buildAlloc emptyUFM+ $ bagToList joins++ let patched = map (patchEraseLive (sinkReg alloc)) code++ return patched+++-- | Add a v1 = v2 register renaming to the map.+-- The register with the lowest lexical name is set as the+-- canonical version.+buildAlloc :: UniqFM Reg Reg -> (Reg, Reg) -> UniqFM Reg Reg+buildAlloc fm (r1, r2)+ = let rmin = min r1 r2+ rmax = max r1 r2+ in addToUFM fm rmax rmin+++-- | Determine the canonical name for a register by following+-- v1 = v2 renamings in this map.+sinkReg :: UniqFM Reg Reg -> Reg -> Reg+sinkReg fm r+ = case lookupUFM fm r of+ Nothing -> r+ Just r' -> sinkReg fm r'+++-- | Slurp out mov instructions that only serve to join live ranges.+--+-- During a mov, if the source reg dies and the destination reg is+-- born then we can rename the two regs to the same thing and+-- eliminate the move.+slurpJoinMovs+ :: Instruction instr+ => LiveCmmDecl statics instr+ -> Bag (Reg, Reg)++slurpJoinMovs live+ = slurpCmm emptyBag live+ where+ slurpCmm rs CmmData{}+ = rs++ slurpCmm rs (CmmProc _ _ _ sccs)+ = foldl' slurpBlock rs (flattenSCCs sccs)++ slurpBlock rs (BasicBlock _ instrs)+ = foldl' slurpLI rs instrs++ slurpLI rs (LiveInstr _ Nothing) = rs+ slurpLI rs (LiveInstr instr (Just live))+ | Just (r1, r2) <- takeRegRegMoveInstr instr+ , elementOfUniqSet r1 $ liveDieRead live+ , elementOfUniqSet r2 $ liveBorn live++ -- only coalesce movs between two virtuals for now,+ -- else we end up with allocatable regs in the live+ -- regs list..+ , isVirtualReg r1 && isVirtualReg r2+ = consBag (r1, r2) rs++ | otherwise+ = rs+
compiler/GHC/CmmToAsm/Reg/Graph/Spill.hs view
@@ -147,8 +147,7 @@ $ mapLookup blockId slotMap moreSlotsLive = IntSet.fromList- $ catMaybes- $ map (lookupUFM regSlotMap)+ $ mapMaybe (lookupUFM regSlotMap) $ nonDetEltsUniqSet regsLive -- See Note [Unique Determinism and code generation]
compiler/GHC/CmmToAsm/Reg/Graph/SpillClean.hs view
@@ -390,8 +390,7 @@ = do let slotsReloadedByTargets = IntSet.unions- $ catMaybes- $ map (flip mapLookup liveSlotsOnEntry)+ $ mapMaybe (flip mapLookup liveSlotsOnEntry) $ targets let noReloads'
compiler/GHC/CmmToAsm/Reg/Graph/SpillCost.hs view
@@ -130,8 +130,8 @@ -- Increment counts for what regs were read/written from. let (RU read written) = regUsageOfInstr platform instr- mapM_ (incUses scale) $ catMaybes $ map takeVirtualReg $ nub read- mapM_ (incDefs scale) $ catMaybes $ map takeVirtualReg $ nub written+ mapM_ (incUses scale) $ mapMaybe takeVirtualReg $ nub read+ mapM_ (incDefs scale) $ mapMaybe takeVirtualReg $ nub written -- Compute liveness for entry to next instruction. let liveDieRead_virt = takeVirtuals (liveDieRead live)
compiler/GHC/CmmToAsm/Reg/Graph/TrivColorable.hs view
@@ -116,7 +116,9 @@ ArchMipsel -> panic "trivColorable ArchMipsel" ArchS390X -> panic "trivColorable ArchS390X" ArchRISCV64 -> panic "trivColorable ArchRISCV64"+ ArchLoongArch64->panic "trivColorable ArchLoongArch64" ArchJavaScript-> panic "trivColorable ArchJavaScript"+ ArchWasm32 -> panic "trivColorable ArchWasm32" ArchUnknown -> panic "trivColorable ArchUnknown") , count2 <- accSqueeze 0 cALLOCATABLE_REGS_INTEGER (virtualRegSqueeze RcInteger)@@ -140,7 +142,7 @@ ArchPPC -> 0 ArchPPC_64 _ -> 0 ArchARM _ _ _ -> panic "trivColorable ArchARM"- -- we can in princple address all the float regs as+ -- we can in principle address all the float regs as -- segments. So we could have 64 Float regs. Or -- 128 Half regs, or even 256 Byte regs. ArchAArch64 -> 0@@ -149,7 +151,9 @@ ArchMipsel -> panic "trivColorable ArchMipsel" ArchS390X -> panic "trivColorable ArchS390X" ArchRISCV64 -> panic "trivColorable ArchRISCV64"+ ArchLoongArch64->panic "trivColorable ArchLoongArch64" ArchJavaScript-> panic "trivColorable ArchJavaScript"+ ArchWasm32 -> panic "trivColorable ArchWasm32" ArchUnknown -> panic "trivColorable ArchUnknown") , count2 <- accSqueeze 0 cALLOCATABLE_REGS_FLOAT (virtualRegSqueeze RcFloat)@@ -181,7 +185,9 @@ ArchMipsel -> panic "trivColorable ArchMipsel" ArchS390X -> panic "trivColorable ArchS390X" ArchRISCV64 -> panic "trivColorable ArchRISCV64"+ ArchLoongArch64->panic "trivColorable ArchLoongArch64" ArchJavaScript-> panic "trivColorable ArchJavaScript"+ ArchWasm32 -> panic "trivColorable ArchWasm32" ArchUnknown -> panic "trivColorable ArchUnknown") , count2 <- accSqueeze 0 cALLOCATABLE_REGS_DOUBLE (virtualRegSqueeze RcDouble)
+ compiler/GHC/CmmToAsm/Reg/Graph/X86.hs view
@@ -0,0 +1,161 @@++-- | A description of the register set of the X86.+--+-- This isn't used directly in GHC proper.+--+-- See RegArchBase.hs for the reference.+-- See MachRegs.hs for the actual trivColorable function used in GHC.+--+module GHC.CmmToAsm.Reg.Graph.X86 (+ classOfReg,+ regsOfClass,+ regName,+ regAlias,+ worst,+ squeese,+) where++import GHC.Prelude++import GHC.CmmToAsm.Reg.Graph.Base (Reg(..), RegSub(..), RegClass(..))+import GHC.Types.Unique.Set++import qualified Data.Array as A+++-- | Determine the class of a register+classOfReg :: Reg -> RegClass+classOfReg reg+ = case reg of+ Reg c _ -> c++ RegSub SubL16 _ -> ClassG16+ RegSub SubL8 _ -> ClassG8+ RegSub SubL8H _ -> ClassG8+++-- | Determine all the regs that make up a certain class.+regsOfClass :: RegClass -> UniqSet Reg+regsOfClass c+ = case c of+ ClassG32+ -> mkUniqSet [ Reg ClassG32 i+ | i <- [0..7] ]++ ClassG16+ -> mkUniqSet [ RegSub SubL16 (Reg ClassG32 i)+ | i <- [0..7] ]++ ClassG8+ -> unionUniqSets+ (mkUniqSet [ RegSub SubL8 (Reg ClassG32 i) | i <- [0..3] ])+ (mkUniqSet [ RegSub SubL8H (Reg ClassG32 i) | i <- [0..3] ])++ ClassF64+ -> mkUniqSet [ Reg ClassF64 i+ | i <- [0..5] ]+++-- | Determine the common name of a reg+-- returns Nothing if this reg is not part of the machine.+regName :: Reg -> Maybe String+regName reg+ = case reg of+ Reg ClassG32 i+ | i <= 7 ->+ let names = A.listArray (0,8)+ [ "eax", "ebx", "ecx", "edx"+ , "ebp", "esi", "edi", "esp" ]+ in Just $ names A.! i++ RegSub SubL16 (Reg ClassG32 i)+ | i <= 7 ->+ let names = A.listArray (0,8)+ [ "ax", "bx", "cx", "dx"+ , "bp", "si", "di", "sp"]+ in Just $ names A.! i++ RegSub SubL8 (Reg ClassG32 i)+ | i <= 3 ->+ let names = A.listArray (0,4) [ "al", "bl", "cl", "dl"]+ in Just $ names A.! i++ RegSub SubL8H (Reg ClassG32 i)+ | i <= 3 ->+ let names = A.listArray (0,4) [ "ah", "bh", "ch", "dh"]+ in Just $ names A.! i++ _ -> Nothing+++-- | Which regs alias what other regs.+regAlias :: Reg -> UniqSet Reg+regAlias reg+ = case reg of++ -- 32 bit regs alias all of the subregs+ Reg ClassG32 i++ -- for eax, ebx, ecx, eds+ | i <= 3+ -> mkUniqSet+ $ [ Reg ClassG32 i, RegSub SubL16 reg+ , RegSub SubL8 reg, RegSub SubL8H reg ]++ -- for esi, edi, esp, ebp+ | 4 <= i && i <= 7+ -> mkUniqSet+ $ [ Reg ClassG32 i, RegSub SubL16 reg ]++ -- 16 bit subregs alias the whole reg+ RegSub SubL16 r@(Reg ClassG32 _)+ -> regAlias r++ -- 8 bit subregs alias the 32 and 16, but not the other 8 bit subreg+ RegSub SubL8 r@(Reg ClassG32 _)+ -> mkUniqSet $ [ r, RegSub SubL16 r, RegSub SubL8 r ]++ RegSub SubL8H r@(Reg ClassG32 _)+ -> mkUniqSet $ [ r, RegSub SubL16 r, RegSub SubL8H r ]++ -- fp+ Reg ClassF64 _+ -> unitUniqSet reg++ _ -> error "regAlias: invalid register"+++-- | Optimised versions of RegColorBase.{worst, squeese} specific to x86+worst :: Int -> RegClass -> RegClass -> Int+worst n classN classC+ = case classN of+ ClassG32+ -> case classC of+ ClassG32 -> min n 8+ ClassG16 -> min n 8+ ClassG8 -> min n 4+ ClassF64 -> 0++ ClassG16+ -> case classC of+ ClassG32 -> min n 8+ ClassG16 -> min n 8+ ClassG8 -> min n 4+ ClassF64 -> 0++ ClassG8+ -> case classC of+ ClassG32 -> min (n*2) 8+ ClassG16 -> min (n*2) 8+ ClassG8 -> min n 8+ ClassF64 -> 0++ ClassF64+ -> case classC of+ ClassF64 -> min n 6+ _ -> 0++squeese :: RegClass -> [(Int, RegClass)] -> Int+squeese classN countCs+ = sum (map (\(i, classC) -> worst i classN classC) countCs)+
compiler/GHC/CmmToAsm/Reg/Linear.hs view
@@ -224,7 +224,9 @@ ArchMipseb -> panic "linearRegAlloc ArchMipseb" ArchMipsel -> panic "linearRegAlloc ArchMipsel" ArchRISCV64 -> panic "linearRegAlloc ArchRISCV64"+ ArchLoongArch64-> panic "linearRegAlloc ArchLoongArch64" ArchJavaScript -> panic "linearRegAlloc ArchJavaScript"+ ArchWasm32 -> panic "linearRegAlloc ArchWasm32" ArchUnknown -> panic "linearRegAlloc ArchUnknown" where go :: (FR regs, Outputable regs)@@ -595,7 +597,7 @@ -- we need to ensure they are emitted in the correct order. We used to only -- emit single instructions in mkSpill/mkReload/mkRegRegMove. -- As such order of spills and reloads didn't matter. However, with- -- mutliple instructions potentially issued by those functions we need to be+ -- multiple instructions potentially issued by those functions we need to be -- careful to not break execution order. Reversing the spills (clobber will -- also spill), will ensure they are emitted in the right order. --@@ -618,7 +620,7 @@ ] -- pprTrace "patched-code" ((vcat $ map (docToSDoc . pprInstr) code)) $ do--- pprTrace "pached-fixup" ((ppr fixup_blocks)) $ do+-- pprTrace "patched-fixup" ((ppr fixup_blocks)) $ do return (code, fixup_blocks)
compiler/GHC/CmmToAsm/Reg/Linear/AArch64.hs view
@@ -35,7 +35,7 @@ -- | - Operates on 32-bit | - Separate reg. bank, | - Separate reg. bank, | -- | GP ARM registers | 32x64-bit NEON regs | 32x128-bit NEON regs | -- | - 8-bit/16-bit integer | - 8/16/32/64-bit int | - 8/16/32/64-bit int |--- | | - Single percision fp | - Single percision fp |+-- | | - Single precision fp | - Single precision fp | -- | | | - Double precision fp | -- | | | - Single/Double fp are | -- | | | IEEE compliant |
compiler/GHC/CmmToAsm/Reg/Linear/FreeRegs.hs view
@@ -77,5 +77,7 @@ ArchMipseb -> panic "maxSpillSlots ArchMipseb" ArchMipsel -> panic "maxSpillSlots ArchMipsel" ArchRISCV64 -> panic "maxSpillSlots ArchRISCV64"+ ArchLoongArch64->panic "maxSpillSlots ArchLoongArch64" ArchJavaScript-> panic "maxSpillSlots ArchJavaScript"+ ArchWasm32 -> panic "maxSpillSlots ArchWasm32" ArchUnknown -> panic "maxSpillSlots ArchUnknown"
compiler/GHC/CmmToAsm/Reg/Linear/JoinToTargets.hs view
@@ -31,6 +31,8 @@ import GHC.Types.Unique.FM import GHC.Types.Unique.Set +import GHC.Utils.Outputable+ -- | For a jump instruction at the end of a block, generate fixup code so its -- vregs are in the correct regs for its destination. --@@ -375,6 +377,5 @@ -- we don't handle memory to memory moves. -- they shouldn't happen because we don't share -- stack slots between vregs.- panic ("makeMove " ++ show vreg ++ " (" ++ show src ++ ") ("- ++ show dst ++ ")"- ++ " we don't handle mem->mem moves.")+ pprPanic "makeMove: we don't handle mem->mem moves"+ (ppr vreg <+> parens (ppr src) <+> parens (ppr dst))
compiler/GHC/CmmToAsm/Reg/Liveness.hs view
@@ -67,7 +67,7 @@ import GHC.Data.Bag import GHC.Utils.Monad.State.Strict -import Data.List (mapAccumL, groupBy, partition)+import Data.List (mapAccumL, partition) import Data.Maybe import Data.IntSet (IntSet) @@ -911,13 +911,11 @@ -> a -> b -> (a,c) - iterateUntilUnchanged f eq a b- = head $- concatMap tail $- groupBy (\(a1, _) (a2, _) -> eq a1 a2) $- iterate (\(a, _) -> f a b) $- (a, panic "RegLiveness.livenessSCCs")-+ iterateUntilUnchanged f eq aa b = go aa+ where+ go a = if eq a a' then ac else go a'+ where+ ac@(a', _) = f a b linearLiveness :: Instruction instr
compiler/GHC/CmmToAsm/Reg/Target.hs view
@@ -50,7 +50,9 @@ ArchMipseb -> panic "targetVirtualRegSqueeze ArchMipseb" ArchMipsel -> panic "targetVirtualRegSqueeze ArchMipsel" ArchRISCV64 -> panic "targetVirtualRegSqueeze ArchRISCV64"+ ArchLoongArch64->panic "targetVirtualRegSqueeze ArchLoongArch64" ArchJavaScript-> panic "targetVirtualRegSqueeze ArchJavaScript"+ ArchWasm32 -> panic "targetVirtualRegSqueeze ArchWasm32" ArchUnknown -> panic "targetVirtualRegSqueeze ArchUnknown" @@ -68,7 +70,9 @@ ArchMipseb -> panic "targetRealRegSqueeze ArchMipseb" ArchMipsel -> panic "targetRealRegSqueeze ArchMipsel" ArchRISCV64 -> panic "targetRealRegSqueeze ArchRISCV64"+ ArchLoongArch64->panic "targetRealRegSqueeze ArchLoongArch64" ArchJavaScript-> panic "targetRealRegSqueeze ArchJavaScript"+ ArchWasm32 -> panic "targetRealRegSqueeze ArchWasm32" ArchUnknown -> panic "targetRealRegSqueeze ArchUnknown" targetClassOfRealReg :: Platform -> RealReg -> RegClass@@ -85,7 +89,9 @@ ArchMipseb -> panic "targetClassOfRealReg ArchMipseb" ArchMipsel -> panic "targetClassOfRealReg ArchMipsel" ArchRISCV64 -> panic "targetClassOfRealReg ArchRISCV64"+ ArchLoongArch64->panic "targetClassOfRealReg ArchLoongArch64" ArchJavaScript-> panic "targetClassOfRealReg ArchJavaScript"+ ArchWasm32 -> panic "targetClassOfRealReg ArchWasm32" ArchUnknown -> panic "targetClassOfRealReg ArchUnknown" targetMkVirtualReg :: Platform -> Unique -> Format -> VirtualReg@@ -102,7 +108,9 @@ ArchMipseb -> panic "targetMkVirtualReg ArchMipseb" ArchMipsel -> panic "targetMkVirtualReg ArchMipsel" ArchRISCV64 -> panic "targetMkVirtualReg ArchRISCV64"+ ArchLoongArch64->panic "targetMkVirtualReg ArchLoongArch64" ArchJavaScript-> panic "targetMkVirtualReg ArchJavaScript"+ ArchWasm32 -> panic "targetMkVirtualReg ArchWasm32" ArchUnknown -> panic "targetMkVirtualReg ArchUnknown" targetRegDotColor :: Platform -> RealReg -> SDoc@@ -119,7 +127,9 @@ ArchMipseb -> panic "targetRegDotColor ArchMipseb" ArchMipsel -> panic "targetRegDotColor ArchMipsel" ArchRISCV64 -> panic "targetRegDotColor ArchRISCV64"+ ArchLoongArch64->panic "targetRegDotColor ArchLoongArch64" ArchJavaScript-> panic "targetRegDotColor ArchJavaScript"+ ArchWasm32 -> panic "targetRegDotColor ArchWasm32" ArchUnknown -> panic "targetRegDotColor ArchUnknown"
+ compiler/GHC/CmmToAsm/Wasm.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Strict #-}++module GHC.CmmToAsm.Wasm (ncgWasm) where++import Data.ByteString.Builder+import Data.Maybe+import Data.Semigroup+import GHC.Cmm+import GHC.CmmToAsm.Wasm.Asm+import GHC.CmmToAsm.Wasm.FromCmm+import GHC.CmmToAsm.Wasm.Types+import GHC.Data.Stream (Stream, StreamS (..), runStream)+import GHC.Platform+import GHC.Prelude+import GHC.Settings+import GHC.Types.Unique.Supply+import GHC.Unit+import GHC.Utils.CliOption+import System.IO++ncgWasm ::+ Platform ->+ ToolSettings ->+ UniqSupply ->+ ModLocation ->+ Handle ->+ Stream IO RawCmmGroup a ->+ IO a+ncgWasm platform ts us loc h cmms = do+ (r, s) <- streamCmmGroups platform us cmms+ hPutBuilder h $ "# " <> string7 (fromJust $ ml_hs_file loc) <> "\n\n"+ hPutBuilder h $ execWasmAsmM do_tail_call $ asmTellEverything TagI32 s+ pure r+ where+ -- See Note [WasmTailCall]+ do_tail_call = doTailCall ts++streamCmmGroups ::+ Platform ->+ UniqSupply ->+ Stream IO RawCmmGroup a ->+ IO (a, WasmCodeGenState 'I32)+streamCmmGroups platform us cmms =+ go (initialWasmCodeGenState platform us) $+ runStream cmms+ where+ go s (Done r) = pure (r, s)+ go s (Effect m) = m >>= go s+ go s (Yield cmm k) = go (wasmExecM (onCmmGroup cmm) s) k++doTailCall :: ToolSettings -> Bool+doTailCall ts = Option "-mtail-call" `elem` as_args+ where+ (_, as_args) = toolSettings_pgm_a ts
+ compiler/GHC/CmmToAsm/Wasm/Asm.hs view
@@ -0,0 +1,538 @@+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE Strict #-}++module GHC.CmmToAsm.Wasm.Asm (asmTellEverything, execWasmAsmM) where++import Control.Monad+import Control.Monad.Trans.Reader+import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import Data.ByteString.Builder+import Data.Coerce+import Data.Foldable+import qualified Data.IntSet as IS+import Data.Maybe+import Data.Semigroup+import GHC.Cmm+import GHC.CmmToAsm.Ppr+import GHC.CmmToAsm.Wasm.FromCmm+import GHC.CmmToAsm.Wasm.Types+import GHC.CmmToAsm.Wasm.Utils+import GHC.Data.FastString+import GHC.Float+import GHC.Prelude+import GHC.Types.Basic+import GHC.Types.Unique+import GHC.Types.Unique.Map+import GHC.Utils.Monad.State.Strict+import GHC.Utils.Outputable hiding ((<>))+import GHC.Utils.Panic (panic)++-- | Reads current indentation, appends result to state+newtype WasmAsmM a = WasmAsmM (Bool -> Builder -> State Builder a)+ deriving+ ( Functor,+ Applicative,+ Monad+ )+ via (ReaderT Bool (ReaderT Builder (State Builder)))++instance Semigroup a => Semigroup (WasmAsmM a) where+ (<>) = liftA2 (<>)++instance Monoid a => Monoid (WasmAsmM a) where+ mempty = pure mempty++-- | To tail call or not, that is the question+doTailCall :: WasmAsmM Bool+doTailCall = WasmAsmM $ \do_tail_call _ -> pure do_tail_call++-- | Default indent level is none+execWasmAsmM :: Bool -> WasmAsmM a -> Builder+execWasmAsmM do_tail_call (WasmAsmM m) =+ execState (m do_tail_call mempty) mempty++-- | Increase indent level by a tab+asmWithTab :: WasmAsmM a -> WasmAsmM a+asmWithTab (WasmAsmM m) =+ WasmAsmM $ \do_tail_call t -> m do_tail_call $! char7 '\t' <> t++-- | Writes a single line starting with the current indent+asmTellLine :: Builder -> WasmAsmM ()+asmTellLine b = WasmAsmM $ \_ t -> modify $ \acc -> acc <> t <> b <> char7 '\n'++-- | Writes a single line break+asmTellLF :: WasmAsmM ()+asmTellLF = WasmAsmM $ \_ _ -> modify $ \acc -> acc <> char7 '\n'++-- | Writes a line starting with a single tab, ignoring current indent+-- level+asmTellTabLine :: Builder -> WasmAsmM ()+asmTellTabLine b =+ WasmAsmM $ \_ _ -> modify $ \acc -> acc <> char7 '\t' <> b <> char7 '\n'++asmFromWasmType :: WasmTypeTag t -> Builder+asmFromWasmType ty = case ty of+ TagI32 -> "i32"+ TagI64 -> "i64"+ TagF32 -> "f32"+ TagF64 -> "f64"++asmFromSomeWasmType :: SomeWasmType -> Builder+asmFromSomeWasmType (SomeWasmType t) = asmFromWasmType t++asmFromSomeWasmTypes :: [SomeWasmType] -> Builder+asmFromSomeWasmTypes ts = "(" <> builderCommas asmFromSomeWasmType ts <> ")"++asmFromFuncType :: [SomeWasmType] -> [SomeWasmType] -> Builder+asmFromFuncType arg_tys ret_tys =+ asmFromSomeWasmTypes arg_tys <> " -> " <> asmFromSomeWasmTypes ret_tys++asmTellFuncType ::+ SymName -> ([SomeWasmType], [SomeWasmType]) -> WasmAsmM ()+asmTellFuncType sym (arg_tys, ret_tys) =+ asmTellTabLine $+ ".functype "+ <> asmFromSymName sym+ <> " "+ <> asmFromFuncType arg_tys ret_tys++asmTellLocals :: [SomeWasmType] -> WasmAsmM ()+asmTellLocals [] = mempty+asmTellLocals local_tys =+ asmTellTabLine $ ".local " <> builderCommas asmFromSomeWasmType local_tys++asmFromSymName :: SymName -> Builder+asmFromSymName = shortByteString . coerce fastStringToShortByteString++asmTellDefSym :: SymName -> WasmAsmM ()+asmTellDefSym sym = do+ asmTellTabLine $ ".hidden " <> asm_sym+ asmTellTabLine $ ".globl " <> asm_sym+ where+ asm_sym = asmFromSymName sym++asmTellDataSectionContent :: WasmTypeTag w -> DataSectionContent -> WasmAsmM ()+asmTellDataSectionContent ty_word c = asmTellTabLine $ case c of+ DataI8 i -> ".int8 0x" <> word8Hex i+ DataI16 i -> ".int16 0x" <> word16Hex i+ DataI32 i -> ".int32 0x" <> word32Hex i+ DataI64 i -> ".int64 0x" <> word64Hex i+ DataF32 f -> ".int32 0x" <> word32Hex (castFloatToWord32 f)+ DataF64 d -> ".int64 0x" <> word64Hex (castDoubleToWord64 d)+ DataSym sym o ->+ ( case ty_word of+ TagI32 -> ".int32 "+ TagI64 -> ".int64 "+ _ -> panic "asmTellDataSectionContent: unreachable"+ )+ <> asmFromSymName sym+ <> ( case compare o 0 of+ EQ -> mempty+ GT -> "+" <> intDec o+ LT -> intDec o+ )+ DataSkip i -> ".skip " <> intDec i+ DataASCII s+ | not (BS.null s) && BS.last s == 0 ->+ ".asciz \""+ <> string7+ (showSDocOneLine defaultSDocContext $ pprASCII $ BS.init s)+ <> "\""+ | otherwise ->+ ".ascii \""+ <> string7+ (showSDocOneLine defaultSDocContext $ pprASCII s)+ <> "\""+ DataIncBin f _ ->+ ".incbin "+ <> string7+ (showSDocOneLine defaultSDocContext $ pprFilePathString f)++dataSectionContentSize :: WasmTypeTag w -> DataSectionContent -> Int+dataSectionContentSize ty_word c = case c of+ DataI8 {} -> 1+ DataI16 {} -> 2+ DataI32 {} -> 4+ DataI64 {} -> 8+ DataF32 {} -> 4+ DataF64 {} -> 8+ DataSym {} -> alignmentBytes $ alignmentFromWordType ty_word+ DataSkip i -> i+ DataASCII s -> BS.length s+ DataIncBin _ l -> l++dataSectionSize :: WasmTypeTag w -> [DataSectionContent] -> Int+dataSectionSize ty_word =+ coerce+ . foldMap'+ (Sum . dataSectionContentSize ty_word)++asmTellAlign :: Alignment -> WasmAsmM ()+asmTellAlign a = case alignmentBytes a of+ 1 -> mempty+ i -> asmTellTabLine $ ".p2align " <> intDec (countTrailingZeros i)++asmTellSectionHeader :: Builder -> WasmAsmM ()+asmTellSectionHeader k = asmTellTabLine $ ".section " <> k <> ",\"\",@"++asmTellDataSection ::+ WasmTypeTag w -> IS.IntSet -> SymName -> DataSection -> WasmAsmM ()+asmTellDataSection ty_word def_syms sym DataSection {..} = do+ when (getKey (getUnique sym) `IS.member` def_syms) $ asmTellDefSym sym+ asmTellSectionHeader sec_name+ asmTellAlign dataSectionAlignment+ asmTellTabLine asm_size+ asmTellLine $ asm_sym <> ":"+ for_ dataSectionContents $ asmTellDataSectionContent ty_word+ asmTellLF+ where+ asm_sym = asmFromSymName sym++ sec_name =+ ( case dataSectionKind of+ SectionData -> ".data."+ SectionROData -> ".rodata."+ )+ <> asm_sym++ asm_size =+ ".size "+ <> asm_sym+ <> ", "+ <> intDec+ (dataSectionSize ty_word dataSectionContents)++asmFromWasmBlockType :: WasmTypeTag w -> WasmFunctionType pre post -> Builder+asmFromWasmBlockType+ _+ (WasmFunctionType {ft_pops = TypeListNil, ft_pushes = TypeListNil}) =+ mempty+asmFromWasmBlockType+ TagI32+ ( WasmFunctionType+ { ft_pops = TypeListNil,+ ft_pushes = TypeListCons TagI32 TypeListNil+ }+ ) =+ " i32"+asmFromWasmBlockType+ TagI64+ ( WasmFunctionType+ { ft_pops = TypeListNil,+ ft_pushes = TypeListCons TagI64 TypeListNil+ }+ ) =+ " i64"+asmFromWasmBlockType _ _ = panic "asmFromWasmBlockType: invalid block type"++asmFromAlignmentSpec :: AlignmentSpec -> Builder+asmFromAlignmentSpec NaturallyAligned = mempty+asmFromAlignmentSpec Unaligned = ":p2align=0"++asmTellWasmInstr :: WasmTypeTag w -> WasmInstr w pre post -> WasmAsmM ()+asmTellWasmInstr ty_word instr = case instr of+ WasmComment c -> asmTellLine $ stringUtf8 $ "# " <> c+ WasmNop -> mempty+ WasmDrop -> asmTellLine "drop"+ WasmUnreachable -> asmTellLine "unreachable"+ WasmConst TagI32 i -> asmTellLine $ "i32.const " <> integerDec i+ WasmConst TagI64 i -> asmTellLine $ "i64.const " <> integerDec i+ WasmConst {} -> panic "asmTellWasmInstr: unreachable"+ WasmSymConst sym ->+ asmTellLine $+ ( case ty_word of+ TagI32 -> "i32.const "+ TagI64 -> "i64.const "+ _ -> panic "asmTellWasmInstr: unreachable"+ )+ <> asmFromSymName sym+ WasmLoad ty (Just w) s o align ->+ asmTellLine $+ asmFromWasmType ty+ <> ".load"+ <> intDec w+ <> ( case s of+ Signed -> "_s"+ Unsigned -> "_u"+ )+ <> " "+ <> intDec o+ <> asmFromAlignmentSpec align+ WasmLoad ty Nothing _ o align ->+ asmTellLine $+ asmFromWasmType ty+ <> ".load"+ <> " "+ <> intDec o+ <> asmFromAlignmentSpec align+ WasmStore ty (Just w) o align ->+ asmTellLine $+ asmFromWasmType ty+ <> ".store"+ <> intDec w+ <> " "+ <> intDec o+ <> asmFromAlignmentSpec align+ WasmStore ty Nothing o align ->+ asmTellLine $+ asmFromWasmType ty+ <> ".store"+ <> " "+ <> intDec o+ <> asmFromAlignmentSpec align+ WasmGlobalGet _ sym -> asmTellLine $ "global.get " <> asmFromSymName sym+ WasmGlobalSet _ sym -> asmTellLine $ "global.set " <> asmFromSymName sym+ WasmLocalGet _ i -> asmTellLine $ "local.get " <> intDec i+ WasmLocalSet _ i -> asmTellLine $ "local.set " <> intDec i+ WasmLocalTee _ i -> asmTellLine $ "local.tee " <> intDec i+ WasmCCall sym -> asmTellLine $ "call " <> asmFromSymName sym+ WasmCCallIndirect arg_tys ret_tys ->+ asmTellLine $+ "call_indirect "+ <> asmFromFuncType+ (someWasmTypesFromTypeList arg_tys)+ (someWasmTypesFromTypeList ret_tys)+ WasmConcat instr0 instr1 -> do+ asmTellWasmInstr ty_word instr0+ asmTellWasmInstr ty_word instr1+ WasmReinterpret t0 t1 ->+ asmTellLine $+ asmFromWasmType t1 <> ".reinterpret_" <> asmFromWasmType t0+ WasmTruncSat Signed t0 t1 ->+ asmTellLine $+ asmFromWasmType t1 <> ".trunc_sat_" <> asmFromWasmType t0 <> "_s"+ WasmTruncSat Unsigned t0 t1 ->+ asmTellLine $+ asmFromWasmType t1 <> ".trunc_sat_" <> asmFromWasmType t0 <> "_u"+ WasmConvert Signed t0 t1 ->+ asmTellLine $+ asmFromWasmType t1 <> ".convert_" <> asmFromWasmType t0 <> "_s"+ WasmConvert Unsigned t0 t1 ->+ asmTellLine $+ asmFromWasmType t1 <> ".convert_" <> asmFromWasmType t0 <> "_u"+ WasmAdd ty -> asmTellLine $ asmFromWasmType ty <> ".add"+ WasmSub ty -> asmTellLine $ asmFromWasmType ty <> ".sub"+ WasmMul ty -> asmTellLine $ asmFromWasmType ty <> ".mul"+ WasmDiv _ TagF32 -> asmTellLine "f32.div"+ WasmDiv _ TagF64 -> asmTellLine "f64.div"+ WasmDiv Signed ty -> asmTellLine $ asmFromWasmType ty <> ".div_s"+ WasmDiv Unsigned ty -> asmTellLine $ asmFromWasmType ty <> ".div_u"+ WasmRem Signed ty -> asmTellLine $ asmFromWasmType ty <> ".rem_s"+ WasmRem Unsigned ty -> asmTellLine $ asmFromWasmType ty <> ".rem_u"+ WasmAnd ty -> asmTellLine $ asmFromWasmType ty <> ".and"+ WasmOr ty -> asmTellLine $ asmFromWasmType ty <> ".or"+ WasmXor ty -> asmTellLine $ asmFromWasmType ty <> ".xor"+ WasmEq ty -> asmTellLine $ asmFromWasmType ty <> ".eq"+ WasmNe ty -> asmTellLine $ asmFromWasmType ty <> ".ne"+ WasmLt _ TagF32 -> asmTellLine "f32.lt"+ WasmLt _ TagF64 -> asmTellLine "f64.lt"+ WasmLt Signed ty -> asmTellLine $ asmFromWasmType ty <> ".lt_s"+ WasmLt Unsigned ty -> asmTellLine $ asmFromWasmType ty <> ".lt_u"+ WasmGt _ TagF32 -> asmTellLine "f32.gt"+ WasmGt _ TagF64 -> asmTellLine "f64.gt"+ WasmGt Signed ty -> asmTellLine $ asmFromWasmType ty <> ".gt_s"+ WasmGt Unsigned ty -> asmTellLine $ asmFromWasmType ty <> ".gt_u"+ WasmLe _ TagF32 -> asmTellLine "f32.le"+ WasmLe _ TagF64 -> asmTellLine "f64.le"+ WasmLe Signed ty -> asmTellLine $ asmFromWasmType ty <> ".le_s"+ WasmLe Unsigned ty -> asmTellLine $ asmFromWasmType ty <> ".le_u"+ WasmGe _ TagF32 -> asmTellLine "f32.ge"+ WasmGe _ TagF64 -> asmTellLine "f64.ge"+ WasmGe Signed ty -> asmTellLine $ asmFromWasmType ty <> ".ge_s"+ WasmGe Unsigned ty -> asmTellLine $ asmFromWasmType ty <> ".ge_u"+ WasmShl ty -> asmTellLine $ asmFromWasmType ty <> ".shl"+ WasmShr Signed ty -> asmTellLine $ asmFromWasmType ty <> ".shr_s"+ WasmShr Unsigned ty -> asmTellLine $ asmFromWasmType ty <> ".shr_u"+ WasmI32Extend8S -> asmTellLine "i32.extend8_s"+ WasmI32Extend16S -> asmTellLine "i32.extend16_s"+ WasmI64Extend8S -> asmTellLine "i64.extend8_s"+ WasmI64Extend16S -> asmTellLine "i64.extend16_s"+ WasmI64Extend32S -> asmTellLine "i64.extend32_s"+ WasmI64ExtendI32 Signed -> asmTellLine "i64.extend_i32_s"+ WasmI64ExtendI32 Unsigned -> asmTellLine "i64.extend_i32_u"+ WasmI32WrapI64 -> asmTellLine "i32.wrap_i64"+ WasmF32DemoteF64 -> asmTellLine "f32.demote_f64"+ WasmF64PromoteF32 -> asmTellLine "f64.promote_f32"+ WasmAbs ty -> asmTellLine $ asmFromWasmType ty <> ".abs"+ WasmNeg ty -> asmTellLine $ asmFromWasmType ty <> ".neg"+ WasmCond t -> do+ asmTellLine "if"+ asmWithTab $ asmTellWasmInstr ty_word t+ asmTellLine "end_if"++asmTellWasmControl ::+ WasmTypeTag w ->+ WasmControl+ (WasmStatements w)+ (WasmExpr w a)+ pre+ post ->+ WasmAsmM ()+asmTellWasmControl ty_word c = case c of+ WasmPush _ (WasmExpr e) -> asmTellWasmInstr ty_word e+ WasmBlock bt c -> do+ asmTellLine $ "block" <> asmFromWasmBlockType ty_word bt+ asmWithTab $ asmTellWasmControl ty_word c+ asmTellLine "end_block"+ WasmLoop bt c -> do+ asmTellLine $ "loop" <> asmFromWasmBlockType ty_word bt+ asmWithTab $ asmTellWasmControl ty_word c+ asmTellLine "end_loop"+ WasmIfTop bt t f -> do+ asmTellLine $ "if" <> asmFromWasmBlockType ty_word bt+ asmWithTab $ asmTellWasmControl ty_word t+ asmTellLine "else"+ asmWithTab $ asmTellWasmControl ty_word f+ asmTellLine "end_if"+ WasmBr i -> asmTellLine $ "br " <> intDec i+ WasmFallthrough -> mempty+ WasmBrTable (WasmExpr e) _ ts t -> do+ asmTellWasmInstr ty_word e+ asmTellLine $ "br_table {" <> builderCommas intDec (ts <> [t]) <> "}"+ -- See Note [WasmTailCall]+ WasmTailCall (WasmExpr e) -> do+ do_tail_call <- doTailCall+ if+ | do_tail_call,+ WasmSymConst sym <- e ->+ asmTellLine $ "return_call " <> asmFromSymName sym+ | do_tail_call ->+ do+ asmTellWasmInstr ty_word e+ asmTellLine $+ "return_call_indirect "+ <> asmFromFuncType+ []+ [SomeWasmType ty_word]+ | otherwise ->+ do+ asmTellWasmInstr ty_word e+ asmTellLine "return"+ WasmActions (WasmStatements a) -> asmTellWasmInstr ty_word a+ WasmSeq c0 c1 -> do+ asmTellWasmControl ty_word c0+ asmTellWasmControl ty_word c1++asmTellFunc ::+ WasmTypeTag w ->+ IS.IntSet ->+ SymName ->+ (([SomeWasmType], [SomeWasmType]), FuncBody w) ->+ WasmAsmM ()+asmTellFunc ty_word def_syms sym (func_ty, FuncBody {..}) = do+ when (getKey (getUnique sym) `IS.member` def_syms) $ asmTellDefSym sym+ asmTellSectionHeader $ ".text." <> asm_sym+ asmTellLine $ asm_sym <> ":"+ asmTellFuncType sym func_ty+ asmTellLocals funcLocals+ asmWithTab $ asmTellWasmControl ty_word funcBody+ asmTellTabLine "end_function"+ asmTellLF+ where+ asm_sym = asmFromSymName sym++asmTellGlobals :: WasmTypeTag w -> WasmAsmM ()+asmTellGlobals ty_word = do+ for_ supportedCmmGlobalRegs $ \reg ->+ let (sym, ty) = fromJust $ globalInfoFromCmmGlobalReg ty_word reg+ in asmTellTabLine $+ ".globaltype "+ <> asmFromSymName sym+ <> ", "+ <> asmFromSomeWasmType ty+ asmTellLF++asmTellCtors :: WasmTypeTag w -> [SymName] -> WasmAsmM ()+asmTellCtors _ [] = mempty+asmTellCtors ty_word syms = do+ asmTellSectionHeader ".init_array"+ asmTellAlign $ alignmentFromWordType ty_word+ for_ syms $ \sym ->+ asmTellTabLine $+ ( case ty_word of+ TagI32 -> ".int32 "+ TagI64 -> ".int64 "+ _ -> panic "asmTellCtors: unreachable"+ )+ <> asmFromSymName sym+ asmTellLF++asmTellBS :: ByteString -> WasmAsmM ()+asmTellBS s = do+ asmTellTabLine $ ".int8 " <> intDec (BS.length s)+ asmTellTabLine $+ ".ascii \""+ <> string7+ (showSDocOneLine defaultSDocContext $ pprASCII s)+ <> "\""++asmTellVec :: [WasmAsmM ()] -> WasmAsmM ()+asmTellVec xs = do+ asmTellTabLine $ ".int8 " <> intDec (length xs)+ sequence_ xs++asmTellProducers :: WasmAsmM ()+asmTellProducers = do+ asmTellSectionHeader ".custom_section.producers"+ asmTellVec+ [ do+ asmTellBS "processed-by"+ asmTellVec+ [ do+ asmTellBS "ghc"+ asmTellBS "9.6"+ ]+ ]++asmTellTargetFeatures :: WasmAsmM ()+asmTellTargetFeatures = do+ do_tail_call <- doTailCall+ asmTellSectionHeader ".custom_section.target_features"+ asmTellVec+ [ do+ asmTellTabLine ".int8 0x2b"+ asmTellBS feature+ | feature <-+ ["tail-call" | do_tail_call]+ <> [ "bulk-memory",+ "mutable-globals",+ "nontrapping-fptoint",+ "reference-types",+ "sign-ext"+ ]+ ]++asmTellEverything :: WasmTypeTag w -> WasmCodeGenState w -> WasmAsmM ()+asmTellEverything ty_word WasmCodeGenState {..} = do+ asmTellGlobals ty_word+ asm_functypes+ asm_funcs+ asm_data_secs+ asm_ctors+ asmTellProducers+ asmTellTargetFeatures+ where+ asm_functypes = do+ for_+ (detEltsUniqMap $ funcTypes `minusUniqMap` funcBodies)+ (uncurry asmTellFuncType)+ asmTellLF++ asm_funcs = do+ for_+ (detEltsUniqMap $ intersectUniqMap_C (,) funcTypes funcBodies)+ (uncurry $ asmTellFunc ty_word defaultSyms)+ asmTellLF++ asm_data_secs = do+ for_+ (detEltsUniqMap dataSections)+ (uncurry (asmTellDataSection ty_word defaultSyms))+ asmTellLF++ asm_ctors = asmTellCtors ty_word ctors
+ compiler/GHC/CmmToAsm/Wasm/FromCmm.hs view
@@ -0,0 +1,1686 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE Strict #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE UndecidableInstances #-}+{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}++{-# HLINT ignore "Use camelCase" #-}+module GHC.CmmToAsm.Wasm.FromCmm+ ( alignmentFromWordType,+ globalInfoFromCmmGlobalReg,+ supportedCmmGlobalRegs,+ onCmmGroup,+ )+where++import Control.Monad+import qualified Data.ByteString as BS+import Data.Foldable+import Data.Functor+import qualified Data.IntSet as IS+import Data.Semigroup+import Data.String+import Data.Traversable+import Data.Type.Equality+import GHC.Cmm+import GHC.Cmm.BlockId+import GHC.Cmm.CLabel+import GHC.Cmm.Dataflow.Block+import GHC.Cmm.Dataflow.Label+import GHC.Cmm.InitFini+import GHC.CmmToAsm.Wasm.Types+import GHC.CmmToAsm.Wasm.Utils+import GHC.Float+import GHC.Platform+import GHC.Prelude+import GHC.StgToCmm.CgUtils+import GHC.Types.Basic+import GHC.Types.ForeignCall+import GHC.Types.Unique+import GHC.Types.Unique.FM+import GHC.Types.Unique.Map+import GHC.Utils.Outputable hiding ((<>))+import GHC.Utils.Panic+import GHC.Wasm.ControlFlow.FromCmm++-- | Calculate the wasm representation type from a 'CmmType'. This is+-- a lossy conversion, and sometimes we need to pass the original+-- 'CmmType' or at least its 'Width' around, so to properly add+-- subword truncation or extension logic.+someWasmTypeFromCmmType :: CmmType -> SomeWasmType+someWasmTypeFromCmmType t+ | isWord32 t = SomeWasmType TagI32+ | isWord64 t = SomeWasmType TagI64+ | t `cmmEqType` b16 = SomeWasmType TagI32+ | t `cmmEqType` b8 = SomeWasmType TagI32+ | isFloat64 t = SomeWasmType TagF64+ | isFloat32 t = SomeWasmType TagF32+ | otherwise =+ panic $+ "someWasmTypeFromCmmType: unsupported CmmType "+ <> showSDocOneLine defaultSDocContext (ppr t)++-- | Calculate the optional memory narrowing of a 'CmmLoad' or+-- 'CmmStore'.+wasmMemoryNarrowing :: WasmTypeTag t -> CmmType -> Maybe Int+wasmMemoryNarrowing ty ty_cmm = case (# ty, typeWidth ty_cmm #) of+ (# TagI32, W8 #) -> Just 8+ (# TagI32, W16 #) -> Just 16+ (# TagI32, W32 #) -> Nothing+ (# TagI64, W8 #) -> Just 8+ (# TagI64, W16 #) -> Just 16+ (# TagI64, W32 #) -> Just 32+ (# TagI64, W64 #) -> Nothing+ (# TagF32, W32 #) -> Nothing+ (# TagF64, W64 #) -> Nothing+ _ -> panic "wasmMemoryNarrowing: unreachable"++-- | Despite this is used by the WebAssembly native codegen, we use+-- 'pprCLabel' instead of 'pprAsmLabel' when emitting the textual+-- symbol name. Either one would work, but 'pprCLabel' makes the+-- output assembly code looks closer to the unregisterised codegen+-- output, which can be handy when using the unregisterised codegen as+-- a source of truth when debugging the native codegen.+symNameFromCLabel :: CLabel -> SymName+symNameFromCLabel lbl =+ fromString $+ showSDocOneLine defaultSDocContext {sdocStyle = PprCode} $+ pprCLabel genericPlatform lbl++-- | Calculate a symbol's visibility.+symVisibilityFromCLabel :: CLabel -> SymVisibility+symVisibilityFromCLabel lbl+ | externallyVisibleCLabel lbl = SymDefault+ | otherwise = SymStatic++-- | Calculate a symbol's kind, see haddock docs of 'SymKind' for more+-- explanation.+symKindFromCLabel :: CLabel -> SymKind+symKindFromCLabel lbl+ | isCFunctionLabel lbl = SymFunc+ | otherwise = SymData++-- | Calculate a data section's kind, see haddock docs of+-- 'DataSectionKind' for more explanation.+dataSectionKindFromCmmSection :: Section -> DataSectionKind+dataSectionKindFromCmmSection s = case sectionProtection s of+ ReadWriteSection -> SectionData+ _ -> SectionROData++-- | Calculate the natural alignment size given the platform word+-- type.+alignmentFromWordType :: WasmTypeTag w -> Alignment+alignmentFromWordType TagI32 = mkAlignment 4+alignmentFromWordType TagI64 = mkAlignment 8+alignmentFromWordType _ = panic "alignmentFromWordType: unreachable"++-- | Calculate a data section's alignment. As a conservative+-- optimization, a data section with a single CmmString/CmmFileEmbed+-- has no alignment requirement, otherwise we always align to the word+-- size to satisfy pointer tagging requirements and avoid unaligned+-- loads/stores.+alignmentFromCmmSection :: WasmTypeTag w -> [DataSectionContent] -> Alignment+alignmentFromCmmSection _ [DataASCII {}] = mkAlignment 1+alignmentFromCmmSection _ [DataIncBin {}] = mkAlignment 1+alignmentFromCmmSection t _ = alignmentFromWordType t++-- | Lower a 'CmmStatic'.+lower_CmmStatic :: CmmStatic -> WasmCodeGenM w DataSectionContent+lower_CmmStatic s = case s of+ CmmStaticLit (CmmInt i W8) -> pure $ DataI8 $ fromInteger $ narrowU W8 i+ CmmStaticLit (CmmInt i W16) -> pure $ DataI16 $ fromInteger $ narrowU W16 i+ CmmStaticLit (CmmInt i W32) -> pure $ DataI32 $ fromInteger $ narrowU W32 i+ CmmStaticLit (CmmInt i W64) -> pure $ DataI64 $ fromInteger $ narrowU W64 i+ CmmStaticLit (CmmFloat f W32) -> pure $ DataF32 $ fromRational f+ CmmStaticLit (CmmFloat d W64) -> pure $ DataF64 $ fromRational d+ CmmStaticLit (CmmLabel lbl) ->+ onAnySym lbl+ $> DataSym+ (symNameFromCLabel lbl)+ 0+ CmmStaticLit (CmmLabelOff lbl o) ->+ onAnySym lbl+ $> DataSym+ (symNameFromCLabel lbl)+ o+ CmmUninitialised i -> pure $ DataSkip i+ CmmString b -> pure $ DataASCII b+ CmmFileEmbed f l -> pure $ DataIncBin f l+ _ -> panic "lower_CmmStatic: unreachable"++{-+Note [Register mapping on WebAssembly]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++Unlike typical ISAs, WebAssembly doesn't expose a fixed set of+registers. For now, we map each Cmm LocalReg to a wasm local, and each+Cmm GlobalReg to a wasm global. The wasm globals are defined in+rts/wasm/Wasm.S, and must be kept in sync with+'globalInfoFromCmmGlobalReg' and 'supportedCmmGlobalRegs' here.++There are some other Cmm GlobalRegs which are still represented by+StgRegTable fields instead of wasm globals (e.g. HpAlloc). It's cheap+to add wasm globals, but other parts of rts logic only work with the+StgRegTable fields, so we also need to instrument StgRun/StgReturn to+sync the wasm globals with the StgRegTable. It's not really worth the+trouble.++-}+globalInfoFromCmmGlobalReg :: WasmTypeTag w -> GlobalReg -> Maybe GlobalInfo+globalInfoFromCmmGlobalReg t reg = case reg of+ VanillaReg i _+ | i >= 1 && i <= 10 -> Just (fromString $ "__R" <> show i, ty_word)+ FloatReg i+ | i >= 1 && i <= 6 ->+ Just (fromString $ "__F" <> show i, SomeWasmType TagF32)+ DoubleReg i+ | i >= 1 && i <= 6 ->+ Just (fromString $ "__D" <> show i, SomeWasmType TagF64)+ LongReg i+ | i == 1 -> Just (fromString $ "__L" <> show i, SomeWasmType TagI64)+ Sp -> Just ("__Sp", ty_word)+ SpLim -> Just ("__SpLim", ty_word)+ Hp -> Just ("__Hp", ty_word)+ HpLim -> Just ("__HpLim", ty_word)+ _ -> Nothing+ where+ ty_word = SomeWasmType t++supportedCmmGlobalRegs :: [GlobalReg]+supportedCmmGlobalRegs =+ [VanillaReg i VGcPtr | i <- [1 .. 10]]+ <> [FloatReg i | i <- [1 .. 6]]+ <> [DoubleReg i | i <- [1 .. 6]]+ <> [LongReg i | i <- [1 .. 1]]+ <> [Sp, SpLim, Hp, HpLim]++-- | Truncate a subword.+truncSubword :: Width -> WasmTypeTag t -> WasmExpr w t -> WasmExpr w t+truncSubword W8 ty (WasmExpr instr) =+ WasmExpr $ instr `WasmConcat` WasmConst ty 0xFF `WasmConcat` WasmAnd ty+truncSubword W16 ty (WasmExpr instr) =+ WasmExpr $ instr `WasmConcat` WasmConst ty 0xFFFF `WasmConcat` WasmAnd ty+truncSubword _ _ expr = expr++-- | Sign-extend a subword.+extendSubword :: Width -> WasmTypeTag t -> WasmExpr w t -> WasmExpr w t+extendSubword W8 TagI32 (WasmExpr instr) =+ WasmExpr $ instr `WasmConcat` WasmI32Extend8S+extendSubword W16 TagI32 (WasmExpr instr) =+ WasmExpr $ instr `WasmConcat` WasmI32Extend16S+extendSubword W8 TagI64 (WasmExpr instr) =+ WasmExpr $ instr `WasmConcat` WasmI64Extend8S+extendSubword W16 TagI64 (WasmExpr instr) =+ WasmExpr $ instr `WasmConcat` WasmI64Extend16S+extendSubword W32 TagI64 (WasmExpr instr) =+ WasmExpr $ instr `WasmConcat` WasmI64Extend32S+extendSubword _ _ expr = expr++-- | Lower an unary homogeneous operation.+lower_MO_Un_Homo ::+ ( forall pre t.+ WasmTypeTag t ->+ WasmInstr+ w+ (t : pre)+ (t : pre)+ ) ->+ CLabel ->+ CmmType ->+ [CmmExpr] ->+ WasmCodeGenM w (SomeWasmExpr w)+lower_MO_Un_Homo op lbl t0 [x] = case someWasmTypeFromCmmType t0 of+ SomeWasmType ty -> do+ WasmExpr x_instr <- lower_CmmExpr_Typed lbl ty x+ pure $+ SomeWasmExpr ty $+ WasmExpr $+ x_instr `WasmConcat` op ty+lower_MO_Un_Homo _ _ _ _ = panic "lower_MO_Un_Homo: unreachable"++-- | Lower a binary homogeneous operation. Homogeneous: result type is+-- the same with operand types.+lower_MO_Bin_Homo ::+ ( forall pre t.+ WasmTypeTag t ->+ WasmInstr+ w+ (t : t : pre)+ (t : pre)+ ) ->+ CLabel ->+ CmmType ->+ [CmmExpr] ->+ WasmCodeGenM w (SomeWasmExpr w)+lower_MO_Bin_Homo op lbl t0 [x, y] = case someWasmTypeFromCmmType t0 of+ SomeWasmType ty -> do+ WasmExpr x_instr <- lower_CmmExpr_Typed lbl ty x+ WasmExpr y_instr <- lower_CmmExpr_Typed lbl ty y+ pure $+ SomeWasmExpr ty $+ WasmExpr $+ x_instr `WasmConcat` y_instr `WasmConcat` op ty+lower_MO_Bin_Homo _ _ _ _ = panic "lower_MO_Bin_Homo: unreachable"++-- | Lower a binary homogeneous operation, and truncate the result if+-- it's a subword.+lower_MO_Bin_Homo_Trunc ::+ (forall pre t. WasmTypeTag t -> WasmInstr w (t : t : pre) (t : pre)) ->+ CLabel ->+ Width ->+ [CmmExpr] ->+ WasmCodeGenM w (SomeWasmExpr w)+lower_MO_Bin_Homo_Trunc op lbl w0 [x, y] =+ case someWasmTypeFromCmmType (cmmBits w0) of+ SomeWasmType ty -> do+ WasmExpr x_instr <- lower_CmmExpr_Typed lbl ty x+ WasmExpr y_instr <- lower_CmmExpr_Typed lbl ty y+ pure $+ SomeWasmExpr ty $+ truncSubword w0 ty $+ WasmExpr $+ x_instr `WasmConcat` y_instr `WasmConcat` op ty+lower_MO_Bin_Homo_Trunc _ _ _ _ = panic "lower_MO_Bin_Homo_Trunc: unreachable"++-- | Lower a binary homogeneous operation, first sign extending the+-- operands, then truncating the result.+lower_MO_Bin_Homo_Ext_Trunc ::+ (forall pre t. WasmTypeTag t -> WasmInstr w (t : t : pre) (t : pre)) ->+ CLabel ->+ Width ->+ [CmmExpr] ->+ WasmCodeGenM w (SomeWasmExpr w)+lower_MO_Bin_Homo_Ext_Trunc op lbl w0 [x, y] =+ case someWasmTypeFromCmmType (cmmBits w0) of+ SomeWasmType ty -> do+ WasmExpr x_instr <-+ extendSubword w0 ty <$> lower_CmmExpr_Typed lbl ty x+ WasmExpr y_instr <-+ extendSubword w0 ty <$> lower_CmmExpr_Typed lbl ty y+ pure $+ SomeWasmExpr ty $+ truncSubword w0 ty $+ WasmExpr $+ x_instr `WasmConcat` y_instr `WasmConcat` op ty+lower_MO_Bin_Homo_Ext_Trunc _ _ _ _ =+ panic "lower_MO_Bin_Homo_Ext_Trunc: unreachable"++-- | Lower a relational binary operation, first sign extending the+-- operands. Relational: result type is a boolean (word type).+lower_MO_Bin_Rel_Ext ::+ (forall pre t. WasmTypeTag t -> WasmInstr w (t : t : pre) (w : pre)) ->+ CLabel ->+ Width ->+ [CmmExpr] ->+ WasmCodeGenM w (SomeWasmExpr w)+lower_MO_Bin_Rel_Ext op lbl w0 [x, y] =+ case someWasmTypeFromCmmType (cmmBits w0) of+ SomeWasmType ty -> do+ WasmExpr x_instr <-+ extendSubword w0 ty <$> lower_CmmExpr_Typed lbl ty x+ WasmExpr y_instr <-+ extendSubword w0 ty <$> lower_CmmExpr_Typed lbl ty y+ ty_word <- wasmWordTypeM+ pure $+ SomeWasmExpr ty_word $+ WasmExpr $+ x_instr `WasmConcat` y_instr `WasmConcat` op ty+lower_MO_Bin_Rel_Ext _ _ _ _ = panic "lower_MO_Bin_Rel_Ext: unreachable"++-- | Lower a relational binary operation.+lower_MO_Bin_Rel ::+ ( forall pre t.+ WasmTypeTag t ->+ WasmInstr+ w+ (t : t : pre)+ (w : pre)+ ) ->+ CLabel ->+ CmmType ->+ [CmmExpr] ->+ WasmCodeGenM w (SomeWasmExpr w)+lower_MO_Bin_Rel op lbl t0 [x, y] = case someWasmTypeFromCmmType t0 of+ SomeWasmType ty -> do+ WasmExpr x_instr <- lower_CmmExpr_Typed lbl ty x+ WasmExpr y_instr <- lower_CmmExpr_Typed lbl ty y+ ty_word <- wasmWordTypeM+ pure $+ SomeWasmExpr ty_word $+ WasmExpr $+ x_instr `WasmConcat` y_instr `WasmConcat` op ty+lower_MO_Bin_Rel _ _ _ _ = panic "lower_MO_Bin_Rel: unreachable"++-- | Cast a shiftL/shiftR RHS to the same type as LHS. Because we may+-- have a 64-bit LHS and 32-bit RHS, but wasm shift operators are+-- homogeneous.+shiftRHSCast ::+ CLabel ->+ WasmTypeTag t ->+ CmmExpr ->+ WasmCodeGenM+ w+ (WasmExpr w t)+shiftRHSCast lbl t1 x = do+ SomeWasmExpr t0 (WasmExpr x_instr) <- lower_CmmExpr lbl x+ if+ | Just Refl <- t0 `testEquality` t1 -> pure $ WasmExpr x_instr+ | TagI32 <- t0,+ TagI64 <- t1 ->+ pure $ WasmExpr $ x_instr `WasmConcat` WasmI64ExtendI32 Unsigned+ | otherwise -> panic "shiftRHSCast: unreachable"++-- | Lower a 'MO_Shl' operation, truncating the result.+lower_MO_Shl ::+ CLabel ->+ Width ->+ [CmmExpr] ->+ WasmCodeGenM+ w+ (SomeWasmExpr w)+lower_MO_Shl lbl w0 [x, y] = case someWasmTypeFromCmmType (cmmBits w0) of+ SomeWasmType ty -> do+ WasmExpr x_instr <- lower_CmmExpr_Typed lbl ty x+ WasmExpr y_instr <- shiftRHSCast lbl ty y+ pure $+ SomeWasmExpr ty $+ truncSubword w0 ty $+ WasmExpr $+ x_instr `WasmConcat` y_instr `WasmConcat` WasmShl ty+lower_MO_Shl _ _ _ = panic "lower_MO_Shl: unreachable"++-- | Lower a 'MO_U_Shr' operation.+lower_MO_U_Shr ::+ CLabel ->+ Width ->+ [CmmExpr] ->+ WasmCodeGenM+ w+ (SomeWasmExpr w)+lower_MO_U_Shr lbl w0 [x, y] = case someWasmTypeFromCmmType (cmmBits w0) of+ SomeWasmType ty -> do+ WasmExpr x_instr <- lower_CmmExpr_Typed lbl ty x+ WasmExpr y_instr <- shiftRHSCast lbl ty y+ pure $+ SomeWasmExpr ty $+ WasmExpr $+ x_instr `WasmConcat` y_instr `WasmConcat` WasmShr Unsigned ty+lower_MO_U_Shr _ _ _ = panic "lower_MO_U_Shr: unreachable"++-- | Lower a 'MO_S_Shr' operation, first sign-extending the LHS, then+-- truncating the result.+lower_MO_S_Shr ::+ CLabel ->+ Width ->+ [CmmExpr] ->+ WasmCodeGenM+ w+ (SomeWasmExpr w)+lower_MO_S_Shr lbl w0 [x, y] = case someWasmTypeFromCmmType (cmmBits w0) of+ SomeWasmType ty -> do+ WasmExpr x_instr <- extendSubword w0 ty <$> lower_CmmExpr_Typed lbl ty x+ WasmExpr y_instr <- shiftRHSCast lbl ty y+ pure $+ SomeWasmExpr ty $+ truncSubword w0 ty $+ WasmExpr $+ x_instr `WasmConcat` y_instr `WasmConcat` WasmShr Signed ty+lower_MO_S_Shr _ _ _ = panic "lower_MO_S_Shr: unreachable"++-- | Lower a 'MO_MulMayOflo' operation. It's translated to a ccall to+-- @hs_mulIntMayOflo@ function in @ghc-prim/cbits/mulIntMayOflo@,+-- otherwise it's quite non-trivial to implement as inline assembly.+lower_MO_MulMayOflo ::+ CLabel -> Width -> [CmmExpr] -> WasmCodeGenM w (SomeWasmExpr w)+lower_MO_MulMayOflo lbl w0 [x, y] = case someWasmTypeFromCmmType ty_cmm of+ SomeWasmType ty -> do+ WasmExpr x_instr <- lower_CmmExpr_Typed lbl ty x+ WasmExpr y_instr <- lower_CmmExpr_Typed lbl ty y+ onFuncSym "hs_mulIntMayOflo" [ty_cmm, ty_cmm] [ty_cmm]+ pure $+ SomeWasmExpr ty $+ WasmExpr $+ x_instr+ `WasmConcat` y_instr+ `WasmConcat` WasmCCall "hs_mulIntMayOflo"+ where+ ty_cmm = cmmBits w0+lower_MO_MulMayOflo _ _ _ = panic "lower_MO_MulMayOflo: unreachable"++-- | Lower an unary conversion operation.+lower_MO_Un_Conv ::+ ( forall pre t0 t1.+ WasmTypeTag t0 ->+ WasmTypeTag t1 ->+ WasmInstr w (t0 : pre) (t1 : pre)+ ) ->+ CLabel ->+ CmmType ->+ CmmType ->+ [CmmExpr] ->+ WasmCodeGenM w (SomeWasmExpr w)+lower_MO_Un_Conv op lbl t0 t1 [x] =+ case (# someWasmTypeFromCmmType t0, someWasmTypeFromCmmType t1 #) of+ (# SomeWasmType ty0, SomeWasmType ty1 #) -> do+ WasmExpr x_instr <- lower_CmmExpr_Typed lbl ty0 x+ pure $ SomeWasmExpr ty1 $ WasmExpr $ x_instr `WasmConcat` op ty0 ty1+lower_MO_Un_Conv _ _ _ _ _ = panic "lower_MO_Un_Conv: unreachable"++-- | Lower a 'MO_SS_Conv' operation.+lower_MO_SS_Conv ::+ CLabel ->+ Width ->+ Width ->+ [CmmExpr] ->+ WasmCodeGenM+ w+ (SomeWasmExpr w)+lower_MO_SS_Conv lbl w0 w1 [x]+ | w0 == w1 = lower_CmmExpr lbl x+lower_MO_SS_Conv lbl w0 w1 [CmmLoad ptr _ align]+ | w0 < w1,+ w1 <= W32 = do+ (WasmExpr ptr_instr, o) <- lower_CmmExpr_Ptr lbl ptr+ pure $+ SomeWasmExpr TagI32 $+ truncSubword w1 TagI32 $+ WasmExpr $+ ptr_instr+ `WasmConcat` WasmLoad+ TagI32+ (wasmMemoryNarrowing TagI32 (cmmBits w0))+ Signed+ o+ align+ | w0 > w1 =+ SomeWasmExpr TagI32+ <$> lower_CmmLoad_Typed+ lbl+ ptr+ TagI32+ (cmmBits w1)+ align+lower_MO_SS_Conv lbl w0 W64 [CmmLoad ptr _ align] = do+ (WasmExpr ptr_instr, o) <- lower_CmmExpr_Ptr lbl ptr+ pure $+ SomeWasmExpr TagI64 $+ WasmExpr $+ ptr_instr+ `WasmConcat` WasmLoad+ TagI64+ (wasmMemoryNarrowing TagI64 (cmmBits w0))+ Signed+ o+ align+lower_MO_SS_Conv lbl w0 w1 [x]+ | w0 < w1,+ w1 <= W32 = do+ x_expr <- lower_CmmExpr_Typed lbl TagI32 x+ pure $+ SomeWasmExpr TagI32 $+ truncSubword w1 TagI32 $+ extendSubword w0 TagI32 x_expr+ | W32 >= w0,+ w0 > w1 = do+ x_expr <- lower_CmmExpr_Typed lbl TagI32 x+ pure $ SomeWasmExpr TagI32 $ truncSubword w1 TagI32 x_expr+lower_MO_SS_Conv lbl W32 W64 [x] = do+ WasmExpr x_instr <- lower_CmmExpr_Typed lbl TagI32 x+ pure $+ SomeWasmExpr TagI64 $+ WasmExpr $+ x_instr `WasmConcat` WasmI64ExtendI32 Signed+lower_MO_SS_Conv lbl w0 W64 [x] = do+ WasmExpr x_instr <- lower_CmmExpr_Typed lbl TagI32 x+ pure $+ SomeWasmExpr TagI64 $+ extendSubword w0 TagI64 $+ WasmExpr $+ x_instr `WasmConcat` WasmI64ExtendI32 Unsigned+lower_MO_SS_Conv lbl W64 w1 [x] = do+ WasmExpr x_instr <- lower_CmmExpr_Typed lbl TagI64 x+ pure $+ SomeWasmExpr TagI32 $+ truncSubword w1 TagI32 $+ WasmExpr $+ x_instr `WasmConcat` WasmI32WrapI64+lower_MO_SS_Conv _ _ _ _ = panic "lower_MO_SS_Conv: unreachable"++-- | Lower a 'MO_UU_Conv' operation.+lower_MO_UU_Conv ::+ CLabel ->+ Width ->+ Width ->+ [CmmExpr] ->+ WasmCodeGenM+ w+ (SomeWasmExpr w)+lower_MO_UU_Conv lbl w0 w1 [CmmLoad ptr _ align] =+ case someWasmTypeFromCmmType (cmmBits w1) of+ SomeWasmType ty ->+ SomeWasmExpr ty+ <$> lower_CmmLoad_Typed+ lbl+ ptr+ ty+ (cmmBits (min w0 w1))+ align+lower_MO_UU_Conv lbl w0 w1 [x]+ | w0 == w1 = lower_CmmExpr lbl x+ | w0 < w1, w1 <= W32 = lower_CmmExpr lbl x+ | W32 >= w0,+ w0 > w1 = do+ x_expr <- lower_CmmExpr_Typed lbl TagI32 x+ pure $ SomeWasmExpr TagI32 $ truncSubword w1 TagI32 x_expr+lower_MO_UU_Conv lbl _ W64 [x] = do+ WasmExpr x_instr <- lower_CmmExpr_Typed lbl TagI32 x+ pure $+ SomeWasmExpr TagI64 $+ WasmExpr $+ x_instr `WasmConcat` WasmI64ExtendI32 Unsigned+lower_MO_UU_Conv lbl W64 w1 [x] = do+ WasmExpr x_instr <- lower_CmmExpr_Typed lbl TagI64 x+ pure $+ SomeWasmExpr TagI32 $+ truncSubword w1 TagI32 $+ WasmExpr $+ x_instr `WasmConcat` WasmI32WrapI64+lower_MO_UU_Conv _ _ _ _ = panic "lower_MO_UU_Conv: unreachable"++-- | Lower a 'MO_FF_Conv' operation.+lower_MO_FF_Conv ::+ CLabel ->+ Width ->+ Width ->+ [CmmExpr] ->+ WasmCodeGenM+ w+ (SomeWasmExpr w)+lower_MO_FF_Conv lbl W32 W64 [x] = do+ WasmExpr x_instr <- lower_CmmExpr_Typed lbl TagF32 x+ pure $+ SomeWasmExpr TagF64 $+ WasmExpr $+ x_instr `WasmConcat` WasmF64PromoteF32+lower_MO_FF_Conv lbl W64 W32 [x] = do+ WasmExpr x_instr <- lower_CmmExpr_Typed lbl TagF64 x+ pure $+ SomeWasmExpr TagF32 $+ WasmExpr $+ x_instr `WasmConcat` WasmF32DemoteF64+lower_MO_FF_Conv _ _ _ _ = panic "lower_MO_FF_Conv: unreachable"++-- | Lower a 'CmmMachOp'.+lower_CmmMachOp ::+ CLabel ->+ MachOp ->+ [CmmExpr] ->+ WasmCodeGenM+ w+ (SomeWasmExpr w)+lower_CmmMachOp lbl (MO_Add w0) xs = lower_MO_Bin_Homo_Trunc WasmAdd lbl w0 xs+lower_CmmMachOp lbl (MO_Sub w0) xs = lower_MO_Bin_Homo_Trunc WasmSub lbl w0 xs+lower_CmmMachOp lbl (MO_Eq w0) xs = lower_MO_Bin_Rel WasmEq lbl (cmmBits w0) xs+lower_CmmMachOp lbl (MO_Ne w0) xs = lower_MO_Bin_Rel WasmNe lbl (cmmBits w0) xs+lower_CmmMachOp lbl (MO_Mul w0) xs = lower_MO_Bin_Homo_Trunc WasmMul lbl w0 xs+lower_CmmMachOp lbl (MO_S_MulMayOflo w0) xs = lower_MO_MulMayOflo lbl w0 xs+lower_CmmMachOp lbl (MO_S_Quot w0) xs =+ lower_MO_Bin_Homo_Ext_Trunc+ (WasmDiv Signed)+ lbl+ w0+ xs+lower_CmmMachOp lbl (MO_S_Rem w0) xs =+ lower_MO_Bin_Homo_Ext_Trunc+ (WasmRem Signed)+ lbl+ w0+ xs+lower_CmmMachOp lbl (MO_S_Neg w0) [x] =+ lower_CmmMachOp+ lbl+ (MO_Sub w0)+ [CmmLit $ CmmInt 0 w0, x]+lower_CmmMachOp lbl (MO_U_Quot w0) xs =+ lower_MO_Bin_Homo+ (WasmDiv Unsigned)+ lbl+ (cmmBits w0)+ xs+lower_CmmMachOp lbl (MO_U_Rem w0) xs =+ lower_MO_Bin_Homo+ (WasmRem Unsigned)+ lbl+ (cmmBits w0)+ xs+lower_CmmMachOp lbl (MO_S_Ge w0) xs =+ lower_MO_Bin_Rel_Ext+ (WasmGe Signed)+ lbl+ w0+ xs+lower_CmmMachOp lbl (MO_S_Le w0) xs =+ lower_MO_Bin_Rel_Ext+ (WasmLe Signed)+ lbl+ w0+ xs+lower_CmmMachOp lbl (MO_S_Gt w0) xs =+ lower_MO_Bin_Rel_Ext+ (WasmGt Signed)+ lbl+ w0+ xs+lower_CmmMachOp lbl (MO_S_Lt w0) xs =+ lower_MO_Bin_Rel_Ext+ (WasmLt Signed)+ lbl+ w0+ xs+lower_CmmMachOp lbl (MO_U_Ge w0) xs =+ lower_MO_Bin_Rel+ (WasmGe Unsigned)+ lbl+ (cmmBits w0)+ xs+lower_CmmMachOp lbl (MO_U_Le w0) xs =+ lower_MO_Bin_Rel+ (WasmLe Unsigned)+ lbl+ (cmmBits w0)+ xs+lower_CmmMachOp lbl (MO_U_Gt w0) xs =+ lower_MO_Bin_Rel+ (WasmGt Unsigned)+ lbl+ (cmmBits w0)+ xs+lower_CmmMachOp lbl (MO_U_Lt w0) xs =+ lower_MO_Bin_Rel+ (WasmLt Unsigned)+ lbl+ (cmmBits w0)+ xs+lower_CmmMachOp lbl (MO_F_Add w0) xs =+ lower_MO_Bin_Homo+ WasmAdd+ lbl+ (cmmFloat w0)+ xs+lower_CmmMachOp lbl (MO_F_Sub w0) xs =+ lower_MO_Bin_Homo+ WasmSub+ lbl+ (cmmFloat w0)+ xs+lower_CmmMachOp lbl (MO_F_Neg w0) xs =+ lower_MO_Un_Homo+ WasmNeg+ lbl+ (cmmFloat w0)+ xs+lower_CmmMachOp lbl (MO_F_Mul w0) xs =+ lower_MO_Bin_Homo+ WasmMul+ lbl+ (cmmFloat w0)+ xs+lower_CmmMachOp lbl (MO_F_Quot w0) xs =+ lower_MO_Bin_Homo+ (WasmDiv Signed)+ lbl+ (cmmFloat w0)+ xs+lower_CmmMachOp lbl (MO_F_Eq w0) xs =+ lower_MO_Bin_Rel+ WasmEq+ lbl+ (cmmFloat w0)+ xs+lower_CmmMachOp lbl (MO_F_Ne w0) xs =+ lower_MO_Bin_Rel+ WasmNe+ lbl+ (cmmFloat w0)+ xs+lower_CmmMachOp lbl (MO_F_Ge w0) xs =+ lower_MO_Bin_Rel+ (WasmGe Signed)+ lbl+ (cmmFloat w0)+ xs+lower_CmmMachOp lbl (MO_F_Le w0) xs =+ lower_MO_Bin_Rel+ (WasmLe Signed)+ lbl+ (cmmFloat w0)+ xs+lower_CmmMachOp lbl (MO_F_Gt w0) xs =+ lower_MO_Bin_Rel+ (WasmGt Signed)+ lbl+ (cmmFloat w0)+ xs+lower_CmmMachOp lbl (MO_F_Lt w0) xs =+ lower_MO_Bin_Rel+ (WasmLt Signed)+ lbl+ (cmmFloat w0)+ xs+lower_CmmMachOp lbl (MO_And w0) xs =+ lower_MO_Bin_Homo+ WasmAnd+ lbl+ (cmmBits w0)+ xs+lower_CmmMachOp lbl (MO_Or w0) xs = lower_MO_Bin_Homo WasmOr lbl (cmmBits w0) xs+lower_CmmMachOp lbl (MO_Xor w0) xs =+ lower_MO_Bin_Homo+ WasmXor+ lbl+ (cmmBits w0)+ xs+lower_CmmMachOp lbl (MO_Not w0) [x] =+ lower_CmmMachOp+ lbl+ (MO_Xor w0)+ [x, CmmLit $ CmmInt (widthMax w0) w0]+lower_CmmMachOp lbl (MO_Shl w0) xs = lower_MO_Shl lbl w0 xs+lower_CmmMachOp lbl (MO_U_Shr w0) xs = lower_MO_U_Shr lbl w0 xs+lower_CmmMachOp lbl (MO_S_Shr w0) xs = lower_MO_S_Shr lbl w0 xs+lower_CmmMachOp lbl (MO_SF_Conv w0 w1) xs =+ lower_MO_Un_Conv+ (WasmConvert Signed)+ lbl+ (cmmBits w0)+ (cmmFloat w1)+ xs+lower_CmmMachOp lbl (MO_FS_Conv w0 w1) xs =+ lower_MO_Un_Conv+ (WasmTruncSat Signed)+ lbl+ (cmmFloat w0)+ (cmmBits w1)+ xs+lower_CmmMachOp lbl (MO_SS_Conv w0 w1) xs = lower_MO_SS_Conv lbl w0 w1 xs+lower_CmmMachOp lbl (MO_UU_Conv w0 w1) xs = lower_MO_UU_Conv lbl w0 w1 xs+lower_CmmMachOp lbl (MO_XX_Conv w0 w1) xs = lower_MO_UU_Conv lbl w0 w1 xs+lower_CmmMachOp lbl (MO_FF_Conv w0 w1) xs = lower_MO_FF_Conv lbl w0 w1 xs+lower_CmmMachOp _ _ _ = panic "lower_CmmMachOp: unreachable"++-- | Lower a 'CmmLit'. Note that we don't emit 'f32.const' or+-- 'f64.const' for the time being, and instead emit their relative bit+-- pattern as int literals, then use an reinterpret cast. This is+-- simpler than dealing with textual representation of floating point+-- values.+lower_CmmLit :: CmmLit -> WasmCodeGenM w (SomeWasmExpr w)+lower_CmmLit lit = do+ ty_word <- wasmWordTypeM+ case lit of+ CmmInt i w -> case someWasmTypeFromCmmType (cmmBits w) of+ SomeWasmType ty ->+ pure $+ SomeWasmExpr ty $+ WasmExpr $+ WasmConst ty $+ narrowU w i+ CmmFloat f W32 ->+ pure $+ SomeWasmExpr TagF32 $+ WasmExpr $+ WasmConst+ TagI32+ (toInteger $ castFloatToWord32 $ fromRational f)+ `WasmConcat` WasmReinterpret TagI32 TagF32+ CmmFloat f W64 ->+ pure $+ SomeWasmExpr TagF64 $+ WasmExpr $+ WasmConst+ TagI64+ (toInteger $ castDoubleToWord64 $ fromRational f)+ `WasmConcat` WasmReinterpret TagI64 TagF64+ CmmLabel lbl' -> do+ onAnySym lbl'+ let sym = symNameFromCLabel lbl'+ pure $ SomeWasmExpr ty_word $ WasmExpr $ WasmSymConst sym+ CmmLabelOff lbl' o -> do+ onAnySym lbl'+ let sym = symNameFromCLabel lbl'+ pure $+ SomeWasmExpr ty_word $+ WasmExpr $+ WasmSymConst sym+ `WasmConcat` WasmConst ty_word (toInteger o)+ `WasmConcat` WasmAdd ty_word+ CmmBlock bid -> lower_CmmLit $ CmmLabel $ infoTblLbl bid+ _ -> panic "lower_CmmLit: unreachable"++-- | Lower a 'CmmReg'. Some of the logic here wouldn't be needed if+-- we have run 'fixStgRegisters' on the wasm NCG's input Cmm, but we+-- haven't run it yet for certain reasons.+lower_CmmReg :: CLabel -> CmmReg -> WasmCodeGenM w (SomeWasmExpr w)+lower_CmmReg _ (CmmLocal reg) = do+ (reg_i, SomeWasmType ty) <- onCmmLocalReg reg+ pure $ SomeWasmExpr ty $ WasmExpr $ WasmLocalGet ty reg_i+lower_CmmReg _ (CmmGlobal EagerBlackholeInfo) = do+ ty_word <- wasmWordTypeM+ pure $+ SomeWasmExpr ty_word $+ WasmExpr $+ WasmSymConst "stg_EAGER_BLACKHOLE_info"+lower_CmmReg _ (CmmGlobal GCEnter1) = do+ ty_word <- wasmWordTypeM+ ty_word_cmm <- wasmWordCmmTypeM+ onFuncSym "__stg_gc_enter_1" [] [ty_word_cmm]+ pure $ SomeWasmExpr ty_word $ WasmExpr $ WasmSymConst "__stg_gc_enter_1"+lower_CmmReg _ (CmmGlobal GCFun) = do+ ty_word <- wasmWordTypeM+ ty_word_cmm <- wasmWordCmmTypeM+ onFuncSym "__stg_gc_fun" [] [ty_word_cmm]+ pure $ SomeWasmExpr ty_word $ WasmExpr $ WasmSymConst "__stg_gc_fun"+lower_CmmReg lbl (CmmGlobal BaseReg) = do+ platform <- wasmPlatformM+ lower_CmmExpr lbl $ regTableOffset platform 0+lower_CmmReg lbl (CmmGlobal reg) = do+ ty_word <- wasmWordTypeM+ if+ | Just (sym_global, SomeWasmType ty) <-+ globalInfoFromCmmGlobalReg ty_word reg ->+ pure $ SomeWasmExpr ty $ WasmExpr $ WasmGlobalGet ty sym_global+ | otherwise -> do+ platform <- wasmPlatformM+ case someWasmTypeFromCmmType $ globalRegType platform reg of+ SomeWasmType ty -> do+ (WasmExpr ptr_instr, o) <-+ lower_CmmExpr_Ptr lbl $+ get_GlobalReg_addr platform reg+ pure $+ SomeWasmExpr ty $+ WasmExpr $+ ptr_instr+ `WasmConcat` WasmLoad+ ty+ Nothing+ Unsigned+ o+ NaturallyAligned++-- | Lower a 'CmmRegOff'.+lower_CmmRegOff :: CLabel -> CmmReg -> Int -> WasmCodeGenM w (SomeWasmExpr w)+lower_CmmRegOff lbl reg 0 = lower_CmmReg lbl reg+lower_CmmRegOff lbl reg o = do+ SomeWasmExpr ty (WasmExpr reg_instr) <- lower_CmmReg lbl reg+ pure $+ SomeWasmExpr ty $+ WasmExpr $+ reg_instr+ `WasmConcat` WasmConst+ ty+ (toInteger o)+ `WasmConcat` WasmAdd ty++-- | Lower a 'CmmLoad', passing in the expected wasm representation+-- type, and also the Cmm type (which contains width info needed for+-- memory narrowing).+--+-- The Cmm type system doesn't track signedness, so all 'CmmLoad's are+-- unsigned loads. However, as an optimization, we do emit signed+-- loads when a 'CmmLoad' result is immediately used as a 'MO_SS_Conv'+-- operand.+lower_CmmLoad_Typed ::+ CLabel ->+ CmmExpr ->+ WasmTypeTag t ->+ CmmType ->+ AlignmentSpec ->+ WasmCodeGenM w (WasmExpr w t)+lower_CmmLoad_Typed lbl ptr_expr ty ty_cmm align = do+ (WasmExpr ptr_instr, o) <- lower_CmmExpr_Ptr lbl ptr_expr+ pure $+ WasmExpr $+ ptr_instr+ `WasmConcat` WasmLoad+ ty+ (wasmMemoryNarrowing ty ty_cmm)+ Unsigned+ o+ align++-- | Lower a 'CmmLoad'.+lower_CmmLoad ::+ CLabel ->+ CmmExpr ->+ CmmType ->+ AlignmentSpec ->+ WasmCodeGenM+ w+ (SomeWasmExpr w)+lower_CmmLoad lbl ptr_expr ty_cmm align = case someWasmTypeFromCmmType ty_cmm of+ SomeWasmType ty ->+ SomeWasmExpr ty <$> lower_CmmLoad_Typed lbl ptr_expr ty ty_cmm align++-- | Lower a 'CmmExpr'.+lower_CmmExpr :: CLabel -> CmmExpr -> WasmCodeGenM w (SomeWasmExpr w)+lower_CmmExpr lbl expr = case expr of+ CmmLit lit -> lower_CmmLit lit+ CmmLoad ptr_expr ty_cmm align -> lower_CmmLoad lbl ptr_expr ty_cmm align+ CmmReg reg -> lower_CmmReg lbl reg+ CmmRegOff reg o -> lower_CmmRegOff lbl reg o+ CmmMachOp op xs -> lower_CmmMachOp lbl op xs+ _ -> panic "lower_CmmExpr: unreachable"++-- | Lower a 'CmmExpr', passing in the expected wasm representation+-- type.+lower_CmmExpr_Typed ::+ CLabel ->+ WasmTypeTag t ->+ CmmExpr ->+ WasmCodeGenM+ w+ (WasmExpr w t)+lower_CmmExpr_Typed lbl ty expr = do+ SomeWasmExpr ty' r <- lower_CmmExpr lbl expr+ if+ | Just Refl <- ty' `testEquality` ty -> pure r+ | otherwise -> panic "lower_CmmExpr_Typed: unreachable"++-- | Lower a 'CmmExpr' as a pointer, returning the pair of base+-- pointer and non-negative offset.+lower_CmmExpr_Ptr :: CLabel -> CmmExpr -> WasmCodeGenM w (WasmExpr w w, Int)+lower_CmmExpr_Ptr lbl ptr = do+ ty_word <- wasmWordTypeM+ case ptr of+ CmmLit (CmmLabelOff lbl o)+ | o >= 0 -> do+ instrs <-+ lower_CmmExpr_Typed+ lbl+ ty_word+ (CmmLit $ CmmLabel lbl)+ pure (instrs, o)+ CmmMachOp (MO_Add _) [base, CmmLit (CmmInt o _)]+ | o >= 0 -> do+ instrs <- lower_CmmExpr_Typed lbl ty_word base+ pure (instrs, fromInteger o)+ _ -> do+ instrs <- lower_CmmExpr_Typed lbl ty_word ptr+ pure (instrs, 0)++-- | Push a series of values onto the wasm value stack, returning the+-- result stack type.+type family+ WasmPushes (ts :: [WasmType]) (pre :: [WasmType]) ::+ [WasmType]+ where+ WasmPushes '[] pre = pre+ WasmPushes (t : ts) pre = WasmPushes ts (t : pre)++-- | Push the arguments onto the wasm value stack before a ccall.+data SomeWasmPreCCall w where+ SomeWasmPreCCall ::+ TypeList ts ->+ (forall pre. WasmInstr w pre (WasmPushes ts pre)) ->+ SomeWasmPreCCall w++-- | Pop the results into locals after a ccall.+data SomeWasmPostCCall w where+ SomeWasmPostCCall ::+ TypeList ts ->+ (forall post. WasmInstr w (WasmPushes ts post) post) ->+ SomeWasmPostCCall w++-- | Lower an unary homogeneous 'CallishMachOp' to a ccall.+lower_CMO_Un_Homo ::+ CLabel ->+ SymName ->+ [CmmFormal] ->+ [CmmActual] ->+ WasmCodeGenM w (WasmStatements w)+lower_CMO_Un_Homo lbl op [reg] [x] = do+ (ri, SomeWasmType ty) <- onCmmLocalReg reg+ WasmExpr x_instr <- lower_CmmExpr_Typed lbl ty x+ let ty_cmm = localRegType reg+ onFuncSym op [ty_cmm] [ty_cmm]+ pure $+ WasmStatements $+ x_instr `WasmConcat` WasmCCall op `WasmConcat` WasmLocalSet ty ri+lower_CMO_Un_Homo _ _ _ _ = panic "lower_CMO_Un_Homo: unreachable"++-- | Lower a binary homogeneous 'CallishMachOp' to a ccall.+lower_CMO_Bin_Homo ::+ CLabel ->+ SymName ->+ [CmmFormal] ->+ [CmmActual] ->+ WasmCodeGenM w (WasmStatements w)+lower_CMO_Bin_Homo lbl op [reg] [x, y] = do+ (ri, SomeWasmType ty) <- onCmmLocalReg reg+ WasmExpr x_instr <- lower_CmmExpr_Typed lbl ty x+ WasmExpr y_instr <- lower_CmmExpr_Typed lbl ty y+ let ty_cmm = localRegType reg+ onFuncSym op [ty_cmm, ty_cmm] [ty_cmm]+ pure $+ WasmStatements $+ x_instr+ `WasmConcat` y_instr+ `WasmConcat` WasmCCall op+ `WasmConcat` WasmLocalSet ty ri+lower_CMO_Bin_Homo _ _ _ _ = panic "lower_CMO_Bin_Homo: unreachable"++-- | Lower a 'MO_UF_Conv' operation.+lower_MO_UF_Conv ::+ CLabel ->+ Width ->+ [CmmFormal] ->+ [CmmActual] ->+ WasmCodeGenM w (WasmStatements w)+lower_MO_UF_Conv lbl W32 [reg] [x] = do+ ri <- onCmmLocalReg_Typed TagF32 reg+ SomeWasmExpr ty0 (WasmExpr x_instr) <- lower_CmmExpr lbl x+ pure $+ WasmStatements $+ x_instr+ `WasmConcat` WasmConvert Unsigned ty0 TagF32+ `WasmConcat` WasmLocalSet TagF32 ri+lower_MO_UF_Conv lbl W64 [reg] [x] = do+ ri <- onCmmLocalReg_Typed TagF64 reg+ SomeWasmExpr ty0 (WasmExpr x_instr) <- lower_CmmExpr lbl x+ pure $+ WasmStatements $+ x_instr+ `WasmConcat` WasmConvert Unsigned ty0 TagF64+ `WasmConcat` WasmLocalSet TagF64 ri+lower_MO_UF_Conv _ _ _ _ = panic "lower_MO_UF_Conv: unreachable"++-- | Lower a 'MO_Cmpxchg' operation to inline assembly. Currently we+-- target wasm without atomics and threads, so it's just lowered to+-- regular memory loads and stores.+lower_MO_Cmpxchg ::+ CLabel ->+ Width ->+ [CmmFormal] ->+ [CmmActual] ->+ WasmCodeGenM w (WasmStatements w)+lower_MO_Cmpxchg lbl w0 [reg] [ptr, expected, new] =+ case someWasmTypeFromCmmType ty_cmm of+ SomeWasmType ty -> do+ reg_i <- onCmmLocalReg_Typed ty reg+ let narrowing = wasmMemoryNarrowing ty ty_cmm+ (WasmExpr ptr_instr, o) <- lower_CmmExpr_Ptr lbl ptr+ WasmExpr expected_instr <- lower_CmmExpr_Typed lbl ty expected+ WasmExpr new_instr <- lower_CmmExpr_Typed lbl ty new+ pure $+ WasmStatements $+ ptr_instr+ `WasmConcat` WasmLoad ty narrowing Unsigned o NaturallyAligned+ `WasmConcat` WasmLocalTee ty reg_i+ `WasmConcat` expected_instr+ `WasmConcat` WasmEq ty+ `WasmConcat` WasmCond+ ( ptr_instr+ `WasmConcat` new_instr+ `WasmConcat` WasmStore ty narrowing o NaturallyAligned+ )+ where+ ty_cmm = cmmBits w0+lower_MO_Cmpxchg _ _ _ _ = panic "lower_MO_Cmpxchg: unreachable"++-- | Lower a 'CallishMachOp'.+lower_CallishMachOp ::+ CLabel ->+ CallishMachOp ->+ [CmmFormal] ->+ [CmmActual] ->+ WasmCodeGenM w (WasmStatements w)+lower_CallishMachOp lbl MO_F64_Pwr rs xs = lower_CMO_Bin_Homo lbl "pow" rs xs+lower_CallishMachOp lbl MO_F64_Sin rs xs = lower_CMO_Un_Homo lbl "sin" rs xs+lower_CallishMachOp lbl MO_F64_Cos rs xs = lower_CMO_Un_Homo lbl "cos" rs xs+lower_CallishMachOp lbl MO_F64_Tan rs xs = lower_CMO_Un_Homo lbl "tan" rs xs+lower_CallishMachOp lbl MO_F64_Sinh rs xs = lower_CMO_Un_Homo lbl "sinh" rs xs+lower_CallishMachOp lbl MO_F64_Cosh rs xs = lower_CMO_Un_Homo lbl "cosh" rs xs+lower_CallishMachOp lbl MO_F64_Tanh rs xs = lower_CMO_Un_Homo lbl "tanh" rs xs+lower_CallishMachOp lbl MO_F64_Asin rs xs = lower_CMO_Un_Homo lbl "asin" rs xs+lower_CallishMachOp lbl MO_F64_Acos rs xs = lower_CMO_Un_Homo lbl "acos" rs xs+lower_CallishMachOp lbl MO_F64_Atan rs xs = lower_CMO_Un_Homo lbl "atan" rs xs+lower_CallishMachOp lbl MO_F64_Asinh rs xs = lower_CMO_Un_Homo lbl "asinh" rs xs+lower_CallishMachOp lbl MO_F64_Acosh rs xs = lower_CMO_Un_Homo lbl "acosh" rs xs+lower_CallishMachOp lbl MO_F64_Atanh rs xs = lower_CMO_Un_Homo lbl "atanh" rs xs+lower_CallishMachOp lbl MO_F64_Log rs xs = lower_CMO_Un_Homo lbl "log" rs xs+lower_CallishMachOp lbl MO_F64_Log1P rs xs = lower_CMO_Un_Homo lbl "log1p" rs xs+lower_CallishMachOp lbl MO_F64_Exp rs xs = lower_CMO_Un_Homo lbl "exp" rs xs+lower_CallishMachOp lbl MO_F64_ExpM1 rs xs = lower_CMO_Un_Homo lbl "expm1" rs xs+lower_CallishMachOp lbl MO_F64_Fabs rs xs = lower_CMO_Un_Homo lbl "fabs" rs xs+lower_CallishMachOp lbl MO_F64_Sqrt rs xs = lower_CMO_Un_Homo lbl "sqrt" rs xs+lower_CallishMachOp lbl MO_F32_Pwr rs xs = lower_CMO_Bin_Homo lbl "powf" rs xs+lower_CallishMachOp lbl MO_F32_Sin rs xs = lower_CMO_Un_Homo lbl "sinf" rs xs+lower_CallishMachOp lbl MO_F32_Cos rs xs = lower_CMO_Un_Homo lbl "cosf" rs xs+lower_CallishMachOp lbl MO_F32_Tan rs xs = lower_CMO_Un_Homo lbl "tanf" rs xs+lower_CallishMachOp lbl MO_F32_Sinh rs xs = lower_CMO_Un_Homo lbl "sinhf" rs xs+lower_CallishMachOp lbl MO_F32_Cosh rs xs = lower_CMO_Un_Homo lbl "coshf" rs xs+lower_CallishMachOp lbl MO_F32_Tanh rs xs = lower_CMO_Un_Homo lbl "tanhf" rs xs+lower_CallishMachOp lbl MO_F32_Asin rs xs = lower_CMO_Un_Homo lbl "asinf" rs xs+lower_CallishMachOp lbl MO_F32_Acos rs xs = lower_CMO_Un_Homo lbl "acosf" rs xs+lower_CallishMachOp lbl MO_F32_Atan rs xs = lower_CMO_Un_Homo lbl "atanf" rs xs+lower_CallishMachOp lbl MO_F32_Asinh rs xs =+ lower_CMO_Un_Homo lbl "asinhf" rs xs+lower_CallishMachOp lbl MO_F32_Acosh rs xs =+ lower_CMO_Un_Homo lbl "acoshf" rs xs+lower_CallishMachOp lbl MO_F32_Atanh rs xs =+ lower_CMO_Un_Homo lbl "atanhf" rs xs+lower_CallishMachOp lbl MO_F32_Log rs xs = lower_CMO_Un_Homo lbl "logf" rs xs+lower_CallishMachOp lbl MO_F32_Log1P rs xs =+ lower_CMO_Un_Homo lbl "log1pf" rs xs+lower_CallishMachOp lbl MO_F32_Exp rs xs = lower_CMO_Un_Homo lbl "expf" rs xs+lower_CallishMachOp lbl MO_F32_ExpM1 rs xs =+ lower_CMO_Un_Homo lbl "expm1f" rs xs+lower_CallishMachOp lbl MO_F32_Fabs rs xs = lower_CMO_Un_Homo lbl "fabsf" rs xs+lower_CallishMachOp lbl MO_F32_Sqrt rs xs = lower_CMO_Un_Homo lbl "sqrtf" rs xs+lower_CallishMachOp lbl (MO_UF_Conv w0) rs xs = lower_MO_UF_Conv lbl w0 rs xs+lower_CallishMachOp _ MO_ReadBarrier _ _ = pure $ WasmStatements WasmNop+lower_CallishMachOp _ MO_WriteBarrier _ _ = pure $ WasmStatements WasmNop+lower_CallishMachOp _ MO_Touch _ _ = pure $ WasmStatements WasmNop+lower_CallishMachOp _ (MO_Prefetch_Data {}) _ _ = pure $ WasmStatements WasmNop+lower_CallishMachOp lbl (MO_Memcpy {}) [] xs = do+ ty_word_cmm <- wasmWordCmmTypeM+ lower_CmmUnsafeForeignCall_Drop lbl "memcpy" ty_word_cmm xs+lower_CallishMachOp lbl (MO_Memset {}) [] xs = do+ ty_word_cmm <- wasmWordCmmTypeM+ lower_CmmUnsafeForeignCall_Drop lbl "memset" ty_word_cmm xs+lower_CallishMachOp lbl (MO_Memmove {}) [] xs = do+ ty_word_cmm <- wasmWordCmmTypeM+ lower_CmmUnsafeForeignCall_Drop lbl "memmove" ty_word_cmm xs+lower_CallishMachOp lbl (MO_Memcmp {}) rs xs =+ lower_CmmUnsafeForeignCall+ lbl+ (Left "memcmp")+ Nothing+ CmmMayReturn+ rs+ xs+lower_CallishMachOp lbl (MO_PopCnt w0) rs xs =+ lower_CmmUnsafeForeignCall+ lbl+ (Left $ fromString $ "hs_popcnt" <> show (widthInBits w0))+ Nothing+ CmmMayReturn+ rs+ xs+lower_CallishMachOp lbl (MO_Pdep w0) rs xs =+ lower_CmmUnsafeForeignCall+ lbl+ (Left $ fromString $ "hs_pdep" <> show (widthInBits w0))+ Nothing+ CmmMayReturn+ rs+ xs+lower_CallishMachOp lbl (MO_Pext w0) rs xs =+ lower_CmmUnsafeForeignCall+ lbl+ (Left $ fromString $ "hs_pext" <> show (widthInBits w0))+ Nothing+ CmmMayReturn+ rs+ xs+lower_CallishMachOp lbl (MO_Clz w0) rs xs =+ lower_CmmUnsafeForeignCall+ lbl+ (Left $ fromString $ "hs_clz" <> show (widthInBits w0))+ Nothing+ CmmMayReturn+ rs+ xs+lower_CallishMachOp lbl (MO_Ctz w0) rs xs =+ lower_CmmUnsafeForeignCall+ lbl+ (Left $ fromString $ "hs_ctz" <> show (widthInBits w0))+ Nothing+ CmmMayReturn+ rs+ xs+lower_CallishMachOp lbl (MO_BSwap w0) rs xs =+ lower_CmmUnsafeForeignCall+ lbl+ (Left $ fromString $ "hs_bswap" <> show (widthInBits w0))+ Nothing+ CmmMayReturn+ rs+ xs+lower_CallishMachOp lbl (MO_BRev w0) rs xs =+ lower_CmmUnsafeForeignCall+ lbl+ (Left $ fromString $ "hs_bitrev" <> show (widthInBits w0))+ Nothing+ CmmMayReturn+ rs+ xs+lower_CallishMachOp lbl (MO_AtomicRMW w0 op) rs xs =+ lower_CmmUnsafeForeignCall+ lbl+ ( Left $+ fromString $+ ( case op of+ AMO_Add -> "hs_atomic_add"+ AMO_Sub -> "hs_atomic_sub"+ AMO_And -> "hs_atomic_and"+ AMO_Nand -> "hs_atomic_nand"+ AMO_Or -> "hs_atomic_or"+ AMO_Xor -> "hs_atomic_xor"+ )+ <> show (widthInBits w0)+ )+ Nothing+ CmmMayReturn+ rs+ xs+lower_CallishMachOp lbl (MO_AtomicRead w0 _) [reg] [ptr] = do+ SomeWasmExpr ty (WasmExpr ret_instr) <-+ lower_CmmLoad+ lbl+ ptr+ (cmmBits w0)+ NaturallyAligned+ ri <- onCmmLocalReg_Typed ty reg+ pure $ WasmStatements $ ret_instr `WasmConcat` WasmLocalSet ty ri+lower_CallishMachOp lbl (MO_AtomicWrite _ _) [] [ptr, val] =+ lower_CmmStore lbl ptr val NaturallyAligned+lower_CallishMachOp lbl (MO_Cmpxchg w0) rs xs = lower_MO_Cmpxchg lbl w0 rs xs+lower_CallishMachOp lbl (MO_Xchg w0) rs xs =+ lower_CmmUnsafeForeignCall+ lbl+ (Left $ fromString $ "hs_xchg" <> show (widthInBits w0))+ Nothing+ CmmMayReturn+ rs+ xs+lower_CallishMachOp lbl MO_SuspendThread rs xs =+ lower_CmmUnsafeForeignCall+ lbl+ (Left "suspendThread")+ Nothing+ CmmMayReturn+ rs+ xs+lower_CallishMachOp lbl MO_ResumeThread rs xs =+ lower_CmmUnsafeForeignCall+ lbl+ (Left "resumeThread")+ Nothing+ CmmMayReturn+ rs+ xs+lower_CallishMachOp _ _ _ _ = panic "lower_CallishMachOp: unreachable"++-- | Lower a ccall, but drop the result by assigning it to an unused+-- local. This is only used for lowering 'MO_Memcpy' and such, where+-- the libc functions do have a return value, but the corresponding+-- 'CallishMachOp' does not expect one.+lower_CmmUnsafeForeignCall_Drop ::+ CLabel ->+ SymName ->+ CmmType ->+ [CmmActual] ->+ WasmCodeGenM w (WasmStatements w)+lower_CmmUnsafeForeignCall_Drop lbl sym_callee ret_cmm_ty arg_exprs = do+ ret_uniq <- wasmUniq+ let ret_local = LocalReg ret_uniq ret_cmm_ty+ lower_CmmUnsafeForeignCall+ lbl+ (Left sym_callee)+ Nothing+ CmmMayReturn+ [ret_local]+ arg_exprs++-- | Lower a 'CmmUnsafeForeignCall'. The target is 'Either' a symbol,+-- which translates to a direct @call@, or an expression, which+-- translates to a @call_indirect@. The callee function signature is+-- inferred from the passed in arguments here.+lower_CmmUnsafeForeignCall ::+ CLabel ->+ (Either SymName CmmExpr) ->+ Maybe+ ([ForeignHint], [ForeignHint]) ->+ CmmReturnInfo ->+ [CmmFormal] ->+ [CmmActual] ->+ WasmCodeGenM w (WasmStatements w)+lower_CmmUnsafeForeignCall lbl target mb_hints ret_info ret_locals arg_exprs = do+ platform <- wasmPlatformM+ SomeWasmPreCCall arg_tys args_instr <-+ foldrM+ ( \(arg_expr, arg_hint) (SomeWasmPreCCall acc_tys acc_instr) -> do+ SomeWasmExpr arg_ty arg_wasm_expr <- lower_CmmExpr lbl arg_expr+ let WasmExpr arg_instr = case arg_hint of+ SignedHint ->+ extendSubword+ (cmmExprWidth platform arg_expr)+ arg_ty+ arg_wasm_expr+ _ -> arg_wasm_expr+ pure $+ SomeWasmPreCCall (arg_ty `TypeListCons` acc_tys) $+ arg_instr `WasmConcat` acc_instr+ )+ (SomeWasmPreCCall TypeListNil WasmNop)+ arg_exprs_hints+ SomeWasmPostCCall ret_tys ret_instr <-+ foldrM+ ( \(reg, ret_hint) (SomeWasmPostCCall acc_tys acc_instr) -> do+ (reg_i, SomeWasmType reg_ty) <- onCmmLocalReg reg+ pure $+ SomeWasmPostCCall (reg_ty `TypeListCons` acc_tys) $+ case (# ret_hint, cmmRegWidth platform $ CmmLocal reg #) of+ (# SignedHint, W8 #) ->+ acc_instr+ `WasmConcat` WasmConst reg_ty 0xFF+ `WasmConcat` WasmAnd reg_ty+ `WasmConcat` WasmLocalSet reg_ty reg_i+ (# SignedHint, W16 #) ->+ acc_instr+ `WasmConcat` WasmConst reg_ty 0xFFFF+ `WasmConcat` WasmAnd reg_ty+ `WasmConcat` WasmLocalSet reg_ty reg_i+ _ -> acc_instr `WasmConcat` WasmLocalSet reg_ty reg_i+ )+ (SomeWasmPostCCall TypeListNil WasmNop)+ ret_locals_hints+ case target of+ Left sym_callee -> do+ platform <- wasmPlatformM+ let arg_cmm_tys = map (cmmExprType platform) arg_exprs+ ret_cmm_tys = map localRegType ret_locals+ onFuncSym sym_callee arg_cmm_tys ret_cmm_tys+ pure $+ WasmStatements $+ args_instr+ `WasmConcat` WasmCCall sym_callee+ `WasmConcat` ( case ret_info of+ CmmMayReturn -> ret_instr+ CmmNeverReturns -> WasmUnreachable+ )+ Right fptr_callee -> do+ (WasmExpr instr_callee, _) <- lower_CmmExpr_Ptr lbl fptr_callee+ pure $+ WasmStatements $+ args_instr+ `WasmConcat` instr_callee+ `WasmConcat` WasmCCallIndirect arg_tys ret_tys+ `WasmConcat` ( case ret_info of+ CmmMayReturn -> ret_instr+ CmmNeverReturns -> WasmUnreachable+ )+ where+ (# arg_exprs_hints, ret_locals_hints #) = case mb_hints of+ Just (arg_hints, ret_hints) ->+ (# zip arg_exprs arg_hints, zip ret_locals ret_hints #)+ _ -> (# map (,NoHint) arg_exprs, map (,NoHint) ret_locals #)++-- | Lower a 'CmmStore'.+lower_CmmStore ::+ CLabel ->+ CmmExpr ->+ CmmExpr ->+ AlignmentSpec ->+ WasmCodeGenM+ w+ (WasmStatements w)+lower_CmmStore lbl ptr val align = do+ platform <- wasmPlatformM+ (WasmExpr ptr_instr, o) <- lower_CmmExpr_Ptr lbl ptr+ let ty_cmm = cmmExprType platform val+ SomeWasmExpr ty (WasmExpr val_instr) <- lower_CmmExpr lbl val+ pure $+ WasmStatements $+ ptr_instr+ `WasmConcat` val_instr+ `WasmConcat` WasmStore ty (wasmMemoryNarrowing ty ty_cmm) o align++-- | Lower a single Cmm action.+lower_CmmAction :: CLabel -> CmmNode O O -> WasmCodeGenM w (WasmStatements w)+lower_CmmAction lbl act = do+ ty_word <- wasmWordTypeM+ platform <- wasmPlatformM+ case act of+ CmmComment {} -> pure $ WasmStatements WasmNop+ CmmTick {} -> pure $ WasmStatements WasmNop+ CmmUnwind {} -> pure $ WasmStatements WasmNop+ CmmAssign (CmmLocal reg) e -> do+ (i, SomeWasmType ty_reg) <- onCmmLocalReg reg+ WasmExpr instrs <- lower_CmmExpr_Typed lbl ty_reg e+ pure $ WasmStatements $ instrs `WasmConcat` WasmLocalSet ty_reg i+ CmmAssign (CmmGlobal reg) e+ | BaseReg <- reg -> pure $ WasmStatements WasmNop+ | Just (sym_global, SomeWasmType ty_reg) <-+ globalInfoFromCmmGlobalReg ty_word reg -> do+ WasmExpr instrs <- lower_CmmExpr_Typed lbl ty_reg e+ pure $+ WasmStatements $+ instrs `WasmConcat` WasmGlobalSet ty_reg sym_global+ | otherwise -> do+ (WasmExpr ptr_instr, o) <-+ lower_CmmExpr_Ptr lbl $ get_GlobalReg_addr platform reg+ SomeWasmExpr ty_e (WasmExpr instrs) <- lower_CmmExpr lbl e+ pure $+ WasmStatements $+ ptr_instr+ `WasmConcat` instrs+ `WasmConcat` WasmStore ty_e Nothing o NaturallyAligned+ CmmStore ptr val align -> lower_CmmStore lbl ptr val align+ CmmUnsafeForeignCall+ ( ForeignTarget+ (CmmLit (CmmLabel lbl_callee))+ (ForeignConvention conv arg_hints ret_hints ret_info)+ )+ ret_locals+ arg_exprs+ | conv `elem` [CCallConv, CApiConv] ->+ lower_CmmUnsafeForeignCall+ lbl+ (Left $ symNameFromCLabel lbl_callee)+ (Just (arg_hints, ret_hints))+ ret_info+ ret_locals+ arg_exprs+ CmmUnsafeForeignCall+ (ForeignTarget target_expr (ForeignConvention conv arg_hints ret_hints ret_info))+ ret_locals+ arg_exprs+ | conv `elem` [CCallConv, CApiConv] ->+ lower_CmmUnsafeForeignCall+ lbl+ (Right target_expr)+ (Just (arg_hints, ret_hints))+ ret_info+ ret_locals+ arg_exprs+ CmmUnsafeForeignCall (PrimTarget op) ret_locals arg_exprs ->+ lower_CallishMachOp lbl op ret_locals arg_exprs+ _ -> panic "lower_CmmAction: unreachable"++-- | Lower a block of Cmm actions.+lower_CmmActions ::+ CLabel ->+ Label ->+ Block CmmNode O O ->+ WasmCodeGenM+ w+ (WasmStatements w)+lower_CmmActions lbl _ blk =+ foldlM+ ( \(WasmStatements acc) act ->+ (\(WasmStatements stmts) -> WasmStatements $ acc `WasmConcat` stmts)+ <$> lower_CmmAction lbl act+ )+ (WasmStatements WasmNop)+ acts+ where+ acts = blockToList blk++-- | Lower a 'CmmGraph'.+lower_CmmGraph :: CLabel -> CmmGraph -> WasmCodeGenM w (FuncBody w)+lower_CmmGraph lbl g = do+ ty_word <- wasmWordTypeM+ platform <- wasmPlatformM+ body <-+ structuredControl+ platform+ (\_ -> lower_CmmExpr_Typed lbl ty_word)+ (lower_CmmActions lbl)+ g+ locals <- wasmStateM $ \s ->+ (#+ map snd $ detEltsUFM $ localRegs s,+ s {localRegs = emptyUFM, localRegsCount = 0}+ #)+ pure FuncBody {funcLocals = locals, funcBody = wasmControlCast $ body}++-- | Invoked once for each 'CLabel' which indexes a 'CmmData' or+-- 'CmmProc'.+onTopSym :: CLabel -> WasmCodeGenM w ()+onTopSym lbl = case sym_vis of+ SymDefault -> wasmModifyM $ \s ->+ s+ { defaultSyms =+ IS.insert+ (getKey $ getUnique sym)+ $ defaultSyms s+ }+ _ -> pure ()+ where+ sym = symNameFromCLabel lbl++ sym_vis = symVisibilityFromCLabel lbl++-- | Invoked for each function 'CLabel' with known type (e.g. a+-- 'CmmProc', or callee of 'CmmUnsafeForeignCall').+onFuncSym :: SymName -> [CmmType] -> [CmmType] -> WasmCodeGenM w ()+onFuncSym sym arg_tys ret_tys = wasmModifyM $+ \s@WasmCodeGenState {..} ->+ s+ { funcTypes =+ addToUniqMap+ funcTypes+ sym+ ( map someWasmTypeFromCmmType arg_tys,+ map someWasmTypeFromCmmType ret_tys+ )+ }++-- | Invoked for all other 'CLabel's along the way, e.g. in+-- 'CmmStatic's or 'CmmExpr's.+onAnySym :: CLabel -> WasmCodeGenM w ()+onAnySym lbl = case sym_kind of+ SymFunc -> do+ ty_word <- wasmWordTypeM+ wasmModifyM $ \s@WasmCodeGenState {..} ->+ s {funcTypes = addToUniqMap_C const funcTypes sym ([], [SomeWasmType ty_word])}+ _ -> pure ()+ where+ sym = symNameFromCLabel lbl++ sym_kind = symKindFromCLabel lbl++-- | Invoked for each 'LocalReg', returning its wasm local id and+-- representation type.+onCmmLocalReg :: LocalReg -> WasmCodeGenM w LocalInfo+onCmmLocalReg reg = wasmStateM $ \s@WasmCodeGenState {..} ->+ let reg_info =+ (localRegsCount, someWasmTypeFromCmmType $ localRegType reg)+ in case addToUFM_L (\_ i _ -> i) reg reg_info localRegs of+ (Just i, _) -> (# i, s #)+ (_, localRegs') ->+ (#+ reg_info,+ s+ { localRegs = localRegs',+ localRegsCount =+ localRegsCount + 1+ }+ #)++-- | Invoked for each 'LocalReg' with expected representation type,+-- only returning its wasm local id.+onCmmLocalReg_Typed :: WasmTypeTag t -> LocalReg -> WasmCodeGenM w Int+onCmmLocalReg_Typed ty reg = do+ (i, SomeWasmType ty') <- onCmmLocalReg reg+ if+ | Just Refl <- ty' `testEquality` ty -> pure i+ | otherwise -> panic "onCmmLocalReg_Typed: unreachable"++-- | Invoked for dtors. We don't bother to implement dtors yet;+-- there's no native @.fini_array@ support for wasm, and the way+-- @clang@ handles dtors is generating a ctor that calls @atexit()@+-- for dtors. Which makes some sense, but we don't need to do the same+-- thing yet.+onFini :: [SymName] -> WasmCodeGenM w ()+onFini syms = do+ let n_finis = length syms+ when (n_finis /= 0) $ panic "dtors unsupported by wasm32 NCG"++-- | Invoked for ctors and dtors.+onCmmInitFini :: InitOrFini -> [CLabel] -> WasmCodeGenM w ()+onCmmInitFini iof lbls = do+ for_ lbls $ \lbl -> onFuncSym (symNameFromCLabel lbl) [] []+ case iof of+ IsInitArray -> wasmModifyM $ \s -> s {ctors = syms <> ctors s}+ IsFiniArray -> onFini syms+ where+ syms = map symNameFromCLabel lbls++-- | Invoked for each data section.+onCmmData :: CLabel -> Section -> [CmmStatic] -> WasmCodeGenM w ()+onCmmData lbl s statics = do+ ty_word <- wasmWordTypeM+ onTopSym lbl+ cs <- for statics lower_CmmStatic+ let sym = symNameFromCLabel lbl+ sec =+ DataSection+ { dataSectionKind =+ dataSectionKindFromCmmSection s,+ dataSectionAlignment =+ alignmentFromCmmSection ty_word cs,+ dataSectionContents =+ case cs of+ [DataASCII buf] -> [DataASCII $ buf `BS.snoc` 0]+ [DataIncBin p l] -> [DataIncBin p l, DataI8 0]+ _ -> cs+ }+ wasmModifyM $ \s ->+ s+ { dataSections =+ addToUniqMap (dataSections s) sym sec+ }++-- | Invoked for each 'CmmProc'.+onCmmProc :: CLabel -> CmmGraph -> WasmCodeGenM w ()+onCmmProc lbl g = do+ ty_word <- wasmWordCmmTypeM+ onTopSym lbl+ onFuncSym sym [] [ty_word]+ body <- lower_CmmGraph lbl g+ wasmModifyM $ \s -> s {funcBodies = addToUniqMap (funcBodies s) sym body}+ where+ sym = symNameFromCLabel lbl++-- | Invoked for each 'RawCmmDecl'.+onCmmDecl :: RawCmmDecl -> WasmCodeGenM w ()+onCmmDecl decl+ | Just (iof, lbls) <- isInitOrFiniArray decl = onCmmInitFini iof lbls+onCmmDecl (CmmData s (CmmStaticsRaw lbl statics)) = onCmmData lbl s statics+onCmmDecl (CmmProc _ lbl _ g) = onCmmProc lbl g++-- | Invoked for each 'RawCmmGroup'.+onCmmGroup :: RawCmmGroup -> WasmCodeGenM w ()+onCmmGroup cmms = wasmStateM $ \s0 ->+ (# (), foldl' (\s cmm -> wasmExecM (onCmmDecl cmm) s) s0 cmms #)
+ compiler/GHC/CmmToAsm/Wasm/Types.hs view
@@ -0,0 +1,475 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE Strict #-}+{-# LANGUAGE TypeFamilyDependencies #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE UndecidableInstances #-}++module GHC.CmmToAsm.Wasm.Types+ ( WasmType (..),+ WasmTypeTag (..),+ SomeWasmType (..),+ TypeList (..),+ someWasmTypesFromTypeList,+ WasmFunctionType (..),+ SymName (..),+ SymVisibility (..),+ SymKind (..),+ DataSectionKind (..),+ DataSectionContent (..),+ DataSection (..),+ GlobalInfo,+ LocalInfo,+ FuncBody (..),+ Signage (..),+ WasmInstr (..),+ WasmExpr (..),+ SomeWasmExpr (..),+ WasmStatements (..),+ WasmControl (..),+ BrTableInterval (..),+ wasmControlCast,+ WasmCodeGenState (..),+ initialWasmCodeGenState,+ WasmCodeGenM (..),+ wasmGetsM,+ wasmPlatformM,+ wasmWordTypeM,+ wasmWordCmmTypeM,+ wasmStateM,+ wasmModifyM,+ wasmExecM,+ wasmUniq,+ )+where++import Control.Applicative+import Data.ByteString (ByteString)+import Data.Coerce+import Data.Functor+import qualified Data.IntSet as IS+import Data.Kind+import Data.String+import Data.Type.Equality+import Data.Word+import GHC.Cmm+import GHC.Data.FastString+import GHC.Float+import GHC.Platform+import GHC.Prelude+import GHC.Types.Basic+import GHC.Types.Unique+import GHC.Types.Unique.FM+import GHC.Types.Unique.Map+import GHC.Types.Unique.Supply+import GHC.Utils.Monad.State.Strict+import GHC.Utils.Outputable hiding ((<>))+import Unsafe.Coerce++-- | WebAssembly type of a WebAssembly value that WebAssembly code+-- could either expect on the evaluation stack or leave on the+-- evaluation stack.+data WasmType = I32 | I64 | F32 | F64++-- | Singleton type useful for programming with `WasmType` at the type+-- level.+data WasmTypeTag :: WasmType -> Type where+ TagI32 :: WasmTypeTag 'I32+ TagI64 :: WasmTypeTag 'I64+ TagF32 :: WasmTypeTag 'F32+ TagF64 :: WasmTypeTag 'F64++deriving instance Show (WasmTypeTag t)++instance TestEquality WasmTypeTag where+ TagI32 `testEquality` TagI32 = Just Refl+ TagI64 `testEquality` TagI64 = Just Refl+ TagF32 `testEquality` TagF32 = Just Refl+ TagF64 `testEquality` TagF64 = Just Refl+ _ `testEquality` _ = Nothing++data SomeWasmType where+ SomeWasmType :: WasmTypeTag t -> SomeWasmType++instance Eq SomeWasmType where+ SomeWasmType ty0 == SomeWasmType ty1+ | Just Refl <- ty0 `testEquality` ty1 = True+ | otherwise = False++-- | List of WebAssembly types used to describe the sequence of+-- WebAssembly values that a block of code may expect on the stack or+-- leave on the stack.+data TypeList :: [WasmType] -> Type where+ TypeListNil :: TypeList '[]+ TypeListCons :: WasmTypeTag t -> TypeList ts -> TypeList (t : ts)++someWasmTypesFromTypeList :: TypeList ts -> [SomeWasmType]+someWasmTypesFromTypeList TypeListNil = []+someWasmTypesFromTypeList (ty `TypeListCons` tys) =+ SomeWasmType ty : someWasmTypesFromTypeList tys++-- | The type of a WebAssembly function, loop, block, or conditional.+-- This type says what values the code expects to pop off the stack+-- and what values it promises to push. The WebAssembly standard+-- requires that this type appear explicitly in the code.+data WasmFunctionType pre post = WasmFunctionType {ft_pops :: TypeList pre, ft_pushes :: TypeList post}++-- | For simplicity, we record other metadata in 'WasmCodeGenState' by+-- need, instead of carrying them along with 'SymName'.+newtype SymName = SymName FastString+ deriving (Eq, IsString, Show, Uniquable) via FastString+ deriving (Ord) via LexicalFastString++data SymVisibility+ = -- | Not defined in the current compilation unit.+ --+ -- @[ undefined binding=global vis=default ]@+ SymUndefined+ | -- | Defined, not visible to other compilation units.+ --+ -- @[ binding=local vis=default ]@+ SymStatic+ | -- | Defined, visible to other compilation units.+ --+ -- Adds @.hidden@ & @.globl@ directives in the output assembly.+ --+ -- @[ binding=global vis=hidden ]@+ SymDefault++-- | Represents whether a symbol is a data symbol or a function+-- symbol. Unlike linkers for other targets, @wasm-ld@ does panic at+-- link-time if it finds symbol kind inconsistency between the+-- definition site and other use sites.+--+-- Currently we solely rely on 'isCFunctionLabel' to determine a+-- symbol's kind, but it does take extra effort to make it work. The+-- main source of inconsistency arises from hand-written Cmm sources,+-- where it's possible to refer to external entities like @xxx_info@+-- and @xxx_closure@ without explicit @import CLOSURE@ declarations.+-- The Cmm parser will implicitly assume those are foreign function+-- labels, and then this will break the WebAssembly backend. #22368+-- provides more context on this issue.+--+-- tl;dr for any GHC contributor that accidentally triggers @wasm-ld@+-- errors when hacking Cmm: whatever data symbols are used in new+-- code, just add the corresponding @import CLOSURE@ declarations at+-- the top of that Cmm file.+data SymKind = SymData | SymFunc+ deriving (Eq)++-- | WebAssembly doesn't really have proper read-only memory regions+-- yet. Neverthless we add the .rodata logic here, wasm-ld will+-- aggregate all .rodata sections into a single one, which adds+-- possibility for runtime checks later, either via a customized+-- runtime, or via code instrumentation. See+-- <https://github.com/llvm/llvm-project/blob/b296aed8ae239c20ebdd7969e978f8d2a3b9c178/lld/wasm/Writer.cpp#L856>+data DataSectionKind = SectionData | SectionROData++-- | Neither Cmm or Wasm type system takes integer signedness into+-- account, therefore we always round up a 'CmmLit' to the right width+-- and handle it as an untyped integer.+data DataSectionContent+ = DataI8 Word8+ | DataI16 Word16+ | DataI32 Word32+ | DataI64 Word64+ | DataF32 Float+ | DataF64 Double+ | DataSym SymName Int+ | DataSkip Int+ | DataASCII ByteString+ | DataIncBin FilePath Int++data DataSection = DataSection+ { dataSectionKind :: DataSectionKind,+ dataSectionAlignment ::+ Alignment,+ dataSectionContents :: [DataSectionContent]+ }++-- | We need to remember the symbols. Determinism is achieved by+-- sorting symbols before writing the assembly.+type SymMap = UniqMap SymName++-- | No need to remember the symbols.+type SymSet = IS.IntSet++type GlobalInfo = (SymName, SomeWasmType)++type LocalInfo = (Int, SomeWasmType)++data FuncBody w = FuncBody+ { funcLocals :: [SomeWasmType],+ -- | Most are Cmm functions, but may also contain synthesized+ -- function of other types, sigh.+ funcBody :: WasmControl (WasmStatements w) (WasmExpr w w) '[] '[w]+ }++data Signage = Signed | Unsigned++-- | The @w@ type variable in the Wasm IR stands for "platform word+-- type", so 'TagI32' on wasm32, and 'TagI64' on wasm64. This way, we+-- can make the codegen logic work on both wasm32/wasm64 in a+-- type-safe manner.+data WasmInstr :: WasmType -> [WasmType] -> [WasmType] -> Type where+ WasmComment :: String -> WasmInstr w pre pre+ WasmNop :: WasmInstr w pre pre+ WasmDrop :: WasmInstr w (t : pre) pre+ WasmUnreachable :: WasmInstr w pre post+ WasmConst :: WasmTypeTag t -> Integer -> WasmInstr w pre (t : pre)+ WasmSymConst :: SymName -> WasmInstr w pre (w : pre)+ WasmLoad ::+ WasmTypeTag t ->+ Maybe Int ->+ Signage ->+ Int ->+ AlignmentSpec ->+ WasmInstr w (w : pre) (t : pre)+ WasmStore ::+ WasmTypeTag t ->+ Maybe Int ->+ Int ->+ AlignmentSpec ->+ WasmInstr+ w+ (t : w : pre)+ pre+ WasmGlobalGet :: WasmTypeTag t -> SymName -> WasmInstr w pre (t : pre)+ WasmGlobalSet :: WasmTypeTag t -> SymName -> WasmInstr w (t : pre) pre+ WasmLocalGet :: WasmTypeTag t -> Int -> WasmInstr w pre (t : pre)+ WasmLocalSet :: WasmTypeTag t -> Int -> WasmInstr w (t : pre) pre+ WasmLocalTee :: WasmTypeTag t -> Int -> WasmInstr w (t : pre) (t : pre)+ WasmCCall :: SymName -> WasmInstr w pre post+ WasmCCallIndirect ::+ TypeList arg_tys ->+ TypeList ret_tys ->+ WasmInstr+ w+ (w : pre)+ post+ WasmConcat ::+ WasmInstr w pre mid ->+ WasmInstr w mid post ->+ WasmInstr w pre post+ WasmReinterpret ::+ WasmTypeTag t0 ->+ WasmTypeTag t1 ->+ WasmInstr+ w+ (t0 : pre)+ (t1 : pre)+ WasmTruncSat ::+ Signage ->+ WasmTypeTag t0 ->+ WasmTypeTag t1 ->+ WasmInstr+ w+ (t0 : pre)+ (t1 : pre)+ WasmConvert ::+ Signage ->+ WasmTypeTag t0 ->+ WasmTypeTag t1 ->+ WasmInstr+ w+ (t0 : pre)+ (t1 : pre)+ WasmAdd :: WasmTypeTag t -> WasmInstr w (t : t : pre) (t : pre)+ WasmSub :: WasmTypeTag t -> WasmInstr w (t : t : pre) (t : pre)+ WasmMul :: WasmTypeTag t -> WasmInstr w (t : t : pre) (t : pre)+ WasmDiv :: Signage -> WasmTypeTag t -> WasmInstr w (t : t : pre) (t : pre)+ WasmRem :: Signage -> WasmTypeTag t -> WasmInstr w (t : t : pre) (t : pre)+ WasmAnd :: WasmTypeTag t -> WasmInstr w (t : t : pre) (t : pre)+ WasmOr :: WasmTypeTag t -> WasmInstr w (t : t : pre) (t : pre)+ WasmXor :: WasmTypeTag t -> WasmInstr w (t : t : pre) (t : pre)+ WasmEq :: WasmTypeTag t -> WasmInstr w (t : t : pre) (w : pre)+ WasmNe :: WasmTypeTag t -> WasmInstr w (t : t : pre) (w : pre)+ WasmLt :: Signage -> WasmTypeTag t -> WasmInstr w (t : t : pre) (w : pre)+ WasmGt :: Signage -> WasmTypeTag t -> WasmInstr w (t : t : pre) (w : pre)+ WasmLe :: Signage -> WasmTypeTag t -> WasmInstr w (t : t : pre) (w : pre)+ WasmGe :: Signage -> WasmTypeTag t -> WasmInstr w (t : t : pre) (w : pre)+ WasmShl :: WasmTypeTag t -> WasmInstr w (t : t : pre) (t : pre)+ WasmShr :: Signage -> WasmTypeTag t -> WasmInstr w (t : t : pre) (t : pre)+ WasmI32Extend8S :: WasmInstr w ('I32 : pre) ('I32 : pre)+ WasmI32Extend16S :: WasmInstr w ('I32 : pre) ('I32 : pre)+ WasmI64Extend8S :: WasmInstr w ('I64 : pre) ('I64 : pre)+ WasmI64Extend16S :: WasmInstr w ('I64 : pre) ('I64 : pre)+ WasmI64Extend32S :: WasmInstr w ('I64 : pre) ('I64 : pre)+ WasmI64ExtendI32 :: Signage -> WasmInstr w ('I32 : pre) ('I64 : pre)+ WasmI32WrapI64 :: WasmInstr w ('I64 : pre) ('I32 : pre)+ WasmF32DemoteF64 :: WasmInstr w ('F64 : pre) ('F32 : pre)+ WasmF64PromoteF32 :: WasmInstr w ('F32 : pre) ('F64 : pre)+ WasmAbs :: WasmTypeTag t -> WasmInstr w (t : pre) (t : pre)+ WasmNeg :: WasmTypeTag t -> WasmInstr w (t : pre) (t : pre)+ WasmCond :: WasmInstr w pre pre -> WasmInstr w (w : pre) pre++newtype WasmExpr w t = WasmExpr (forall pre. WasmInstr w pre (t : pre))++data SomeWasmExpr w where+ SomeWasmExpr :: WasmTypeTag t -> WasmExpr w t -> SomeWasmExpr w++newtype WasmStatements w = WasmStatements (forall pre. WasmInstr w pre pre)++-- | Representation of WebAssembly control flow.+-- Normally written as+-- @+-- WasmControl s e pre post+-- @+-- Type parameter `s` is the type of (unspecified) statements.+-- It might be instantiated with an open Cmm block or with a sequence+-- of Wasm instructions.+-- Parameter `e` is the type of expressions.+-- Parameter `pre` represents the values that are expected on the+-- WebAssembly stack when the code runs, and `post` represents+-- the state of the stack on completion.+data WasmControl :: Type -> Type -> [WasmType] -> [WasmType] -> Type where+ WasmPush :: WasmTypeTag t -> e -> WasmControl s e stack (t : stack)+ WasmBlock ::+ WasmFunctionType pre post ->+ WasmControl s e pre post ->+ WasmControl s e pre post+ WasmLoop ::+ WasmFunctionType pre post ->+ WasmControl s e pre post ->+ WasmControl s e pre post+ WasmIfTop ::+ WasmFunctionType pre post ->+ WasmControl s e pre post ->+ WasmControl s e pre post ->+ WasmControl s e ('I32 : pre) post+ WasmBr :: Int -> WasmControl s e dropped destination -- not typechecked+ WasmFallthrough :: WasmControl s e dropped destination+ -- generates no code, but has the same type as a branch+ WasmBrTable ::+ e ->+ BrTableInterval -> -- for testing+ [Int] -> -- targets+ Int -> -- default target+ WasmControl s e dropped destination+ -- invariant: the table interval is contained+ -- within [0 .. pred (length targets)]++ -- Note [WasmTailCall]+ -- ~~~~~~~~~~~~~~~~~~~+ -- This represents the exit point of each CmmGraph: tail calling the+ -- destination in CmmCall. The STG stack may grow before the call,+ -- but it's always a tail call in the sense that the C call stack is+ -- guaranteed not to grow.+ --+ -- In the wasm backend, WasmTailCall is lowered to different+ -- assembly code given whether the wasm tail-call extension is+ -- enabled:+ --+ -- When tail-call is not enabled (which is the default as of today),+ -- a WasmTailCall is lowered to code that pushes the callee function+ -- pointer onto the value stack and returns immediately. The actual+ -- call is done by the trampoline in StgRun.+ --+ -- When tail-call is indeed enabled via passing -mtail-call in+ -- CONF_CC_OPTS_STAGE2 at configure time, a WasmTailCall is lowered+ -- to return_call/return_call_indirect, thus tail calling into its+ -- callee without returning to StgRun.+ WasmTailCall ::+ e ->+ WasmControl s e t1star t2star -- as per type system+ WasmActions ::+ s ->+ WasmControl s e stack stack -- basic block: one entry, one exit+ WasmSeq ::+ WasmControl s e pre mid ->+ WasmControl s e mid post ->+ WasmControl s e pre post++data BrTableInterval = BrTableInterval {bti_lo :: Integer, bti_count :: Integer}+ deriving (Show)++instance Outputable BrTableInterval where+ ppr range =+ brackets $+ hcat+ [integer (bti_lo range), text "..", integer hi]+ where+ hi = bti_lo range + bti_count range - 1++wasmControlCast :: WasmControl s e pre post -> WasmControl s e pre' post'+wasmControlCast = unsafeCoerce++data WasmCodeGenState w = WasmCodeGenState+ { -- | Target platform+ wasmPlatform :: Platform,+ -- | Defined symbols with 'SymDefault' visibility.+ defaultSyms :: SymSet,+ -- | Function types, defined or not. There may exist a function+ -- whose type is unknown (e.g. as a function pointer), in that+ -- case we fall back to () -> (), it's imperfect but works with+ -- wasm-ld.+ funcTypes :: SymMap ([SomeWasmType], [SomeWasmType]),+ -- | Defined function bodies.+ funcBodies :: SymMap (FuncBody w),+ -- | Defined data sections.+ dataSections :: SymMap DataSection,+ -- | ctors in the current compilation unit.+ ctors :: [SymName],+ localRegs ::+ UniqFM LocalReg LocalInfo,+ localRegsCount ::+ Int,+ wasmUniqSupply :: UniqSupply+ }++initialWasmCodeGenState :: Platform -> UniqSupply -> WasmCodeGenState w+initialWasmCodeGenState platform us =+ WasmCodeGenState+ { wasmPlatform =+ platform,+ defaultSyms = IS.empty,+ funcTypes = emptyUniqMap,+ funcBodies =+ emptyUniqMap,+ dataSections = emptyUniqMap,+ ctors =+ [],+ localRegs = emptyUFM,+ localRegsCount = 0,+ wasmUniqSupply = us+ }++newtype WasmCodeGenM w a = WasmCodeGenM (State (WasmCodeGenState w) a)+ deriving newtype (Functor, Applicative, Monad)++wasmGetsM :: (WasmCodeGenState w -> a) -> WasmCodeGenM w a+wasmGetsM = coerce . gets++wasmPlatformM :: WasmCodeGenM w Platform+wasmPlatformM = wasmGetsM wasmPlatform++wasmWordTypeM :: WasmCodeGenM w (WasmTypeTag w)+wasmWordTypeM = wasmGetsM $ \s ->+ if target32Bit $ wasmPlatform s+ then unsafeCoerce TagI32+ else unsafeCoerce TagI64++wasmWordCmmTypeM :: WasmCodeGenM w CmmType+wasmWordCmmTypeM = wasmGetsM (bWord . wasmPlatform)++wasmStateM ::+ (WasmCodeGenState w -> (# a, WasmCodeGenState w #)) ->+ WasmCodeGenM w a+wasmStateM = coerce . State++wasmModifyM :: (WasmCodeGenState w -> WasmCodeGenState w) -> WasmCodeGenM w ()+wasmModifyM = coerce . modify++wasmExecM :: WasmCodeGenM w a -> WasmCodeGenState w -> WasmCodeGenState w+wasmExecM (WasmCodeGenM s) = execState s++wasmUniq :: WasmCodeGenM w Unique+wasmUniq = wasmStateM $+ \s@WasmCodeGenState {..} -> case takeUniqFromSupply wasmUniqSupply of+ (u, us) -> (# u, s {wasmUniqSupply = us} #)
+ compiler/GHC/CmmToAsm/Wasm/Utils.hs view
@@ -0,0 +1,29 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Strict #-}++module GHC.CmmToAsm.Wasm.Utils+ ( widthMax,+ detEltsUFM,+ detEltsUniqMap,+ builderCommas,+ )+where++import Data.ByteString.Builder+import Data.List (intersperse, sortOn)+import GHC.Cmm+import GHC.Prelude+import GHC.Types.Unique.FM+import GHC.Types.Unique.Map++widthMax :: Width -> Integer+widthMax w = (1 `shiftL` widthInBits w) - 1++detEltsUFM :: Ord k => UniqFM k0 (k, a) -> [(k, a)]+detEltsUFM = sortOn fst . nonDetEltsUFM++detEltsUniqMap :: Ord k => UniqMap k a -> [(k, a)]+detEltsUniqMap = sortOn fst . nonDetEltsUniqMap++builderCommas :: (a -> Builder) -> [a] -> Builder+builderCommas f xs = mconcat (intersperse ", " (map f xs))
compiler/GHC/CmmToAsm/X86.hs view
@@ -33,11 +33,12 @@ , canShortcut = X86.canShortcut , shortcutStatics = X86.shortcutStatics , shortcutJump = X86.shortcutJump- , pprNatCmmDecl = X86.pprNatCmmDecl config+ , pprNatCmmDeclS = X86.pprNatCmmDecl config+ , pprNatCmmDeclH = X86.pprNatCmmDecl config , maxSpillSlots = X86.maxSpillSlots config , allocatableRegs = X86.allocatableRegs platform , ncgAllocMoreStack = X86.allocMoreStack platform- , ncgMakeFarBranches = \_p _i bs -> pure bs+ , ncgMakeFarBranches = const id , extractUnwindPoints = X86.extractUnwindPoints , invertCondBranches = X86.invertCondBranches }
compiler/GHC/CmmToAsm/X86/CodeGen.hs view
@@ -326,7 +326,7 @@ -> genForeignCall target result_regs args bid _ -> (,Nothing) <$> case stmt of- CmmComment s -> return (unitOL (COMMENT $ ftext s))+ CmmComment s -> return (unitOL (COMMENT s)) CmmTick {} -> return nilOL CmmUnwind regs -> do@@ -1979,10 +1979,10 @@ -- Use ASSERT so we don't break releases if -- LTT/LE creep in somehow. LTT ->- assertPpr False (ppr "Should have been turned into >")+ assertPpr False (text "Should have been turned into >") and_ordered LE ->- assertPpr False (ppr "Should have been turned into >=")+ assertPpr False (text "Should have been turned into >=") and_ordered _ -> and_ordered @@ -2892,25 +2892,10 @@ let op = OpAddr (AddrBaseIndex (EABaseReg tableReg) (EAIndex reg (platformWordSizeInBytes platform)) (ImmInt 0)) - offsetReg <- getNewRegNat (intFormat (platformWordWidth platform))- return $ if is32bit || os == OSDarwin- then e_code `appOL` t_code `appOL` toOL [+ return $ e_code `appOL` t_code `appOL` toOL [ ADD (intFormat (platformWordWidth platform)) op (OpReg tableReg), JMP_TBL (OpReg tableReg) ids rosection lbl ]- else -- HACK: On x86_64 binutils<2.17 is only able to generate- -- PC32 relocations, hence we only get 32-bit offsets in- -- the jump table. As these offsets are always negative- -- we need to properly sign extend them to 64-bit. This- -- hack should be removed in conjunction with the hack in- -- PprMach.hs/pprDataItem once binutils 2.17 is standard.- e_code `appOL` t_code `appOL` toOL [- MOVSxL II32 op (OpReg offsetReg),- ADD (intFormat (platformWordWidth platform))- (OpReg offsetReg)- (OpReg tableReg),- JMP_TBL (OpReg tableReg) ids rosection lbl- ] else do (reg,e_code) <- getSomeReg indexExpr lbl <- getNewLabelNat@@ -3032,7 +3017,7 @@ -- ja _c2g2 -- jmp _c2g1 ----- Removing the jump reduces the pressure on the branch predidiction system+-- Removing the jump reduces the pressure on the branch prediction system -- and plays better with the uOP cache. condFltReg :: Bool -> Cond -> CmmExpr -> CmmExpr -> NatM Register@@ -3052,9 +3037,9 @@ GU -> plain_test dst GEU -> plain_test dst -- Use ASSERT so we don't break releases if these creep in.- LTT -> assertPpr False (ppr "Should have been turned into >") $+ LTT -> assertPpr False (text "Should have been turned into >") $ and_ordered dst- LE -> assertPpr False (ppr "Should have been turned into >=") $+ LE -> assertPpr False (text "Should have been turned into >=") $ and_ordered dst _ -> and_ordered dst) @@ -3317,9 +3302,9 @@ invert bs where invert :: [NatBasicBlock Instr] -> [NatBasicBlock Instr]- invert ((BasicBlock lbl1 ins@(_:_:_xs)):b2@(BasicBlock lbl2 _):bs)+ invert (BasicBlock lbl1 ins:b2@(BasicBlock lbl2 _):bs) | --pprTrace "Block" (ppr lbl1) True,- (jmp1,jmp2) <- last2 ins+ Just (jmp1,jmp2) <- last2 ins , JXX cond1 target1 <- jmp1 , target1 == lbl2 --, pprTrace "CutChance" (ppr b1) True
compiler/GHC/CmmToAsm/X86/Instr.hs view
@@ -38,6 +38,7 @@ where import GHC.Prelude+import GHC.Data.FastString import GHC.CmmToAsm.X86.Cond import GHC.CmmToAsm.X86.Regs@@ -170,7 +171,7 @@ data Instr -- comment pseudo-op- = COMMENT SDoc+ = COMMENT FastString -- location pseudo-op (file, line, col, name) | LOCATION Int Int Int String@@ -868,7 +869,7 @@ case platformArch platform of ArchX86 | needs_probe_call platform amount -> [ MOV II32 (OpImm (ImmInt amount)) (OpReg eax)- , CALL (Left $ strImmLit "___chkstk_ms") [eax]+ , CALL (Left $ strImmLit (fsLit "___chkstk_ms")) [eax] , SUB II32 (OpReg eax) (OpReg esp) ] | otherwise ->@@ -877,7 +878,7 @@ ] ArchX86_64 | needs_probe_call platform amount -> [ MOV II64 (OpImm (ImmInt amount)) (OpReg rax)- , CALL (Left $ strImmLit "___chkstk_ms") [rax]+ , CALL (Left $ strImmLit (fsLit "___chkstk_ms")) [rax] , SUB II64 (OpReg rax) (OpReg rsp) ] | otherwise ->
compiler/GHC/CmmToAsm/X86/Ppr.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE ScopedTypeVariables #-} ----------------------------------------------------------------------------- --@@ -11,11 +12,7 @@ module GHC.CmmToAsm.X86.Ppr ( pprNatCmmDecl,- pprData, pprInstr,- pprFormat,- pprImm,- pprDataItem, ) where@@ -39,6 +36,7 @@ import GHC.Cmm.Dataflow.Label import GHC.Cmm.BlockId import GHC.Cmm.CLabel+import GHC.Cmm.DebugBlock (pprUnwindTable) import GHC.Types.Basic (Alignment, mkAlignment, alignmentBytes) import GHC.Types.Unique ( pprUniqueAlways )@@ -65,12 +63,12 @@ -- .subsections_via_symbols and -dead_strip can be found at -- <https://developer.apple.com/library/mac/documentation/DeveloperTools/Reference/Assembler/040-Assembler_Directives/asm_directives.html#//apple_ref/doc/uid/TP30000823-TPXREF101> -pprProcAlignment :: NCGConfig -> SDoc+pprProcAlignment :: IsDoc doc => NCGConfig -> doc pprProcAlignment config = maybe empty (pprAlign platform . mkAlignment) (ncgProcAlignment config) where platform = ncgPlatform config -pprNatCmmDecl :: NCGConfig -> NatCmmDecl (Alignment, RawCmmStatics) Instr -> SDoc+pprNatCmmDecl :: IsDoc doc => NCGConfig -> NatCmmDecl (Alignment, RawCmmStatics) Instr -> doc pprNatCmmDecl config (CmmData section dats) = pprSectionAlign config section $$ pprDatas config dats @@ -85,7 +83,7 @@ pprProcLabel config lbl $$ pprLabel platform lbl $$ -- blocks guaranteed not null, so label needed vcat (map (pprBasicBlock config top_info) blocks) $$- ppWhen (ncgDwarfEnabled config) (pprBlockEndLabel platform lbl $$ pprProcEndLabel platform lbl) $$+ ppWhen (ncgDwarfEnabled config) (line (pprBlockEndLabel platform lbl) $$ line (pprProcEndLabel platform lbl)) $$ pprSizeDecl platform lbl Just (CmmStaticsRaw info_lbl _) ->@@ -93,48 +91,51 @@ pprProcAlignment config $$ pprProcLabel config lbl $$ (if platformHasSubsectionsViaSymbols platform- then pdoc platform (mkDeadStripPreventer info_lbl) <> colon+ then line (pprAsmLabel platform (mkDeadStripPreventer info_lbl) <> colon) else empty) $$ vcat (map (pprBasicBlock config top_info) blocks) $$- ppWhen (ncgDwarfEnabled config) (pprProcEndLabel platform info_lbl) $$+ ppWhen (ncgDwarfEnabled config) (line (pprProcEndLabel platform info_lbl)) $$ -- above: Even the first block gets a label, because with branch-chain -- elimination, it might be the target of a goto. (if platformHasSubsectionsViaSymbols platform then -- See Note [Subsections Via Symbols]- text "\t.long "- <+> pdoc platform info_lbl+ line+ $ text "\t.long "+ <+> pprAsmLabel platform info_lbl <+> char '-'- <+> pdoc platform (mkDeadStripPreventer info_lbl)+ <+> pprAsmLabel platform (mkDeadStripPreventer info_lbl) else empty) $$ pprSizeDecl platform info_lbl+{-# SPECIALIZE pprNatCmmDecl :: NCGConfig -> NatCmmDecl (Alignment, RawCmmStatics) Instr -> SDoc #-}+{-# SPECIALIZE pprNatCmmDecl :: NCGConfig -> NatCmmDecl (Alignment, RawCmmStatics) Instr -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable -- | Output an internal proc label. See Note [Internal proc labels] in CLabel.-pprProcLabel :: NCGConfig -> CLabel -> SDoc+pprProcLabel :: IsDoc doc => NCGConfig -> CLabel -> doc pprProcLabel config lbl | ncgExposeInternalSymbols config , Just lbl' <- ppInternalProcLabel (ncgThisModule config) lbl- = lbl' <> colon+ = line (lbl' <> colon) | otherwise = empty -pprProcEndLabel :: Platform -> CLabel -- ^ Procedure name- -> SDoc+pprProcEndLabel :: IsLine doc => Platform -> CLabel -- ^ Procedure name+ -> doc pprProcEndLabel platform lbl =- pdoc platform (mkAsmTempProcEndLabel lbl) <> colon+ pprAsmLabel platform (mkAsmTempProcEndLabel lbl) <> colon -pprBlockEndLabel :: Platform -> CLabel -- ^ Block name- -> SDoc+pprBlockEndLabel :: IsLine doc => Platform -> CLabel -- ^ Block name+ -> doc pprBlockEndLabel platform lbl =- pdoc platform (mkAsmTempEndLabel lbl) <> colon+ pprAsmLabel platform (mkAsmTempEndLabel lbl) <> colon -- | Output the ELF .size directive.-pprSizeDecl :: Platform -> CLabel -> SDoc+pprSizeDecl :: IsDoc doc => Platform -> CLabel -> doc pprSizeDecl platform lbl = if osElfTarget (platformOS platform)- then text "\t.size" <+> pdoc platform lbl <> text ", .-" <> pdoc platform lbl+ then line (text "\t.size" <+> pprAsmLabel platform lbl <> text ", .-" <> pprAsmLabel platform lbl) else empty -pprBasicBlock :: NCGConfig -> LabelMap RawCmmStatics -> NatBasicBlock Instr -> SDoc+pprBasicBlock :: IsDoc doc => NCGConfig -> LabelMap RawCmmStatics -> NatBasicBlock Instr -> doc pprBasicBlock config info_env (BasicBlock blockid instrs) = maybe_infotable $ pprLabel platform asmLbl $$@@ -142,8 +143,8 @@ ppWhen (ncgDwarfEnabled config) ( -- Emit both end labels since this may end up being a standalone -- top-level block- pprBlockEndLabel platform asmLbl- <> pprProcEndLabel platform asmLbl+ line (pprBlockEndLabel platform asmLbl+ <> pprProcEndLabel platform asmLbl) ) where asmLbl = blockLbl blockid@@ -156,7 +157,7 @@ vcat (map (pprData config) info) $$ pprLabel platform infoLbl $$ c $$- ppWhen (ncgDwarfEnabled config) (pdoc platform (mkAsmTempEndLabel infoLbl) <> colon)+ ppWhen (ncgDwarfEnabled config) (line (pprAsmLabel platform (mkAsmTempEndLabel infoLbl) <> colon)) -- Make sure the info table has the right .loc for the block -- coming right after it. See Note [Info Offset]@@ -165,7 +166,7 @@ _other -> empty -pprDatas :: NCGConfig -> (Alignment, RawCmmStatics) -> SDoc+pprDatas :: IsDoc doc => NCGConfig -> (Alignment, RawCmmStatics) -> doc -- See Note [emit-time elimination of static indirections] in "GHC.Cmm.CLabel". pprDatas config (_, CmmStaticsRaw alias [CmmStaticLit (CmmLabel lbl), CmmStaticLit ind, _, _]) | lbl == mkIndStaticInfoLabel@@ -175,31 +176,32 @@ , Just ind' <- labelInd ind , alias `mayRedirectTo` ind' = pprGloblDecl (ncgPlatform config) alias- $$ text ".equiv" <+> pdoc (ncgPlatform config) alias <> comma <> pdoc (ncgPlatform config) (CmmLabel ind')+ $$ line (text ".equiv" <+> pprAsmLabel (ncgPlatform config) alias <> comma <> pprAsmLabel (ncgPlatform config) ind') pprDatas config (align, (CmmStaticsRaw lbl dats)) = vcat (pprAlign platform align : pprLabel platform lbl : map (pprData config) dats) where platform = ncgPlatform config -pprData :: NCGConfig -> CmmStatic -> SDoc-pprData _config (CmmString str) = pprString str-pprData _config (CmmFileEmbed path) = pprFileEmbed path+pprData :: IsDoc doc => NCGConfig -> CmmStatic -> doc+pprData _config (CmmString str) = line (pprString str)+pprData _config (CmmFileEmbed path _) = line (pprFileEmbed path) pprData config (CmmUninitialised bytes)- = let platform = ncgPlatform config+ = line+ $ let platform = ncgPlatform config in if platformOS platform == OSDarwin then text ".space " <> int bytes else text ".skip " <> int bytes pprData config (CmmStaticLit lit) = pprDataItem config lit -pprGloblDecl :: Platform -> CLabel -> SDoc+pprGloblDecl :: IsDoc doc => Platform -> CLabel -> doc pprGloblDecl platform lbl | not (externallyVisibleCLabel lbl) = empty- | otherwise = text ".globl " <> pdoc platform lbl+ | otherwise = line (text ".globl " <> pprAsmLabel platform lbl) -pprLabelType' :: Platform -> CLabel -> SDoc+pprLabelType' :: IsLine doc => Platform -> CLabel -> doc pprLabelType' platform lbl = if isCFunctionLabel lbl || functionOkInfoTable then text "@function"@@ -254,24 +256,24 @@ but mess up with the relocation. https://phabricator.haskell.org/D4730 -} functionOkInfoTable = platformTablesNextToCode platform &&- isInfoTableLabel lbl && not (isConInfoTableLabel lbl)+ isInfoTableLabel lbl && not (isCmmInfoTableLabel lbl) && not (isConInfoTableLabel lbl) -pprTypeDecl :: Platform -> CLabel -> SDoc+pprTypeDecl :: IsDoc doc => Platform -> CLabel -> doc pprTypeDecl platform lbl = if osElfTarget (platformOS platform) && externallyVisibleCLabel lbl- then text ".type " <> pdoc platform lbl <> text ", " <> pprLabelType' platform lbl+ then line (text ".type " <> pprAsmLabel platform lbl <> text ", " <> pprLabelType' platform lbl) else empty -pprLabel :: Platform -> CLabel -> SDoc+pprLabel :: IsDoc doc => Platform -> CLabel -> doc pprLabel platform lbl = pprGloblDecl platform lbl $$ pprTypeDecl platform lbl- $$ (pdoc platform lbl <> colon)+ $$ line (pprAsmLabel platform lbl <> colon) -pprAlign :: Platform -> Alignment -> SDoc+pprAlign :: IsDoc doc => Platform -> Alignment -> doc pprAlign platform alignment- = text ".align " <> int (alignmentOn platform)+ = line $ text ".align " <> int (alignmentOn platform) where bytes = alignmentBytes alignment alignmentOn platform = if platformOS platform == OSDarwin@@ -285,7 +287,7 @@ log2 8 = 3 log2 n = 1 + log2 (n `quot` 2) -pprReg :: Platform -> Format -> Reg -> SDoc+pprReg :: forall doc. IsLine doc => Platform -> Format -> Reg -> doc pprReg platform f r = case r of RegReal (RealRegSingle i) ->@@ -297,7 +299,7 @@ RegVirtual (VirtualRegD u) -> text "%vD_" <> pprUniqueAlways u where- ppr32_reg_no :: Format -> Int -> SDoc+ ppr32_reg_no :: Format -> Int -> doc ppr32_reg_no II8 = ppr32_reg_byte ppr32_reg_no II16 = ppr32_reg_word ppr32_reg_no _ = ppr32_reg_long@@ -327,7 +329,7 @@ _ -> ppr_reg_float i } - ppr64_reg_no :: Format -> Int -> SDoc+ ppr64_reg_no :: Format -> Int -> doc ppr64_reg_no II8 = ppr64_reg_byte ppr64_reg_no II16 = ppr64_reg_word ppr64_reg_no II32 = ppr64_reg_long@@ -385,7 +387,7 @@ _ -> ppr_reg_float i } -ppr_reg_float :: Int -> SDoc+ppr_reg_float :: IsLine doc => Int -> doc ppr_reg_float i = case i of 16 -> text "%xmm0" ; 17 -> text "%xmm1" 18 -> text "%xmm2" ; 19 -> text "%xmm3"@@ -397,7 +399,7 @@ 30 -> text "%xmm14"; 31 -> text "%xmm15" _ -> text "very naughty x86 register" -pprFormat :: Format -> SDoc+pprFormat :: IsLine doc => Format -> doc pprFormat x = case x of II8 -> text "b" II16 -> text "w"@@ -406,14 +408,14 @@ FF32 -> text "ss" -- "scalar single-precision float" (SSE2) FF64 -> text "sd" -- "scalar double-precision float" (SSE2) -pprFormat_x87 :: Format -> SDoc+pprFormat_x87 :: IsLine doc => Format -> doc pprFormat_x87 x = case x of FF32 -> text "s" FF64 -> text "l" _ -> panic "X86.Ppr.pprFormat_x87" -pprCond :: Cond -> SDoc+pprCond :: IsLine doc => Cond -> doc pprCond c = case c of { GEU -> text "ae"; LU -> text "b"; EQQ -> text "e"; GTT -> text "g";@@ -426,13 +428,13 @@ ALWAYS -> text "mp"} -pprImm :: Platform -> Imm -> SDoc+pprImm :: IsLine doc => Platform -> Imm -> doc pprImm platform = \case ImmInt i -> int i ImmInteger i -> integer i- ImmCLbl l -> pdoc platform l- ImmIndex l i -> pdoc platform l <> char '+' <> int i- ImmLit s -> s+ ImmCLbl l -> pprAsmLabel platform l+ ImmIndex l i -> pprAsmLabel platform l <> char '+' <> int i+ ImmLit s -> ftext s ImmFloat f -> float $ fromRational f ImmDouble d -> double $ fromRational d ImmConstantSum a b -> pprImm platform a <> char '+' <> pprImm platform b@@ -440,7 +442,7 @@ -pprAddr :: Platform -> AddrMode -> SDoc+pprAddr :: IsLine doc => Platform -> AddrMode -> doc pprAddr platform (ImmAddr imm off) = let pp_imm = pprImm platform imm in@@ -471,28 +473,26 @@ ppr_disp imm = pprImm platform imm -- | Print section header and appropriate alignment for that section.-pprSectionAlign :: NCGConfig -> Section -> SDoc+pprSectionAlign :: IsDoc doc => NCGConfig -> Section -> doc pprSectionAlign _config (Section (OtherSection _) _) = panic "X86.Ppr.pprSectionAlign: unknown section" pprSectionAlign config sec@(Section seg _) =- pprSectionHeader config sec $$+ line (pprSectionHeader config sec) $$ pprAlignForSection (ncgPlatform config) seg -- | Print appropriate alignment for the given section type.-pprAlignForSection :: Platform -> SectionType -> SDoc-pprAlignForSection platform seg =+pprAlignForSection :: IsDoc doc => Platform -> SectionType -> doc+pprAlignForSection platform seg = line $ text ".align " <> case platformOS platform of -- Darwin: alignments are given as shifts. OSDarwin | target32Bit platform -> case seg of- ReadOnlyData16 -> int 4 CString -> int 1 _ -> int 2 | otherwise -> case seg of- ReadOnlyData16 -> int 4 CString -> int 1 _ -> int 3 -- Other: alignments are given as bytes.@@ -500,18 +500,16 @@ | target32Bit platform -> case seg of Text -> text "4,0x90"- ReadOnlyData16 -> int 16 CString -> int 1 _ -> int 4 | otherwise -> case seg of- ReadOnlyData16 -> int 16 CString -> int 1 _ -> int 8 -pprDataItem :: NCGConfig -> CmmLit -> SDoc+pprDataItem :: IsDoc doc => NCGConfig -> CmmLit -> doc pprDataItem config lit- = vcat (ppr_item (cmmTypeFormat $ cmmLitType platform lit) lit)+ = lines_ (ppr_item (cmmTypeFormat $ cmmLitType platform lit) lit) where platform = ncgPlatform config imm = litToImm lit@@ -536,51 +534,29 @@ <> int (fromIntegral (fromIntegral (x `shiftR` 32) :: Word32))] _ -> panic "X86.Ppr.ppr_item: no match for II64"- | otherwise ->- [text "\t.quad\t" <> pprImm platform imm]- _- | target32Bit platform ->- [text "\t.quad\t" <> pprImm platform imm]- | otherwise ->- -- x86_64: binutils can't handle the R_X86_64_PC64- -- relocation type, which means we can't do- -- pc-relative 64-bit addresses. Fortunately we're- -- assuming the small memory model, in which all such- -- offsets will fit into 32 bits, so we have to stick- -- to 32-bit offset fields and modify the RTS- -- appropriately- --- -- See Note [x86-64-relative] in rts/include/rts/storage/InfoTables.h- --- case lit of- -- A relative relocation:- CmmLabelDiffOff _ _ _ _ ->- [text "\t.long\t" <> pprImm platform imm,- text "\t.long\t0"]- _ ->- [text "\t.quad\t" <> pprImm platform imm]+ _ -> [text "\t.quad\t" <> pprImm platform imm] -asmComment :: SDoc -> SDoc+asmComment :: IsLine doc => doc -> doc asmComment c = whenPprDebug $ text "# " <> c -pprInstr :: Platform -> Instr -> SDoc+pprInstr :: forall doc. IsDoc doc => Platform -> Instr -> doc pprInstr platform i = case i of COMMENT s- -> asmComment s+ -> line (asmComment (ftext s)) - LOCATION file line col _name- -> text "\t.loc " <> ppr file <+> ppr line <+> ppr col+ LOCATION file line' col _name+ -> line (text "\t.loc " <> int file <+> int line' <+> int col) DELTA d- -> asmComment $ text ("\tdelta = " ++ show d)+ -> line (asmComment $ text ("\tdelta = " ++ show d)) NEWBLOCK _ -> panic "pprInstr: NEWBLOCK" UNWIND lbl d- -> asmComment (text "\tunwind = " <> pdoc platform d)- $$ pdoc platform lbl <> colon+ -> line (asmComment (text "\tunwind = " <> pprUnwindTable platform d))+ $$ line (pprAsmLabel platform lbl <> colon) LDATA _ _ -> panic "pprInstr: LDATA"@@ -798,19 +774,19 @@ -- POPA -> text "\tpopal" NOP- -> text "\tnop"+ -> line $ text "\tnop" CLTD II8- -> text "\tcbtw"+ -> line $ text "\tcbtw" CLTD II16- -> text "\tcwtd"+ -> line $ text "\tcwtd" CLTD II32- -> text "\tcltd"+ -> line $ text "\tcltd" CLTD II64- -> text "\tcqto"+ -> line $ text "\tcqto" CLTD x -> panic $ "pprInstr: CLTD " ++ show x@@ -822,26 +798,26 @@ -> pprFormatOpReg (text "xchg") format src val JXX cond blockid- -> pprCondInstr (text "j") cond (pdoc platform lab)+ -> pprCondInstr (text "j") cond (pprAsmLabel platform lab) where lab = blockLbl blockid JXX_GBL cond imm -> pprCondInstr (text "j") cond (pprImm platform imm) JMP (OpImm imm) _- -> text "\tjmp " <> pprImm platform imm+ -> line $ text "\tjmp " <> pprImm platform imm JMP op _- -> text "\tjmp *" <> pprOperand platform (archWordFormat (target32Bit platform)) op+ -> line $ text "\tjmp *" <> pprOperand platform (archWordFormat (target32Bit platform)) op JMP_TBL op _ _ _ -> pprInstr platform (JMP op []) CALL (Left imm) _- -> text "\tcall " <> pprImm platform imm+ -> line $ text "\tcall " <> pprImm platform imm CALL (Right reg) _- -> text "\tcall *" <> pprReg platform (archWordFormat (target32Bit platform)) reg+ -> line $ text "\tcall *" <> pprReg platform (archWordFormat (target32Bit platform)) reg IDIV fmt op -> pprFormatOp (text "idiv") fmt op@@ -885,20 +861,20 @@ -- FETCHGOT for PIC on ELF platforms FETCHGOT reg- -> vcat [ text "\tcall 1f",- hcat [ text "1:\tpopl\t", pprReg platform II32 reg ],- hcat [ text "\taddl\t$_GLOBAL_OFFSET_TABLE_+(.-1b), ",- pprReg platform II32 reg ]- ]+ -> lines_ [ text "\tcall 1f",+ hcat [ text "1:\tpopl\t", pprReg platform II32 reg ],+ hcat [ text "\taddl\t$_GLOBAL_OFFSET_TABLE_+(.-1b), ",+ pprReg platform II32 reg ]+ ] -- FETCHPC for PIC on Darwin/x86 -- get the instruction pointer into a register -- (Terminology note: the IP is called Program Counter on PPC, -- and it's a good thing to use the same name on both platforms) FETCHPC reg- -> vcat [ text "\tcall 1f",- hcat [ text "1:\tpopl\t", pprReg platform II32 reg ]- ]+ -> lines_ [ text "\tcall 1f",+ hcat [ text "1:\tpopl\t", pprReg platform II32 reg ]+ ] -- the -- GST fmt src addr ==> FLD dst ; FSTPsz addr@@ -907,10 +883,10 @@ -- Atomics LOCK i- -> text "\tlock" $$ pprInstr platform i+ -> line (text "\tlock") $$ pprInstr platform i MFENCE- -> text "\tmfence"+ -> line $ text "\tmfence" XADD format src dst -> pprFormatOpOp (text "xadd") format src dst@@ -920,46 +896,46 @@ where- gtab :: SDoc+ gtab :: Line doc gtab = char '\t' - gsp :: SDoc+ gsp :: Line doc gsp = char ' ' - pprX87 :: Instr -> SDoc -> SDoc+ pprX87 :: Instr -> Line doc -> doc pprX87 fake actual- = (char '#' <> pprX87Instr fake) $$ actual+ = line (char '#' <> pprX87Instr fake) $$ line actual - pprX87Instr :: Instr -> SDoc+ pprX87Instr :: Instr -> Line doc pprX87Instr (X87Store fmt dst) = pprFormatAddr (text "gst") fmt dst pprX87Instr _ = panic "X86.Ppr.pprX87Instr: no match" - pprDollImm :: Imm -> SDoc+ pprDollImm :: Imm -> Line doc pprDollImm i = text "$" <> pprImm platform i - pprOperand :: Platform -> Format -> Operand -> SDoc+ pprOperand :: Platform -> Format -> Operand -> Line doc pprOperand platform f op = case op of OpReg r -> pprReg platform f r OpImm i -> pprDollImm i OpAddr ea -> pprAddr platform ea - pprMnemonic_ :: SDoc -> SDoc+ pprMnemonic_ :: Line doc -> Line doc pprMnemonic_ name = char '\t' <> name <> space - pprMnemonic :: SDoc -> Format -> SDoc+ pprMnemonic :: Line doc -> Format -> Line doc pprMnemonic name format = char '\t' <> name <> pprFormat format <> space - pprFormatImmOp :: SDoc -> Format -> Imm -> Operand -> SDoc+ pprFormatImmOp :: Line doc -> Format -> Imm -> Operand -> doc pprFormatImmOp name format imm op1- = hcat [+ = line $ hcat [ pprMnemonic name format, char '$', pprImm platform imm,@@ -968,24 +944,24 @@ ] - pprFormatOp_ :: SDoc -> Format -> Operand -> SDoc+ pprFormatOp_ :: Line doc -> Format -> Operand -> doc pprFormatOp_ name format op1- = hcat [+ = line $ hcat [ pprMnemonic_ name , pprOperand platform format op1 ] - pprFormatOp :: SDoc -> Format -> Operand -> SDoc+ pprFormatOp :: Line doc -> Format -> Operand -> doc pprFormatOp name format op1- = hcat [+ = line $ hcat [ pprMnemonic name format, pprOperand platform format op1 ] - pprFormatOpOp :: SDoc -> Format -> Operand -> Operand -> SDoc+ pprFormatOpOp :: Line doc -> Format -> Operand -> Operand -> doc pprFormatOpOp name format op1 op2- = hcat [+ = line $ hcat [ pprMnemonic name format, pprOperand platform format op1, comma,@@ -993,18 +969,18 @@ ] - pprOpOp :: SDoc -> Format -> Operand -> Operand -> SDoc+ pprOpOp :: Line doc -> Format -> Operand -> Operand -> doc pprOpOp name format op1 op2- = hcat [+ = line $ hcat [ pprMnemonic_ name, pprOperand platform format op1, comma, pprOperand platform format op2 ] - pprRegReg :: SDoc -> Reg -> Reg -> SDoc+ pprRegReg :: Line doc -> Reg -> Reg -> doc pprRegReg name reg1 reg2- = hcat [+ = line $ hcat [ pprMnemonic_ name, pprReg platform (archWordFormat (target32Bit platform)) reg1, comma,@@ -1012,18 +988,18 @@ ] - pprFormatOpReg :: SDoc -> Format -> Operand -> Reg -> SDoc+ pprFormatOpReg :: Line doc -> Format -> Operand -> Reg -> doc pprFormatOpReg name format op1 reg2- = hcat [+ = line $ hcat [ pprMnemonic name format, pprOperand platform format op1, comma, pprReg platform (archWordFormat (target32Bit platform)) reg2 ] - pprCondOpReg :: SDoc -> Format -> Cond -> Operand -> Reg -> SDoc+ pprCondOpReg :: Line doc -> Format -> Cond -> Operand -> Reg -> doc pprCondOpReg name format cond op1 reg2- = hcat [+ = line $ hcat [ char '\t', name, pprCond cond,@@ -1033,18 +1009,18 @@ pprReg platform format reg2 ] - pprFormatFormatOpReg :: SDoc -> Format -> Format -> Operand -> Reg -> SDoc+ pprFormatFormatOpReg :: Line doc -> Format -> Format -> Operand -> Reg -> doc pprFormatFormatOpReg name format1 format2 op1 reg2- = hcat [+ = line $ hcat [ pprMnemonic name format2, pprOperand platform format1 op1, comma, pprReg platform format2 reg2 ] - pprFormatOpOpReg :: SDoc -> Format -> Operand -> Operand -> Reg -> SDoc+ pprFormatOpOpReg :: Line doc -> Format -> Operand -> Operand -> Reg -> doc pprFormatOpOpReg name format op1 op2 reg3- = hcat [+ = line $ hcat [ pprMnemonic name format, pprOperand platform format op1, comma,@@ -1055,7 +1031,7 @@ - pprFormatAddr :: SDoc -> Format -> AddrMode -> SDoc+ pprFormatAddr :: Line doc -> Format -> AddrMode -> Line doc pprFormatAddr name format op = hcat [ pprMnemonic name format,@@ -1063,9 +1039,9 @@ pprAddr platform op ] - pprShift :: SDoc -> Format -> Operand -> Operand -> SDoc+ pprShift :: Line doc -> Format -> Operand -> Operand -> doc pprShift name format src dest- = hcat [+ = line $ hcat [ pprMnemonic name format, pprOperand platform II8 src, -- src is 8-bit sized comma,@@ -1073,15 +1049,15 @@ ] - pprFormatOpOpCoerce :: SDoc -> Format -> Format -> Operand -> Operand -> SDoc+ pprFormatOpOpCoerce :: Line doc -> Format -> Format -> Operand -> Operand -> doc pprFormatOpOpCoerce name format1 format2 op1 op2- = hcat [ char '\t', name, pprFormat format1, pprFormat format2, space,+ = line $ hcat [ char '\t', name, pprFormat format1, pprFormat format2, space, pprOperand platform format1 op1, comma, pprOperand platform format2 op2 ] - pprCondInstr :: SDoc -> Cond -> SDoc -> SDoc+ pprCondInstr :: Line doc -> Cond -> Line doc -> doc pprCondInstr name cond arg- = hcat [ char '\t', name, pprCond cond, space, arg]+ = line $ hcat [ char '\t', name, pprCond cond, space, arg]
compiler/GHC/CmmToAsm/X86/Regs.hs view
@@ -48,6 +48,7 @@ where import GHC.Prelude+import GHC.Data.FastString import GHC.Platform.Regs import GHC.Platform.Reg@@ -55,7 +56,6 @@ import GHC.Cmm import GHC.Cmm.CLabel ( CLabel )-import GHC.Utils.Outputable import GHC.Utils.Panic import GHC.Platform @@ -111,15 +111,15 @@ = ImmInt Int | ImmInteger Integer -- Sigh. | ImmCLbl CLabel -- AbstractC Label (with baggage)- | ImmLit SDoc -- Simple string+ | ImmLit FastString | ImmIndex CLabel Int | ImmFloat Rational | ImmDouble Rational | ImmConstantSum Imm Imm | ImmConstantDiff Imm Imm -strImmLit :: String -> Imm-strImmLit s = ImmLit (text s)+strImmLit :: FastString -> Imm+strImmLit s = ImmLit s litToImm :: CmmLit -> Imm
compiler/GHC/CmmToC.hs view
@@ -1,6 +1,9 @@ {-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DerivingVia #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE LambdaCase #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE ViewPatterns #-} ----------------------------------------------------------------------------- --@@ -33,8 +36,7 @@ import GHC.Cmm.BlockId import GHC.Cmm.CLabel-import GHC.Cmm hiding (pprBBlock)-import GHC.Cmm.Ppr () -- For Outputable instances+import GHC.Cmm hiding (pprBBlock, pprStatic) import GHC.Cmm.Dataflow.Block import GHC.Cmm.Dataflow.Collections import GHC.Cmm.Dataflow.Graph@@ -49,16 +51,16 @@ import GHC.Utils.Outputable import GHC.Utils.Panic+import GHC.Utils.Monad.State.Strict (State (..), runState, state) import GHC.Utils.Misc-import GHC.Utils.Trace import Data.ByteString (ByteString) import qualified Data.ByteString as BS import Data.Char import Data.List (intersperse)+import Data.List.NonEmpty (NonEmpty (..)) import Data.Map (Map) import qualified Data.Map as Map-import Control.Monad (ap) import GHC.Float -- --------------------------------------------------------------------------@@ -85,7 +87,7 @@ blankLine, extern_decls, (if (externallyVisibleCLabel clbl)- then mkFN_ else mkIF_) (pprCLabel platform CStyle clbl) <+> lbrace,+ then mkFN_ else mkIF_) (pprCLabel platform clbl) <+> lbrace, nest 8 temp_decls, vcat (map (pprBBlock platform) blocks), rbrace ]@@ -107,14 +109,14 @@ (CmmData section (CmmStaticsRaw lbl [CmmString str])) -> pprExternDecl platform lbl $$ hcat [- pprLocalness lbl, pprConstness (isSecConstant section), text "char ", pprCLabel platform CStyle lbl,+ pprLocalness lbl, pprConstness (isSecConstant section), text "char ", pprCLabel platform lbl, text "[] = ", pprStringInCStyle str, semi ] (CmmData section (CmmStaticsRaw lbl [CmmUninitialised size])) -> pprExternDecl platform lbl $$ hcat [- pprLocalness lbl, pprConstness (isSecConstant section), text "char ", pprCLabel platform CStyle lbl,+ pprLocalness lbl, pprConstness (isSecConstant section), text "char ", pprCLabel platform lbl, brackets (int size), semi ] @@ -131,7 +133,7 @@ -- BasicBlocks are self-contained entities: they always end in a jump. -- -- Like nativeGen/AsmCodeGen, we could probably reorder blocks to turn--- as many jumps as possible into fall throughs.+-- as many jumps as possible into fallthroughs. -- pprBBlock :: Platform -> CmmBlock -> SDoc@@ -150,7 +152,7 @@ = -- TODO: align closures only pprExternDecl platform lbl $$ hcat [ pprLocalness lbl, pprConstness is_ro, text "StgWord"- , space, pprCLabel platform CStyle lbl, text "[]"+ , space, pprCLabel platform lbl, text "[]" -- See Note [StgWord alignment] , pprAlignment (wordWidth platform) , text "= {" ]@@ -242,16 +244,16 @@ case fn of CmmLit (CmmLabel lbl) | StdCallConv <- cconv ->- pprCall platform (pprCLabel platform CStyle lbl) cconv hresults hargs+ pprCall platform (pprCLabel platform lbl) cconv hresults hargs -- stdcall functions must be declared with -- a function type, otherwise the C compiler -- doesn't add the @n suffix to the label. We -- can't add the @n suffix ourselves, because -- it isn't valid C. | CmmNeverReturns <- ret ->- pprCall platform cast_fn cconv hresults hargs <> semi+ pprCall platform cast_fn cconv hresults hargs <> semi <> text "__builtin_unreachable();" | not (isMathFun lbl) ->- pprForeignCall platform (pprCLabel platform CStyle lbl) cconv hresults hargs+ pprForeignCall platform (pprCLabel platform lbl) cconv hresults hargs _ -> pprCall platform cast_fn cconv hresults hargs <> semi -- for a dynamic call, no declaration is necessary.@@ -345,7 +347,7 @@ rep = typeWidth (cmmExprType platform e) -- fall through case- caseify (ix:ixs, ident) = vcat (map do_fallthrough ixs) $$ final_branch ix+ caseify (ix:|ixs, ident) = vcat (map do_fallthrough ixs) $$ final_branch ix where do_fallthrough ix = hsep [ text "case" , pprHexVal platform ix rep <> colon ,@@ -355,8 +357,6 @@ hsep [ text "case" , pprHexVal platform ix rep <> colon , text "goto" , (pprBlockId ident) <> semi ] - caseify (_ , _ ) = panic "pprSwitch: switch with no cases!"- def | Just l <- mbdef = text "default: goto" <+> pprBlockId l <> semi | otherwise = text "default: __builtin_unreachable();" @@ -382,8 +382,8 @@ CmmRegOff reg 0 -> pprCastReg reg -- CmmRegOff is an alias of MO_Add- CmmRegOff reg i -> pprCastReg reg <> char '+' <>- pprHexVal platform (fromIntegral i) (wordWidth platform)+ CmmRegOff reg i -> pprExpr platform $ CmmMachOp (MO_Add w) [CmmReg reg, CmmLit $ CmmInt (toInteger i) w]+ where w = cmmRegWidth platform reg CmmMachOp mop args -> pprMachOpApp platform mop args @@ -430,8 +430,7 @@ pprMachOpApp platform op args | isMulMayOfloOp op = text "mulIntMayOflo" <> parens (commafy (map (pprExpr platform) args))- where isMulMayOfloOp (MO_U_MulMayOflo _) = True- isMulMayOfloOp (MO_S_MulMayOflo _) = True+ where isMulMayOfloOp (MO_S_MulMayOflo _) = True isMulMayOfloOp _ = False pprMachOpApp platform mop args@@ -594,7 +593,7 @@ -> mkW_ <> pprCLabelAddr clbl1 <> char '+' <> int i where- pprCLabelAddr lbl = char '&' <> pprCLabel platform CStyle lbl+ pprCLabelAddr lbl = char '&' <> pprCLabel platform lbl pprLit1 :: Platform -> CmmLit -> SDoc pprLit1 platform lit = case lit of@@ -775,10 +774,6 @@ (text "MO_S_MulMayOflo") (panic $ "PprC.pprMachOp_for_C: MO_S_MulMayOflo" ++ " should have been handled earlier!")- MO_U_MulMayOflo _ -> pprTrace "offending mop:"- (text "MO_U_MulMayOflo")- (panic $ "PprC.pprMachOp_for_C: MO_U_MulMayOflo"- ++ " should have been handled earlier!") MO_V_Insert {} -> pprTrace "offending mop:" (text "MO_V_Insert")@@ -854,7 +849,7 @@ (panic $ "PprC.pprMachOp_for_C: MO_VF_Quot" ++ " should have been handled earlier!") - MO_AlignmentCheck {} -> panic "-falignment-santisation not supported by unregisterised backend"+ MO_AlignmentCheck {} -> panic "-falignment-sanitisation not supported by unregisterised backend" signedOp :: MachOp -> Bool -- Argument type(s) are signed ints signedOp (MO_S_Quot _) = True@@ -969,39 +964,37 @@ -- Not adding it for now (MO_Prefetch_Data _ ) -> unsupported - MO_I64_ToI -> dontReach64- MO_I64_FromI -> dontReach64- MO_W64_ToW -> dontReach64- MO_W64_FromW -> dontReach64- MO_x64_Neg -> dontReach64- MO_x64_Add -> dontReach64- MO_x64_Sub -> dontReach64- MO_x64_Mul -> dontReach64- MO_I64_Quot -> dontReach64- MO_I64_Rem -> dontReach64- MO_W64_Quot -> dontReach64- MO_W64_Rem -> dontReach64- MO_x64_And -> dontReach64- MO_x64_Or -> dontReach64- MO_x64_Xor -> dontReach64- MO_x64_Not -> dontReach64- MO_x64_Shl -> dontReach64- MO_I64_Shr -> dontReach64- MO_W64_Shr -> dontReach64- MO_x64_Eq -> dontReach64- MO_x64_Ne -> dontReach64- MO_I64_Ge -> dontReach64- MO_I64_Gt -> dontReach64- MO_I64_Le -> dontReach64- MO_I64_Lt -> dontReach64- MO_W64_Ge -> dontReach64- MO_W64_Gt -> dontReach64- MO_W64_Le -> dontReach64- MO_W64_Lt -> dontReach64+ MO_I64_ToI -> text "hs_int64ToInt"+ MO_I64_FromI -> text "hs_intToInt64"+ MO_W64_ToW -> text "hs_word64ToWord"+ MO_W64_FromW -> text "hs_wordToWord64"+ MO_x64_Neg -> text "hs_neg64"+ MO_x64_Add -> text "hs_add64"+ MO_x64_Sub -> text "hs_sub64"+ MO_x64_Mul -> text "hs_mul64"+ MO_I64_Quot -> text "hs_quotInt64"+ MO_I64_Rem -> text "hs_remInt64"+ MO_W64_Quot -> text "hs_quotWord64"+ MO_W64_Rem -> text "hs_remWord64"+ MO_x64_And -> text "hs_and64"+ MO_x64_Or -> text "hs_or64"+ MO_x64_Xor -> text "hs_xor64"+ MO_x64_Not -> text "hs_not64"+ MO_x64_Shl -> text "hs_uncheckedShiftL64"+ MO_I64_Shr -> text "hs_uncheckedIShiftRA64"+ MO_W64_Shr -> text "hs_uncheckedShiftRL64"+ MO_x64_Eq -> text "hs_eq64"+ MO_x64_Ne -> text "hs_ne64"+ MO_I64_Ge -> text "hs_geInt64"+ MO_I64_Gt -> text "hs_gtInt64"+ MO_I64_Le -> text "hs_leInt64"+ MO_I64_Lt -> text "hs_ltInt64"+ MO_W64_Ge -> text "hs_geWord64"+ MO_W64_Gt -> text "hs_gtWord64"+ MO_W64_Le -> text "hs_leWord64"+ MO_W64_Lt -> text "hs_ltWord64" where unsupported = panic ("pprCallishMachOp_for_C: " ++ show mop ++ " not supported!")- dontReach64 = panic ("pprCallishMachOp_for_C: " ++ show mop- ++ " should be not be encountered because the regular primop for this 64-bit operation is used instead.") -- --------------------------------------------------------------------- -- Useful #defines@@ -1210,7 +1203,7 @@ | not (needsCDecl lbl) = empty | Just sz <- foreignLabelStdcallInfo lbl = stdcall_decl sz | otherwise =- hcat [ visibility, label_type lbl , lparen, pprCLabel platform CStyle lbl, text ");"+ hcat [ visibility, label_type lbl , lparen, pprCLabel platform lbl, text ");" -- occasionally useful to see label type -- , text "/* ", pprDebugCLabel lbl, text " */" ]@@ -1233,19 +1226,19 @@ -- we must generate an appropriate prototype for it, so that the C compiler will -- add the @n suffix to the label (#2276) stdcall_decl sz =- text "extern __attribute__((stdcall)) void " <> pprCLabel platform CStyle lbl+ text "extern __attribute__((stdcall)) void " <> pprCLabel platform lbl <> parens (commafy (replicate (sz `quot` platformWordSizeInBytes platform) (machRep_U_CType platform (wordWidth platform)))) <> semi type TEState = (UniqSet LocalReg, Map CLabel ())-newtype TE a = TE { unTE :: TEState -> (a, TEState) } deriving (Functor)--instance Applicative TE where- pure a = TE $ \s -> (a, s)- (<*>) = ap+newtype TE a = TE' (State TEState a)+ deriving stock (Functor)+ deriving (Applicative, Monad) via State TEState -instance Monad TE where- TE m >>= k = TE $ \s -> case m s of (a, s') -> unTE (k a) s'+pattern TE :: (TEState -> (a, TEState)) -> TE a+pattern TE f <- TE' (runState -> f)+ where TE f = TE' (state f)+{-# COMPLETE TE #-} te_lbl :: CLabel -> TE () te_lbl lbl = TE $ \(temps,lbls) -> ((), (temps, Map.insert lbl () lbls))@@ -1503,8 +1496,8 @@ <> text "void _hs_" <> attribute <> text "()" <> braces body where- body = vcat [ pprCLabel platform CStyle lbl <> text " ();" | lbl <- lbls ]- decls = vcat [ text "void" <+> pprCLabel platform CStyle lbl <> text " (void);" | lbl <- lbls ]+ body = vcat [ pprCLabel platform lbl <> text " ();" | lbl <- lbls ]+ decls = vcat [ text "void" <+> pprCLabel platform lbl <> text " (void);" | lbl <- lbls ] attribute = case initOrFini of IsInitArray -> text "constructor" IsFiniArray -> text "destructor"
compiler/GHC/CmmToLlvm.hs view
@@ -11,7 +11,7 @@ ) where -import GHC.Prelude+import GHC.Prelude hiding ( head ) import GHC.Llvm import GHC.CmmToLlvm.Base@@ -25,7 +25,6 @@ import GHC.StgToCmm.CgUtils ( fixStgRegisters ) import GHC.Cmm import GHC.Cmm.Dataflow.Collections-import GHC.Cmm.Ppr import GHC.Utils.BufHandle import GHC.Driver.Session@@ -38,6 +37,7 @@ import qualified GHC.Data.Stream as Stream import Control.Monad ( when, forM_ )+import Data.List.NonEmpty ( head ) import Data.Maybe ( fromMaybe, catMaybes ) import System.IO @@ -69,7 +69,7 @@ "System LLVM version: " <> text (llvmVersionStr ver) $$ "We will try though..." let isS390X = platformArch (llvmCgPlatform cfg) == ArchS390X- let major_ver = head . llvmVersionList $ ver+ let major_ver = head . llvmVersionNE $ ver when (isS390X && major_ver < 10 && doWarn) $ putMsg logger $ "Warning: For s390x the GHC calling convention is only supported since LLVM version 10." <+> "You are using LLVM version: " <> text (llvmVersionStr ver)@@ -110,8 +110,8 @@ header = let target = llvmCgLlvmTarget cfg llvmCfg = llvmCgLlvmConfig cfg- in text ("target datalayout = \"" ++ getDataLayout llvmCfg target ++ "\"")- $+$ text ("target triple = \"" ++ target ++ "\"")+ in (text "target datalayout = \"" <> text (getDataLayout llvmCfg target) <> text "\"")+ $+$ (text "target triple = \"" <> text target <> text "\"") getDataLayout :: LlvmConfig -> String -> String getDataLayout config target =
compiler/GHC/CmmToLlvm/Base.hs view
@@ -1,5 +1,5 @@-{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DerivingVia #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} @@ -15,10 +15,6 @@ LiveGlobalRegs, LlvmUnresData, LlvmData, UnresLabel, UnresStatic, - LlvmVersion, supportedLlvmVersionLowerBound, supportedLlvmVersionUpperBound,- llvmVersionSupported, parseLlvmVersion,- llvmVersionStr, llvmVersionList,- LlvmM, runLlvm, withClearVars, varLookup, varInsert, markStackReg, checkStackReg,@@ -39,8 +35,6 @@ aliasify, llvmDefLabel ) where -#include "ghc-llvm-version.h"- import GHC.Prelude import GHC.Utils.Panic @@ -49,7 +43,6 @@ import GHC.CmmToLlvm.Config import GHC.Cmm.CLabel-import GHC.Cmm.Ppr.Expr () import GHC.Platform.Regs ( activeStgRegs, globalRegMaybe ) import GHC.Driver.Session import GHC.Data.FastString@@ -65,11 +58,10 @@ import GHC.Utils.Logger import Data.Maybe (fromJust)-import Control.Monad (ap)-import Data.Char (isDigit)-import Data.List (sortBy, groupBy, intercalate, isPrefixOf)-import Data.Ord (comparing)+import Control.Monad.Trans.State (StateT (..))+import Data.List (isPrefixOf) import qualified Data.List.NonEmpty as NE+import Data.Ord (comparing) -- ---------------------------------------------------------------------------- -- * Some Data Types@@ -201,7 +193,7 @@ -- set of real registers to be passed. E.g. FloatReg, DoubleReg and XmmReg -- all use the same real regs on X86-64 (XMM registers). --- classes = groupBy sharesClass fprLive+ classes = NE.groupBy sharesClass fprLive sharesClass a b = regsOverlap platform (norm a) (norm b) -- check if mapped to overlapping registers norm x = CmmGlobal ((fpr_ctor x) 1) -- get the first register of the family @@ -211,10 +203,10 @@ -- E.g. sortedRs = [ F2, XMM4, D5] -- output = [D1, D3] padded = concatMap padClass classes- padClass rs = go sortedRs [1..]+ padClass rs = go (NE.toList sortedRs) 1 where- sortedRs = sortBy (comparing fpr_num) rs- maxr = last sortedRs+ sortedRs = NE.sortBy (comparing fpr_num) rs+ maxr = NE.last sortedRs ctor = fpr_ctor maxr go [] _ = []@@ -225,10 +217,9 @@ text "Found two different Cmm registers (" <> ppr c1 <> text "," <> ppr c2 <> text ") both alive AND mapped to the same real register: " <> ppr real <> text ". This isn't currently supported by the LLVM backend."- go (c:cs) (f:fs)- | fpr_num c == f = go cs fs -- already covered by a real register- | otherwise = ctor f : go (c:cs) fs -- add padding register- go _ _ = undefined -- unreachable+ go (c:cs) f+ | fpr_num c == f = go cs f -- already covered by a real register+ | otherwise = ctor f : go (c:cs) (f + 1) -- add padding register fpr_ctor :: GlobalReg -> Int -> GlobalReg fpr_ctor (FloatReg _) = FloatReg@@ -261,42 +252,6 @@ llvmPtrBits platform = widthInBits $ typeWidth $ gcWord platform -- ------------------------------------------------------------------------------- * Llvm Version-----parseLlvmVersion :: String -> Maybe LlvmVersion-parseLlvmVersion =- fmap LlvmVersion . NE.nonEmpty . go [] . dropWhile (not . isDigit)- where- go vs s- | null ver_str- = reverse vs- | '.' : rest' <- rest- = go (read ver_str : vs) rest'- | otherwise- = reverse (read ver_str : vs)- where- (ver_str, rest) = span isDigit s---- | The (inclusive) lower bound on the LLVM Version that is currently supported.-supportedLlvmVersionLowerBound :: LlvmVersion-supportedLlvmVersionLowerBound = LlvmVersion (sUPPORTED_LLVM_VERSION_MIN NE.:| [])---- | The (not-inclusive) upper bound bound on the LLVM Version that is currently supported.-supportedLlvmVersionUpperBound :: LlvmVersion-supportedLlvmVersionUpperBound = LlvmVersion (sUPPORTED_LLVM_VERSION_MAX NE.:| [])--llvmVersionSupported :: LlvmVersion -> Bool-llvmVersionSupported v =- v >= supportedLlvmVersionLowerBound && v < supportedLlvmVersionUpperBound--llvmVersionStr :: LlvmVersion -> String-llvmVersionStr = intercalate "." . map show . llvmVersionList--llvmVersionList :: LlvmVersion -> [Int]-llvmVersionList = NE.toList . llvmVersionNE---- ---------------------------------------------------------------------------- -- * Environment Handling -- @@ -321,15 +276,8 @@ -- | The Llvm monad. Wraps @LlvmEnv@ state as well as the @IO@ monad newtype LlvmM a = LlvmM { runLlvmM :: LlvmEnv -> IO (a, LlvmEnv) }- deriving (Functor)--instance Applicative LlvmM where- pure x = LlvmM $ \env -> return (x, env)- (<*>) = ap--instance Monad LlvmM where- m >>= f = LlvmM $ \env -> do (x, env') <- runLlvmM m env- runLlvmM (f x) env'+ deriving stock (Functor)+ deriving (Applicative, Monad) via StateT LlvmEnv IO instance HasLogger LlvmM where getLogger = LlvmM $ \env -> return (envLogger env, env)@@ -492,8 +440,8 @@ strCLabel_llvm lbl = do ctx <- llvmCgContext <$> getConfig platform <- getPlatform- let sdoc = pprCLabel platform CStyle lbl- str = Outp.renderWithContext ctx sdoc+ let sdoc = pprCLabel platform lbl+ str = Outp.showSDocOneLine ctx sdoc return (fsLit str) -- ----------------------------------------------------------------------------
compiler/GHC/CmmToLlvm/CodeGen.hs view
@@ -19,7 +19,6 @@ import GHC.Cmm.BlockId import GHC.Cmm.CLabel import GHC.Cmm-import GHC.Cmm.Ppr as PprCmm import GHC.Cmm.Utils import GHC.Cmm.Switch import GHC.Cmm.Dataflow.Block@@ -760,7 +759,7 @@ let (vars', stmts) = unzip done return (vars', toOL stmts) --- | Cast an LLVM variable to a specific type, panicing if it can't be done.+-- | Cast an LLVM variable to a specific type, panicking if it can't be done. castVar :: Signage -> LlvmVar -> LlvmType -> LlvmM (LlvmVar, LlvmStatement) castVar signage v t | getVarType v == t = return (v, Nop)@@ -1205,10 +1204,10 @@ other -> pprPanic "genStore: ptr not right type!"- (PprCmm.pprExpr platform addr <+> text (- "Size of Ptr: " ++ show (llvmPtrBits platform) ++- ", Size of var: " ++ show (llvmWidthInBits platform other) ++- ", Var: " ++ renderWithContext (llvmCgContext cfg) (ppVar cfg vaddr)))+ (pdoc platform addr $$+ text "Size of Ptr:" <+> ppr (llvmPtrBits platform) $$+ text "Size of var:" <+> ppr (llvmWidthInBits platform other) $$+ text "Var:" <+> ppVar cfg vaddr) mkStore :: LlvmVar -> LlvmVar -> AlignmentSpec -> LlvmStatement mkStore vval vptr alignment =@@ -1257,7 +1256,7 @@ lit = LMLitVar $ LMIntLit expLit expTy llvmExpectName- | isInt expTy = fsLit $ "llvm.expect." ++ renderWithContext (llvmCgContext cfg) (ppr expTy)+ | isInt expTy = fsLit $ "llvm.expect." ++ showSDocOneLine (llvmCgContext cfg) (ppr expTy) | otherwise = panic "genExpectedLit: Type not an int!" (llvmExpect, stmts, top) <-@@ -1453,7 +1452,6 @@ MO_S_MulMayOflo _ -> panicOp MO_S_Quot _ -> panicOp MO_S_Rem _ -> panicOp- MO_U_MulMayOflo _ -> panicOp MO_U_Quot _ -> panicOp MO_U_Rem _ -> panicOp @@ -1635,8 +1633,6 @@ MO_Sub _ -> genBinMach LM_MO_Sub MO_Mul _ -> genBinMach LM_MO_Mul - MO_U_MulMayOflo _ -> panic "genMachOp: MO_U_MulMayOflo unsupported!"- MO_S_MulMayOflo w -> isSMulOK w x y MO_S_Quot _ -> genBinMach LM_MO_SDiv@@ -1707,7 +1703,6 @@ where binLlvmOp ty binOp allow_y_cast = do- cfg <- getConfig platform <- getPlatform runExprData $ do vx <- exprToVarW x@@ -1723,13 +1718,7 @@ doExprW (ty vx) $ binOp vx vy' | otherwise- -> do- -- Error. Continue anyway so we can debug the generated ll file.- let render = renderWithContext (llvmCgContext cfg)- cmmToStr = (lines . render . PprCmm.pprExpr platform)- statement $ Comment $ map fsLit $ cmmToStr x- statement $ Comment $ map fsLit $ cmmToStr y- doExprW (ty vx) $ binOp vx vy+ -> pprPanic "binLlvmOp types" (pdoc platform x $$ pdoc platform y) binCastLlvmOp ty binOp = runExprData $ do vx <- exprToVarW x@@ -1883,10 +1872,10 @@ doExprW (cmmToLlvmType ty) (MExpr meta $ mkLoad atomic ptr align) other -> pprPanic "exprToVar: CmmLoad expression is not right type!"- (PprCmm.pprExpr platform e <+> text (- "Size of Ptr: " ++ show (llvmPtrBits platform) ++- ", Size of var: " ++ show (llvmWidthInBits platform other) ++- ", Var: " ++ renderWithContext (llvmCgContext cfg) (ppVar cfg iptr)))+ (pdoc platform e $$+ text "Size of Ptr:" <+> ppr (llvmPtrBits platform) $$+ text "Size of var:" <+> ppr (llvmWidthInBits platform other) $$+ text "Var:" <+> (ppVar cfg iptr)) {- Note [Alignment of vector-typed values]
− compiler/GHC/CmmToLlvm/Config.hs
@@ -1,31 +0,0 @@--- | Llvm code generator configuration-module GHC.CmmToLlvm.Config- ( LlvmCgConfig(..)- , LlvmVersion(..)- )-where--import GHC.Prelude-import GHC.Platform--import GHC.Utils.Outputable-import GHC.Driver.Session--import qualified Data.List.NonEmpty as NE--newtype LlvmVersion = LlvmVersion { llvmVersionNE :: NE.NonEmpty Int }- deriving (Eq, Ord)--data LlvmCgConfig = LlvmCgConfig- { llvmCgPlatform :: !Platform -- ^ Target platform- , llvmCgContext :: !SDocContext -- ^ Context for LLVM code generation- , llvmCgFillUndefWithGarbage :: !Bool -- ^ Fill undefined literals with garbage values- , llvmCgSplitSection :: !Bool -- ^ Split sections- , llvmCgBmiVersion :: Maybe BmiVersion -- ^ (x86) BMI instructions- , llvmCgLlvmVersion :: Maybe LlvmVersion -- ^ version of Llvm we're using- , llvmCgDoWarn :: !Bool -- ^ True ==> warn unsupported Llvm version- , llvmCgLlvmTarget :: !String -- ^ target triple passed to LLVM- , llvmCgLlvmConfig :: !LlvmConfig -- ^ mirror DynFlags LlvmConfig.- -- see Note [LLVM configuration] in "GHC.SysTools". This can be strict since- -- GHC.Driver.Config.CmmToLlvm.initLlvmCgConfig verifies the files are present.- }
compiler/GHC/CmmToLlvm/Data.hs view
@@ -138,9 +138,6 @@ RelocatableReadOnlyData -> case platformOS p of OSMinGW32 -> fsLit ".rdata$rel.ro" _ -> fsLit ".data.rel.ro"- ReadOnlyData16 -> case platformOS p of- OSMinGW32 -> fsLit ".rdata$cst16"- _ -> fsLit ".rodata.cst16" Data -> fsLit ".data" UninitialisedData -> fsLit ".bss" CString -> case platformOS p of
compiler/GHC/CmmToLlvm/Ppr.hs view
@@ -56,7 +56,7 @@ funDec <- llvmFunSig live lbl link cfg <- getConfig platform <- getPlatform- let buildArg = fsLit . renderWithContext (llvmCgContext cfg). ppPlainName cfg+ let buildArg = fsLit . showSDocOneLine (llvmCgContext cfg). ppPlainName cfg funArgs = map buildArg (llvmFunArgs platform live) funSect = llvmFunSection cfg (decName funDec)
compiler/GHC/Core/LateCC.hs view
@@ -10,11 +10,10 @@ ) where import Control.Applicative-import GHC.Utils.Monad.State.Strict import Control.Monad+import qualified Data.Set as S import GHC.Prelude-import GHC.Driver.Session import GHC.Types.CostCentre import GHC.Types.CostCentre.State import GHC.Types.Name hiding (varName)@@ -25,14 +24,15 @@ import GHC.Data.FastString import GHC.Core import GHC.Core.Opt.Monad-import GHC.Types.Id import GHC.Core.Utils (mkTick)+import GHC.Types.Id+import GHC.Driver.Session -import qualified Data.Set as S import GHC.Utils.Logger import GHC.Utils.Outputable import GHC.Utils.Misc import GHC.Utils.Error (withTiming)+import GHC.Utils.Monad.State.Strict {- Note [Collecting late cost centres]@@ -142,7 +142,7 @@ initLateCCState = LateCCState newCostCentreState mempty getCCFlavour :: FastString -> M CCFlavour-getCCFlavour name = LateCC <$> getCCIndex' name+getCCFlavour name = mkLateCCFlavour <$> getCCIndex' name getCCIndex' :: FastString -> M CostCentreIndex getCCIndex' name = do
compiler/GHC/Core/Opt/CSE.hs view
@@ -4,11 +4,6 @@ \section{Common subexpression} -} ---{-# OPTIONS_GHC -Wno-incomplete-record-updates #-}-{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}- module GHC.Core.Opt.CSE (cseProgram, cseOneExpr) where import GHC.Prelude@@ -19,7 +14,7 @@ import GHC.Types.Id ( Id, idType, idHasRules, zapStableUnfolding , idInlineActivation, setInlineActivation , zapIdOccInfo, zapIdUsageInfo, idInlinePragma- , isJoinId, isJoinId_maybe )+ , isJoinId, isJoinId_maybe, idUnfolding ) import GHC.Core.Utils ( mkAltExpr , exprIsTickedString , stripTicksE, stripTicksT, mkTicks )@@ -32,6 +27,7 @@ import GHC.Core.Map.Expr import GHC.Utils.Misc ( filterOut, equalLength ) import GHC.Utils.Panic+import Data.Functor.Identity ( Identity (..) ) import Data.List ( mapAccumL ) {-@@ -144,7 +140,7 @@ the binding instead? Well, not at top level! See Note [Top level and postInlineUnconditionally] in GHC.Core.Opt.Simplify.Utils; and in any case CSE applies only to the /bindings/ of the program, and we leave-it to the simplifier to propate effects to the RULES. Finally, it+it to the simplifier to propagate effects to the RULES. Finally, it doesn't seem worth the effort to discard the nested bindings because the simplifier will do it next. @@ -228,7 +224,7 @@ might replace <rhs> by 'bar', and then later be unable to see that it really was <rhs>. -An except to the rule is when the INLINE pragma is not from the user, e.g. from+An exception to the rule is when the INLINE pragma is not from the user, e.g. from WorkWrap (see Note [Wrapper activation]). We can tell because noUserInlineSpec is then true. @@ -262,28 +258,32 @@ but now bar's inlining behaviour will change, and importing modules might see that. So it seems dodgy and we don't do it. -Stable unfoldings are also created during worker/wrapper when we decide-that a function's definition is so small that it should always inline.-In this case we still want to do CSE (#13340). Hence the use of-isAnyInlinePragma rather than isStableUnfolding.+Wrinkles -Now consider- foo = <expr>- bar {-# Unf = Stable ... #-}- = <expr>+* Stable unfoldings are also created during worker/wrapper when we+ decide that a function's definition is so small that it should+ always inline, or indeed for the wrapper function itself. In this+ case we still want to do CSE (#13340). Hence the use of+ isStableUserUnfolding/isStableSystemUnfolding rather than+ isStableUnfolding. -where the unfolding was added by strictness analysis, say. Then-CSE goes ahead, so we get- bar = foo-and probably use SUBSTITUTE that will make 'bar' dead. But just-possibly not -- see Note [Dealing with ticks]. In that case we might-be left with- bar = tick t1 (tick t2 foo)-in which case we would really like to get rid of the stable unfolding-(generated by the strictness analyser, say). Hence the zapStableUnfolding-in cse_bind. Not a big deal, and only makes a difference when ticks-get into the picture.+* Consider+ foo = <expr>+ bar {-# Unf = Stable ... #-}+ = <expr>+ where the unfolding was added by strictness analysis, say. Then+ CSE goes ahead, so we get+ bar = foo+ and probably use SUBSTITUTE that will make 'bar' dead. But just+ possibly not -- see Note [Dealing with ticks]. In that case we might+ be left with+ bar = tick t1 (tick t2 foo)+ in which case we would really like to get rid of the stable unfolding+ (generated by the strictness analyser, say). + Hence the zapStableUnfolding in cse_bind. Not a big deal, and only+ makes a difference when ticks get into the picture.+ Note [Corner case for case expressions] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Here is another reason that we do not use SUBSTITUTE for@@ -291,8 +291,8 @@ case x |> co of (y::Array# Int) { ... } We do not want to extend the substitution with (y -> x |> co); since y-is of unlifted type, this would destroy the let/app invariant if (x |>-co) was not ok-for-speculation.+is of unlifted type, this would destroy the let-can-float invariant if+(x |> co) was not ok-for-speculation. But surely (x |> co) is ok-for-speculation, because it's a trivial expression, and x's type is also unlifted, presumably. Well, maybe@@ -356,7 +356,7 @@ Note [Separate envs for let rhs and body] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Substituting occurances of the binder in the rhs with the+Substituting occurrences of the binder in the rhs with the renamed binder is wrong for non-recursive bindings. Why? Consider this core. @@ -408,7 +408,7 @@ = (extendCSRecEnv env1 out_id rhs'' id_expr', Rec [(zapped_id, rhs')]) where- (env1, [out_id]) = addRecBinders env [in_id]+ (env1, Identity out_id) = addRecBinders env (Identity in_id) rhs' = cseExpr env1 rhs rhs'' = stripTicksE tickishFloatable rhs' ticks = stripTicksT tickishFloatable rhs'@@ -508,14 +508,17 @@ -- | Given a binder `let x = e`, this function -- determines whether we should add `e -> x` to the cs_map noCSE :: InId -> Bool-noCSE id = not (isAlwaysActive (idInlineActivation id)) &&- not (noUserInlineSpec (inlinePragmaSpec (idInlinePragma id)))- -- See Note [CSE for INLINE and NOINLINE]- || isAnyInlinePragma (idInlinePragma id)- -- See Note [CSE for stable unfoldings]- || isJoinId id- -- See Note [CSE for join points?]-+noCSE id+ | isJoinId id = no_cse -- See Note [CSE for join points?]+ | isStableUserUnfolding unf = no_cse -- See Note [CSE for stable unfoldings]+ | user_activation_control = no_cse -- See Note [CSE for INLINE and NOINLINE]+ | otherwise = yes_cse+ where+ unf = idUnfolding id+ user_activation_control = not (isAlwaysActive (idInlineActivation id))+ && not (noUserInlineSpec (inlinePragmaSpec (idInlinePragma id)))+ yes_cse = False+ no_cse = True {- Note [Take care with literal strings] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -696,7 +699,7 @@ where env = emptyCSEnv {cs_subst = mkEmptySubst (mkInScopeSet (exprFreeVars e)) } cseExpr :: CSEnv -> InExpr -> OutExpr-cseExpr env (Type t) = Type (substTy (csEnvSubst env) t)+cseExpr env (Type t) = Type (substTyUnchecked (csEnvSubst env) t) cseExpr env (Coercion c) = Coercion (substCo (csEnvSubst env) c) cseExpr _ (Lit lit) = Lit lit cseExpr env (Var v) = lookupSubst env v@@ -714,7 +717,7 @@ = Case scrut1 bndr3 ty' $ combineAlts (map cse_alt alts) where- ty' = substTy (csEnvSubst env) ty+ ty' = substTyUnchecked (csEnvSubst env) ty (cse_done, scrut1) = try_for_cse env scrut bndr1 = zapIdOccInfo bndr@@ -810,7 +813,7 @@ W y z -> e2 In the simplifier we use cheapEqExpr, because it is called a lot.-But here in CSE we use the full eqExpr. After all, two alternatives usually+But here in CSE we use the full eqCoreExpr. After all, two alternatives usually differ near the root, so it probably isn't expensive to compare the full alternative. It seems like the same kind of thing that CSE is supposed to be doing, which is why I put it here.@@ -909,7 +912,8 @@ where (sub', vs') = substBndrs (cs_subst cse) vs -addRecBinders :: CSEnv -> [Id] -> (CSEnv, [Id])-addRecBinders cse vs = (cse { cs_subst = sub' }, vs')- where- (sub', vs') = substRecBndrs (cs_subst cse) vs+addRecBinders :: Traversable f => CSEnv -> f Id -> (CSEnv, f Id)+addRecBinders = \ cse vs ->+ let (sub', vs') = substRecBndrs (cs_subst cse) vs+ in (cse { cs_subst = sub' }, vs')+{-# INLINE addRecBinders #-}
compiler/GHC/Core/Opt/CallArity.hs view
@@ -150,7 +150,7 @@ any useful co-call information. Return (fv e)² * Case alternatives alt₁,alt₂,...:- Only one can be execuded, so+ Only one can be executed, so Return (alt₁ ∪ alt₂ ∪...) * App e₁ e₂ (and analogously Case scrut alts), with non-trivial e₂: We get the results from both sides, with the argument evaluated at most once.@@ -277,7 +277,7 @@ Note [Analysis type signature] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The work-hourse of the analysis is the function `callArityAnal`, with the+The workhorse of the analysis is the function `callArityAnal`, with the following type: type CallArityRes = (UnVarGraph, VarEnv Arity)@@ -377,15 +377,14 @@ CallArityRes (the co-call graph is the complete graph, all arityies 0). Note [Trimming arity]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~-+~~~~~~~~~~~~~~~~~~~~~ In the Call Arity papers, we are working on an untyped lambda calculus with no other id annotations, where eta-expansion is always possible. But this is not the case for Core! 1. We need to ensure the invariant callArity e <= typeArity (exprType e) for the same reasons that exprArity needs this invariant (see Note- [exprArity invariant] in GHC.Core.Opt.Arity).+ [typeArity invariants] in GHC.Core.Opt.Arity). If we are not doing that, a too-high arity annotation will be stored with the id, confusing the simplifier later on.@@ -544,7 +543,7 @@ -- Which bindings should we look at? -- See Note [Which variables are interesting] isInteresting :: Var -> Bool-isInteresting v = not $ null (typeArity (idType v))+isInteresting v = typeArity (idType v) > 0 interestingBinds :: CoreBind -> [Var] interestingBinds = filter isInteresting . bindersOf@@ -700,7 +699,7 @@ trimArity :: Id -> Arity -> Arity trimArity v a = minimum [a, max_arity_by_type, max_arity_by_strsig] where- max_arity_by_type = length (typeArity (idType v))+ max_arity_by_type = typeArity (idType v) max_arity_by_strsig | isDeadEndDiv result_info = length demands | otherwise = a@@ -729,7 +728,7 @@ resDel v (!g, !ae) = (g `delNode` v, ae `delVarEnv` v) domRes :: CallArityRes -> UnVarSet-domRes (_, ae) = varEnvDom ae+domRes (_, ae) = varEnvDomain ae -- In the result, find out the minimum arity and whether the variable is called -- at most once.
compiler/GHC/Core/Opt/CprAnal.hs view
@@ -10,7 +10,7 @@ import GHC.Prelude -import GHC.Driver.Session+import GHC.Driver.Flags ( DumpFlag (..) ) import GHC.Builtin.Names ( runRWKey ) @@ -553,13 +553,16 @@ *workers*, because their transformers need to adapt to CPR for their arguments in 'cprTransformDataConWork' to enable Note [Nested CPR]. Better keep it all in this module! The alternative would be that- 'GHC.Types.Id.Make' depends on DmdAnal.+ 'GHC.Types.Id.Make' depends on CprAnal. 3. In the future, Nested CPR could take a better account of incoming args in cprAnalApp and do some beta-reduction on the fly, like !1866 did. If any of those args had the CPR property, then we'd even get Nested CPR for DataCon wrapper calls, for free. Not so if we simply give the wrapper a single CPR sig in 'GHC.Types.Id.Make.mkDataConRep'! +DmdAnal also looks through the wrapper's unfolding:+See Note [DmdAnal for DataCon wrappers].+ Note [Trimming to mAX_CPR_SIZE] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We do not treat very big tuples as CPR-ish:@@ -741,7 +744,7 @@ Note that - * Whether or not something unboxes is decided by 'wantToUnboxArg', else we may+ * Whether or not something unboxes is decided by 'canUnboxArg', else we may get over-optimistic CPR results (e.g., from \(x :: a) -> x!). * If the demand unboxes deeply, we can give the binder a /nested/ CPR
compiler/GHC/Core/Opt/DmdAnal.hs view
@@ -31,2258 +31,2578 @@ import GHC.Core.Utils import GHC.Core.TyCon import GHC.Core.Type-import GHC.Core.Predicate ( isClassPred )-import GHC.Core.FVs ( rulesRhsFreeIds, bndrRuleAndUnfoldingIds )-import GHC.Core.Coercion ( Coercion )-import GHC.Core.TyCo.FVs ( coVarsOfCos )-import GHC.Core.FamInstEnv-import GHC.Core.Opt.Arity ( typeArity )-import GHC.Utils.Misc-import GHC.Utils.Panic-import GHC.Utils.Panic.Plain-import GHC.Data.Maybe-import GHC.Builtin.PrimOps-import GHC.Builtin.Types.Prim ( realWorldStatePrimTy )-import GHC.Types.Unique.Set-import GHC.Types.Unique.MemoFun--import GHC.Utils.Trace-_ = pprTrace -- Tired of commenting out the import all the time--{--************************************************************************-* *-\subsection{Top level stuff}-* *-************************************************************************--}---- | Options for the demand analysis-data DmdAnalOpts = DmdAnalOpts- { dmd_strict_dicts :: !Bool -- ^ Use strict dictionaries- , dmd_unbox_width :: !Int -- ^ Use strict dictionaries- , dmd_max_worker_args :: !Int- }---- This is a strict alternative to (,)--- See Note [Space Leaks in Demand Analysis]-data WithDmdType a = WithDmdType !DmdType !a--getAnnotated :: WithDmdType a -> a-getAnnotated (WithDmdType _ a) = a--data DmdResult a b = R !a !b---- | Outputs a new copy of the Core program in which binders have been annotated--- with demand and strictness information.------ Note: use `seqBinds` on the result to avoid leaks due to lazyness (cf Note--- [Stamp out space leaks in demand analysis])-dmdAnalProgram :: DmdAnalOpts -> FamInstEnvs -> [CoreRule] -> CoreProgram -> CoreProgram-dmdAnalProgram opts fam_envs rules binds- = getAnnotated $ go (emptyAnalEnv opts fam_envs) binds- where- -- See Note [Analysing top-level bindings]- -- and Note [Why care for top-level demand annotations?]- go _ [] = WithDmdType nopDmdType []- go env (b:bs) = cons_up $ dmdAnalBind TopLevel env topSubDmd b anal_body- where- anal_body env'- | WithDmdType body_ty bs' <- go env' bs- = WithDmdType (body_ty `plusDmdType` keep_alive_roots env' (bindersOf b)) bs'-- cons_up :: WithDmdType (DmdResult b [b]) -> WithDmdType [b]- cons_up (WithDmdType dmd_ty (R b' bs')) = WithDmdType dmd_ty (b' : bs')-- keep_alive_roots :: AnalEnv -> [Id] -> DmdEnv- -- See Note [Absence analysis for stable unfoldings and RULES]- -- Here we keep alive "roots", e.g., exported ids and stuff mentioned in- -- orphan RULES- keep_alive_roots env ids = plusDmdEnvs (map (demandRoot env) (filter is_root ids))-- is_root :: Id -> Bool- is_root id = isExportedId id || elemVarSet id rule_fvs-- rule_fvs :: IdSet- rule_fvs = rulesRhsFreeIds rules--demandRoot :: AnalEnv -> Id -> DmdEnv--- See Note [Absence analysis for stable unfoldings and RULES]-demandRoot env id = fst (dmdAnalStar env topDmd (Var id))--demandRoots :: AnalEnv -> [Id] -> DmdEnv--- See Note [Absence analysis for stable unfoldings and RULES]-demandRoots env roots = plusDmdEnvs (map (demandRoot env) roots)--demandRootSet :: AnalEnv -> IdSet -> DmdEnv-demandRootSet env ids = demandRoots env (nonDetEltsUniqSet ids)- -- It's OK to use nonDetEltsUniqSet here because plusDmdType is commutative---- | We attach useful (e.g. not 'topDmd') 'idDemandInfo' to top-level bindings--- that satisfy this function.------ Basically, we want to know how top-level *functions* are *used*--- (e.g. called). The information will always be lazy.--- Any other top-level bindings are boring.------ See also Note [Why care for top-level demand annotations?].-isInterestingTopLevelFn :: Id -> Bool--- SG tried to set this to True and got a +2% ghc/alloc regression in T5642--- (which is dominated by the Simplifier) at no gain in analysis precision.--- If there was a gain, that regression might be acceptable.--- Plus, we could use LetUp for thunks and share some code with local let--- bindings.-isInterestingTopLevelFn id =- typeArity (idType id) `lengthExceeds` 0--{- Note [Stamp out space leaks in demand analysis]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-The demand analysis pass outputs a new copy of the Core program in-which binders have been annotated with demand and strictness-information. It's tiresome to ensure that this information is fully-evaluated everywhere that we produce it, so we just run a single-seqBinds over the output before returning it, to ensure that there are-no references holding on to the input Core program.--This makes a ~30% reduction in peak memory usage when compiling-DynFlags (cf #9675 and #13426).--This is particularly important when we are doing late demand analysis,-since we don't do a seqBinds at any point thereafter. Hence code-generation would hold on to an extra copy of the Core program, via-unforced thunks in demand or strictness information; and it is the-most memory-intensive part of the compilation process, so this added-seqBinds makes a big difference in peak memory usage.--Note [Analysing top-level bindings]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider a CoreProgram like- e1 = ...- n1 = ...- e2 = \a b -> ... fst (n1 a b) ...- n2 = \c d -> ... snd (e2 c d) ...- ...-where e* are exported, but n* are not.-Intuitively, we can see that @n1@ is only ever called with two arguments-and in every call site, the first component of the result of the call-is evaluated. Thus, we'd like it to have idDemandInfo @LCL(CM(P(1L,A))@.-NB: We may *not* give e2 a similar annotation, because it is exported and-external callers might use it in arbitrary ways, expressed by 'topDmd'.-This can then be exploited by Nested CPR and eta-expansion,-see Note [Why care for top-level demand annotations?].--How do we get this result? Answer: By analysing the program as if it was a let-expression of this form:- let e1 = ... in- let n1 = ... in- let e2 = ... in- let n2 = ... in- (e1,e2, ...)-E.g. putting all bindings in nested lets and returning all exported binders in a tuple.-Of course, we will not actually build that CoreExpr! Instead we faithfully-simulate analysis of said expression by adding the free variable 'DmdEnv'-of @e*@'s strictness signatures to the 'DmdType' we get from analysing the-nested bindings.--And even then the above form blows up analysis performance in T10370:-If @e1@ uses many free variables, we'll unnecessarily carry their demands around-with us from the moment we analyse the pair to the moment we bubble back up to-the binding for @e1@. So instead we analyse as if we had- let e1 = ... in- (e1, let n1 = ... in- ( let e2 = ... in- (e2, let n2 = ... in- ( ...))))-That is, a series of right-nested pairs, where the @fst@ are the exported-binders of the last enclosing let binding and @snd@ continues the nested-lets.--Variables occurring free in RULE RHSs are to be handled the same as exported Ids.-See also Note [Absence analysis for stable unfoldings and RULES].--Note [Why care for top-level demand annotations?]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Reading Note [Analysing top-level bindings], you might think that we go through-quite some trouble to get useful demands for top-level bindings. They can never-be strict, for example, so why bother?--First, we get to eta-expand top-level bindings that we weren't able to-eta-expand before without Call Arity. From T18894b:- module T18894b (f) where- eta :: Int -> Int -> Int- eta x = if fst (expensive x) == 13 then \y -> ... else \y -> ...- f m = ... eta m 2 ... eta 2 m ...-Since only @f@ is exported, we see all call sites of @eta@ and can eta-expand to-arity 2.--The call demands we get for some top-level bindings will also allow Nested CPR-to unbox deeper. From T18894:- module T18894 (h) where- g m n = (2 * m, 2 `div` n)- {-# NOINLINE g #-}- h :: Int -> Int- h m = ... snd (g m 2) ... uncurry (+) (g 2 m) ...-Only @h@ is exported, hence we see that @g@ is always called in contexts were we-also force the division in the second component of the pair returned by @g@.-This allows Nested CPR to evaluate the division eagerly and return an I# in its-position.--}--{--************************************************************************-* *-\subsection{The analyser itself}-* *-************************************************************************--}---- | Analyse a binding group and its \"body\", e.g. where it is in scope.------ It calls a function that knows how to analyse this \"body\" given--- an 'AnalEnv' with updated demand signatures for the binding group--- (reflecting their 'idDmdSigInfo') and expects to receive a--- 'DmdType' in return, which it uses to annotate the binding group with their--- 'idDemandInfo'.-dmdAnalBind- :: TopLevelFlag- -> AnalEnv- -> SubDemand -- ^ Demand put on the "body"- -- (important for join points)- -> CoreBind- -> (AnalEnv -> WithDmdType a) -- ^ How to analyse the "body", e.g.- -- where the binding is in scope- -> WithDmdType (DmdResult CoreBind a)-dmdAnalBind top_lvl env dmd bind anal_body = case bind of- NonRec id rhs- | useLetUp top_lvl id- -> dmdAnalBindLetUp top_lvl env_rhs id rhs anal_body- _ -> dmdAnalBindLetDown top_lvl env_rhs dmd bind anal_body- where- env_rhs = enterDFun bind env---- | Annotates uninteresting top level functions ('isInterestingTopLevelFn')--- with 'topDmd', the rest with the given demand.-setBindIdDemandInfo :: TopLevelFlag -> Id -> Demand -> Id-setBindIdDemandInfo top_lvl id dmd = setIdDemandInfo id $ case top_lvl of- TopLevel | not (isInterestingTopLevelFn id) -> topDmd- _ -> dmd---- | Let bindings can be processed in two ways:--- Down (RHS before body) or Up (body before RHS).--- This function handles the up variant.------ It is very simple. For let x = rhs in body--- * Demand-analyse 'body' in the current environment--- * Find the demand, 'rhs_dmd' placed on 'x' by 'body'--- * Demand-analyse 'rhs' in 'rhs_dmd'------ This is used for a non-recursive local let without manifest lambdas (see--- 'useLetUp').------ This is the LetUp rule in the paper “Higher-Order Cardinality Analysis”.-dmdAnalBindLetUp :: TopLevelFlag- -> AnalEnv- -> Id- -> CoreExpr- -> (AnalEnv -> WithDmdType a)- -> WithDmdType (DmdResult CoreBind a)-dmdAnalBindLetUp top_lvl env id rhs anal_body = WithDmdType final_ty (R (NonRec id' rhs') (body'))- where- WithDmdType body_ty body' = anal_body (addInScopeAnalEnv env id)- -- See Note [Bringing a new variable into scope]- WithDmdType body_ty' id_dmd = findBndrDmd env body_ty id- -- See Note [Finalising boxity for demand signatures]-- id_dmd' = finaliseLetBoxity (ae_fam_envs env) (idType id) id_dmd- !id' = setBindIdDemandInfo top_lvl id id_dmd'- (rhs_ty, rhs') = dmdAnalStar env (dmdTransformThunkDmd rhs id_dmd') rhs-- -- See Note [Absence analysis for stable unfoldings and RULES]- rule_fvs = bndrRuleAndUnfoldingIds id- final_ty = body_ty' `plusDmdType` rhs_ty `plusDmdType` demandRootSet env rule_fvs---- | Let bindings can be processed in two ways:--- Down (RHS before body) or Up (body before RHS).--- This function handles the down variant.------ It computes a demand signature (by means of 'dmdAnalRhsSig') and uses--- that at call sites in the body.------ It is used for toplevel definitions, recursive definitions and local--- non-recursive definitions that have manifest lambdas (cf. 'useLetUp').--- Local non-recursive definitions without a lambda are handled with LetUp.------ This is the LetDown rule in the paper “Higher-Order Cardinality Analysis”.-dmdAnalBindLetDown :: TopLevelFlag -> AnalEnv -> SubDemand -> CoreBind -> (AnalEnv -> WithDmdType a) -> WithDmdType (DmdResult CoreBind a)-dmdAnalBindLetDown top_lvl env dmd bind anal_body = case bind of- NonRec id rhs- | (env', weak_fv, id1, rhs1) <-- dmdAnalRhsSig top_lvl NonRecursive env dmd id rhs- -> do_rest env' weak_fv [(id1, rhs1)] (uncurry NonRec . only)- Rec pairs- | (env', weak_fv, pairs') <- dmdFix top_lvl env dmd pairs- -> do_rest env' weak_fv pairs' Rec- where- do_rest env' weak_fv pairs1 build_bind = WithDmdType final_ty (R (build_bind pairs2) body')- where- WithDmdType body_ty body' = anal_body env'- -- see Note [Lazy and unleashable free variables]- dmd_ty = addWeakFVs body_ty weak_fv- WithDmdType final_ty id_dmds = findBndrsDmds env' dmd_ty (strictMap fst pairs1)- -- Important to force this as build_bind might not force it.- !pairs2 = strictZipWith do_one pairs1 id_dmds- do_one (id', rhs') dmd = ((,) $! setBindIdDemandInfo top_lvl id' dmd) $! rhs'- -- If the actual demand is better than the vanilla call- -- demand, you might think that we might do better to re-analyse- -- the RHS with the stronger demand.- -- But (a) That seldom happens, because it means that *every* path in- -- the body of the let has to use that stronger demand- -- (b) It often happens temporarily in when fixpointing, because- -- the recursive function at first seems to place a massive demand.- -- But we don't want to go to extra work when the function will- -- probably iterate to something less demanding.- -- In practice, all the times the actual demand on id2 is more than- -- the vanilla call demand seem to be due to (b). So we don't- -- bother to re-analyse the RHS.---- If e is complicated enough to become a thunk, its contents will be evaluated--- at most once, so oneify it.-dmdTransformThunkDmd :: CoreExpr -> Demand -> Demand-dmdTransformThunkDmd e- | exprIsTrivial e = id- | otherwise = oneifyDmd---- Do not process absent demands--- Otherwise act like in a normal demand analysis--- See ↦* relation in the Cardinality Analysis paper-dmdAnalStar :: AnalEnv- -> Demand -- This one takes a *Demand*- -> CoreExpr- -> (DmdEnv, CoreExpr)-dmdAnalStar env (n :* sd) e- -- NB: (:*) expands AbsDmd and BotDmd as needed- -- See Note [Analysing with absent demand]- | WithDmdType dmd_ty e' <- dmdAnal env sd e- = assertPpr (mightBeLiftedType (exprType e) || exprOkForSpeculation e) (ppr e)- -- The argument 'e' should satisfy the let/app invariant- (discardArgDmds $ multDmdType n dmd_ty, e')---- Main Demand Analsysis machinery-dmdAnal, dmdAnal' :: AnalEnv- -> SubDemand -- The main one takes a *SubDemand*- -> CoreExpr -> WithDmdType CoreExpr--dmdAnal env d e = -- pprTrace "dmdAnal" (ppr d <+> ppr e) $- dmdAnal' env d e--dmdAnal' _ _ (Lit lit) = WithDmdType nopDmdType (Lit lit)-dmdAnal' _ _ (Type ty) = WithDmdType nopDmdType (Type ty) -- Doesn't happen, in fact-dmdAnal' _ _ (Coercion co)- = WithDmdType (noArgsDmdType (coercionDmdEnv co)) (Coercion co)--dmdAnal' env dmd (Var var)- = WithDmdType (dmdTransform env var dmd) (Var var)--dmdAnal' env dmd (Cast e co)- = WithDmdType (dmd_ty `plusDmdType` coercionDmdEnv co) (Cast e' co)- where- WithDmdType dmd_ty e' = dmdAnal env dmd e--dmdAnal' env dmd (Tick t e)- = WithDmdType dmd_ty (Tick t e')- where- WithDmdType dmd_ty e' = dmdAnal env dmd e--dmdAnal' env dmd (App fun (Type ty))- = WithDmdType fun_ty (App fun' (Type ty))- where- WithDmdType fun_ty fun' = dmdAnal env dmd fun---- Lots of the other code is there to make this--- beautiful, compositional, application rule :-)-dmdAnal' env dmd (App fun arg)- = -- This case handles value arguments (type args handled above)- -- Crucially, coercions /are/ handled here, because they are- -- value arguments (#10288)- let- call_dmd = mkCalledOnceDmd dmd- WithDmdType fun_ty fun' = dmdAnal env call_dmd fun- (arg_dmd, res_ty) = splitDmdTy fun_ty- (arg_ty, arg') = dmdAnalStar env (dmdTransformThunkDmd arg arg_dmd) arg- in--- pprTrace "dmdAnal:app" (vcat--- [ text "dmd =" <+> ppr dmd--- , text "expr =" <+> ppr (App fun arg)--- , text "fun dmd_ty =" <+> ppr fun_ty--- , text "arg dmd =" <+> ppr arg_dmd--- , text "arg dmd_ty =" <+> ppr arg_ty--- , text "res dmd_ty =" <+> ppr res_ty--- , text "overall res dmd_ty =" <+> ppr (res_ty `bothDmdType` arg_ty) ])- WithDmdType (res_ty `plusDmdType` arg_ty) (App fun' arg')--dmdAnal' env dmd (Lam var body)- | isTyVar var- = let- WithDmdType body_ty body' = dmdAnal (addInScopeAnalEnv env var) dmd body- -- See Note [Bringing a new variable into scope]- in- WithDmdType body_ty (Lam var body')-- | otherwise- = let (n, body_dmd) = peelCallDmd dmd- -- body_dmd: a demand to analyze the body-- WithDmdType body_ty body' = dmdAnal (addInScopeAnalEnv env var) body_dmd body- -- See Note [Bringing a new variable into scope]- WithDmdType lam_ty var' = annotateLamIdBndr env body_ty var- new_dmd_type = multDmdType n lam_ty- in- WithDmdType new_dmd_type (Lam var' body')--dmdAnal' env dmd (Case scrut case_bndr ty [Alt alt bndrs rhs])- -- Only one alternative.- -- If it's a DataAlt, it should be the only constructor of the type and we- -- can consider its field demands when analysing the scrutinee.- | want_precise_field_dmds alt- = let- rhs_env = addInScopeAnalEnvs env (case_bndr:bndrs)- -- See Note [Bringing a new variable into scope]- WithDmdType rhs_ty rhs' = dmdAnal rhs_env dmd rhs- WithDmdType alt_ty1 fld_dmds = findBndrsDmds env rhs_ty bndrs- WithDmdType alt_ty2 case_bndr_dmd = findBndrDmd env alt_ty1 case_bndr- !case_bndr' = setIdDemandInfo case_bndr case_bndr_dmd- -- Evaluation cardinality on the case binder is irrelevant and a no-op.- -- What matters is its nested sub-demand!- -- NB: If case_bndr_dmd is absDmd, boxity will say Unboxed, which is- -- what we want, because then `seq` will put a `seqDmd` on its scrut.- (_ :* case_bndr_sd) = case_bndr_dmd- -- Compute demand on the scrutinee- -- FORCE the result, otherwise thunks will end up retaining the- -- whole DmdEnv- !(!bndrs', !scrut_sd)- | DataAlt _ <- alt- -- See Note [Demand on the scrutinee of a product case]- , let !scrut_sd = scrutSubDmd case_bndr_sd fld_dmds- -- See Note [Demand on case-alternative binders]- , let !fld_dmds' = fieldBndrDmds scrut_sd (length fld_dmds)- , let !bndrs' = setBndrsDemandInfo bndrs fld_dmds'- = (bndrs', scrut_sd)- | otherwise- -- __DEFAULT and literal alts. Simply add demands and discard the- -- evaluation cardinality, as we evaluate the scrutinee exactly once.- = assert (null bndrs) (bndrs, case_bndr_sd)- alt_ty3- -- See Note [Precise exceptions and strictness analysis] in "GHC.Types.Demand"- | exprMayThrowPreciseException (ae_fam_envs env) scrut- = deferAfterPreciseException alt_ty2- | otherwise- = alt_ty2-- WithDmdType scrut_ty scrut' = dmdAnal env scrut_sd scrut- res_ty = alt_ty3 `plusDmdType` discardArgDmds scrut_ty- in--- pprTrace "dmdAnal:Case1" (vcat [ text "scrut" <+> ppr scrut--- , text "dmd" <+> ppr dmd--- , text "case_bndr_dmd" <+> ppr (idDemandInfo case_bndr')--- , text "scrut_sd" <+> ppr scrut_sd--- , text "scrut_ty" <+> ppr scrut_ty--- , text "alt_ty" <+> ppr alt_ty2--- , text "res_ty" <+> ppr res_ty ]) $- WithDmdType res_ty (Case scrut' case_bndr' ty [Alt alt bndrs' rhs'])- where- want_precise_field_dmds alt = case alt of- (DataAlt dc)- | Nothing <- tyConSingleAlgDataCon_maybe $ dataConTyCon dc -> False- | DefinitelyRecursive <- ae_rec_dc env dc -> False- -- See Note [Demand analysis for recursive data constructors]- _ -> True-----dmdAnal' env dmd (Case scrut case_bndr ty alts)- = let -- Case expression with multiple alternatives- WithDmdType alt_ty alts' = combineAltDmds alts-- combineAltDmds [] = WithDmdType botDmdType []- combineAltDmds (a:as) =- let- WithDmdType cur_ty a' = dmdAnalSumAlt env dmd case_bndr a- WithDmdType rest_ty as' = combineAltDmds as- in WithDmdType (lubDmdType cur_ty rest_ty) (a':as')-- WithDmdType alt_ty1 case_bndr_dmd = findBndrDmd env alt_ty case_bndr- !case_bndr' = setIdDemandInfo case_bndr case_bndr_dmd- WithDmdType scrut_ty scrut' = dmdAnal env topSubDmd scrut- -- NB: Base case is botDmdType, for empty case alternatives- -- This is a unit for lubDmdType, and the right result- -- when there really are no alternatives- fam_envs = ae_fam_envs env- alt_ty2- -- See Note [Precise exceptions and strictness analysis] in "GHC.Types.Demand"- | exprMayThrowPreciseException fam_envs scrut- = deferAfterPreciseException alt_ty1- | otherwise- = alt_ty1- res_ty = scrut_ty `plusDmdType` discardArgDmds alt_ty2-- in--- pprTrace "dmdAnal:Case2" (vcat [ text "scrut" <+> ppr scrut--- , text "scrut_ty" <+> ppr scrut_ty--- , text "alt_tys" <+> ppr alt_tys--- , text "alt_ty2" <+> ppr alt_ty2--- , text "res_ty" <+> ppr res_ty ]) $- WithDmdType res_ty (Case scrut' case_bndr' ty alts')--dmdAnal' env dmd (Let bind body)- = WithDmdType final_ty (Let bind' body')- where- !(WithDmdType final_ty (R bind' body')) = dmdAnalBind NotTopLevel env dmd bind go'- go' !env' = dmdAnal env' dmd body---- | A simple, syntactic analysis of whether an expression MAY throw a precise--- exception when evaluated. It's always sound to return 'True'.--- See Note [Which scrutinees may throw precise exceptions].-exprMayThrowPreciseException :: FamInstEnvs -> CoreExpr -> Bool-exprMayThrowPreciseException envs e- | not (forcesRealWorld envs (exprType e))- = False -- 1. in the Note- | (Var f, _) <- collectArgs e- , Just op <- isPrimOpId_maybe f- , op /= RaiseIOOp- = False -- 2. in the Note- | (Var f, _) <- collectArgs e- , Just fcall <- isFCallId_maybe f- , not (isSafeForeignCall fcall)- = False -- 3. in the Note- | otherwise- = True -- _. in the Note---- | Recognises types that are--- * @State# RealWorld@--- * Unboxed tuples with a @State# RealWorld@ field--- modulo coercions. This will detect 'IO' actions (even post Nested CPR! See--- T13380e) and user-written variants thereof by their type.-forcesRealWorld :: FamInstEnvs -> Type -> Bool-forcesRealWorld fam_envs ty- | ty `eqType` realWorldStatePrimTy- = True- | Just (tc, tc_args, _co) <- normSplitTyConApp_maybe fam_envs ty- , isUnboxedTupleTyCon tc- , let field_tys = dataConInstArgTys (tyConSingleDataCon tc) tc_args- = any (eqType realWorldStatePrimTy . scaledThing) field_tys- | otherwise- = False--dmdAnalSumAlt :: AnalEnv -> SubDemand -> Id -> Alt Var -> WithDmdType CoreAlt-dmdAnalSumAlt env dmd case_bndr (Alt con bndrs rhs)- | let rhs_env = addInScopeAnalEnvs env (case_bndr:bndrs)- -- See Note [Bringing a new variable into scope]- , WithDmdType rhs_ty rhs' <- dmdAnal rhs_env dmd rhs- , WithDmdType alt_ty dmds <- findBndrsDmds env rhs_ty bndrs- , let (_ :* case_bndr_sd) = findIdDemand alt_ty case_bndr- -- See Note [Demand on case-alternative binders]- -- we can't use the scrut_sd, because it says 'Prod' and we'll use- -- topSubDmd anyway for scrutinees of sum types.- scrut_sd = scrutSubDmd case_bndr_sd dmds- dmds' = fieldBndrDmds scrut_sd (length dmds)- -- Do not put a thunk into the Alt- !new_ids = setBndrsDemandInfo bndrs dmds'- = WithDmdType alt_ty (Alt con new_ids rhs')---- See Note [Demand on the scrutinee of a product case]-scrutSubDmd :: SubDemand -> [Demand] -> SubDemand-scrutSubDmd case_sd fld_dmds =- -- pprTraceWith "scrutSubDmd" (\scrut_sd -> ppr case_sd $$ ppr fld_dmds $$ ppr scrut_sd) $- case_sd `plusSubDmd` mkProd Unboxed fld_dmds---- See Note [Demand on case-alternative binders]-fieldBndrDmds :: SubDemand -- on the scrutinee- -> Arity- -> [Demand] -- Final demands for the components of the DataCon-fieldBndrDmds scrut_sd n_flds =- case viewProd n_flds scrut_sd of- Just (_, ds) -> ds- Nothing -> replicate n_flds topDmd- -- Either an arity mismatch or scrut_sd was a call demand.- -- See Note [Untyped demand on case-alternative binders]--{--Note [Analysing with absent demand]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Suppose we analyse an expression with demand A. The "A" means-"absent", so this expression will never be needed. What should happen?-There are several wrinkles:--* We *do* want to analyse the expression regardless.- Reason: Note [Always analyse in virgin pass]-- But we can post-process the results to ignore all the usage- demands coming back. This is done by multDmdType.--* Nevertheless, which sub-demand should we pick for analysis?- Since the demand was absent, any would do. Worker/wrapper will replace- absent bindings with an absent filler anyway, so annotations in the RHS- of an absent binding don't matter much.- Picking 'botSubDmd' would be the most useful, but would also look a bit- misleading in the Core output of DmdAnal, because all nested annotations would- be bottoming. Better pick 'seqSubDmd', so that we annotate many of those- nested bindings with A themselves.--* In a previous incarnation of GHC we needed to be extra careful in the- case of an *unlifted type*, because unlifted values are evaluated- even if they are not used. Example (see #9254):- f :: (() -> (# Int#, () #)) -> ()- -- Strictness signature is- -- <CS(S(A,SU))>- -- I.e. calls k, but discards first component of result- f k = case k () of (# _, r #) -> r-- g :: Int -> ()- g y = f (\n -> (# case y of I# y2 -> y2, n #))-- Here f's strictness signature says (correctly) that it calls its- argument function and ignores the first component of its result.- This is correct in the sense that it'd be fine to (say) modify the- function so that always returned 0# in the first component.-- But in function g, we *will* evaluate the 'case y of ...', because- it has type Int#. So 'y' will be evaluated. So we must record this- usage of 'y', else 'g' will say 'y' is absent, and will w/w so that- 'y' is bound to an aBSENT_ERROR thunk.-- However, the argument of toSubDmd always satisfies the let/app- invariant; so if it is unlifted it is also okForSpeculation, and so- can be evaluated in a short finite time -- and that rules out nasty- cases like the one above. (I'm not quite sure why this was a- problem in an earlier version of GHC, but it isn't now.)--Note [Always analyse in virgin pass]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Tricky point: make sure that we analyse in the 'virgin' pass. Consider- rec { f acc x True = f (...rec { g y = ...g... }...)- f acc x False = acc }-In the virgin pass for 'f' we'll give 'f' a very strict (bottom) type.-That might mean that we analyse the sub-expression containing the-E = "...rec g..." stuff in a bottom demand. Suppose we *didn't analyse*-E, but just returned botType.--Then in the *next* (non-virgin) iteration for 'f', we might analyse E-in a weaker demand, and that will trigger doing a fixpoint iteration-for g. But *because it's not the virgin pass* we won't start g's-iteration at bottom. Disaster. (This happened in $sfibToList' of-nofib/spectral/fibheaps.)--So in the virgin pass we make sure that we do analyse the expression-at least once, to initialise its signatures.--Note [Which scrutinees may throw precise exceptions]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-This is the specification of 'exprMayThrowPreciseExceptions',-which is important for Scenario 2 of-Note [Precise exceptions and strictness analysis] in GHC.Types.Demand.--For an expression @f a1 ... an :: ty@ we determine that- 1. False If ty is *not* @State# RealWorld@ or an unboxed tuple thereof.- This check is done by 'forcesRealWorld'.- (Why not simply unboxed pairs as above? This is motivated by- T13380{d,e}.)- 2. False If f is a PrimOp, and it is *not* raiseIO#- 3. False If f is an unsafe FFI call ('PlayRisky')- _. True Otherwise "give up".--It is sound to return False in those cases, because- 1. We don't give any guarantees for unsafePerformIO, so no precise exceptions- from pure code.- 2. raiseIO# is the only primop that may throw a precise exception.- 3. Unsafe FFI calls may not interact with the RTS (to throw, for example).- See haddock on GHC.Types.ForeignCall.PlayRisky.--We *need* to return False in those cases, because- 1. We would lose too much strictness in pure code, all over the place.- 2. We would lose strictness for primops like getMaskingState#, which- introduces a substantial regression in- GHC.IO.Handle.Internals.wantReadableHandle.- 3. We would lose strictness for code like GHC.Fingerprint.fingerprintData,- where an intermittent FFI call to c_MD5Init would otherwise lose- strictness on the arguments len and buf, leading to regressions in T9203- (2%) and i386's haddock.base (5%). Tested by T13380f.--In !3014 we tried a more sophisticated analysis by introducing ConOrDiv (nic)-to the Divergence lattice, but in practice it turned out to be hard to untaint-from 'topDiv' to 'conDiv', leading to bugs, performance regressions and-complexity that didn't justify the single fixed testcase T13380c.--Note [Demand analysis for recursive data constructors]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-T11545 features a single-product, recursive data type- data A = A A A ... A- deriving Eq-Naturally, `(==)` is deeply strict in `A` and in fact will never terminate. That-leads to very large (exponential in the depth) demand signatures and fruitless-churn in boxity analysis, demand analysis and worker/wrapper.-So we detect `A` as a recursive data constructor-(see Note [Detecting recursive data constructors]) analysing `case x of A ...`-and simply assume L for the demand on field binders, which is the same code-path as we take for sum types.-Combined with the B demand on the case binder, we get the very small demand-signature <1S><1S>b on `(==)`. This improves ghc/alloc performance on T11545-tenfold! See also Note [CPR for recursive data constructors] which describes the-sibling mechanism in CPR analysis.--Note [Demand on the scrutinee of a product case]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-When figuring out the demand on the scrutinee of a product case,-we use the demands of the case alternative, i.e. id_dmds.-But note that these include the demand on the case binder;-see Note [Demand on case-alternative binders].-This is crucial. Example:- f x = case x of y { (a,b) -> k y a }-If we just take scrut_demand = 1P(L,A), then we won't pass x to the-worker, so the worker will rebuild- x = (a, absent-error)-and that'll crash.--Note [Demand on case-alternative binders]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-The demand on a binder in a case alternative comes- (a) From the demand on the binder itself- (b) From the demand on the case binder-Forgetting (b) led directly to #10148.--Example. Source code:- f x@(p,_) = if p then foo x else True-- foo (p,True) = True- foo (p,q) = foo (q,p)--After strictness analysis, forgetting (b):- f = \ (x_an1 [Dmd=1P(1L,ML)] :: (Bool, Bool)) ->- case x_an1- of wild_X7 [Dmd=MP(ML,ML)]- { (p_an2 [Dmd=1L], ds_dnz [Dmd=A]) ->- case p_an2 of _ {- False -> GHC.Types.True;- True -> foo wild_X7 }--Note that ds_dnz is syntactically dead, but the expression bound to it is-reachable through the case binder wild_X7. Now watch what happens if we inline-foo's wrapper:- f = \ (x_an1 [Dmd=1P(1L,ML)] :: (Bool, Bool)) ->- case x_an1- of _ [Dmd=MP(ML,ML)]- { (p_an2 [Dmd=1L], ds_dnz [Dmd=A]) ->- case p_an2 of _ {- False -> GHC.Types.True;- True -> $wfoo_soq GHC.Types.True ds_dnz }--Look at that! ds_dnz has come back to life in the call to $wfoo_soq! A second-run of demand analysis would no longer infer ds_dnz to be absent.-But unlike occurrence analysis, which infers properties of the *syntactic*-shape of the program, the results of demand analysis describe expressions-*semantically* and are supposed to be mostly stable across Simplification.-That's why we should better account for (b).-In #10148, we ended up emitting a single-entry thunk instead of an updateable-thunk for a let binder that was an an absent case-alt binder during DmdAnal.--This is needed even for non-product types, in case the case-binder-is used but the components of the case alternative are not.--Note [Untyped demand on case-alternative binders]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-With unsafeCoerce, #8037 and #22039 taught us that the demand on the case binder-may be a call demand or have a different number of fields than the constructor-of the case alternative it is used in. From T22039:-- blarg :: (Int, Int) -> Int- blarg (x,y) = x+y- -- blarg :: <1!P(1L,1L)>-- f :: Either Int Int -> Int- f Left{} = 0- f e = blarg (unsafeCoerce e)- ==> { desugars to }- f = \ (ds_d1nV :: Either Int Int) ->- case ds_d1nV of wild_X1 {- Left ds_d1oV -> lvl_s1Q6;- Right ipv_s1Pl ->- blarg- (case unsafeEqualityProof @(*) @(Either Int Int) @(Int, Int) of- { UnsafeRefl co_a1oT ->- wild_X1 `cast` (Sub (Sym co_a1oT) :: Either Int Int ~R# (Int, Int))- })- }--The case binder `e`/`wild_X1` has demand 1!P(1L,1L), with two fields, from the call-to `blarg`, but `Right` only has one field. Although the code will crash when-executed, we must be able to analyse it in 'fieldBndrDmds' and conservatively-approximate with Top instead of panicking because of the mismatch.-In #22039, this kind of code was guarded behind a safe `cast` and thus dead-code, but nevertheless led to a panic of the compiler.--You might wonder why the same problem doesn't come up when scrutinising a-product type instead of a sum type. It appears that for products, `wild_X1`-will be inlined before DmdAnal.--See also Note [mkWWstr and unsafeCoerce] for a related issue.--Note [Aggregated demand for cardinality]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-FIXME: This Note should be named [LetUp vs. LetDown] and probably predates-said separation. SG--We use different strategies for strictness and usage/cardinality to-"unleash" demands captured on free variables by bindings. Let us-consider the example:--f1 y = let {-# NOINLINE h #-}- h = y- in (h, h)--We are interested in obtaining cardinality demand U1 on |y|, as it is-used only in a thunk, and, therefore, is not going to be updated any-more. Therefore, the demand on |y|, captured and unleashed by usage of-|h| is U1. However, if we unleash this demand every time |h| is used,-and then sum up the effects, the ultimate demand on |y| will be U1 +-U1 = U. In order to avoid it, we *first* collect the aggregate demand-on |h| in the body of let-expression, and only then apply the demand-transformer:--transf[x](U) = {y |-> U1}--so the resulting demand on |y| is U1.--The situation is, however, different for strictness, where this-aggregating approach exhibits worse results because of the nature of-|both| operation for strictness. Consider the example:--f y c =- let h x = y |seq| x- in case of- True -> h True- False -> y--It is clear that |f| is strict in |y|, however, the suggested analysis-will infer from the body of |let| that |h| is used lazily (as it is-used in one branch only), therefore lazy demand will be put on its-free variable |y|. Conversely, if the demand on |h| is unleashed right-on the spot, we will get the desired result, namely, that |f| is-strict in |y|.---************************************************************************-* *- Demand transformer-* *-************************************************************************--}--dmdTransform :: AnalEnv -- ^ The analysis environment- -> Id -- ^ The variable- -> SubDemand -- ^ The evaluation context of the var- -> DmdType -- ^ The demand type unleashed by the variable in this- -- context. The returned DmdEnv includes the demand on- -- this function plus demand on its free variables--- See Note [What are demand signatures?] in "GHC.Types.Demand"-dmdTransform env var sd- -- Data constructors- | Just con <- isDataConWorkId_maybe var- = -- pprTraceWith "dmdTransform:DataCon" (\ty -> ppr con $$ ppr sd $$ ppr ty) $- dmdTransformDataConSig (dataConRepStrictness con) sd- -- Dictionary component selectors- -- Used to be controlled by a flag.- -- See #18429 for some perf measurements.- | Just _ <- isClassOpId_maybe var- = -- pprTrace "dmdTransform:DictSel" (ppr var $$ ppr (idDmdSig var) $$ ppr sd) $- dmdTransformDictSelSig (idDmdSig var) sd- -- Imported functions- | isGlobalId var- , let res = dmdTransformSig (idDmdSig var) sd- = -- pprTrace "dmdTransform:import" (vcat [ppr var, ppr (idDmdSig var), ppr sd, ppr res])- res- -- Top-level or local let-bound thing for which we use LetDown ('useLetUp').- -- In that case, we have a strictness signature to unleash in our AnalEnv.- | Just (sig, top_lvl) <- lookupSigEnv env var- , let fn_ty = dmdTransformSig sig sd- = -- pprTrace "dmdTransform:LetDown" (vcat [ppr var, ppr sig, ppr sd, ppr fn_ty]) $- case top_lvl of- NotTopLevel -> addVarDmd fn_ty var (C_11 :* sd)- TopLevel- | isInterestingTopLevelFn var- -- Top-level things will be used multiple times or not at- -- all anyway, hence the multDmd below: It means we don't- -- have to track whether @var@ is used strictly or at most- -- once, because ultimately it never will.- -> addVarDmd fn_ty var (C_0N `multDmd` (C_11 :* sd)) -- discard strictness- | otherwise- -> fn_ty -- don't bother tracking; just annotate with 'topDmd' later- -- Everything else:- -- * Local let binders for which we use LetUp (cf. 'useLetUp')- -- * Lambda binders- -- * Case and constructor field binders- | otherwise- = -- pprTrace "dmdTransform:other" (vcat [ppr var, ppr boxity, ppr sd]) $- noArgsDmdType (addVarDmdEnv nopDmdEnv var (C_11 :* sd))--{- *********************************************************************-* *- Binding right-hand sides-* *-********************************************************************* -}---- | An environment in which all demands are weak according to 'isWeakDmd'.--- See Note [Lazy and unleashable free variables].-type WeakDmds = VarEnv Demand---- | @dmdAnalRhsSig@ analyses the given RHS to compute a demand signature--- for the LetDown rule. It works as follows:------ * assuming the weakest possible body sub-demand, L--- * looking at the definition--- * determining a strictness signature------ Since it assumed a body sub-demand of L, the resulting signature is--- applicable at any call site.-dmdAnalRhsSig- :: TopLevelFlag- -> RecFlag- -> AnalEnv -> SubDemand- -> Id -> CoreExpr- -> (AnalEnv, WeakDmds, Id, CoreExpr)--- Process the RHS of the binding, add the strictness signature--- to the Id, and augment the environment with the signature as well.--- See Note [NOINLINE and strictness]-dmdAnalRhsSig top_lvl rec_flag env let_dmd id rhs- = -- pprTrace "dmdAnalRhsSig" (ppr id $$ ppr let_dmd $$ ppr rhs_dmds $$ ppr sig $$ ppr weak_fvs) $- (final_env, weak_fvs, final_id, final_rhs)- where- rhs_arity = idArity id- -- See Note [Demand signatures are computed for a threshold demand based on idArity]-- rhs_dmd = mkCalledOnceDmds rhs_arity body_dmd-- body_dmd- | isJoinId id- -- See Note [Demand analysis for join points]- -- See Note [Invariants on join points] invariant 2b, in GHC.Core- -- rhs_arity matches the join arity of the join point- -- See Note [Unboxed demand on function bodies returning small products]- = unboxedWhenSmall env rec_flag (resultType_maybe id) let_dmd- | otherwise- -- See Note [Unboxed demand on function bodies returning small products]- = unboxedWhenSmall env rec_flag (resultType_maybe id) topSubDmd-- WithDmdType rhs_dmd_ty rhs' = dmdAnal env rhs_dmd rhs- DmdType rhs_env rhs_dmds = rhs_dmd_ty- (final_rhs_dmds, final_rhs) = finaliseArgBoxities env id rhs_arity rhs' (de_div rhs_env)- `orElse` (rhs_dmds, rhs')-- sig = mkDmdSigForArity rhs_arity (DmdType sig_env final_rhs_dmds)-- final_id = id `setIdDmdSig` sig- !final_env = extendAnalEnv top_lvl env final_id sig-- -- See Note [Aggregated demand for cardinality]- -- FIXME: That Note doesn't explain the following lines at all. The reason- -- is really much different: When we have a recursive function, we'd- -- have to also consider the free vars of the strictness signature- -- when checking whether we found a fixed-point. That is expensive;- -- we only want to check whether argument demands of the sig changed.- -- reuseEnv makes it so that the FV results are stable as long as the- -- last argument demands were. Strictness won't change. But used-once- -- might turn into used-many even if the signature was stable and- -- we'd have to do an additional iteration. reuseEnv makes sure that- -- we never get used-once info for FVs of recursive functions.- -- See #14816 where we try to get rid of reuseEnv.- rhs_env1 = case rec_flag of- Recursive -> reuseEnv rhs_env- NonRecursive -> rhs_env-- -- See Note [Absence analysis for stable unfoldings and RULES]- rhs_env2 = rhs_env1 `plusDmdEnv` demandRootSet env (bndrRuleAndUnfoldingIds id)-- -- See Note [Lazy and unleashable free variables]- !(!sig_env, !weak_fvs) = splitWeakDmds rhs_env2--splitWeakDmds :: DmdEnv -> (DmdEnv, WeakDmds)-splitWeakDmds (DE fvs div) = (DE sig_fvs div, weak_fvs)- where (!weak_fvs, !sig_fvs) = partitionVarEnv isWeakDmd fvs---- | The result type after applying 'idArity' many arguments. Returns 'Nothing'--- when the type doesn't have exactly 'idArity' many arrows.-resultType_maybe :: Id -> Maybe Type-resultType_maybe id- | (pis,ret_ty) <- splitPiTys (idType id)- , count (not . isNamedBinder) pis == idArity id- = Just $! ret_ty- | otherwise- = Nothing--unboxedWhenSmall :: AnalEnv -> RecFlag -> Maybe Type -> SubDemand -> SubDemand--- See Note [Unboxed demand on function bodies returning small products]-unboxedWhenSmall _ _ Nothing sd = sd-unboxedWhenSmall env rec_flag (Just ret_ty) sd = go 1 ret_ty sd- where- -- Magic constant, bounding the depth of optimistic 'Unboxed' flags. We- -- might want to minmax in the future.- max_depth | isRec rec_flag = 3 -- So we get at most something as deep as !P(L!P(L!L))- | otherwise = 1 -- Otherwise be unbox too deep in T18109, T18174 and others and get a bunch of stack overflows- go :: Int -> Type -> SubDemand -> SubDemand- go depth ty sd- | depth <= max_depth- , Just (tc, tc_args, _co) <- normSplitTyConApp_maybe (ae_fam_envs env) ty- , Just dc <- tyConSingleAlgDataCon_maybe tc- , null (dataConExTyCoVars dc) -- Can't unbox results with existentials- , dataConRepArity dc <= dmd_unbox_width (ae_opts env)- , Just (_, ds) <- viewProd (dataConRepArity dc) sd- , arg_tys <- map scaledThing $ dataConInstArgTys dc tc_args- , equalLength ds arg_tys- = mkProd Unboxed $! strictZipWith (go_dmd (depth+1)) arg_tys ds- | otherwise- = sd-- go_dmd :: Int -> Type -> Demand -> Demand- go_dmd depth ty dmd = case dmd of- AbsDmd -> AbsDmd- BotDmd -> BotDmd- n :* sd -> n :* go depth ty sd---- | If given the (local, non-recursive) let-bound 'Id', 'useLetUp' determines--- whether we should process the binding up (body before rhs) or down (rhs--- before body).------ We use LetDown if there is a chance to get a useful strictness signature to--- unleash at call sites. LetDown is generally more precise than LetUp if we can--- correctly guess how it will be used in the body, that is, for which incoming--- demand the strictness signature should be computed, which allows us to--- unleash higher-order demands on arguments at call sites. This is mostly the--- case when------ * The binding takes any arguments before performing meaningful work (cf.--- 'idArity'), in which case we are interested to see how it uses them.--- * The binding is a join point, hence acting like a function, not a value.--- As a big plus, we know *precisely* how it will be used in the body; since--- it's always tail-called, we can directly unleash the incoming demand of--- the let binding on its RHS when computing a strictness signature. See--- [Demand analysis for join points].------ Thus, if the binding is not a join point and its arity is 0, we have a thunk--- and use LetUp, implying that we have no usable demand signature available--- when we analyse the let body.------ Since thunk evaluation is memoised, we want to unleash its 'DmdEnv' of free--- vars at most once, regardless of how many times it was forced in the body.--- This makes a real difference wrt. usage demands. The other reason is being--- able to unleash a more precise product demand on its RHS once we know how the--- thunk was used in the let body.------ Characteristic examples, always assuming a single evaluation:------ * @let x = 2*y in x + x@ => LetUp. Compared to LetDown, we find out that--- the expression uses @y@ at most once.--- * @let x = (a,b) in fst x@ => LetUp. Compared to LetDown, we find out that--- @b@ is absent.--- * @let f x = x*2 in f y@ => LetDown. Compared to LetUp, we find out that--- the expression uses @y@ strictly, because we have @f@'s demand signature--- available at the call site.--- * @join exit = 2*y in if a then exit else if b then exit else 3*y@ =>--- LetDown. Compared to LetUp, we find out that the expression uses @y@--- strictly, because we can unleash @exit@'s signature at each call site.--- * For a more convincing example with join points, see Note [Demand analysis--- for join points].----useLetUp :: TopLevelFlag -> Var -> Bool-useLetUp top_lvl f = isNotTopLevel top_lvl && idArity f == 0 && not (isJoinId f)--{- Note [Demand analysis for join points]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider- g :: (Int,Int) -> Int- g (p,q) = p+q-- f :: T -> Int -> Int- f x p = g (join j y = (p,y)- in case x of- A -> j 3- B -> j 4- C -> (p,7))--If j was a vanilla function definition, we'd analyse its body with-evalDmd, and think that it was lazy in p. But for join points we can-do better! We know that j's body will (if called at all) be evaluated-with the demand that consumes the entire join-binding, in this case-the argument demand from g. Whizzo! g evaluates both components of-its argument pair, so p will certainly be evaluated if j is called.--For f to be strict in p, we need /all/ paths to evaluate p; in this-case the C branch does so too, so we are fine. So, as usual, we need-to transport demands on free variables to the call site(s). Compare-Note [Lazy and unleashable free variables].--The implementation is easy. When analysing a join point, we can-analyse its body with the demand from the entire join-binding (written-let_dmd here).--Another win for join points! #13543.--However, note that the strictness signature for a join point can-look a little puzzling. E.g.-- (join j x = \y. error "urk")- (in case v of )- ( A -> j 3 ) x- ( B -> j 4 )- ( C -> \y. blah )--The entire thing is in a C1(L) context, so j's strictness signature-will be [A]b-meaning one absent argument, returns bottom. That seems odd because-there's a \y inside. But it's right because when consumed in a C1(L)-context the RHS of the join point is indeed bottom.--Note [Demand signatures are computed for a threshold demand based on idArity]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We compute demand signatures assuming idArity incoming arguments to approximate-behavior for when we have a call site with at least that many arguments. idArity-is /at least/ the number of manifest lambdas, but might be higher for PAPs and-trivial RHS (see Note [Demand analysis for trivial right-hand sides]).--Because idArity of a function varies independently of its cardinality-properties (cf. Note [idArity varies independently of dmdTypeDepth]), we-implicitly encode the arity for when a demand signature is sound to unleash-in its 'dmdTypeDepth' (cf. Note [Understanding DmdType and DmdSig] in-GHC.Types.Demand). It is unsound to unleash a demand signature when the-incoming number of arguments is less than that.-See Note [What are demand signatures?] in GHC.Types.Demand for more details-on soundness.--Why idArity arguments? Because that's a conservative estimate of how many-arguments we must feed a function before it does anything interesting with them.-Also it elegantly subsumes the trivial RHS and PAP case.--There might be functions for which we might want to analyse for more incoming-arguments than idArity. Example:-- f x =- if expensive- then \y -> ... y ...- else \y -> ... y ...--We'd analyse `f` under a unary call demand C1(L), corresponding to idArity-being 1. That's enough to look under the manifest lambda and find out how a-unary call would use `x`, but not enough to look into the lambdas in the if-branches.--On the other hand, if we analysed for call demand C1(C1(L)), we'd get useful-strictness info for `y` (and more precise info on `x`) and possibly CPR-information, but-- * We would no longer be able to unleash the signature at unary call sites- * Performing the worker/wrapper split based on this information would be- implicitly eta-expanding `f`, playing fast and loose with divergence and- even being unsound in the presence of newtypes, so we refrain from doing so.- Also see Note [Don't eta expand in w/w] in GHC.Core.Opt.WorkWrap.--Since we only compute one signature, we do so for arity 1. Computing multiple-signatures for different arities (i.e., polyvariance) would be entirely-possible, if it weren't for the additional runtime and implementation-complexity.--Note [idArity varies independently of dmdTypeDepth]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We used to check in GHC.Core.Lint that dmdTypeDepth <= idArity for a let-bound-identifier. But that means we would have to zap demand signatures every time we-reset or decrease arity. That's an unnecessary dependency, because-- * The demand signature captures a semantic property that is independent of- what the binding's current arity is- * idArity is analysis information itself, thus volatile- * We already *have* dmdTypeDepth, wo why not just use it to encode the- threshold for when to unleash the signature- (cf. Note [Understanding DmdType and DmdSig] in GHC.Types.Demand)--Consider the following expression, for example:-- (let go x y = `x` seq ... in go) |> co--`go` might have a strictness signature of `<1L><L>`. The simplifier will identify-`go` as a nullary join point through `joinPointBinding_maybe` and float the-coercion into the binding, leading to an arity decrease:-- join go = (\x y -> `x` seq ...) |> co in go--With the CoreLint check, we would have to zap `go`'s perfectly viable strictness-signature.--Note [Demand analysis for trivial right-hand sides]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider- foo = plusInt |> co-where plusInt is an arity-2 function with known strictness. Clearly-we want plusInt's strictness to propagate to foo! But because it has-no manifest lambdas, it won't do so automatically, and indeed 'co' might-have type (Int->Int->Int) ~ T.--Fortunately, GHC.Core.Opt.Arity gives 'foo' arity 2, which is enough for LetDown to-forward plusInt's demand signature, and all is well (see Note [Newtype arity] in-GHC.Core.Opt.Arity)! A small example is the test case NewtypeArity.--Note [Absence analysis for stable unfoldings and RULES]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Among others, tickets #18638 and #23208 show that it's really important to treat-stable unfoldings as demanded. Consider-- g = blah-- f = \x. ...no use of g....- {- f's stable unfolding is f = \x. ...g... -}--If f is ever inlined we use 'g'. But f's current RHS makes no use-of 'g', so if we don't look at the unfolding we'll mark g as Absent,-and transform to-- g = error "Entered absent value"- f = \x. ...- {- f's stable unfolding is f = \x. ...g... -}--Now if f is subsequently inlined, we'll use 'g' and ... disaster.--SOLUTION: if f has a stable unfolding, treat every free variable as a-/demand root/, that is: Analyse it as if it was a variable occuring in a-'topDmd' context. This is done in `demandRoot` (which we also use for exported-top-level ids). Do the same for Ids free in the RHS of any RULES for f.--Wrinkles:-- (W1) You may wonder how it can be that f's optimised RHS has somehow- discarded 'g', but when f is inlined we /don't/ discard g in the same- way. I think a simple example is- g = (a,b)- f = \x. fst g- {-# INLINE f #-}-- Now f's optimised RHS will be \x.a, but if we change g to (error "..")- (since it is apparently Absent) and then inline (\x. fst g) we get- disaster. But regardless, #18638 was a more complicated version of- this, that actually happened in practice.-- (W2) You might wonder why we don't simply take the free vars of the- unfolding/RULE and map them to topDmd. The reason is that any of the free vars- might have demand signatures themselves that in turn demand transitive free- variables and that we hence need to unleash! This came up in #23208.- Consider-- err :: Int -> b- err = error "really important message"-- sg :: Int -> Int- sg _ = case err of {} -- Str=<1B>b {err:->S}-- g :: a -> a -- g is exported- g x = x- {-# RULES "g" g @Int = sg #-}-- Here, `err` is only demanded by `sg`'s demand signature: It doesn't occur- in the weak_fvs of `sg`'s RHS at all. Hence when we `demandRoots` `sg`- because it occurs in the RULEs of `g` (which is exported), we better unleash- the demand signature of `sg`, too! Before #23208 we simply added a 'topDmd'- for `sg`, failing to unleash the signature and hence observed an absent- error instead of the `really important message`.--Note [Boxity for bottoming functions]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider-```hs-indexError :: Show a => (a, a) -> a -> String -> b--- Str=<..><1!P(S,S)><1S><S>b-indexError rng i s = error (show rng ++ show i ++ show s)--get :: (Int, Int) -> Int -> [a] -> a-get p@(l,u) i xs- | l <= i, i < u = xs !! (i-u)- | otherwise = indexError p i "get"-```-The hot path of `get` certainly wants to unbox `p` as well as `l` and `u`, but-the unimportant, diverging error path needs `l` and `u` boxed (although the-wrapper for `indexError` *will* unbox `p`). This pattern often occurs in-performance sensitive code that does bounds-checking.--It would be a shame to let `Boxed` win for the fields! So here's what we do:-While to summarising `indexError`'s boxity signature in `finaliseArgBoxities`,-we `unboxDeeplyDmd` all its argument demands and are careful not to discard-excess boxity in the `StopUnboxing` case, to get the signature-`<1!P(!S,!S)><1!S><S!S>b`.--Then worker/wrapper will not only unbox the pair passed to `indexError` (as it-would do anyway), demand analysis will also pretend that `indexError` needs `l`-and `u` unboxed (and the two other args). Which is a lie, because `indexError`'s-type abstracts over their types and could never unbox them.--The important change is at the *call sites* of `$windexError`: Boxity analysis-will conclude to unbox `l` and `u`, which *will* incur reboxing of crud that-should better float to the call site of `$windexError`. There we don't care-much, because it's in the slow, diverging code path! And that floating often-happens, but not always. See Note [Reboxed crud for bottoming calls].--Note [Reboxed crud for bottoming calls]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-For functions like `get` in Note [Boxity for bottoming functions], it's clear-that the reboxed crud will be floated inside to the call site of `$windexError`.-But here's an example where that is not the case:-```hs-import GHC.Ix--theresCrud :: Int -> Int -> Int-theresCrud x y = go x- where- go 0 = index (0,y) 0- go 1 = index (x,y) 1- go n = go (n-1)- {-# NOINLINE theresCrud #-}-```-If you look at the Core, you'll see that `y` will be reboxed and used in the-two exit join points for the `$windexError` calls, while `x` is only reboxed in the-exit join point for `index (x,y) 1` (happens in lvl below):-```-$wtheresCrud = \ ww ww1 ->- let { y = I# ww1 } in- join { lvl2 = ... case lvl1 ww y of wild { }; ... } in- join { lvl3 = ... case lvl y of wild { }; ... } in- ...-```-This is currently a bug that we willingly accept and it's documented in #21128.--}--{- *********************************************************************-* *- Finalising boxity-* *-********************************************************************* -}--{- Note [Finalising boxity for demand signatures]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-The worker/wrapper pass must strictly adhere to the boxity decisions-encoded in the demand signature, because that is the information that-demand analysis propagates throughout the program. Failing to-implement the strategy laid out in the signature can result in-reboxing in unexpected places. Hence, we must completely anticipate-unboxing decisions during demand analysis and reflect these decicions-in demand annotations. That is the job of 'finaliseArgBoxities',-which is defined here and called from demand analysis.--Here is a list of different Notes it has to take care of:-- * Note [No lazy, Unboxed demands in demand signature] such as `L!P(L)` in- general, but still allow Note [Unboxing evaluated arguments]- * Note [No nested Unboxed inside Boxed in demand signature] such as `1P(1!L)`- * Implement fixes for corner cases Note [Do not unbox class dictionaries]- and Note [mkWWstr and unsafeCoerce]--Then, in worker/wrapper blindly trusts the boxity info in the demand signature-and will not look at strictness info *at all*, in 'wantToUnboxArg'.--Note [Finalising boxity for let-bound Ids]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider- let x = e in body-where the demand on 'x' is 1!P(blah). We want to unbox x according to-Note [Thunk splitting] in GHC.Core.Opt.WorkWrap. We must do this becuase-worker/wrapper ignores stricness and looks only at boxity flags; so if-x's demand is L!P(blah) we might still split it (wrongly). We want to-switch to Boxed on any lazy demand.--That is what finaliseLetBoxity does. It has no worker-arg budget, so it-is much simpler than finaliseArgBoxities.--Note [No nested Unboxed inside Boxed in demand signature]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider-```-f p@(x,y)- | even (x+y) = []- | otherwise = [p]-```-Demand analysis will infer that the function body puts a demand of `1P(1!L,1!L)`-on 'p', e.g., Boxed on the outside but Unboxed on the inside. But worker/wrapper-can't unbox the pair components without unboxing the pair! So we better say-`1P(1L,1L)` in the demand signature in order not to spread wrong Boxity info.-That happens via the call to trimBoxity in 'finaliseArgBoxities'/'finaliseLetBoxity'.--Note [No lazy, Unboxed demands in demand signature]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider T19407:-- data Huge = Huge Bool () ... () -- think: DynFlags- data T = T { h :: Huge, n :: Int }- f t@(T h _) = g h t- g (H b _ ... _) t = if b then 1 else n t--The body of `g` puts (approx.) demand `L!P(A,1)` on `t`. But we better-not put that demand in `g`'s demand signature, because worker/wrapper will not-in general unbox a lazy-and-unboxed demand like `L!P(..)`.-(The exception are known-to-be-evaluated arguments like strict fields,-see Note [Unboxing evaluated arguments].)--The program above is an example where spreading misinformed boxity through the-signature is particularly egregious. If we give `g` that signature, then `f`-puts demand `S!P(1!P(1L,A,..),ML)` on `t`. Now we will unbox `t` in `f` it and-we get-- f (T (H b _ ... _) n) = $wf b n- $wf b n = $wg b (T (H b x ... x) n)- $wg = ...--Massive reboxing in `$wf`! Solution: Trim boxity on lazy demands in-'trimBoxity', modulo Note [Unboxing evaluated arguments].--Note [Unboxing evaluated arguments]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider this program (due to Roman):-- data X a = X !a-- foo :: X Int -> Int -> Int- foo x@(X a) n = go 0- where- go i | i < n = a + go (i+1)- | otherwise = 0--We want the worker for 'foo' to look like this:-- $wfoo :: Int# -> Int# -> Int#--with the first argument unboxed, so that it is not eval'd each time around the-'go' loop (which would otherwise happen, since 'foo' is not strict in 'a'). It-is sound for the wrapper to pass an unboxed arg because X is strict-(see Note [Strictness and Unboxing] in "GHC.Core.Opt.DmdAnal"), so its argument-must be evaluated. And if we *don't* pass an unboxed argument, we can't even-repair it by adding a `seq` thus:-- foo (X a) n = a `seq` go 0--because the seq is discarded (very early) since X is strict!--So here's what we do--* Since this has nothing to do with how 'foo' uses 'a', we leave demand- analysis alone, but account for the additional evaluatedness when- annotating the binder 'finaliseArgBoxities', which will retain the Unboxed- boxity on 'a' in the definition of 'foo' in the demand 'L!P(L)'; meaning- it's used lazily but unboxed nonetheless. This seems to contradict Note- [No lazy, Unboxed demands in demand signature], but we know that 'a' is- evaluated and thus can be unboxed.--* When 'finaliseArgBoxities' decides to unbox a record, it will zip the field demands- together with the respective 'StrictnessMark'. In case of 'x', it will pair- up the lazy field demand 'L!P(L)' on 'a' with 'MarkedStrict' to account for- the strict field.--* Said 'StrictnessMark' is passed to the recursive invocation of 'go_args' in- 'finaliseArgBoxities' when deciding whether to unbox 'a'. 'a' was used lazily, but- since it also says 'MarkedStrict', we'll retain the 'Unboxed' boxity on 'a'.--* Worker/wrapper will consult 'wantToUnboxArg' for its unboxing decision. It will- /not/ look at the strictness bits of the demand, only at Boxity flags. As such,- it will happily unbox 'a' despite the lazy demand on it.--The net effect is that boxity analysis and the w/w transformation are more-aggressive about unboxing the strict arguments of a data constructor than when-looking at strictness info exclusively. It is very much like (Nested) CPR, which-needs its nested fields to be evaluated in order for it to unbox nestedly.--There is the usual danger of reboxing, which as usual we ignore. But-if X is monomorphic, and has an UNPACK pragma, then this optimisation-is even more important. We don't want the wrapper to rebox an unboxed-argument, and pass an Int to $wfoo!--This works in nested situations like T10482-- data family Bar a- data instance Bar (a, b) = BarPair !(Bar a) !(Bar b)- newtype instance Bar Int = Bar Int-- foo :: Bar ((Int, Int), Int) -> Int -> Int- foo f k = case f of BarPair x y ->- case burble of- True -> case x of- BarPair p q -> ...- False -> ...--The extra eagerness lets us produce a worker of type:- $wfoo :: Int# -> Int# -> Int# -> Int -> Int- $wfoo p# q# y# = ...--even though the `case x` is only lazily evaluated.----------- Historical note -------------We used to add data-con strictness demands when demand analysing case-expression. However, it was noticed in #15696 that this misses some cases. For-instance, consider the program (from T10482)-- data family Bar a- data instance Bar (a, b) = BarPair !(Bar a) !(Bar b)- newtype instance Bar Int = Bar Int-- foo :: Bar ((Int, Int), Int) -> Int -> Int- foo f k =- case f of- BarPair x y -> case burble of- True -> case x of- BarPair p q -> ...- False -> ...--We really should be able to assume that `p` is already evaluated since it came-from a strict field of BarPair. This strictness would allow us to produce a-worker of type:-- $wfoo :: Int# -> Int# -> Int# -> Int -> Int- $wfoo p# q# y# = ...--even though the `case x` is only lazily evaluated--Indeed before we fixed #15696 this would happen since we would float the inner-`case x` through the `case burble` to get:-- foo f k =- case f of- BarPair x y -> case x of- BarPair p q -> case burble of- True -> ...- False -> ...--However, after fixing #15696 this could no longer happen (for the reasons-discussed in ticket:15696#comment:76). This means that the demand placed on `f`-would then be significantly weaker (since the False branch of the case on-`burble` is not strict in `p` or `q`).--Consequently, we now instead account for data-con strictness in mkWWstr_one,-applying the strictness demands to the final result of DmdAnal. The result is-that we get the strict demand signature we wanted even if we can't float-the case on `x` up through the case on `burble`.--Note [Do not unbox class dictionaries]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-If we have- f :: Ord a => [a] -> Int -> a- {-# INLINABLE f #-}-and we worker/wrapper f, we'll get a worker with an INLINABLE pragma-(see Note [Worker/wrapper for INLINABLE functions] in GHC.Core.Opt.WorkWrap),-which can still be specialised by the type-class specialiser, something like- fw :: Ord a => [a] -> Int# -> a--BUT if f is strict in the Ord dictionary, we might unpack it, to get- fw :: (a->a->Bool) -> [a] -> Int# -> a-and the type-class specialiser can't specialise that. An example is #6056.--But in any other situation, a dictionary is just an ordinary value,-and can be unpacked. So we track the INLINABLE pragma, and discard the boxity-flag in finaliseArgBoxities (see the isClassPred test).--Historical note: #14955 describes how I got this fix wrong the first time.--2. -fspecialise-aggressively. As #21286 shows, the same phenomenon can occur- occur without INLINABLE, when we use -fexpose-all-unfoldings and- -fspecialise-aggressively to do vigorous cross-module specialisation.--3. #18421 found that unboxing a dictionary can also make the worker less likely- to inline; the inlining heuristics seem to prefer to inline a function- applied to a dictionary over a function applied to a bunch of functions.--TL;DR we /never/ unbox class dictionaries. Unboxing the dictionary, and passing-a raft of higher-order functions isn't a huge win anyway -- you really want to-specialise the function.--Note [Worker argument budget]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-In 'finaliseArgBoxities' we don't want to generate workers with zillions of-argument when, say given a strict record with zillions of fields. So we-limit the maximum number of worker args to the maximum of- - -fmax-worker-args=N- - The number of args in the original function; if it already has has- zillions of arguments we don't want to seek /fewer/ args in the worker.-(Maybe we should /add/ them instead of maxing?)--We pursue a "layered" strategy for unboxing: we unbox the top level of the-argument(s), subject to budget; if there are any arguments left we unbox the-next layer, using that depleted budget.--To achieve this, we use the classic almost-circular programming technique in-which we we write one pass that takes a lazy list of the Budgets for every-layer.--Note [The OPAQUE pragma and avoiding the reboxing of arguments]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-In https://gitlab.haskell.org/ghc/ghc/-/issues/13143 it was identified that when-a function 'f' with a NOINLINE pragma is W/W transformed, then the worker for-'f' should get the NOINLINE annotation, while the wrapper /should/ be inlined.--That's because if the wrapper for 'f' had stayed NOINLINE, then any worker of a-W/W-transformed /caller of/ 'f' would immediately rebox any unboxed arguments-that is applied to the wrapper of 'f'. When the wrapper is inlined, that kind of-reboxing does not happen.--But now we have functions with OPAQUE pragmas, which by definition (See Note-[OPAQUE pragma]) do not get W/W-transformed. So in order to avoid reboxing-workers of any W/W-transformed /callers of/ 'f' we need to strip all boxity-information from 'f' in the demand analysis. This will inform the-W/W-transformation code that boxed arguments of 'f' must definitely be passed-along in boxed form and as such dissuade the creation of reboxing workers.--}--data Budgets = MkB Arity Budgets -- An infinite list of arity budgets--incTopBudget :: Budgets -> Budgets-incTopBudget (MkB n bg) = MkB (n+1) bg--positiveTopBudget :: Budgets -> Bool-positiveTopBudget (MkB n _) = n >= 0--finaliseArgBoxities :: AnalEnv -> Id -> Arity -> CoreExpr -> Divergence- -> Maybe ([Demand], CoreExpr)-finaliseArgBoxities env fn arity rhs div- | arity > count isId bndrs -- Can't find enough binders- = Nothing -- This happens if we have f = g- -- Then there are no binders; we don't worker/wrapper; and we- -- simply want to give f the same demand signature as g-- | otherwise- = Just (arg_dmds', add_demands arg_dmds' rhs)- -- add_demands: we must attach the final boxities to the lambda-binders- -- of the function, both because that's kosher, and because CPR analysis- -- uses the info on the binders directly.- where- opts = ae_opts env- fam_envs = ae_fam_envs env- is_inlinable_fn = isStableUnfolding (realIdUnfolding fn)- (bndrs, _body) = collectBinders rhs- max_wkr_args = dmd_max_worker_args opts `max` arity- -- See Note [Worker argument budget]-- -- This is the key line, which uses almost-circular programming- -- The remaining budget from one layer becomes the initial- -- budget for the next layer down. See Note [Worker argument budget]- (remaining_budget, arg_dmds') = go_args (MkB max_wkr_args remaining_budget) arg_triples-- arg_triples :: [(Type, StrictnessMark, Demand)]- arg_triples = take arity $- map mk_triple $- filter isRuntimeVar bndrs-- mk_triple :: Id -> (Type,StrictnessMark,Demand)- mk_triple bndr | is_cls_arg ty = (ty, NotMarkedStrict, trimBoxity dmd)- | is_bot_fn = (ty, NotMarkedStrict, unboxDeeplyDmd dmd)- -- See Note [OPAQUE pragma]- -- See Note [The OPAQUE pragma and avoiding the reboxing of arguments]- | is_opaque = (ty, NotMarkedStrict, trimBoxity dmd)- | otherwise = (ty, NotMarkedStrict, dmd)- where- ty = idType bndr- dmd = idDemandInfo bndr- is_opaque = isOpaquePragma (idInlinePragma fn)-- -- is_cls_arg: see Note [Do not unbox class dictionaries]- is_cls_arg arg_ty = is_inlinable_fn && isClassPred arg_ty- -- is_bot_fn: see Note [Boxity for bottoming functions]- is_bot_fn = div == botDiv-- go_args :: Budgets -> [(Type,StrictnessMark,Demand)] -> (Budgets, [Demand])- go_args bg triples = mapAccumL go_arg bg triples-- go_arg :: Budgets -> (Type,StrictnessMark,Demand) -> (Budgets, Demand)- go_arg bg@(MkB bg_top bg_inner) (ty, str_mark, dmd@(n :* _))- = case wantToUnboxArg False fam_envs ty dmd of- StopUnboxing- | not is_bot_fn- -- If bot: Keep deep boxity even though WW won't unbox- -- See Note [Boxity for bottoming functions]- -> (MkB (bg_top-1) bg_inner, trimBoxity dmd)-- Unbox DataConPatContext{dcpc_dc=dc, dcpc_tc_args=tc_args} dmds- -> (MkB (bg_top-1) final_bg_inner, final_dmd)- where- dc_arity = dataConRepArity dc- arg_tys = dubiousDataConInstArgTys dc tc_args- (bg_inner', dmds') = go_args (incTopBudget bg_inner) $- zip3 arg_tys (dataConRepStrictness dc) dmds- dmd' = n :* (mkProd Unboxed $! dmds')- (final_bg_inner, final_dmd)- | dmds `lengthIs` dc_arity- , isStrict n || isMarkedStrict str_mark- -- isStrict: see Note [No lazy, Unboxed demands in demand signature]- -- isMarkedStrict: see Note [Unboxing evaluated arguments]- , positiveTopBudget bg_inner'- , NonRecursiveOrUnsure <- ae_rec_dc env dc- -- See Note [Which types are unboxed?]- -- and Note [Demand analysis for recursive data constructors]- = (bg_inner', dmd')- | otherwise- = (bg_inner, trimBoxity dmd)- _ -> (bg, dmd)-- add_demands :: [Demand] -> CoreExpr -> CoreExpr- -- Attach the demands to the outer lambdas of this expression- add_demands [] e = e- add_demands (dmd:dmds) (Lam v e)- | isTyVar v = Lam v (add_demands (dmd:dmds) e)- | otherwise = Lam (v `setIdDemandInfo` dmd) (add_demands dmds e)- add_demands dmds e = pprPanic "add_demands" (ppr dmds $$ ppr e)--finaliseLetBoxity- :: FamInstEnvs- -> Type -- ^ Type of the let-bound Id- -> Demand -- ^ How the Id is used- -> Demand--- See Note [Finalising boxity for let-bound Ids]--- This function is like finaliseArgBoxities, but much simpler because--- it has no "budget". It simply unboxes strict demands, and stops--- when it reaches a lazy one.-finaliseLetBoxity env ty dmd- = go ty NotMarkedStrict dmd- where- go ty mark dmd@(n :* _) =- case wantToUnboxArg False env ty dmd of- DropAbsent -> dmd- StopUnboxing -> trimBoxity dmd- Unbox DataConPatContext{dcpc_dc=dc, dcpc_tc_args=tc_args} dmds- | isStrict n || isMarkedStrict mark- , dmds `lengthIs` dataConRepArity dc- , let arg_tys = dubiousDataConInstArgTys dc tc_args- dmds' = strictZipWith3 go arg_tys (dataConRepStrictness dc) dmds- -> n :* (mkProd Unboxed $! dmds')- | otherwise- -> trimBoxity dmd- Unlift -> panic "No unlifting in DmdAnal"---{- *********************************************************************-* *- Fixpoints-* *-********************************************************************* -}---- Recursive bindings-dmdFix :: TopLevelFlag- -> AnalEnv -- Does not include bindings for this binding- -> SubDemand- -> [(Id,CoreExpr)]- -> (AnalEnv, WeakDmds, [(Id,CoreExpr)]) -- Binders annotated with strictness info-dmdFix top_lvl env let_dmd orig_pairs- = loop 1 initial_pairs- where- -- See Note [Initialising strictness]- initial_pairs | ae_virgin env = [(setIdDmdSig id botSig, rhs) | (id, rhs) <- orig_pairs ]- | otherwise = orig_pairs-- -- If fixed-point iteration does not yield a result we use this instead- -- See Note [Safe abortion in the fixed-point iteration]- abort :: (AnalEnv, WeakDmds, [(Id,CoreExpr)])- abort = (env, weak_fv', zapped_pairs)- where (weak_fv, pairs') = step True (zapIdDmdSig orig_pairs)- -- Note [Lazy and unleashable free variables]- weak_fvs = plusVarEnvList $ map (de_fvs . dmdSigDmdEnv . idDmdSig . fst) pairs'- weak_fv' = plusVarEnv_C plusDmd weak_fv $ mapVarEnv (const topDmd) weak_fvs- zapped_pairs = zapIdDmdSig pairs'-- -- The fixed-point varies the idDmdSig field of the binders, and terminates if that- -- annotation does not change any more.- loop :: Int -> [(Id,CoreExpr)] -> (AnalEnv, WeakDmds, [(Id,CoreExpr)])- loop n pairs = -- pprTrace "dmdFix" (ppr n <+> vcat [ ppr id <+> ppr (idDmdSig id)- -- | (id,_)<- pairs]) $- loop' n pairs-- loop' n pairs- | found_fixpoint = (final_anal_env, weak_fv, pairs')- | n == 10 = abort- | otherwise = loop (n+1) pairs'- where- found_fixpoint = map (idDmdSig . fst) pairs' == map (idDmdSig . fst) pairs- first_round = n == 1- (weak_fv, pairs') = step first_round pairs- final_anal_env = extendAnalEnvs top_lvl env (map fst pairs')-- step :: Bool -> [(Id, CoreExpr)] -> (WeakDmds, [(Id, CoreExpr)])- step first_round pairs = (weak_fv, pairs')- where- -- In all but the first iteration, delete the virgin flag- start_env | first_round = env- | otherwise = nonVirgin env-- start = (extendAnalEnvs top_lvl start_env (map fst pairs), emptyVarEnv)-- !((_,!weak_fv), !pairs') = mapAccumL my_downRhs start pairs- -- mapAccumL: Use the new signature to do the next pair- -- The occurrence analyser has arranged them in a good order- -- so this can significantly reduce the number of iterations needed-- my_downRhs (env, weak_fv) (id,rhs)- = -- pprTrace "my_downRhs" (ppr id $$ ppr (idDmdSig id) $$ ppr sig) $- ((env', weak_fv'), (id', rhs'))- where- !(!env', !weak_fv1, !id', !rhs') = dmdAnalRhsSig top_lvl Recursive env let_dmd id rhs- !weak_fv' = plusVarEnv_C plusDmd weak_fv weak_fv1-- zapIdDmdSig :: [(Id, CoreExpr)] -> [(Id, CoreExpr)]- zapIdDmdSig pairs = [(setIdDmdSig id nopSig, rhs) | (id, rhs) <- pairs ]--{- Note [Safe abortion in the fixed-point iteration]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Fixed-point iteration may fail to terminate. But we cannot simply give up and-return the environment and code unchanged! We still need to do one additional-round, for two reasons:-- * To get information on used free variables (both lazy and strict!)- (see Note [Lazy and unleashable free variables])- * To ensure that all expressions have been traversed at least once, and any left-over- strictness annotations have been updated.--This final iteration does not add the variables to the strictness signature-environment, which effectively assigns them 'nopSig' (see "getStrictness")--Note [Trimming a demand to a type]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-There are two reasons we sometimes trim a demand to match a type.- 1. GADTs- 2. Recursive products and widening--More on both below. But the botttom line is: we really don't want to-have a binder whose demand is more deeply-nested than its type-"allows". So in findBndrDmd we call trimToType and findTypeShape to-trim the demand on the binder to a form that matches the type--Now to the reasons. For (1) consider- f :: a -> Bool- f x = case ... of- A g1 -> case (x |> g1) of (p,q) -> ...- B -> error "urk"--where A,B are the constructors of a GADT. We'll get a 1P(L,L) demand-on x from the A branch, but that's a stupid demand for x itself, which-has type 'a'. Indeed we get ASSERTs going off (notably in-splitUseProdDmd, #8569).--For (2) consider- data T = MkT Int T -- A recursive product- f :: Int -> T -> Int- f 0 _ = 0- f _ (MkT n t) = f n t--Here f is lazy in T, but its *usage* is infinite: P(L,P(L,P(L, ...))).-Notice that this happens because T is a product type, and is recrusive.-If we are not careful, we'll fail to iterate to a fixpoint in dmdFix,-and bale out entirely, which is inefficient and over-conservative.--Worse, as we discovered in #18304, the size of the usages we compute-can grow /exponentially/, so even 10 iterations costs far too much.-Especially since we then discard the result.--To avoid this we use the same findTypeShape function as for (1), but-arrange that it trims the demand if it encounters the same type constructor-twice (or three times, etc). We use our standard RecTcChecker mechanism-for this -- see GHC.Core.Opt.WorkWrap.Utils.findTypeShape.--This is usually call "widening". We could do it just in dmdFix, but-since are doing this findTypeShape business /anyway/ because of (1),-and it has all the right information to hand, it's extremely-convenient to do it there.---}--{- *********************************************************************-* *- Strictness signatures and types-* *-********************************************************************* -}--noArgsDmdType :: DmdEnv -> DmdType-noArgsDmdType dmd_env = DmdType dmd_env []--coercionDmdEnv :: Coercion -> DmdEnv-coercionDmdEnv co = coercionsDmdEnv [co]--coercionsDmdEnv :: [Coercion] -> DmdEnv-coercionsDmdEnv cos- = mkTermDmdEnv $ mapVarEnv (const topDmd) $ getUniqSet $ coVarsOfCos cos- -- The VarSet from coVarsOfCos is really a VarEnv Var--addVarDmd :: DmdType -> Var -> Demand -> DmdType-addVarDmd (DmdType fv ds) var dmd- = DmdType (addVarDmdEnv fv var dmd) ds--addWeakFVs :: DmdType -> WeakDmds -> DmdType-addWeakFVs dmd_ty weak_fvs- = dmd_ty `plusDmdType` mkTermDmdEnv weak_fvs- -- Using plusDmdType (rather than just plus'ing the envs)- -- is vital. Consider- -- let f = \x -> (x,y)- -- in error (f 3)- -- Here, y is treated as a lazy-fv of f, but we must `plusDmd` that L- -- demand with the bottom coming up from 'error'- --- -- I got a loop in the fixpointer without this, due to an interaction- -- with the weak_fv filtering in dmdAnalRhsSig. Roughly, it was- -- letrec f n x- -- = letrec g y = x `fatbar`- -- letrec h z = z + ...g...- -- in h (f (n-1) x)- -- in ...- -- In the initial iteration for f, f=Bot- -- Suppose h is found to be strict in z, but the occurrence of g in its RHS- -- is lazy. Now consider the fixpoint iteration for g, esp the demands it- -- places on its free variables. Suppose it places none. Then the- -- x `fatbar` ...call to h...- -- will give a x->V demand for x. That turns into a L demand for x,- -- which floats out of the defn for h. Without the modifyEnv, that- -- L demand doesn't get both'd with the Bot coming up from the inner- -- call to f. So we just get an L demand for x for g.--setBndrsDemandInfo :: HasCallStack => [Var] -> [Demand] -> [Var]-setBndrsDemandInfo (b:bs) ds- | isTyVar b = b : setBndrsDemandInfo bs ds-setBndrsDemandInfo (b:bs) (d:ds) =- let !new_info = setIdDemandInfo b d- !vars = setBndrsDemandInfo bs ds- in new_info : vars-setBndrsDemandInfo [] ds = assert (null ds) []-setBndrsDemandInfo bs _ = pprPanic "setBndrsDemandInfo" (ppr bs)--annotateLamIdBndr :: AnalEnv- -> DmdType -- Demand type of body- -> Id -- Lambda binder- -> WithDmdType Id -- Demand type of lambda- -- and binder annotated with demand--annotateLamIdBndr env dmd_ty id--- For lambdas we add the demand to the argument demands--- Only called for Ids- = assert (isId id) $- -- pprTrace "annLamBndr" (vcat [ppr id, ppr dmd_ty, ppr final_ty]) $- WithDmdType main_ty new_id- where- new_id = setIdDemandInfo id dmd- main_ty = addDemand dmd dmd_ty'- WithDmdType dmd_ty' dmd = findBndrDmd env dmd_ty id--{- Note [NOINLINE and strictness]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-At one point we disabled strictness for NOINLINE functions, on the-grounds that they should be entirely opaque. But that lost lots of-useful semantic strictness information, so now we analyse them like-any other function, and pin strictness information on them.--That in turn forces us to worker/wrapper them; see-Note [Worker/wrapper for NOINLINE functions] in GHC.Core.Opt.WorkWrap.---Note [Lazy and unleashable free variables]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We put the strict and once-used FVs in the DmdType of the Id, so-that at its call sites we unleash demands on its strict fvs.-An example is 'roll' in imaginary/wheel-sieve2-Something like this:- roll x = letrec- go y = if ... then roll (x-1) else x+1- in- go ms-We want to see that roll is strict in x, which is because-go is called. So we put the DmdEnv for x in go's DmdType.--Another example:-- f :: Int -> Int -> Int- f x y = let t = x+1- h z = if z==0 then t else- if z==1 then x+1 else- x + h (z-1)- in h y--Calling h does indeed evaluate x, but we can only see-that if we unleash a demand on x at the call site for t.--Incidentally, here's a place where lambda-lifting h would-lose the cigar --- we couldn't see the joint strictness in t/x-- ON THE OTHER HAND--We don't want to put *all* the fv's from the RHS into the-DmdType. Because-- * it makes the strictness signatures larger, and hence slows down fixpointing--and-- * it is useless information at the call site anyways:- For lazy, used-many times fv's we will never get any better result than- that, no matter how good the actual demand on the function at the call site- is (unless it is always absent, but then the whole binder is useless).--Therefore we exclude lazy multiple-used fv's from the environment in the-DmdType.--But now the signature lies! (Missing variables are assumed to be absent.) To-make up for this, the code that analyses the binding keeps the demand on those-variable separate (usually called "weak_fv") and adds it to the demand of the-whole binding later.--What if we decide _not_ to store a strictness signature for a binding at all, as-we do when aborting a fixed-point iteration? The we risk losing the information-that the strict variables are being used. In that case, we take all free variables-mentioned in the (unsound) strictness signature, conservatively approximate the-demand put on them (topDmd), and add that to the "weak_fv" returned by "dmdFix".---************************************************************************-* *-\subsection{Strictness signatures}-* *-************************************************************************--}---data AnalEnv = AE- { ae_opts :: !DmdAnalOpts- -- ^ Analysis options- , ae_sigs :: !SigEnv- , ae_virgin :: !Bool- -- ^ True on first iteration only. See Note [Initialising strictness]- , ae_fam_envs :: !FamInstEnvs- , ae_rec_dc :: DataCon -> IsRecDataConResult- -- ^ Memoised result of 'GHC.Core.Opt.WorkWrap.Utils.isRecDataCon'- }-- -- We use the se_env to tell us whether to- -- record info about a variable in the DmdEnv- -- We do so if it's a LocalId, but not top-level- --- -- The DmdEnv gives the demand on the free vars of the function- -- when it is given enough args to satisfy the strictness signature--type SigEnv = VarEnv (DmdSig, TopLevelFlag)--instance Outputable AnalEnv where- ppr env = text "AE" <+> braces (vcat- [ text "ae_virgin =" <+> ppr (ae_virgin env)- , text "ae_sigs =" <+> ppr (ae_sigs env)- ])--emptyAnalEnv :: DmdAnalOpts -> FamInstEnvs -> AnalEnv-emptyAnalEnv opts fam_envs- = AE { ae_opts = opts- , ae_sigs = emptySigEnv- , ae_virgin = True- , ae_fam_envs = fam_envs- , ae_rec_dc = memoiseUniqueFun (isRecDataCon fam_envs 3)- }---- | Unset the 'dmd_strict_dicts' flag if any of the given bindings is a DFun--- binding. Part of the mechanism that detects--- Note [Do not strictify a DFun's parameter dictionaries].-enterDFun :: CoreBind -> AnalEnv -> AnalEnv-enterDFun bind env- | any isDFunId (bindersOf bind)- = env { ae_opts = (ae_opts env) { dmd_strict_dicts = False } }- | otherwise- = env--emptySigEnv :: SigEnv-emptySigEnv = emptyVarEnv---- | Extend an environment with the strictness sigs attached to the Ids-extendAnalEnvs :: TopLevelFlag -> AnalEnv -> [Id] -> AnalEnv-extendAnalEnvs top_lvl env vars- = env { ae_sigs = extendSigEnvs top_lvl (ae_sigs env) vars }--extendSigEnvs :: TopLevelFlag -> SigEnv -> [Id] -> SigEnv-extendSigEnvs top_lvl sigs vars- = extendVarEnvList sigs [ (var, (idDmdSig var, top_lvl)) | var <- vars]--extendAnalEnv :: TopLevelFlag -> AnalEnv -> Id -> DmdSig -> AnalEnv-extendAnalEnv top_lvl env var sig- = env { ae_sigs = extendSigEnv top_lvl (ae_sigs env) var sig }--extendSigEnv :: TopLevelFlag -> SigEnv -> Id -> DmdSig -> SigEnv-extendSigEnv top_lvl sigs var sig = extendVarEnv sigs var (sig, top_lvl)--lookupSigEnv :: AnalEnv -> Id -> Maybe (DmdSig, TopLevelFlag)-lookupSigEnv env id = lookupVarEnv (ae_sigs env) id--addInScopeAnalEnv :: AnalEnv -> Var -> AnalEnv-addInScopeAnalEnv env id = env { ae_sigs = delVarEnv (ae_sigs env) id }--addInScopeAnalEnvs :: AnalEnv -> [Var] -> AnalEnv-addInScopeAnalEnvs env ids = env { ae_sigs = delVarEnvList (ae_sigs env) ids }--nonVirgin :: AnalEnv -> AnalEnv-nonVirgin env = env { ae_virgin = False }--findBndrsDmds :: AnalEnv -> DmdType -> [Var] -> WithDmdType [Demand]--- Return the demands on the Ids in the [Var]-findBndrsDmds env dmd_ty bndrs- = go dmd_ty bndrs- where- go dmd_ty [] = WithDmdType dmd_ty []- go dmd_ty (b:bs)- | isId b = let WithDmdType dmd_ty1 dmds = go dmd_ty bs- WithDmdType dmd_ty2 dmd = findBndrDmd env dmd_ty1 b- in WithDmdType dmd_ty2 (dmd : dmds)- | otherwise = go dmd_ty bs--findBndrDmd :: AnalEnv -> DmdType -> Id -> WithDmdType Demand--- See Note [Trimming a demand to a type]-findBndrDmd env dmd_ty id- = -- pprTrace "findBndrDmd" (ppr id $$ ppr dmd_ty $$ ppr starting_dmd $$ ppr dmd') $- WithDmdType dmd_ty' dmd'- where- dmd' = strictify $- trimToType starting_dmd (findTypeShape fam_envs id_ty)-- (dmd_ty', starting_dmd) = peelFV dmd_ty id-- id_ty = idType id-- strictify dmd- -- See Note [Making dictionary parameters strict]- -- and Note [Do not strictify a DFun's parameter dictionaries]- | dmd_strict_dicts (ae_opts env)- = strictifyDictDmd id_ty dmd- | otherwise- = dmd-- fam_envs = ae_fam_envs env--{- Note [Bringing a new variable into scope]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider- f x = blah- g = ...(\f. ...f...)...--In the body of the '\f', any occurrence of `f` refers to the lambda-bound `f`,-not the top-level `f` (which will be in `ae_sigs`). So it's very important-to delete `f` from `ae_sigs` when we pass a lambda/case/let-up binding of `f`.-Otherwise chaos results (#22718).--Note [Making dictionary parameters strict]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-The Opt_DictsStrict flag makes GHC use call-by-value for dictionaries. Why?--* Generally CBV is more efficient.--* A datatype dictionary is always non-bottom and never takes much work to- compute. E.g. a DFun from an instance decl always returns a dictionary- record immediately. See DFunUnfolding in CoreSyn.- See also Note [Recursive superclasses] in TcInstDcls.--See #17758 for more background and perf numbers.--Wrinkles:--* A newtype dictionary is *not* always non-bottom. E.g.- class C a where op :: a -> a- instance C Int where op = error "urk"- Now a value of type (C Int) is just a newtype wrapper (a cast) around- the error thunk. Don't strictify these!--* Strictifying DFuns risks destroying the invariant that DFuns never take much- work to compute, so we don't do it.- See Note [Do not strictify a DFun's parameter dictionaries] for details.--* Although worker/wrapper *could* unbox strictly used dictionaries, we do not do- so; see Note [Do not unbox class dictionaries].--The implementation is extremly simple: just make the strictness-analyser strictify the demand on a dictionary binder in-'findBndrDmd' if the binder does not belong to a DFun.--Note [Do not strictify a DFun's parameter dictionaries]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-The typechecker can tie recursive knots involving (non-recursive) DFuns, so-we must not strictify a DFun's parameter dictionaries (#22549).-T22549 has an example involving undecidable instances that <<loop>>s when we-strictify the DFun of, e.g., `$fEqSeqT`:-- Main.$fEqSeqT- = \@m @a ($dEq :: Eq (m (ViewT m a))) ($dMonad :: Monad m) ->- GHC.Classes.C:Eq @(SeqT m a) ($c== @m @a $dEq $dMonad)- ($c/= @m @a $dEq $dMonad)-- Rec {- $dEq_a = Main.$fEqSeqT @Identity @Int $dEq_b Main.$fMonadIdentity- $dEq_b = ... $dEq_a ... <another strict context due to DFun>- }--If we make `$fEqSeqT` strict in `$dEq`, we'll collapse the Rec group into a-giant, <<loop>>ing thunk.--To prevent that, we never strictify dictionary params when inside a DFun.-That is implemented by unsetting 'dmd_strict_dicts' when entering a DFun.--See also Note [Speculative evaluation] in GHC.CoreToStg.Prep which has a rather-similar example in #20836. We may never speculate *arguments* of (recursive)-DFun calls, likewise we should not mark *formal parameters* of recursive DFuns-as strict.--Note [Initialising strictness]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-See section 9.2 (Finding fixpoints) of the paper.--Our basic plan is to initialise the strictness of each Id in a-recursive group to "bottom", and find a fixpoint from there. However,-this group B might be inside an *enclosing* recursive group A, in-which case we'll do the entire fixpoint shebang on for each iteration-of A. This can be illustrated by the following example:--Example:-- f [] = []- f (x:xs) = let g [] = f xs- g (y:ys) = y+1 : g ys- in g (h x)--At each iteration of the fixpoint for f, the analyser has to find a-fixpoint for the enclosed function g. In the meantime, the demand-values for g at each iteration for f are *greater* than those we-encountered in the previous iteration for f. Therefore, we can begin-the fixpoint for g not with the bottom value but rather with the-result of the previous analysis. I.e., when beginning the fixpoint-process for g, we can start from the demand signature computed for g-previously and attached to the binding occurrence of g.--To speed things up, we initialise each iteration of A (the enclosing-one) from the result of the last one, which is neatly recorded in each-binder. That way we make use of earlier iterations of the fixpoint-algorithm. (Cunning plan.)--But on the *first* iteration we want to *ignore* the current strictness-of the Id, and start from "bottom". Nowadays the Id can have a current-strictness, because interface files record strictness for nested bindings.-To know when we are in the first iteration, we look at the ae_virgin-field of the AnalEnv.---Note [Final Demand Analyser run]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Some of the information that the demand analyser determines is not always-preserved by the simplifier. For example, the simplifier will happily rewrite- \y [Demand=MU] let x = y in x + x-to- \y [Demand=MU] y + y-which is quite a lie: Now y occurs more than just once.--The once-used information is (currently) only used by the code-generator, though. So:-- * We zap the used-once info in the worker-wrapper;- see Note [Zapping Used Once info in WorkWrap] in- GHC.Core.Opt.WorkWrap.- If it's not reliable, it's better not to have it at all.-- * Just before TidyCore, we add a pass of the demand analyser,- but WITHOUT subsequent worker/wrapper and simplifier,- right before TidyCore. See SimplCore.getCoreToDo.-- This way, correct information finds its way into the module interface- (strictness signatures!) and the code generator (single-entry thunks!)--Note that, in contrast, the single-call information (CM(..)) /can/ be+import GHC.Core.Predicate( isClassPred )+import GHC.Core.FVs ( rulesRhsFreeIds, bndrRuleAndUnfoldingIds )+import GHC.Core.Coercion ( Coercion )+import GHC.Core.TyCo.FVs ( coVarsOfCos )+import GHC.Core.TyCo.Compare ( eqType )+import GHC.Core.FamInstEnv+import GHC.Core.Opt.Arity ( typeArity )+import GHC.Utils.Misc+import GHC.Utils.Panic+import GHC.Utils.Panic.Plain+import GHC.Data.Maybe+import GHC.Builtin.PrimOps+import GHC.Builtin.Types.Prim ( realWorldStatePrimTy )+import GHC.Types.Unique.Set+import GHC.Types.Unique.MemoFun+import GHC.Types.RepType+++{-+************************************************************************+* *+\subsection{Top level stuff}+* *+************************************************************************+-}++-- | Options for the demand analysis+data DmdAnalOpts = DmdAnalOpts+ { dmd_strict_dicts :: !Bool+ -- ^ Value of `-fdicts-strict` (on by default).+ -- When set, all functons are implicitly strict in dictionary args.+ , dmd_do_boxity :: !Bool+ -- ^ Governs whether the analysis should update boxity signatures.+ -- See Note [Don't change boxity without worker/wrapper].+ , dmd_unbox_width :: !Int+ -- ^ Value of `-fdmd-unbox-width`.+ -- See Note [Unboxed demand on function bodies returning small products]+ , dmd_max_worker_args :: !Int+ -- ^ Value of `-fmax-worker-args`.+ -- Don't unbox anything if we end up with more than this many args.+ }++-- This is a strict alternative to (,)+-- See Note [Space Leaks in Demand Analysis]+data WithDmdType a = WithDmdType !DmdType !a++getAnnotated :: WithDmdType a -> a+getAnnotated (WithDmdType _ a) = a++data DmdResult a b = R !a !b++-- | Outputs a new copy of the Core program in which binders have been annotated+-- with demand and strictness information.+--+-- Note: use `seqBinds` on the result to avoid leaks due to lazyness (cf Note+-- [Stamp out space leaks in demand analysis])+dmdAnalProgram :: DmdAnalOpts -> FamInstEnvs -> [CoreRule] -> CoreProgram -> CoreProgram+dmdAnalProgram opts fam_envs rules binds+ = getAnnotated $ go (emptyAnalEnv opts fam_envs) binds+ where+ -- See Note [Analysing top-level bindings]+ -- and Note [Why care for top-level demand annotations?]+ go _ [] = WithDmdType nopDmdType []+ go env (b:bs) = cons_up $ dmdAnalBind TopLevel env topSubDmd b anal_body+ where+ anal_body env'+ | WithDmdType body_ty bs' <- go env' bs+ = WithDmdType (add_exported_uses env' body_ty (bindersOf b)) bs'++ cons_up :: WithDmdType (DmdResult b [b]) -> WithDmdType [b]+ cons_up (WithDmdType dmd_ty (R b' bs')) = WithDmdType dmd_ty (b' : bs')++ add_exported_uses :: AnalEnv -> DmdType -> [Id] -> DmdType+ add_exported_uses env = foldl' (add_exported_use env)++ -- If @e@ is denoted by @dmd_ty@, then @add_exported_use _ dmd_ty id@+ -- corresponds to the demand type of @(id, e)@, but is a lot more direct.+ -- See Note [Analysing top-level bindings].+ add_exported_use :: AnalEnv -> DmdType -> Id -> DmdType+ add_exported_use env dmd_ty id+ | isExportedId id || elemVarSet id rule_fvs+ -- See Note [Absence analysis for stable unfoldings and RULES]+ = dmd_ty `plusDmdType` fst (dmdAnalStar env topDmd (Var id))+ | otherwise+ = dmd_ty++ rule_fvs :: IdSet+ rule_fvs = rulesRhsFreeIds rules++-- | We attach useful (e.g. not 'topDmd') 'idDemandInfo' to top-level bindings+-- that satisfy this function.+--+-- Basically, we want to know how top-level *functions* are *used*+-- (e.g. called). The information will always be lazy.+-- Any other top-level bindings are boring.+--+-- See also Note [Why care for top-level demand annotations?].+isInterestingTopLevelFn :: Id -> Bool+-- SG tried to set this to True and got a +2% ghc/alloc regression in T5642+-- (which is dominated by the Simplifier) at no gain in analysis precision.+-- If there was a gain, that regression might be acceptable.+-- Plus, we could use LetUp for thunks and share some code with local let+-- bindings.+isInterestingTopLevelFn id = typeArity (idType id) > 0++{- Note [Stamp out space leaks in demand analysis]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+The demand analysis pass outputs a new copy of the Core program in+which binders have been annotated with demand and strictness+information. It's tiresome to ensure that this information is fully+evaluated everywhere that we produce it, so we just run a single+seqBinds over the output before returning it, to ensure that there are+no references holding on to the input Core program.++This makes a ~30% reduction in peak memory usage when compiling+DynFlags (cf #9675 and #13426).++This is particularly important when we are doing late demand analysis,+since we don't do a seqBinds at any point thereafter. Hence code+generation would hold on to an extra copy of the Core program, via+unforced thunks in demand or strictness information; and it is the+most memory-intensive part of the compilation process, so this added+seqBinds makes a big difference in peak memory usage.++Note [Don't change boxity without worker/wrapper]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider (T21754)+ f n = n+1+ {-# NOINLINE f #-}+With `-fno-worker-wrapper`, we should not give `f` a boxity signature that says+that it unboxes its argument! Client modules would never be able to cancel away+the box for n. Likewise we shouldn't give `f` the CPR property.++Similarly, in the last run of DmdAnal before codegen (which does not have a+worker/wrapper phase) we should not change boxity in any way. Remember: an+earlier result of the demand analyser, complete with worker/wrapper, has aleady+given a demand signature (with boxity info) to the function.+(The "last run" is mainly there to attach demanded-once info to let-bindings.)++In general, we should not run Note [Boxity analysis] unless worker/wrapper+follows to exploit the boxity and make sure that calling modules can observe the+reported boxity.++Hence DmdAnal is configured by a flag `dmd_do_boxity` that is True only+if worker/wrapper follows after DmdAnal. If it is not set, and the signature+is not subject to Note [Boxity for bottoming functions], DmdAnal tries+to transfer over the previous boxity to the new demand signature, in+`setIdDmdAndBoxSig`.++Why isn't CprAnal configured with a similar flag? Because if we aren't going to+do worker/wrapper we don't run CPR analysis at all. (see GHC.Core.Opt.Pipeline)++It might be surprising that we only try to preserve *arg* boxity, not boxity on+FVs. But FV demands won't make it into interface files anyway, so it's a waste+of energy.+Besides, W/W zaps the `DmdEnv` portion of a signature, so we don't know the old+boxity to begin with; see Note [Zapping DmdEnv after Demand Analyzer].++Note [Analysing top-level bindings]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider a CoreProgram like+ e1 = ...+ n1 = ...+ e2 = \a b -> ... fst (n1 a b) ...+ n2 = \c d -> ... snd (e2 c d) ...+ ...+where e* are exported, but n* are not.+Intuitively, we can see that @n1@ is only ever called with two arguments+and in every call site, the first component of the result of the call+is evaluated. Thus, we'd like it to have idDemandInfo @LC(L,C(M,P(1L,A))@.+NB: We may *not* give e2 a similar annotation, because it is exported and+external callers might use it in arbitrary ways, expressed by 'topDmd'.+This can then be exploited by Nested CPR and eta-expansion,+see Note [Why care for top-level demand annotations?].++How do we get this result? Answer: By analysing the program as if it was a let+expression of this form:+ let e1 = ... in+ let n1 = ... in+ let e2 = ... in+ let n2 = ... in+ (e1,e2, ...)+E.g. putting all bindings in nested lets and returning all exported binders in a tuple.+Of course, we will not actually build that CoreExpr! Instead we faithfully+simulate analysis of said expression by adding the free variable 'DmdEnv'+of @e*@'s strictness signatures to the 'DmdType' we get from analysing the+nested bindings.++And even then the above form blows up analysis performance in T10370:+If @e1@ uses many free variables, we'll unnecessarily carry their demands around+with us from the moment we analyse the pair to the moment we bubble back up to+the binding for @e1@. So instead we analyse as if we had+ let e1 = ... in+ (e1, let n1 = ... in+ ( let e2 = ... in+ (e2, let n2 = ... in+ ( ...))))+That is, a series of right-nested pairs, where the @fst@ are the exported+binders of the last enclosing let binding and @snd@ continues the nested+lets.++Variables occurring free in RULE RHSs are to be handled the same as exported Ids.+See also Note [Absence analysis for stable unfoldings and RULES].++Note [Why care for top-level demand annotations?]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Reading Note [Analysing top-level bindings], you might think that we go through+quite some trouble to get useful demands for top-level bindings. They can never+be strict, for example, so why bother?++First, we get to eta-expand top-level bindings that we weren't able to+eta-expand before without Call Arity. From T18894b:+ module T18894b (f) where+ eta :: Int -> Int -> Int+ eta x = if fst (expensive x) == 13 then \y -> ... else \y -> ...+ f m = ... eta m 2 ... eta 2 m ...+Since only @f@ is exported, we see all call sites of @eta@ and can eta-expand to+arity 2.++The call demands we get for some top-level bindings will also allow Nested CPR+to unbox deeper. From T18894:+ module T18894 (h) where+ g m n = (2 * m, 2 `div` n)+ {-# NOINLINE g #-}+ h :: Int -> Int+ h m = ... snd (g m 2) ... uncurry (+) (g 2 m) ...+Only @h@ is exported, hence we see that @g@ is always called in contexts were we+also force the division in the second component of the pair returned by @g@.+This allows Nested CPR to evaluate the division eagerly and return an I# in its+position.+-}++{-+************************************************************************+* *+\subsection{The analyser itself}+* *+************************************************************************+-}++-- | Analyse a binding group and its \"body\", e.g. where it is in scope.+--+-- It calls a function that knows how to analyse this \"body\" given+-- an 'AnalEnv' with updated demand signatures for the binding group+-- (reflecting their 'idDmdSigInfo') and expects to receive a+-- 'DmdType' in return, which it uses to annotate the binding group with their+-- 'idDemandInfo'.+dmdAnalBind+ :: TopLevelFlag+ -> AnalEnv+ -> SubDemand -- ^ Demand put on the "body"+ -- (important for join points)+ -> CoreBind+ -> (AnalEnv -> WithDmdType a) -- ^ How to analyse the "body", e.g.+ -- where the binding is in scope+ -> WithDmdType (DmdResult CoreBind a)+dmdAnalBind top_lvl env dmd bind anal_body = case bind of+ NonRec id rhs+ | useLetUp top_lvl id+ -> dmdAnalBindLetUp top_lvl env_rhs id rhs anal_body+ _ -> dmdAnalBindLetDown top_lvl env_rhs dmd bind anal_body+ where+ env_rhs = enterDFun bind env++-- | Annotates uninteresting top level functions ('isInterestingTopLevelFn')+-- with 'topDmd', the rest with the given demand.+setBindIdDemandInfo :: TopLevelFlag -> Id -> Demand -> Id+setBindIdDemandInfo top_lvl id dmd = setIdDemandInfo id $ case top_lvl of+ TopLevel | not (isInterestingTopLevelFn id) -> topDmd+ _ -> dmd++-- | Update the demand signature, but be careful not to change boxity info if+-- `dmd_do_boxity` is True or if the signature is bottom.+-- See Note [Don't change boxity without worker/wrapper]+-- and Note [Boxity for bottoming functions].+setIdDmdAndBoxSig :: DmdAnalOpts -> Id -> DmdSig -> Id+setIdDmdAndBoxSig opts id sig = setIdDmdSig id $+ if dmd_do_boxity opts || isBottomingSig sig+ then sig+ else transferArgBoxityDmdSig (idDmdSig id) sig++-- | Let bindings can be processed in two ways:+-- Down (RHS before body) or Up (body before RHS).+-- This function handles the up variant.+--+-- It is very simple. For let x = rhs in body+-- * Demand-analyse 'body' in the current environment+-- * Find the demand, 'rhs_dmd' placed on 'x' by 'body'+-- * Demand-analyse 'rhs' in 'rhs_dmd'+--+-- This is used for a non-recursive local let without manifest lambdas (see+-- 'useLetUp').+--+-- This is the LetUp rule in the paper “Higher-Order Cardinality Analysis”.+dmdAnalBindLetUp :: TopLevelFlag+ -> AnalEnv+ -> Id+ -> CoreExpr+ -> (AnalEnv -> WithDmdType a)+ -> WithDmdType (DmdResult CoreBind a)+dmdAnalBindLetUp top_lvl env id rhs anal_body = WithDmdType final_ty (R (NonRec id' rhs') (body'))+ where+ WithDmdType body_ty body' = anal_body (addInScopeAnalEnv env id)+ -- See Note [Bringing a new variable into scope]+ WithDmdType body_ty' id_dmd = findBndrDmd env body_ty id+ -- See Note [Finalising boxity for demand signatures]++ id_dmd' = finaliseLetBoxity env (idType id) id_dmd+ !id' = setBindIdDemandInfo top_lvl id id_dmd'+ (rhs_ty, rhs') = dmdAnalStar env id_dmd' rhs++ -- See Note [Absence analysis for stable unfoldings and RULES]+ rule_fvs = bndrRuleAndUnfoldingIds id+ final_ty = body_ty' `plusDmdType` rhs_ty `keepAliveDmdType` rule_fvs++-- | Let bindings can be processed in two ways:+-- Down (RHS before body) or Up (body before RHS).+-- This function handles the down variant.+--+-- It computes a demand signature (by means of 'dmdAnalRhsSig') and uses+-- that at call sites in the body.+--+-- It is used for toplevel definitions, recursive definitions and local+-- non-recursive definitions that have manifest lambdas (cf. 'useLetUp').+-- Local non-recursive definitions without a lambda are handled with LetUp.+--+-- This is the LetDown rule in the paper “Higher-Order Cardinality Analysis”.+dmdAnalBindLetDown :: TopLevelFlag -> AnalEnv -> SubDemand -> CoreBind -> (AnalEnv -> WithDmdType a) -> WithDmdType (DmdResult CoreBind a)+dmdAnalBindLetDown top_lvl env dmd bind anal_body = case bind of+ NonRec id rhs+ | (env', lazy_fv, id1, rhs1) <-+ dmdAnalRhsSig top_lvl NonRecursive env dmd id rhs+ -> do_rest env' lazy_fv [(id1, rhs1)] (uncurry NonRec . only)+ Rec pairs+ | (env', lazy_fv, pairs') <- dmdFix top_lvl env dmd pairs+ -> do_rest env' lazy_fv pairs' Rec+ where+ do_rest env' lazy_fv pairs1 build_bind = WithDmdType final_ty (R (build_bind pairs2) body')+ where+ WithDmdType body_ty body' = anal_body env'+ -- see Note [Lazy and unleashable free variables]+ dmd_ty = addLazyFVs body_ty lazy_fv+ WithDmdType final_ty id_dmds = findBndrsDmds env' dmd_ty (strictMap fst pairs1)+ -- Important to force this as build_bind might not force it.+ !pairs2 = strictZipWith do_one pairs1 id_dmds+ do_one (id', rhs') dmd = ((,) $! setBindIdDemandInfo top_lvl id' dmd) $! rhs'+ -- If the actual demand is better than the vanilla call+ -- demand, you might think that we might do better to re-analyse+ -- the RHS with the stronger demand.+ -- But (a) That seldom happens, because it means that *every* path in+ -- the body of the let has to use that stronger demand+ -- (b) It often happens temporarily in when fixpointing, because+ -- the recursive function at first seems to place a massive demand.+ -- But we don't want to go to extra work when the function will+ -- probably iterate to something less demanding.+ -- In practice, all the times the actual demand on id2 is more than+ -- the vanilla call demand seem to be due to (b). So we don't+ -- bother to re-analyse the RHS.++-- | Mimic the effect of 'GHC.Core.Prep.mkFloat', turning non-trivial argument+-- expressions/RHSs into a proper let-bound thunk (lifted) or a case (with+-- unlifted scrutinee).+anticipateANF :: CoreExpr -> Card -> Card+anticipateANF e n+ | exprIsTrivial e = n -- trivial expr won't have a binding+ | Just Unlifted <- typeLevity_maybe (exprType e)+ , not (isAbs n && exprOkForSpeculation e) = case_bind n+ | otherwise = let_bind n+ where+ case_bind _ = C_11 -- evaluated exactly once+ let_bind = oneifyCard -- evaluated at most once++-- Do not process absent demands+-- Otherwise act like in a normal demand analysis+-- See ↦* relation in the Cardinality Analysis paper+dmdAnalStar :: AnalEnv+ -> Demand -- This one takes a *Demand*+ -> CoreExpr+ -> (PlusDmdArg, CoreExpr)+dmdAnalStar env (n :* sd) e+ -- NB: (:*) expands AbsDmd and BotDmd as needed+ | WithDmdType dmd_ty e' <- dmdAnal env sd e+ , n' <- anticipateANF e n+ -- See Note [Anticipating ANF in demand analysis]+ -- and Note [Analysing with absent demand]+ = (toPlusDmdArg $ multDmdType n' dmd_ty, e')++-- Main Demand Analysis machinery+dmdAnal, dmdAnal' :: AnalEnv+ -> SubDemand -- The main one takes a *SubDemand*+ -> CoreExpr -> WithDmdType CoreExpr++dmdAnal env d e = -- pprTrace "dmdAnal" (ppr d <+> ppr e) $+ dmdAnal' env d e++dmdAnal' _ _ (Lit lit) = WithDmdType nopDmdType (Lit lit)+dmdAnal' _ _ (Type ty) = WithDmdType nopDmdType (Type ty) -- Doesn't happen, in fact+dmdAnal' _ _ (Coercion co)+ = WithDmdType (unitDmdType (coercionDmdEnv co)) (Coercion co)++dmdAnal' env dmd (Var var)+ = WithDmdType (dmdTransform env var dmd) (Var var)++dmdAnal' env dmd (Cast e co)+ = WithDmdType (dmd_ty `plusDmdType` mkPlusDmdArg (coercionDmdEnv co)) (Cast e' co)+ where+ WithDmdType dmd_ty e' = dmdAnal env dmd e++dmdAnal' env dmd (Tick t e)+ = WithDmdType dmd_ty (Tick t e')+ where+ WithDmdType dmd_ty e' = dmdAnal env dmd e++dmdAnal' env dmd (App fun (Type ty))+ = WithDmdType fun_ty (App fun' (Type ty))+ where+ WithDmdType fun_ty fun' = dmdAnal env dmd fun++-- Lots of the other code is there to make this+-- beautiful, compositional, application rule :-)+dmdAnal' env dmd (App fun arg)+ = -- This case handles value arguments (type args handled above)+ -- Crucially, coercions /are/ handled here, because they are+ -- value arguments (#10288)+ let+ call_dmd = mkCalledOnceDmd dmd+ WithDmdType fun_ty fun' = dmdAnal env call_dmd fun+ (arg_dmd, res_ty) = splitDmdTy fun_ty+ (arg_ty, arg') = dmdAnalStar env arg_dmd arg+ in+-- pprTrace "dmdAnal:app" (vcat+-- [ text "dmd =" <+> ppr dmd+-- , text "expr =" <+> ppr (App fun arg)+-- , text "fun dmd_ty =" <+> ppr fun_ty+-- , text "arg dmd =" <+> ppr arg_dmd+-- , text "arg dmd_ty =" <+> ppr arg_ty+-- , text "res dmd_ty =" <+> ppr res_ty+-- , text "overall res dmd_ty =" <+> ppr (res_ty `plusDmdType` arg_ty) ])+ WithDmdType (res_ty `plusDmdType` arg_ty) (App fun' arg')++dmdAnal' env dmd (Lam var body)+ | isTyVar var+ = let+ WithDmdType body_ty body' = dmdAnal (addInScopeAnalEnv env var) dmd body+ -- See Note [Bringing a new variable into scope]+ in+ WithDmdType body_ty (Lam var body')++ | otherwise+ = let (n, body_dmd) = peelCallDmd dmd+ -- body_dmd: a demand to analyze the body++ WithDmdType body_ty body' = dmdAnal (addInScopeAnalEnv env var) body_dmd body+ -- See Note [Bringing a new variable into scope]+ WithDmdType lam_ty var' = annotateLamIdBndr env body_ty var+ new_dmd_type = multDmdType n lam_ty+ in+ WithDmdType new_dmd_type (Lam var' body')++dmdAnal' env dmd (Case scrut case_bndr ty [Alt alt_con bndrs rhs])+ -- Only one alternative.+ -- If it's a DataAlt, it should be the only constructor of the type and we+ -- can consider its field demands when analysing the scrutinee.+ | want_precise_field_dmds alt_con+ = let+ rhs_env = addInScopeAnalEnvs env (case_bndr:bndrs)+ -- See Note [Bringing a new variable into scope]+ WithDmdType rhs_ty rhs' = dmdAnal rhs_env dmd rhs+ WithDmdType alt_ty1 fld_dmds = findBndrsDmds env rhs_ty bndrs+ WithDmdType alt_ty2 case_bndr_dmd = findBndrDmd env alt_ty1 case_bndr+ !case_bndr' = setIdDemandInfo case_bndr case_bndr_dmd++ -- Evaluation cardinality on the case binder is irrelevant and a no-op.+ -- What matters is its nested sub-demand!+ -- NB: If case_bndr_dmd is absDmd, boxity will say Unboxed, which is+ -- what we want, because then `seq` will put a `seqDmd` on its scrut.+ (_ :* case_bndr_sd) = strictifyDmd case_bndr_dmd++ -- Compute demand on the scrutinee+ -- FORCE the result, otherwise thunks will end up retaining the+ -- whole DmdEnv+ !(!bndrs', !scrut_sd)+ | DataAlt _ <- alt_con+ -- See Note [Demand on the scrutinee of a product case]+ , let !scrut_sd = scrutSubDmd case_bndr_sd fld_dmds+ -- See Note [Demand on case-alternative binders]+ , let !fld_dmds' = fieldBndrDmds scrut_sd (length fld_dmds)+ , let !bndrs' = setBndrsDemandInfo bndrs fld_dmds'+ = (bndrs', scrut_sd)+ | otherwise+ -- DEFAULT alts. Simply add demands and discard the evaluation+ -- cardinality, as we evaluate the scrutinee exactly once.+ = assert (null bndrs) (bndrs, case_bndr_sd)++ alt_ty3+ -- See Note [Precise exceptions and strictness analysis] in "GHC.Types.Demand"+ | exprMayThrowPreciseException (ae_fam_envs env) scrut+ = deferAfterPreciseException alt_ty2+ | otherwise+ = alt_ty2++ WithDmdType scrut_ty scrut' = dmdAnal env scrut_sd scrut+ res_ty = alt_ty3 `plusDmdType` toPlusDmdArg scrut_ty+ in+-- pprTrace "dmdAnal:Case1" (vcat [ text "scrut" <+> ppr scrut+-- , text "dmd" <+> ppr dmd+-- , text "case_bndr_dmd" <+> ppr (idDemandInfo case_bndr')+-- , text "scrut_sd" <+> ppr scrut_sd+-- , text "scrut_ty" <+> ppr scrut_ty+-- , text "alt_ty" <+> ppr alt_ty2+-- , text "res_ty" <+> ppr res_ty ]) $+ WithDmdType res_ty (Case scrut' case_bndr' ty [Alt alt_con bndrs' rhs'])+ where+ want_precise_field_dmds (DataAlt dc)+ | Nothing <- tyConSingleAlgDataCon_maybe $ dataConTyCon dc+ = False -- Not a product type, even though this is the+ -- only remaining possible data constructor+ | DefinitelyRecursive <- ae_rec_dc env dc+ = False -- See Note [Demand analysis for recursive data constructors]+ | otherwise+ = True+ want_precise_field_dmds (LitAlt {}) = False -- Like the non-product datacon above+ want_precise_field_dmds DEFAULT = True++dmdAnal' env dmd (Case scrut case_bndr ty alts)+ = let -- Case expression with multiple alternatives+ WithDmdType scrut_ty scrut' = dmdAnal env topSubDmd scrut++ WithDmdType alt_ty1 case_bndr_dmd = findBndrDmd env alt_ty case_bndr+ !case_bndr' = setIdDemandInfo case_bndr case_bndr_dmd+ WithDmdType alt_ty alts' = dmdAnalSumAlts env dmd case_bndr alts++ fam_envs = ae_fam_envs env+ alt_ty2+ -- See Note [Precise exceptions and strictness analysis] in "GHC.Types.Demand"+ | exprMayThrowPreciseException fam_envs scrut+ = deferAfterPreciseException alt_ty1+ | otherwise+ = alt_ty1+ res_ty = alt_ty2 `plusDmdType` toPlusDmdArg scrut_ty++ in+-- pprTrace "dmdAnal:Case2" (vcat [ text "scrut" <+> ppr scrut+-- , text "scrut_ty" <+> ppr scrut_ty+-- , text "alt_ty1" <+> ppr alt_ty1+-- , text "alt_ty2" <+> ppr alt_ty2+-- , text "res_ty" <+> ppr res_ty ]) $+ WithDmdType res_ty (Case scrut' case_bndr' ty alts')++dmdAnal' env dmd (Let bind body)+ = WithDmdType final_ty (Let bind' body')+ where+ !(WithDmdType final_ty (R bind' body')) = dmdAnalBind NotTopLevel env dmd bind go'+ go' !env' = dmdAnal env' dmd body++-- | A simple, syntactic analysis of whether an expression MAY throw a precise+-- exception when evaluated. It's always sound to return 'True'.+-- See Note [Which scrutinees may throw precise exceptions].+exprMayThrowPreciseException :: FamInstEnvs -> CoreExpr -> Bool+exprMayThrowPreciseException envs e+ | not (forcesRealWorld envs (exprType e))+ = False -- 1. in the Note+ | (Var f, _) <- collectArgs e+ , Just op <- isPrimOpId_maybe f+ , op /= RaiseIOOp+ = False -- 2. in the Note+ | (Var f, _) <- collectArgs e+ , Just fcall <- isFCallId_maybe f+ , not (isSafeForeignCall fcall)+ = False -- 3. in the Note+ | otherwise+ = True -- _. in the Note++-- | Recognises types that are+-- * @State# RealWorld@+-- * Unboxed tuples with a @State# RealWorld@ field+-- modulo coercions. This will detect 'IO' actions (even post Nested CPR! See+-- T13380e) and user-written variants thereof by their type.+forcesRealWorld :: FamInstEnvs -> Type -> Bool+forcesRealWorld fam_envs ty+ | ty `eqType` realWorldStatePrimTy+ = True+ | Just (tc, tc_args, _co) <- normSplitTyConApp_maybe fam_envs ty+ , isUnboxedTupleTyCon tc+ , let field_tys = dataConInstArgTys (tyConSingleDataCon tc) tc_args+ = any (eqType realWorldStatePrimTy . scaledThing) field_tys+ | otherwise+ = False++dmdAnalSumAlts :: AnalEnv -> SubDemand -> Id -> [CoreAlt] -> WithDmdType [CoreAlt]+dmdAnalSumAlts _ _ _ [] = WithDmdType botDmdType []+ -- Base case is botDmdType, for empty case alternatives+ -- This is a unit for lubDmdType, and the right result+ -- when there really are no alternatives+dmdAnalSumAlts env dmd case_bndr (alt:alts)+ = let+ WithDmdType cur_ty alt' = dmdAnalSumAlt env dmd case_bndr alt+ WithDmdType rest_ty alts' = dmdAnalSumAlts env dmd case_bndr alts+ in WithDmdType (lubDmdType cur_ty rest_ty) (alt':alts')+++dmdAnalSumAlt :: AnalEnv -> SubDemand -> Id -> CoreAlt -> WithDmdType CoreAlt+dmdAnalSumAlt env dmd case_bndr (Alt con bndrs rhs)+ | let rhs_env = addInScopeAnalEnvs env (case_bndr:bndrs)+ -- See Note [Bringing a new variable into scope]+ , WithDmdType rhs_ty rhs' <- dmdAnal rhs_env dmd rhs+ , WithDmdType alt_ty dmds <- findBndrsDmds env rhs_ty bndrs+ , let (_ :* case_bndr_sd) = findIdDemand alt_ty case_bndr+ -- See Note [Demand on case-alternative binders]+ -- we can't use the scrut_sd, because it says 'Prod' and we'll use+ -- topSubDmd anyway for scrutinees of sum types.+ scrut_sd = scrutSubDmd case_bndr_sd dmds+ dmds' = fieldBndrDmds scrut_sd (length dmds)+ -- Do not put a thunk into the Alt+ !new_ids = setBndrsDemandInfo bndrs dmds'+ = -- pprTrace "dmdAnalSumAlt" (ppr con $$ ppr case_bndr $$ ppr dmd $$ ppr alt_ty) $+ WithDmdType alt_ty (Alt con new_ids rhs')++-- See Note [Demand on the scrutinee of a product case]+scrutSubDmd :: SubDemand -> [Demand] -> SubDemand+scrutSubDmd case_sd fld_dmds =+ -- pprTraceWith "scrutSubDmd" (\scrut_sd -> ppr case_sd $$ ppr fld_dmds $$ ppr scrut_sd) $+ case_sd `plusSubDmd` mkProd Unboxed fld_dmds++-- See Note [Demand on case-alternative binders]+fieldBndrDmds :: SubDemand -- on the scrutinee+ -> Arity+ -> [Demand] -- Final demands for the components of the DataCon+fieldBndrDmds scrut_sd n_flds =+ case viewProd n_flds scrut_sd of+ Just (_, ds) -> ds+ Nothing -> replicate n_flds topDmd+ -- Either an arity mismatch or scrut_sd was a call demand.+ -- See Note [Untyped demand on case-alternative binders]++{-+Note [Anticipating ANF in demand analysis]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+When analysing non-complex (e.g., trivial) thunks and complex function+arguments, we have to pretend that the expression is really in administrative+normal form (ANF), the conversion to which is done by CorePrep.++Consider+```+f x = let y = x |> co in y `seq` y `seq` ()+```+E.g., 'y' is a let-binding with a trivial RHS. That may occur if 'y' can't be+inlined, for example. Now, is 'x' used once? It may appear as if that is the+case, since its only occurrence is in 'y's memoised RHS. But actually, CorePrep+will *not* allocate a thunk for 'y', because it is trivial and could just+re-use the memoisation mechanism of 'x'! By saying that 'x' is used once it+becomes a single-entry thunk and a call to 'f' will evaluate it twice.+The same applies to trivial arguments, e.g., `f z` really evaluates `z` twice.++So, somewhat counter-intuitively, trivial arguments and let RHSs will *not* be+memoised. On the other hand, evaluation of non-trivial arguments and let RHSs+*will* be memoised. In fact, consider the effect of conversion to ANF on complex+function arguments (as done by 'GHC.Core.Prep.mkFloat'):+```+f2 (g2 x) ===> let y = g2 x in f2 y (if `y` is lifted)+f3 (g3 x) ===> case g3 x of y { __DEFAULT -> f3 y } (if `y` is not lifted)+```+So if a lifted argument like `g2 x` is complex enough, it will be memoised.+Regardless how many times 'f2' evaluates its parameter, the argument will be+evaluated at most once to WHNF.+Similarly, when an unlifted argument like `g3 x` is complex enough, we will+evaluate it *exactly* once to WHNF, no matter how 'f3' evaluates its parameter.++Note that any evaluation beyond WHNF is not affected by memoisation. So this+Note affects the outer 'Card' of a 'Demand', but not its nested 'SubDemand'.+'anticipateANF' predicts the effect of case-binding and let-binding complex+arguments, as well as the lack of memoisation for trivial let RHSs.+In particular, this takes care of the gripes in+Note [Analysing with absent demand] relating to unlifted types.++Note [Analysing with absent demand]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Suppose we analyse an expression with demand A. The "A" means+"absent", so this expression will never be needed. What should happen?+There are several wrinkles:++* We *do* want to analyse the expression regardless.+ Reason: Note [Always analyse in virgin pass]++ But we can post-process the results to ignore all the usage+ demands coming back. This is done by 'multDmdType' with the appropriate+ (absent) evaluation cardinality A or B.++* Nevertheless, which sub-demand should we pick for analysis?+ Since the demand was absent, any would do. Worker/wrapper will replace+ absent bindings with an absent filler anyway, so annotations in the RHS+ of an absent binding don't matter much.+ Picking 'botSubDmd' would be the most useful, but would also look a bit+ misleading in the Core output of DmdAnal, because all nested annotations would+ be bottoming. Better pick 'seqSubDmd', so that we annotate many of those+ nested bindings with A themselves.++* Since we allow unlifted arguments that are not ok-for-speculation,+ we need to be extra careful in the following situation, because unlifted+ values are evaluated even if they are not used. Example from #9254:+ f :: (() -> (# Int#, () #)) -> ()+ -- Strictness signature is+ -- <1C(1,P(A,1L))>+ -- I.e. calls k, but discards first component of result+ f k = case k () of (# _, r #) -> r++ g :: Int -> ()+ g y = f (\n -> (# case y of I# y2 -> y2, n #))++ Here, f's strictness signature says (correctly) that it calls its argument+ function and ignores the first component of its result.++ But in function g, we *will* evaluate the 'case y of ...', because it has type+ Int#. So in the program as written, 'y' will be evaluated. Hence we must+ record this usage of 'y', else 'g' will say 'y' is absent, and will w/w so+ that 'y' is bound to an absent filler (see Note [Absent fillers]), leading+ to a crash when 'y' is evaluated.++ Now, worker/wrapper could be smarter and replace `case y of I# y2 -> y2`+ with a suitable absent filler such as `RUBBISH[IntRep] @Int#`.+ But as long as worker/wrapper isn't equipped to do so, we must be cautious,+ and follow Note [Anticipating ANF in demand analysis]. That is, in+ 'dmdAnalStar', we will set the evaluation cardinality to C_11, anticipating+ the case binding of the complex argument `case y of I# y2 -> y2`. This+ cardinlities' only effect is in the call to 'multDmdType', where it makes sure+ that the demand on the arg's free variable 'y' is not absent and strict, so+ that it is ultimately passed unboxed to 'g'.++Note [Always analyse in virgin pass]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Tricky point: make sure that we analyse in the 'virgin' pass. Consider+ rec { f acc x True = f (...rec { g y = ...g... }...)+ f acc x False = acc }+In the virgin pass for 'f' we'll give 'f' a very strict (bottom) type.+That might mean that we analyse the sub-expression containing the+E = "...rec g..." stuff in a bottom demand. Suppose we *didn't analyse*+E, but just returned botType.++Then in the *next* (non-virgin) iteration for 'f', we might analyse E+in a weaker demand, and that will trigger doing a fixpoint iteration+for g. But *because it's not the virgin pass* we won't start g's+iteration at bottom. Disaster. (This happened in $sfibToList' of+nofib/spectral/fibheaps.)++So in the virgin pass we make sure that we do analyse the expression+at least once, to initialise its signatures.++Note [Which scrutinees may throw precise exceptions]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+This is the specification of 'exprMayThrowPreciseExceptions',+which is important for Scenario 2 of+Note [Precise exceptions and strictness analysis] in GHC.Types.Demand.++For an expression @f a1 ... an :: ty@ we determine that+ 1. False If ty is *not* @State# RealWorld@ or an unboxed tuple thereof.+ This check is done by 'forcesRealWorld'.+ (Why not simply unboxed pairs as above? This is motivated by+ T13380{d,e}.)+ 2. False If f is a PrimOp, and it is *not* raiseIO#+ 3. False If f is an unsafe FFI call ('PlayRisky')+ _. True Otherwise "give up".++It is sound to return False in those cases, because+ 1. We don't give any guarantees for unsafePerformIO, so no precise exceptions+ from pure code.+ 2. raiseIO# is the only primop that may throw a precise exception.+ 3. Unsafe FFI calls may not interact with the RTS (to throw, for example).+ See haddock on GHC.Types.ForeignCall.PlayRisky.++We *need* to return False in those cases, because+ 1. We would lose too much strictness in pure code, all over the place.+ 2. We would lose strictness for primops like getMaskingState#, which+ introduces a substantial regression in+ GHC.IO.Handle.Internals.wantReadableHandle.+ 3. We would lose strictness for code like GHC.Fingerprint.fingerprintData,+ where an intermittent FFI call to c_MD5Init would otherwise lose+ strictness on the arguments len and buf, leading to regressions in T9203+ (2%) and i386's haddock.base (5%). Tested by T13380f.++In !3014 we tried a more sophisticated analysis by introducing ConOrDiv (nic)+to the Divergence lattice, but in practice it turned out to be hard to untaint+from 'topDiv' to 'conDiv', leading to bugs, performance regressions and+complexity that didn't justify the single fixed testcase T13380c.++Note [Demand analysis for recursive data constructors]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+T11545 features a single-product, recursive data type+ data A = A A A ... A+ deriving Eq+Naturally, `(==)` is deeply strict in `A` and in fact will never terminate. That+leads to very large (exponential in the depth) demand signatures and fruitless+churn in boxity analysis, demand analysis and worker/wrapper.++So we detect `A` as a recursive data constructor (see+Note [Detecting recursive data constructors]) analysing `case x of A ...`+and simply assume L for the demand on field binders, which is the same code+path as we take for sum types. This code happens in want_precise_field_dmds+in the Case equation for dmdAnal.++Combined with the B demand on the case binder, we get the very small demand+signature <1S><1S>b on `(==)`. This improves ghc/alloc performance on T11545+tenfold! See also Note [CPR for recursive data constructors] which describes the+sibling mechanism in CPR analysis.++Note [Demand on the scrutinee of a product case]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+When figuring out the demand on the scrutinee of a product case,+we use the demands of the case alternative, i.e. id_dmds.+But note that these include the demand on the case binder;+see Note [Demand on case-alternative binders].+This is crucial. Example:+ f x = case x of y { (a,b) -> k y a }+If we just take scrut_demand = 1P(L,A), then we won't pass x to the+worker, so the worker will rebuild+ x = (a, absent-error)+and that'll crash.++Note [Demand on case-alternative binders]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+The demand on a binder in a case alternative comes+ (a) From the demand on the binder itself+ (b) From the demand on the case binder+Forgetting (b) led directly to #10148.++Example. Source code:+ f x@(p,_) = if p then foo x else True++ foo (p,True) = True+ foo (p,q) = foo (q,p)++After strictness analysis, forgetting (b):+ f = \ (x_an1 [Dmd=1P(1L,ML)] :: (Bool, Bool)) ->+ case x_an1+ of wild_X7 [Dmd=MP(ML,ML)]+ { (p_an2 [Dmd=1L], ds_dnz [Dmd=A]) ->+ case p_an2 of _ {+ False -> GHC.Types.True;+ True -> foo wild_X7 }++Note that ds_dnz is syntactically dead, but the expression bound to it is+reachable through the case binder wild_X7. Now watch what happens if we inline+foo's wrapper:+ f = \ (x_an1 [Dmd=1P(1L,ML)] :: (Bool, Bool)) ->+ case x_an1+ of _ [Dmd=MP(ML,ML)]+ { (p_an2 [Dmd=1L], ds_dnz [Dmd=A]) ->+ case p_an2 of _ {+ False -> GHC.Types.True;+ True -> $wfoo_soq GHC.Types.True ds_dnz }++Look at that! ds_dnz has come back to life in the call to $wfoo_soq! A second+run of demand analysis would no longer infer ds_dnz to be absent.+But unlike occurrence analysis, which infers properties of the *syntactic*+shape of the program, the results of demand analysis describe expressions+*semantically* and are supposed to be mostly stable across Simplification.+That's why we should better account for (b).+In #10148, we ended up emitting a single-entry thunk instead of an updateable+thunk for a let binder that was an an absent case-alt binder during DmdAnal.++This is needed even for non-product types, in case the case-binder+is used but the components of the case alternative are not.++Note [Untyped demand on case-alternative binders]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+With unsafeCoerce, #8037 and #22039 taught us that the demand on the case binder+may be a call demand or have a different number of fields than the constructor+of the case alternative it is used in. From T22039:++ blarg :: (Int, Int) -> Int+ blarg (x,y) = x+y+ -- blarg :: <1!P(1L,1L)>++ f :: Either Int Int -> Int+ f Left{} = 0+ f e = blarg (unsafeCoerce e)+ ==> { desugars to }+ f = \ (ds_d1nV :: Either Int Int) ->+ case ds_d1nV of wild_X1 {+ Left ds_d1oV -> lvl_s1Q6;+ Right ipv_s1Pl ->+ blarg+ (case unsafeEqualityProof @(*) @(Either Int Int) @(Int, Int) of+ { UnsafeRefl co_a1oT ->+ wild_X1 `cast` (Sub (Sym co_a1oT) :: Either Int Int ~R# (Int, Int))+ })+ }++The case binder `e`/`wild_X1` has demand 1!P(1L,1L), with two fields, from the call+to `blarg`, but `Right` only has one field. Although the code will crash when+executed, we must be able to analyse it in 'fieldBndrDmds' and conservatively+approximate with Top instead of panicking because of the mismatch.+In #22039, this kind of code was guarded behind a safe `cast` and thus dead+code, but nevertheless led to a panic of the compiler.++You might wonder why the same problem doesn't come up when scrutinising a+product type instead of a sum type. It appears that for products, `wild_X1`+will be inlined before DmdAnal.++See also Note [mkWWstr and unsafeCoerce] for a related issue.++Note [Aggregated demand for cardinality]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+FIXME: This Note should be named [LetUp vs. LetDown] and probably predates+said separation. SG++We use different strategies for strictness and usage/cardinality to+"unleash" demands captured on free variables by bindings. Let us+consider the example:++f1 y = let {-# NOINLINE h #-}+ h = y+ in (h, h)++We are interested in obtaining cardinality demand U1 on |y|, as it is+used only in a thunk, and, therefore, is not going to be updated any+more. Therefore, the demand on |y|, captured and unleashed by usage of+|h| is U1. However, if we unleash this demand every time |h| is used,+and then sum up the effects, the ultimate demand on |y| will be U1 ++U1 = U. In order to avoid it, we *first* collect the aggregate demand+on |h| in the body of let-expression, and only then apply the demand+transformer:++transf[x](U) = {y |-> U1}++so the resulting demand on |y| is U1.++The situation is, however, different for strictness, where this+aggregating approach exhibits worse results because of the nature of+|both| operation for strictness. Consider the example:++f y c =+ let h x = y |seq| x+ in case of+ True -> h True+ False -> y++It is clear that |f| is strict in |y|, however, the suggested analysis+will infer from the body of |let| that |h| is used lazily (as it is+used in one branch only), therefore lazy demand will be put on its+free variable |y|. Conversely, if the demand on |h| is unleashed right+on the spot, we will get the desired result, namely, that |f| is+strict in |y|.+++************************************************************************+* *+ Demand transformer+* *+************************************************************************+-}++dmdTransform :: AnalEnv -- ^ The analysis environment+ -> Id -- ^ The variable+ -> SubDemand -- ^ The evaluation context of the var+ -> DmdType -- ^ The demand type unleashed by the variable in this+ -- context. The returned DmdEnv includes the demand on+ -- this function plus demand on its free variables+-- See Note [What are demand signatures?] in "GHC.Types.Demand"+dmdTransform env var sd+ -- Data constructors+ | Just con <- isDataConWorkId_maybe var+ = -- pprTraceWith "dmdTransform:DataCon" (\ty -> ppr con $$ ppr sd $$ ppr ty) $+ dmdTransformDataConSig (dataConRepStrictness con) sd+ -- See Note [DmdAnal for DataCon wrappers]+ | isDataConWrapId var, let rhs = uf_tmpl (realIdUnfolding var)+ , WithDmdType dmd_ty _rhs' <- dmdAnal env sd rhs+ = dmd_ty+ -- Dictionary component selectors+ -- Used to be controlled by a flag.+ -- See #18429 for some perf measurements.+ | Just _ <- isClassOpId_maybe var+ = -- pprTrace "dmdTransform:DictSel" (ppr var $$ ppr (idDmdSig var) $$ ppr sd) $+ dmdTransformDictSelSig (idDmdSig var) sd+ -- Imported functions+ | isGlobalId var+ , let res = dmdTransformSig (idDmdSig var) sd+ = -- pprTrace "dmdTransform:import" (vcat [ppr var, ppr (idDmdSig var), ppr sd, ppr res])+ res+ -- Top-level or local let-bound thing for which we use LetDown ('useLetUp').+ -- In that case, we have a strictness signature to unleash in our AnalEnv.+ | Just (sig, top_lvl) <- lookupSigEnv env var+ , let fn_ty = dmdTransformSig sig sd+ = -- pprTrace "dmdTransform:LetDown" (vcat [ppr var, ppr sig, ppr sd, ppr fn_ty]) $+ case top_lvl of+ NotTopLevel -> addVarDmd fn_ty var (C_11 :* sd)+ TopLevel+ | isInterestingTopLevelFn var+ -- Top-level things will be used multiple times or not at+ -- all anyway, hence the multDmd below: It means we don't+ -- have to track whether @var@ is used strictly or at most+ -- once, because ultimately it never will.+ -> addVarDmd fn_ty var (C_0N `multDmd` (C_11 :* sd)) -- discard strictness+ | otherwise+ -> fn_ty -- don't bother tracking; just annotate with 'topDmd' later+ -- Everything else:+ -- * Local let binders for which we use LetUp (cf. 'useLetUp')+ -- * Lambda binders+ -- * Case and constructor field binders+ | otherwise+ = -- pprTrace "dmdTransform:other" (vcat [ppr var, ppr boxity, ppr sd]) $+ unitDmdType (unitVarEnv var (C_11 :* sd))++{- *********************************************************************+* *+ Binding right-hand sides+* *+********************************************************************* -}++-- | @dmdAnalRhsSig@ analyses the given RHS to compute a demand signature+-- for the LetDown rule. It works as follows:+--+-- * assuming the weakest possible body sub-demand, L+-- * looking at the definition+-- * determining a strictness signature+--+-- Since it assumed a body sub-demand of L, the resulting signature is+-- applicable at any call site.+dmdAnalRhsSig+ :: TopLevelFlag+ -> RecFlag+ -> AnalEnv -> SubDemand+ -> Id -> CoreExpr+ -> (AnalEnv, DmdEnv, Id, CoreExpr)+-- Process the RHS of the binding, add the strictness signature+-- to the Id, and augment the environment with the signature as well.+-- See Note [NOINLINE and strictness]+dmdAnalRhsSig top_lvl rec_flag env let_dmd id rhs+ = -- pprTrace "dmdAnalRhsSig" (ppr id $$ ppr let_dmd $$ ppr rhs_dmds $$ ppr sig $$ ppr lazy_fv) $+ (final_env, lazy_fv, final_id, final_rhs)+ where+ threshold_arity = thresholdArity id rhs++ rhs_dmd = mkCalledOnceDmds threshold_arity body_dmd++ body_dmd+ | isJoinId id+ -- See Note [Demand analysis for join points]+ -- See Note [Invariants on join points] invariant 2b, in GHC.Core+ -- threshold_arity matches the join arity of the join point+ -- See Note [Unboxed demand on function bodies returning small products]+ = unboxedWhenSmall env rec_flag (resultType_maybe id) let_dmd+ | otherwise+ -- See Note [Unboxed demand on function bodies returning small products]+ = unboxedWhenSmall env rec_flag (resultType_maybe id) topSubDmd++ WithDmdType rhs_dmd_ty rhs' = dmdAnal env rhs_dmd rhs+ DmdType rhs_fv rhs_dmds rhs_div = rhs_dmd_ty+ -- See Note [Boxity for bottoming functions]+ (final_rhs_dmds, final_rhs) = finaliseArgBoxities env id threshold_arity rhs' rhs_div+ `orElse` (rhs_dmds, rhs')++ sig = mkDmdSigForArity threshold_arity (DmdType sig_fv final_rhs_dmds rhs_div)++ opts = ae_opts env+ final_id = setIdDmdAndBoxSig opts id sig+ !final_env = extendAnalEnv top_lvl env final_id sig++ -- See Note [Aggregated demand for cardinality]+ -- FIXME: That Note doesn't explain the following lines at all. The reason+ -- is really much different: When we have a recursive function, we'd+ -- have to also consider the free vars of the strictness signature+ -- when checking whether we found a fixed-point. That is expensive;+ -- we only want to check whether argument demands of the sig changed.+ -- reuseEnv makes it so that the FV results are stable as long as the+ -- last argument demands were. Strictness won't change. But used-once+ -- might turn into used-many even if the signature was stable and+ -- we'd have to do an additional iteration. reuseEnv makes sure that+ -- we never get used-once info for FVs of recursive functions.+ -- See #14816 where we try to get rid of reuseEnv.+ rhs_fv1 = case rec_flag of+ Recursive -> reuseEnv rhs_fv+ NonRecursive -> rhs_fv++ -- See Note [Absence analysis for stable unfoldings and RULES]+ rhs_fv2 = rhs_fv1 `keepAliveDmdEnv` bndrRuleAndUnfoldingIds id++ -- See Note [Lazy and unleashable free variables]+ !(!lazy_fv, !sig_fv) = partitionVarEnv isWeakDmd rhs_fv2++thresholdArity :: Id -> CoreExpr -> Arity+-- See Note [Demand signatures are computed for a threshold arity based on idArity]+thresholdArity fn rhs+ = case isJoinId_maybe fn of+ Just join_arity -> count isId $ fst $ collectNBinders join_arity rhs+ Nothing -> idArity fn++-- | The result type after applying 'idArity' many arguments. Returns 'Nothing'+-- when the type doesn't have exactly 'idArity' many arrows.+resultType_maybe :: Id -> Maybe Type+resultType_maybe id+ | (pis,ret_ty) <- splitPiTys (idType id)+ , count isAnonPiTyBinder pis == idArity id+ = Just $! ret_ty+ | otherwise+ = Nothing++unboxedWhenSmall :: AnalEnv -> RecFlag -> Maybe Type -> SubDemand -> SubDemand+-- See Note [Unboxed demand on function bodies returning small products]+unboxedWhenSmall _ _ Nothing sd = sd+unboxedWhenSmall env rec_flag (Just ret_ty) sd = go 1 ret_ty sd+ where+ -- Magic constant, bounding the depth of optimistic 'Unboxed' flags. We+ -- might want to minmax in the future.+ max_depth | isRec rec_flag = 3 -- So we get at most something as deep as !P(L!P(L!L))+ | otherwise = 1 -- Otherwise be unbox too deep in T18109, T18174 and others and get a bunch of stack overflows+ go :: Int -> Type -> SubDemand -> SubDemand+ go depth ty sd+ | depth <= max_depth+ , Just (tc, tc_args, _co) <- normSplitTyConApp_maybe (ae_fam_envs env) ty+ , Just dc <- tyConSingleAlgDataCon_maybe tc+ , null (dataConExTyCoVars dc) -- Can't unbox results with existentials+ , dataConRepArity dc <= dmd_unbox_width (ae_opts env)+ , Just (_, ds) <- viewProd (dataConRepArity dc) sd+ , arg_tys <- map scaledThing $ dataConInstArgTys dc tc_args+ , equalLength ds arg_tys+ = mkProd Unboxed $! strictZipWith (go_dmd (depth+1)) arg_tys ds+ | otherwise+ = sd++ go_dmd :: Int -> Type -> Demand -> Demand+ go_dmd depth ty dmd = case dmd of+ AbsDmd -> AbsDmd+ BotDmd -> BotDmd+ n :* sd -> n :* go depth ty sd++-- | If given the (local, non-recursive) let-bound 'Id', 'useLetUp' determines+-- whether we should process the binding up (body before rhs) or down (rhs+-- before body).+--+-- We use LetDown if there is a chance to get a useful strictness signature to+-- unleash at call sites. LetDown is generally more precise than LetUp if we can+-- correctly guess how it will be used in the body, that is, for which incoming+-- demand the strictness signature should be computed, which allows us to+-- unleash higher-order demands on arguments at call sites. This is mostly the+-- case when+--+-- * The binding takes any arguments before performing meaningful work (cf.+-- 'idArity'), in which case we are interested to see how it uses them.+-- * The binding is a join point, hence acting like a function, not a value.+-- As a big plus, we know *precisely* how it will be used in the body; since+-- it's always tail-called, we can directly unleash the incoming demand of+-- the let binding on its RHS when computing a strictness signature. See+-- [Demand analysis for join points].+--+-- Thus, if the binding is not a join point and its arity is 0, we have a thunk+-- and use LetUp, implying that we have no usable demand signature available+-- when we analyse the let body.+--+-- Since thunk evaluation is memoised, we want to unleash its 'DmdEnv' of free+-- vars at most once, regardless of how many times it was forced in the body.+-- This makes a real difference wrt. usage demands. The other reason is being+-- able to unleash a more precise product demand on its RHS once we know how the+-- thunk was used in the let body.+--+-- Characteristic examples, always assuming a single evaluation:+--+-- * @let x = 2*y in x + x@ => LetUp. Compared to LetDown, we find out that+-- the expression uses @y@ at most once.+-- * @let x = (a,b) in fst x@ => LetUp. Compared to LetDown, we find out that+-- @b@ is absent.+-- * @let f x = x*2 in f y@ => LetDown. Compared to LetUp, we find out that+-- the expression uses @y@ strictly, because we have @f@'s demand signature+-- available at the call site.+-- * @join exit = 2*y in if a then exit else if b then exit else 3*y@ =>+-- LetDown. Compared to LetUp, we find out that the expression uses @y@+-- strictly, because we can unleash @exit@'s signature at each call site.+-- * For a more convincing example with join points, see Note [Demand analysis+-- for join points].+--+useLetUp :: TopLevelFlag -> Var -> Bool+useLetUp top_lvl f = isNotTopLevel top_lvl && idArity f == 0 && not (isJoinId f)++{- Note [Demand analysis for join points]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider+ g :: (Int,Int) -> Int+ g (p,q) = p+q++ f :: T -> Int -> Int+ f x p = g (join j y = (p,y)+ in case x of+ A -> j 3+ B -> j 4+ C -> (p,7))++If j was a vanilla function definition, we'd analyse its body with+evalDmd, and think that it was lazy in p. But for join points we can+do better! We know that j's body will (if called at all) be evaluated+with the demand that consumes the entire join-binding, in this case+the argument demand from g. Whizzo! g evaluates both components of+its argument pair, so p will certainly be evaluated if j is called.++For f to be strict in p, we need /all/ paths to evaluate p; in this+case the C branch does so too, so we are fine. So, as usual, we need+to transport demands on free variables to the call site(s). Compare+Note [Lazy and unleashable free variables].++The implementation is easy. When analysing a join point, we can+analyse its body with the demand from the entire join-binding (written+let_dmd here).++Another win for join points! #13543.++However, note that the strictness signature for a join point can+look a little puzzling. E.g.++ (join j x = \y. error "urk")+ (in case v of )+ ( A -> j 3 ) x+ ( B -> j 4 )+ ( C -> \y. blah )++The entire thing is in a C(1,L) context, so j's strictness signature+will be [A]b+meaning one absent argument, returns bottom. That seems odd because+there's a \y inside. But it's right because when consumed in a C(1,L)+context the RHS of the join point is indeed bottom.++Note [Demand signatures are computed for a threshold arity based on idArity]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Given a binding { f = rhs }, we compute a "theshold arity", and do demand+analysis based on a call with that many value arguments.++The threshold we use is++* Ordinary bindings: idArity f.+ Why idArity arguments? Because that's a conservative estimate of how many+ arguments we must feed a function before it does anything interesting with+ them. Also it elegantly subsumes the trivial RHS and PAP case.++ idArity is /at least/ the number of manifest lambdas, but might be higher for+ PAPs and trivial RHS (see Note [Demand analysis for trivial right-hand sides]).++* Join points: the value-binder subset of the JoinArity. This can+ be less than the number of visible lambdas; e.g.+ join j x = \y. blah+ in ...(jump j 2)....(jump j 3)....+ We know that j will never be applied to more than 1 arg (its join+ arity, and we don't eta-expand join points, so here a threshold+ of 1 is the best we can do.++Note that the idArity of a function varies independently of its cardinality+properties (cf. Note [idArity varies independently of dmdTypeDepth]), so we+implicitly encode the arity for when a demand signature is sound to unleash+in its 'dmdTypeDepth', not in its idArity (cf. Note [Understanding DmdType+and DmdSig] in GHC.Types.Demand). It is unsound to unleash a demand+signature when the incoming number of arguments is less than that. See+GHC.Types.Demand Note [What are demand signatures?] for more details on+soundness.++Note that there might, in principle, be functions for which we might want to+analyse for more incoming arguments than idArity. Example:++ f x =+ if expensive+ then \y -> ... y ...+ else \y -> ... y ...++We'd analyse `f` under a unary call demand C(1,L), corresponding to idArity+being 1. That's enough to look under the manifest lambda and find out how a+unary call would use `x`, but not enough to look into the lambdas in the if+branches.++On the other hand, if we analysed for call demand C(1,C(1,L)), we'd get useful+strictness info for `y` (and more precise info on `x`) and possibly CPR+information, but++ * We would no longer be able to unleash the signature at unary call sites++ * Performing the worker/wrapper split based on this information would be+ implicitly eta-expanding `f`, playing fast and loose with divergence and+ even being unsound in the presence of newtypes, so we refrain from doing so.+ Also see Note [Don't eta expand in w/w] in GHC.Core.Opt.WorkWrap.++Since we only compute one signature, we do so for arity 1. Computing multiple+signatures for different arities (i.e., polyvariance) would be entirely+possible, if it weren't for the additional runtime and implementation+complexity.++Note [idArity varies independently of dmdTypeDepth]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+In general, an Id `f` has two independently varying attributes:++* f's idArity, and+* the dmdTypeDepth of f's demand signature++For example, if f's demand signature is <L><L>, f's arity could be+greater than, or less than 2. Why? Because both are conservative+approximations:++* Arity n means "does no expensive work until applied to at least n args"+ (e.g. (f x1..xm) is cheap to bring to HNF for m<n)++* Dmd sig with n args means "here is how to transform the incoming demand+ when applied to n args". This is /semantic/ property, unrelated to+ arity. See GHC.Types.Demand Note [Understanding DmdType and DmdSig]++We used to check in GHC.Core.Lint that dmdTypeDepth <= idArity for a let-bound+identifier. But that means we would have to zap demand signatures every time we+reset or decrease arity.++For example, consider the following expression:++ (let go x y = `x` seq ... in go) |> co++`go` might have a strictness signature of `<1L><L>`. The simplifier will identify+`go` as a nullary join point through `joinPointBinding_maybe` and float the+coercion into the binding, leading to an arity decrease:++ join go = (\x y -> `x` seq ...) |> co in go++With the CoreLint check, we would have to zap `go`'s perfectly viable strictness+signature.++However, in the case of a /bottoming/ signature, f : <L><L>b, we /can/+say that f's arity is no greater than 2, because it'd be false to say+that f does no work when applied to 3 args. Lint checks this constraint,+in `GHC.Core.Lint.lintLetBind`.++Note [Demand analysis for trivial right-hand sides]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider+ foo = plusInt |> co+where plusInt is an arity-2 function with known strictness. Clearly+we want plusInt's strictness to propagate to foo! But because it has+no manifest lambdas, it won't do so automatically, and indeed 'co' might+have type (Int->Int->Int) ~ T.++Fortunately, GHC.Core.Opt.Arity gives 'foo' arity 2, which is enough for LetDown to+forward plusInt's demand signature, and all is well (see Note [Newtype arity] in+GHC.Core.Opt.Arity)! A small example is the test case NewtypeArity.++Note [Absence analysis for stable unfoldings and RULES]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Ticket #18638 shows that it's really important to do absence analysis+for stable unfoldings. Consider++ g = blah++ f = \x. ...no use of g....+ {- f's stable unfolding is f = \x. ...g... -}++If f is ever inlined we use 'g'. But f's current RHS makes no use+of 'g', so if we don't look at the unfolding we'll mark g as Absent,+and transform to++ g = error "Entered absent value"+ f = \x. ...+ {- f's stable unfolding is f = \x. ...g... -}++Now if f is subsequently inlined, we'll use 'g' and ... disaster.++SOLUTION: if f has a stable unfolding, adjust its DmdEnv (the demands+on its free variables) so that no variable mentioned in its unfolding+is Absent. This is done by the function Demand.keepAliveDmdEnv.++ALSO: do the same for Ids free in the RHS of any RULES for f.++PS: You may wonder how it can be that f's optimised RHS has somehow+discarded 'g', but when f is inlined we /don't/ discard g in the same+way. I think a simple example is+ g = (a,b)+ f = \x. fst g+ {-# INLINE f #-}++Now f's optimised RHS will be \x.a, but if we change g to (error "..")+(since it is apparently Absent) and then inline (\x. fst g) we get+disaster. But regardless, #18638 was a more complicated version of+this, that actually happened in practice.++Note [DmdAnal for DataCon wrappers]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+We give DataCon wrappers a (necessarily flat) demand signature in+`GHC.Types.Id.Make.mkDataConRep`, so that passes such as the Simplifier can+exploit it via the call to `GHC.Core.Opt.Simplify.Utils.isStrictArgInfo` in+`GHC.Core.Opt.Simplify.Iteration.rebuildCall`. But during DmdAnal, we *ignore*+the demand signature of a DataCon wrapper, and instead analyse its unfolding at+every call site.++The reason is that DataCon *worker*s have very precise demand transformers,+computed by `dmdTransformDataConSig`. It would be awkward if DataCon *wrappers*+would behave much less precisely during DmdAnal. Example:++ data T1 = MkT1 { get_x1 :: Int, get_y1 :: Int }+ data T2 = MkT2 { get_x2 :: !Int, get_y2 :: Int }+ f1 x y = get_x1 (MkT1 x y)+ f2 x y = get_x2 (MkT2 x y)++Here `MkT1` has no wrapper. `get_x1` puts a demand `!P(1!L,A)` on its argument,+and `dmdTransformDataConSig` will transform that demand to an absent demand on+`y` in `f1` and an unboxing demand on `x`.+But `MkT2` has a wrapper (to evaluate the first field). If demand analysis deals+with `MkT2` only through its demand signature, demand signatures can't transform+an incoming demand `P(1!L,A)` in a useful way, so we won't get an absent demand+on `y` in `f2` or see that `x` can be unboxed. That's a serious loss.++The example above will not actually occur, because $WMkT2 would be inlined.+Nevertheless, we can get interesting sub-demands on DataCon wrapper+applications in boring contexts; see T22241.++You might worry about the efficiency cost of demand-analysing datacon wrappers+at every call site. But in fact they are inlined /anyway/ in the Final phase,+which happens before DmdAnal, so few wrappers remain. And analysing the+unfoldings for the remaining calls (which are those in a boring context) will be+exactly as (in)efficent as if we'd inlined those calls. It turns out to be not+measurable in practice.++See also Note [CPR for DataCon wrappers] in `GHC.Core.Opt.CprAnal`.++Note [Boxity for bottoming functions]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider (A)+ indexError :: Show a => (a, a) -> a -> String -> b+ -- Str=<..><1!P(S,S)><1S><S>b+ indexError rng i s = error (show rng ++ show i ++ show s)++ get :: (Int, Int) -> Int -> [a] -> a+ get p@(l,u) i xs+ | l <= i, i < u = xs !! (i-u)+ | otherwise = indexError p i "get"++The hot path of `get` certainly wants to unbox `p` as well as `l` and+`u`, but the unimportant, diverging error path needs `l::a` and `u::a`+boxed, since `indexError` can't unbox them because they are polymorphic.+This pattern often occurs in performance sensitive code that does+bounds-checking.++So we want to give `indexError` a signature like `<1!P(!S,!S)><1!S><S!S>b`+where the !S (meaning Poly Unboxed C1N) says that the polymorphic arguments+are unboxed (recursively). The wrapper for `indexError` won't /acutally/+unbox them (because their polymorphic type doesn't allow that) but when+demand-analysing /callers/, we'll behave as if that call needs the args+unboxed.++Then at call sites of `indexError`, we will end up doing some+reboxing, because `$windexError` still takes boxed arguments. This+reboxing should usually float into the slow, diverging code path; but+sometimes (sadly) it doesn't: see Note [Reboxed crud for bottoming calls].++Here is another important case (B):+ f x = Just x -- Suppose f is not inlined for some reason+ -- Main point: f takes its argument boxed++ wombat x = error (show (f x))++ g :: Bool -> Int -> a+ g True x = x+1+ g False x = wombat x++Again we want `wombat` to pretend to take its Int-typed argument unboxed,+even though it has to pass it boxed to `f`, so that `g` can take its+argument unboxed (and rebox it before calling `wombat`).++So here's what we do: while summarising `indexError`'s boxity signature in+`finaliseArgBoxities`:++* To address (B), for bottoming functions, we start by using `unboxDeeplyDmd`+ to make all its argument demands unboxed, right to the leaves; regardless+ of what the analysis said.++* To address (A), for bottoming functions, in the DontUnbox case when the+ argument is a type variable, we /refrain/ from using trimBoxity.+ (Remember the previous bullet: we have already doen `unboxDeeplyDmd`.)++Wrinkle:++* Remember Note [No lazy, Unboxed demands in demand signature]. So+ unboxDeeplyDmd doesn't recurse into lazy demands. It's extremely unusual+ to have lazy demands in the arguments of a bottoming function anyway.+ But it can happen, when the demand analyser gives up because it+ encounters a recursive data type; see Note [Demand analysis for recursive+ data constructors].++Note [Reboxed crud for bottoming calls]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+For functions like `get` in Note [Boxity for bottoming functions], it's clear+that the reboxed crud will be floated inside to the call site of `$windexError`.+But here's an example where that is not the case:+```hs+import GHC.Ix++theresCrud :: Int -> Int -> Int+theresCrud x y = go x+ where+ go 0 = index (0,y) 0+ go 1 = index (x,y) 1+ go n = go (n-1)+ {-# NOINLINE theresCrud #-}+```+If you look at the Core, you'll see that `y` will be reboxed and used in the+two exit join points for the `$windexError` calls, while `x` is only reboxed in the+exit join point for `index (x,y) 1` (happens in lvl below):+```+$wtheresCrud = \ ww ww1 ->+ let { y = I# ww1 } in+ join { lvl2 = ... case lvl1 ww y of wild { }; ... } in+ join { lvl3 = ... case lvl y of wild { }; ... } in+ ...+```+This is currently a bug that we willingly accept and it's documented in #21128.++See also Note [indexError] in base:GHC.Ix, which describes how we use+SPECIALISE to mitigate this problem for indexError.+-}++{- *********************************************************************+* *+ Finalising boxity+* *+********************************************************************* -}++{- Note [Finalising boxity for demand signatures]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+The worker/wrapper pass must strictly adhere to the boxity decisions+encoded in the demand signature, because that is the information that+demand analysis propagates throughout the program. Failing to+implement the strategy laid out in the signature can result in+reboxing in unexpected places. Hence, we must completely anticipate+unboxing decisions during demand analysis and reflect these decisions+in demand annotations. That is the job of 'finaliseArgBoxities',+which is defined here and called from demand analysis.++Here is a list of different Notes it has to take care of:++ * Note [No lazy, Unboxed demands in demand signature] such as `L!P(L)` in+ general, but still allow Note [Unboxing evaluated arguments]+ * Note [No nested Unboxed inside Boxed in demand signature] such as `1P(1!L)`+ * Note [mkWWstr and unsafeCoerce]++NB: Then, the worker/wrapper blindly trusts the boxity info in the+demand signature; that is why 'canUnboxArg' does not look at+strictness -- it is redundant to do so.++Note [Finalising boxity for let-bound Ids]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider+ let x = e in body+where the demand on 'x' is 1!P(blah). We want to unbox x according to+Note [Thunk splitting] in GHC.Core.Opt.WorkWrap. We must do this because+worker/wrapper ignores strictness and looks only at boxity flags; so if+x's demand is L!P(blah) we might still split it (wrongly). We want to+switch to Boxed on any lazy demand.++That is what finaliseLetBoxity does. It has no worker-arg budget, so it+is much simpler than finaliseArgBoxities.++Note [No nested Unboxed inside Boxed in demand signature]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider+```+f p@(x,y)+ | even (x+y) = []+ | otherwise = [p]+```+Demand analysis will infer that the function body puts a demand of `1P(1!L,1!L)`+on 'p', e.g., Boxed on the outside but Unboxed on the inside. But worker/wrapper+can't unbox the pair components without unboxing the pair! So we better say+`1P(1L,1L)` in the demand signature in order not to spread wrong Boxity info.+That happens via the call to trimBoxity in 'finaliseArgBoxities'/'finaliseLetBoxity'.++Note [No lazy, Unboxed demands in demand signature]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider T19407:++ data Huge = Huge Bool () ... () -- think: DynFlags+ data T = T { h :: Huge, n :: Int }+ f t@(T h _) = g h t+ g (H b _ ... _) t = if b then 1 else n t++The body of `g` puts (approx.) demand `L!P(A,1)` on `t`. But we better+not put that demand in `g`'s demand signature, because worker/wrapper will not+in general unbox a lazy-and-unboxed demand like `L!P(..)`.+(The exception are known-to-be-evaluated arguments like strict fields,+see Note [Unboxing evaluated arguments].)++The program above is an example where spreading misinformed boxity through the+signature is particularly egregious. If we give `g` that signature, then `f`+puts demand `S!P(1!P(1L,A,..),ML)` on `t`. Now we will unbox `t` in `f` it and+we get++ f (T (H b _ ... _) n) = $wf b n+ $wf b n = $wg b (T (H b x ... x) n)+ $wg = ...++Massive reboxing in `$wf`! Solution: Trim boxity on lazy demands in+'trimBoxity', modulo Note [Unboxing evaluated arguments].++Note [Unboxing evaluated arguments]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider this program (due to Roman):++ data X a = X !a++ foo :: X Int -> Int -> Int+ foo x@(X a) n = go 0+ where+ go i | i < n = a + go (i+1)+ | otherwise = 0++We want the worker for 'foo' to look like this:++ $wfoo :: Int# -> Int# -> Int#++with the first argument unboxed, so that it is not eval'd each time around the+'go' loop (which would otherwise happen, since 'foo' is not strict in 'a'). It+is sound for the wrapper to pass an unboxed arg because X is strict+(see Note [Strictness and Unboxing] in "GHC.Core.Opt.DmdAnal"), so its argument+must be evaluated. And if we *don't* pass an unboxed argument, we can't even+repair it by adding a `seq` thus:++ foo (X a) n = a `seq` go 0++because the seq is discarded (very early) since X is strict!++So here's what we do++* Since this has nothing to do with how 'foo' uses 'a', we leave demand+ analysis alone, but account for the additional evaluatedness when+ annotating the binder 'finaliseArgBoxities', which will retain the Unboxed+ boxity on 'a' in the definition of 'foo' in the demand 'L!P(L)'; meaning+ it's used lazily but unboxed nonetheless. This seems to contradict Note+ [No lazy, Unboxed demands in demand signature], but we know that 'a' is+ evaluated and thus can be unboxed.++* When 'finaliseArgBoxities' decides to unbox a record, it will zip the field demands+ together with the respective 'StrictnessMark'. In case of 'x', it will pair+ up the lazy field demand 'L!P(L)' on 'a' with 'MarkedStrict' to account for+ the strict field.++* Said 'StrictnessMark' is passed to the recursive invocation of 'go_args' in+ 'finaliseArgBoxities' when deciding whether to unbox 'a'. 'a' was used lazily, but+ since it also says 'MarkedStrict', we'll retain the 'Unboxed' boxity on 'a'.++* Worker/wrapper will consult 'canUnboxArg' for its unboxing decision. It will+ /not/ look at the strictness bits of the demand, only at Boxity flags. As such,+ it will happily unbox 'a' despite the lazy demand on it.++The net effect is that boxity analysis and the w/w transformation are more+aggressive about unboxing the strict arguments of a data constructor than when+looking at strictness info exclusively. It is very much like (Nested) CPR, which+needs its nested fields to be evaluated in order for it to unbox nestedly.++There is the usual danger of reboxing, which as usual we ignore. But+if X is monomorphic, and has an UNPACK pragma, then this optimisation+is even more important. We don't want the wrapper to rebox an unboxed+argument, and pass an Int to $wfoo!++This works in nested situations like T10482++ data family Bar a+ data instance Bar (a, b) = BarPair !(Bar a) !(Bar b)+ newtype instance Bar Int = Bar Int++ foo :: Bar ((Int, Int), Int) -> Int -> Int+ foo f k = case f of BarPair x y ->+ case burble of+ True -> case x of+ BarPair p q -> ...+ False -> ...++The extra eagerness lets us produce a worker of type:+ $wfoo :: Int# -> Int# -> Int# -> Int -> Int+ $wfoo p# q# y# = ...++even though the `case x` is only lazily evaluated.++--------- Historical note ------------+We used to add data-con strictness demands when demand analysing case+expression. However, it was noticed in #15696 that this misses some cases. For+instance, consider the program (from T10482)++ data family Bar a+ data instance Bar (a, b) = BarPair !(Bar a) !(Bar b)+ newtype instance Bar Int = Bar Int++ foo :: Bar ((Int, Int), Int) -> Int -> Int+ foo f k =+ case f of+ BarPair x y -> case burble of+ True -> case x of+ BarPair p q -> ...+ False -> ...++We really should be able to assume that `p` is already evaluated since it came+from a strict field of BarPair. This strictness would allow us to produce a+worker of type:++ $wfoo :: Int# -> Int# -> Int# -> Int -> Int+ $wfoo p# q# y# = ...++even though the `case x` is only lazily evaluated++Indeed before we fixed #15696 this would happen since we would float the inner+`case x` through the `case burble` to get:++ foo f k =+ case f of+ BarPair x y -> case x of+ BarPair p q -> case burble of+ True -> ...+ False -> ...++However, after fixing #15696 this could no longer happen (for the reasons+discussed in ticket:15696#comment:76). This means that the demand placed on `f`+would then be significantly weaker (since the False branch of the case on+`burble` is not strict in `p` or `q`).++Consequently, we now instead account for data-con strictness in mkWWstr_one,+applying the strictness demands to the final result of DmdAnal. The result is+that we get the strict demand signature we wanted even if we can't float+the case on `x` up through the case on `burble`.++Note [Do not unbox class dictionaries]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+We never unbox class dictionaries in worker/wrapper.++1. INLINABLE functions+ If we have+ f :: Ord a => [a] -> Int -> a+ {-# INLINABLE f #-}+ and we worker/wrapper f, we'll get a worker with an INLINABLE pragma+ (see Note [Worker/wrapper for INLINABLE functions] in GHC.Core.Opt.WorkWrap),+ which can still be specialised by the type-class specialiser, something like+ fw :: Ord a => [a] -> Int# -> a++ BUT if f is strict in the Ord dictionary, we might unpack it, to get+ fw :: (a->a->Bool) -> [a] -> Int# -> a+ and the type-class specialiser can't specialise that. An example is #6056.++ Historical note: #14955 describes how I got this fix wrong the first time.+ I got aware of the issue in T5075 by the change in boxity of loop between+ demand analysis runs.++2. -fspecialise-aggressively. As #21286 shows, the same phenomenon can occur+ occur without INLINABLE, when we use -fexpose-all-unfoldings and+ -fspecialise-aggressively to do vigorous cross-module specialisation.++3. #18421 found that unboxing a dictionary can also make the worker less likely+ to inline; the inlining heuristics seem to prefer to inline a function+ applied to a dictionary over a function applied to a bunch of functions.++TL;DR we /never/ unbox class dictionaries. Unboxing the dictionary, and passing+a raft of higher-order functions isn't a huge win anyway -- you really want to+specialise the function.++Note [Worker argument budget]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+In 'finaliseArgBoxities' we don't want to generate workers with zillions of+argument when, say given a strict record with zillions of fields. So we+limit the maximum number of worker args ('max_wkr_args') to the maximum of+ - -fmax-worker-args=N+ - The number of args in the original function; if it already has has+ zillions of arguments we don't want to seek /fewer/ args in the worker.+(Maybe we should /add/ them instead of maxing?)++We pursue a "layered" strategy for unboxing: we unbox the top level of the+argument(s), subject to budget; if there are any arguments left we unbox the+next layer, using that depleted budget.+Unboxing an argument *increases* the budget for the inner layer roughly+according to how many registers that argument takes (unboxed tuples take+multiple registers, see below), as determined by 'unariseArity'.+Budget is spent when we have to pass a non-absent field as a parameter.++To achieve this, we use the classic almost-circular programming technique in+which we we write one pass that takes a lazy list of the Budgets for every+layer. The effect is that of a breadth-first search (over argument type and+demand structure) to compute Budgets followed by a depth-first search to+construct the product demands, but laziness allows us to do it all in one+pass and without intermediate data structures.++Suppose we have -fmax-worker-args=4 for the remainder of this Note.+Then consider this example function:++ boxed :: (Int, Int) -> (Int, (Int, Int, Int)) -> Int+ boxed (a,b) (c, (d,e,f)) = a + b + c + d + e + f++With a budget of 4 args to spend (number of args is only 2), we'd be served well+to unbox both pairs, but not the triple. Indeed, that is what the algorithm+computes, and the following pictogram shows how the budget layers are computed.+Each layer is started with `n ~>`, where `n` is the budget at the start of the+layer. We write -n~> when we spend budget (and n is the remaining budget) and++n~> when we earn budget. We separate unboxed args with ][ and indicate+inner budget threads becoming negative in braces {{}}, so that we see which+unboxing decision we do *not* commit to. Without further ado:++ 4 ~> ][ (a,b) -3~> ][ (c, ...) -2~>+ ][ | | ][ | |+ ][ | +-------------+ ][ | +-----------------++ ][ | | ][ | |+ ][ v v ][ v v+ 2 ~> ][ +3~> a -2~> ][ b -1~> ][ +2~> c -1~> ][ (d, e, f) -0~>+ ][ | ][ | ][ | ][ {{ | | | }}+ ][ | ][ | ][ | ][ {{ | | +----------------+ }}+ ][ v ][ v ][ v ][ {{ v +------v v }}+ 0 ~> ][ +1~> I# -0~> ][ +1~> I# -0~> ][ +1~> I# -0~> ][ {{ +1~> d -0~> ][ e -(-1)~> ][ f -(-2)~> }}++Unboxing increments the budget we have on the next layer (because we don't need+to retain the boxed arg), but in turn the inner layer must afford to retain all+non-absent fields, each decrementing the budget. Note how the budget becomes+negative when trying to unbox the triple and the unboxing decision is "rolled+back". This is done by the 'positiveTopBudget' guard.++There's a bit of complication as a result of handling unboxed tuples correctly;+specifically, handling nested unboxed tuples. Consider (#21737)++ unboxed :: (Int, Int) -> (# Int, (# Int, Int, Int #) #) -> Int+ unboxed (a,b) (# c, (# d, e, f #) #) = a + b + c + d + e + f++Recall that unboxed tuples will be flattened to individual arguments during+unarisation. Here, `unboxed` will have 5 arguments at runtime because of the+nested unboxed tuple, which will be flattened to 4 args. So it's best to leave+`(a,b)` boxed (because we already are above our arg threshold), but unbox `c`+through `f` because that doesn't increase the number of args post unarisation.++Note that the challenge is that syntactically, `(# d, e, f #)` occurs in a+deeper layer than `(a, b)`. Treating unboxed tuples as a regular data type, we'd+make the same unboxing decisions as for `boxed` above; although our starting+budget is 5 (Here, the number of args is greater than -fmax-worker-args), it's+not enough to unbox the triple (we'd finish with budget -1). So we'd unbox `a`+through `c`, but not `d` through `f`, which is silly, because then we'd end up+having 6 arguments at runtime, of which `d` through `f` weren't unboxed.++Hence we pretend that the fields of unboxed tuples appear in the same budget+layer as the tuple itself. For example at the top-level, `(# x,y #)` is to be+treated just like two arguments `x` and `y`.+Of course, for that to work, our budget calculations must initialise+'max_wkr_args' to 5, based on the 'unariseArity' of each Core arg: That would be+1 for the pair and 4 for the unboxed pair. Then when we decide whether to unbox+the unboxed pair, we *directly* recurse into the fields, spending our budget+on retaining `c` and (after recursing once more) `d` through `f` as arguments,+depleting our budget completely in the first layer. Pictorially:++ 5 ~> ][ (a,b) -4~> ][ (# c, ... #)+ ][ {{ | | }} ][ c -3~> ][ (# d, e, f #)+ ][ {{ | +-------+ }} ][ | ][ d -2~> ][ e -1~> ][ f -0~>+ ][ {{ | | }} ][ | ][ | ][ | ][ |+ ][ {{ v v }} ][ v ][ v ][ v ][ v+ 0 ~> ][ {{ +1~> a -0~> ][ b -(-1)~> }} ][ +1~> I# -0~> ][ +1~> I# -0~> ][ +1~> I# -0~> ][ +1~> I# -0~>++As you can see, we have no budget left to justify unboxing `(a,b)` on the second+layer, which is good, because it would increase the number of args. Also note+that we can still unbox `c` through `f` in this layer, because doing so has a+net zero effect on budget.++Note [The OPAQUE pragma and avoiding the reboxing of arguments]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+In https://gitlab.haskell.org/ghc/ghc/-/issues/13143 it was identified that when+a function 'f' with a NOINLINE pragma is W/W transformed, then the worker for+'f' should get the NOINLINE annotation, while the wrapper /should/ be inlined.++That's because if the wrapper for 'f' had stayed NOINLINE, then any worker of a+W/W-transformed /caller of/ 'f' would immediately rebox any unboxed arguments+that is applied to the wrapper of 'f'. When the wrapper is inlined, that kind of+reboxing does not happen.++But now we have functions with OPAQUE pragmas, which by definition (See Note+[OPAQUE pragma]) do not get W/W-transformed. So in order to avoid reboxing+workers of any W/W-transformed /callers of/ 'f' we need to strip all boxity+information from 'f' in the demand analysis. This will inform the+W/W-transformation code that boxed arguments of 'f' must definitely be passed+along in boxed form and as such dissuade the creation of reboxing workers.+-}++-- | How many registers does this type take after unarisation?+unariseArity :: Type -> Arity+unariseArity ty = length (typePrimRep ty)++data Budgets = MkB !Arity Budgets -- An infinite list of arity budgets++earnTopBudget :: Budgets -> Budgets+earnTopBudget (MkB n bg) = MkB (n+1) bg++spendTopBudget :: Arity -> Budgets -> Budgets+spendTopBudget m (MkB n bg) = MkB (n-m) bg++positiveTopBudget :: Budgets -> Bool+positiveTopBudget (MkB n _) = n >= 0++finaliseArgBoxities :: AnalEnv -> Id -> Arity -> CoreExpr -> Divergence+ -> Maybe ([Demand], CoreExpr)+finaliseArgBoxities env fn arity rhs div+ | arity > count isId bndrs -- Can't find enough binders+ = Nothing -- This happens if we have f = g+ -- Then there are no binders; we don't worker/wrapper; and we+ -- simply want to give f the same demand signature as g++ | otherwise -- NB: arity is the threshold_arity, which might be less than+ -- manifest arity for join points+ = -- pprTrace "finaliseArgBoxities" (+ -- vcat [text "function:" <+> ppr fn+ -- , text "dmds before:" <+> ppr (map idDemandInfo (filter isId bndrs))+ -- , text "dmds after: " <+> ppr arg_dmds' ]) $+ Just (arg_dmds', add_demands arg_dmds' rhs)+ -- add_demands: we must attach the final boxities to the lambda-binders+ -- of the function, both because that's kosher, and because CPR analysis+ -- uses the info on the binders directly.+ where+ opts = ae_opts env+ (bndrs, _body) = collectBinders rhs+ unarise_arity = sum [ unariseArity (idType b) | b <- bndrs, isId b ]+ max_wkr_args = dmd_max_worker_args opts `max` unarise_arity+ -- This is the budget initialisation step of+ -- Note [Worker argument budget]++ -- This is the key line, which uses almost-circular programming+ -- The remaining budget from one layer becomes the initial+ -- budget for the next layer down. See Note [Worker argument budget]+ (remaining_budget, arg_dmds') = go_args (MkB max_wkr_args remaining_budget) arg_triples++ arg_triples :: [(Type, StrictnessMark, Demand)]+ arg_triples = take arity $+ [ (bndr_ty, NotMarkedStrict, get_dmd bndr bndr_ty)+ | bndr <- bndrs+ , isRuntimeVar bndr, let bndr_ty = idType bndr ]++ get_dmd :: Id -> Type -> Demand+ get_dmd bndr bndr_ty+ | isClassPred bndr_ty = trimBoxity dmd+ -- See Note [Do not unbox class dictionaries]+ -- NB: 'ty' has not been normalised, so this will (rightly)+ -- catch newtype dictionaries too.+ -- NB: even for bottoming functions, don't unbox dictionaries++ | is_bot_fn = unboxDeeplyDmd dmd+ -- See Note [Boxity for bottoming functions], case (B)++ | is_opaque = trimBoxity dmd+ -- See Note [OPAQUE pragma]+ -- See Note [The OPAQUE pragma and avoiding the reboxing of arguments]++ | otherwise = dmd+ where+ dmd = idDemandInfo bndr+ is_opaque = isOpaquePragma (idInlinePragma fn)++ -- is_bot_fn: see Note [Boxity for bottoming functions]+ is_bot_fn = div == botDiv++ go_args :: Budgets -> [(Type,StrictnessMark,Demand)] -> (Budgets, [Demand])+ go_args bg triples = mapAccumL go_arg bg triples++ go_arg :: Budgets -> (Type,StrictnessMark,Demand) -> (Budgets, Demand)+ go_arg bg@(MkB bg_top bg_inner) (ty, str_mark, dmd@(n :* _))+ = case wantToUnboxArg env ty str_mark dmd of+ DropAbsent -> (bg, dmd)++ DontUnbox | is_bot_fn, isTyVarTy ty -> (retain_budget, dmd)+ | otherwise -> (retain_budget, trimBoxity dmd)+ -- If bot: Keep deep boxity even though WW won't unbox+ -- See Note [Boxity for bottoming functions] case (A)+ -- trimBoxity: see Note [No lazy, Unboxed demands in demand signature]+ where+ retain_budget = spendTopBudget (unariseArity ty) bg+ -- spendTopBudget: spend from our budget the cost of the+ -- retaining the arg+ -- The unboxed case does happen here, for example+ -- app g x = g x :: (# Int, Int #)+ -- here, `x` is used `L`azy and thus Boxed++ DoUnbox triples+ | isUnboxedTupleType ty+ , (bg', dmds') <- go_args bg triples+ -> (bg', n :* (mkProd Unboxed $! dmds'))+ -- See Note [Worker argument budget]+ -- unboxed tuples are always unboxed, deeply+ -- NB: Recurse with bg, *not* bg_inner! The unboxed fields+ -- are at the same budget layer.++ | isUnboxedSumType ty+ -> pprPanic "Unboxing through unboxed sum" (ppr fn <+> ppr ty)+ -- We currently don't return DoUnbox for unboxed sums.+ -- But hopefully we will at some point. When that happens,+ -- it would still be impossible to predict the effect+ -- of dropping absent fields and unboxing others on the+ -- unariseArity of the sum without losing sanity.+ -- We could overwrite bg_top with the one from+ -- retain_budget while still unboxing inside the alts as in+ -- the tuple case for a conservative solution, though.++ | otherwise+ -> (spendTopBudget 1 (MkB bg_top final_bg_inner), final_dmd)+ where+ (bg_inner', dmds') = go_args (earnTopBudget bg_inner) triples+ -- earnTopBudget: give back the cost of retaining the+ -- arg we are insted unboxing.+ dmd' = n :* (mkProd Unboxed $! dmds')+ ~(final_bg_inner, final_dmd) -- "~": This match *must* be lazy!+ | positiveTopBudget bg_inner' = (bg_inner', dmd')+ | otherwise = (bg_inner, trimBoxity dmd)++ add_demands :: [Demand] -> CoreExpr -> CoreExpr+ -- Attach the demands to the outer lambdas of this expression+ add_demands [] e = e+ add_demands (dmd:dmds) (Lam v e)+ | isTyVar v = Lam v (add_demands (dmd:dmds) e)+ | otherwise = Lam (v `setIdDemandInfo` dmd) (add_demands dmds e)+ add_demands dmds e = pprPanic "add_demands" (ppr dmds $$ ppr e)++finaliseLetBoxity+ :: AnalEnv+ -> Type -- ^ Type of the let-bound Id+ -> Demand -- ^ How the Id is used+ -> Demand+-- See Note [Finalising boxity for let-bound Ids]+-- This function is like finaliseArgBoxities, but much simpler because+-- it has no "budget". It simply unboxes strict demands, and stops+-- when it reaches a lazy one.+finaliseLetBoxity env ty dmd+ = go (ty, NotMarkedStrict, dmd)+ where+ go :: (Type,StrictnessMark,Demand) -> Demand+ go (ty, str, dmd@(n :* _)) =+ case wantToUnboxArg env ty str dmd of+ DropAbsent -> dmd+ DontUnbox -> trimBoxity dmd+ DoUnbox triples -> n :* (mkProd Unboxed $! map go triples)++wantToUnboxArg :: AnalEnv -> Type -> StrictnessMark -> Demand+ -> UnboxingDecision [(Type, StrictnessMark, Demand)]+wantToUnboxArg env ty str_mark dmd@(n :* _)+ = case canUnboxArg (ae_fam_envs env) ty dmd of+ DropAbsent -> DropAbsent+ DontUnbox -> DontUnbox++ DoUnbox (DataConPatContext{ dcpc_dc = dc+ , dcpc_tc_args = tc_args+ , dcpc_args = dmds })+ -- OK, so we /can/ unbox it; but do we /want/ to?+ | not (isStrict n || isMarkedStrict str_mark) -- Don't unbox a lazy field+ -- isMarkedStrict: see Note [Unboxing evaluated arguments] in DmdAnal+ -> DontUnbox++ | DefinitelyRecursive <- ae_rec_dc env dc+ -- See Note [Which types are unboxed?]+ -- and Note [Demand analysis for recursive data constructors]+ -> DontUnbox++ | otherwise -- Bad cases dealt with: we want to unbox!+ -> DoUnbox (zip3 (dubiousDataConInstArgTys dc tc_args)+ (dataConRepStrictness dc)+ dmds)++{- *********************************************************************+* *+ Fixpoints+* *+********************************************************************* -}++-- Recursive bindings+dmdFix :: TopLevelFlag+ -> AnalEnv -- Does not include bindings for this binding+ -> SubDemand+ -> [(Id,CoreExpr)]+ -> (AnalEnv, DmdEnv, [(Id,CoreExpr)]) -- Binders annotated with strictness info++dmdFix top_lvl env let_dmd orig_pairs+ = loop 1 initial_pairs+ where+ opts = ae_opts env+ -- See Note [Initialising strictness]+ initial_pairs | ae_virgin env = [(setIdDmdAndBoxSig opts id botSig, rhs) | (id, rhs) <- orig_pairs ]+ | otherwise = orig_pairs++ -- If fixed-point iteration does not yield a result we use this instead+ -- See Note [Safe abortion in the fixed-point iteration]+ abort :: (AnalEnv, DmdEnv, [(Id,CoreExpr)])+ abort = (env, lazy_fv', zapped_pairs)+ where (lazy_fv, pairs') = step True (zapIdDmdSig orig_pairs)+ -- Note [Lazy and unleashable free variables]+ non_lazy_fvs = plusVarEnvList $ map (dmdSigDmdEnv . idDmdSig . fst) pairs'+ lazy_fv' = lazy_fv `plusVarEnv` mapVarEnv (const topDmd) non_lazy_fvs+ zapped_pairs = zapIdDmdSig pairs'++ -- The fixed-point varies the idDmdSig field of the binders, and terminates if that+ -- annotation does not change any more.+ loop :: Int -> [(Id,CoreExpr)] -> (AnalEnv, DmdEnv, [(Id,CoreExpr)])+ loop n pairs = -- pprTrace "dmdFix" (ppr n <+> vcat [ ppr id <+> ppr (idDmdSig id)+ -- | (id,_) <- pairs]) $+ loop' n pairs++ loop' n pairs+ | found_fixpoint = (final_anal_env, lazy_fv, pairs')+ | n == 10 = abort+ | otherwise = loop (n+1) pairs'+ where+ found_fixpoint = map (idDmdSig . fst) pairs' == map (idDmdSig . fst) pairs+ first_round = n == 1+ (lazy_fv, pairs') = step first_round pairs+ final_anal_env = extendAnalEnvs top_lvl env (map fst pairs')++ step :: Bool -> [(Id, CoreExpr)] -> (DmdEnv, [(Id, CoreExpr)])+ step first_round pairs = (lazy_fv, pairs')+ where+ -- In all but the first iteration, delete the virgin flag+ start_env | first_round = env+ | otherwise = nonVirgin env++ start = (extendAnalEnvs top_lvl start_env (map fst pairs), emptyVarEnv)++ !((_,!lazy_fv), !pairs') = mapAccumL my_downRhs start pairs+ -- mapAccumL: Use the new signature to do the next pair+ -- The occurrence analyser has arranged them in a good order+ -- so this can significantly reduce the number of iterations needed++ my_downRhs (env, lazy_fv) (id,rhs)+ = -- pprTrace "my_downRhs" (ppr id $$ ppr (idDmdSig id) $$ ppr sig) $+ ((env', lazy_fv'), (id', rhs'))+ where+ !(!env', !lazy_fv1, !id', !rhs') = dmdAnalRhsSig top_lvl Recursive env let_dmd id rhs+ !lazy_fv' = plusVarEnv_C plusDmd lazy_fv lazy_fv1++ zapIdDmdSig :: [(Id, CoreExpr)] -> [(Id, CoreExpr)]+ zapIdDmdSig pairs = [(setIdDmdSig id nopSig, rhs) | (id, rhs) <- pairs ]++{- Note [Safe abortion in the fixed-point iteration]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Fixed-point iteration may fail to terminate. But we cannot simply give up and+return the environment and code unchanged! We still need to do one additional+round, for two reasons:++ * To get information on used free variables (both lazy and strict!)+ (see Note [Lazy and unleashable free variables])+ * To ensure that all expressions have been traversed at least once, and any left-over+ strictness annotations have been updated.++This final iteration does not add the variables to the strictness signature+environment, which effectively assigns them 'nopSig' (see "getStrictness")++Note [Trimming a demand to a type]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+There are two reasons we sometimes trim a demand to match a type.+ 1. GADTs+ 2. Recursive products and widening++More on both below. But the bottom line is: we really don't want to+have a binder whose demand is more deeply-nested than its type+"allows". So in findBndrDmd we call trimToType and findTypeShape to+trim the demand on the binder to a form that matches the type++Now to the reasons. For (1) consider+ f :: a -> Bool+ f x = case ... of+ A g1 -> case (x |> g1) of (p,q) -> ...+ B -> error "urk"++where A,B are the constructors of a GADT. We'll get a 1P(L,L) demand+on x from the A branch, but that's a stupid demand for x itself, which+has type 'a'. Indeed we get ASSERTs going off (notably in+splitUseProdDmd, #8569).++For (2) consider+ data T = MkT Int T -- A recursive product+ f :: Int -> T -> Int+ f 0 _ = 0+ f _ (MkT n t) = f n t++Here f is lazy in T, but its *usage* is infinite: P(L,P(L,P(L, ...))).+Notice that this happens because T is a product type, and is recursive.+If we are not careful, we'll fail to iterate to a fixpoint in dmdFix,+and bale out entirely, which is inefficient and over-conservative.++Worse, as we discovered in #18304, the size of the usages we compute+can grow /exponentially/, so even 10 iterations costs far too much.+Especially since we then discard the result.++To avoid this we use the same findTypeShape function as for (1), but+arrange that it trims the demand if it encounters the same type constructor+twice (or three times, etc). We use our standard RecTcChecker mechanism+for this -- see GHC.Core.Opt.WorkWrap.Utils.findTypeShape.++This is usually call "widening". We could do it just in dmdFix, but+since are doing this findTypeShape business /anyway/ because of (1),+and it has all the right information to hand, it's extremely+convenient to do it there.++-}++{- *********************************************************************+* *+ Strictness signatures and types+* *+********************************************************************* -}++unitDmdType :: DmdEnv -> DmdType+unitDmdType dmd_env = DmdType dmd_env [] topDiv++coercionDmdEnv :: Coercion -> DmdEnv+coercionDmdEnv co = coercionsDmdEnv [co]++coercionsDmdEnv :: [Coercion] -> DmdEnv+coercionsDmdEnv cos = mapVarEnv (const topDmd) (getUniqSet $ coVarsOfCos cos)+ -- The VarSet from coVarsOfCos is really a VarEnv Var++addVarDmd :: DmdType -> Var -> Demand -> DmdType+addVarDmd (DmdType fv ds res) var dmd+ = DmdType (extendVarEnv_C plusDmd fv var dmd) ds res++addLazyFVs :: DmdType -> DmdEnv -> DmdType+addLazyFVs dmd_ty lazy_fvs+ = dmd_ty `plusDmdType` mkPlusDmdArg lazy_fvs+ -- Using plusDmdType (rather than just plus'ing the envs)+ -- is vital. Consider+ -- let f = \x -> (x,y)+ -- in error (f 3)+ -- Here, y is treated as a lazy-fv of f, but we must `plusDmd` that L+ -- demand with the bottom coming up from 'error'+ --+ -- I got a loop in the fixpointer without this, due to an interaction+ -- with the lazy_fv filtering in dmdAnalRhsSig. Roughly, it was+ -- letrec f n x+ -- = letrec g y = x `fatbar`+ -- letrec h z = z + ...g...+ -- in h (f (n-1) x)+ -- in ...+ -- In the initial iteration for f, f=Bot+ -- Suppose h is found to be strict in z, but the occurrence of g in its RHS+ -- is lazy. Now consider the fixpoint iteration for g, esp the demands it+ -- places on its free variables. Suppose it places none. Then the+ -- x `fatbar` ...call to h...+ -- will give a x->V demand for x. That turns into a L demand for x,+ -- which floats out of the defn for h. Without the modifyEnv, that+ -- L demand doesn't get both'd with the Bot coming up from the inner+ -- call to f. So we just get an L demand for x for g.++setBndrsDemandInfo :: HasCallStack => [Var] -> [Demand] -> [Var]+setBndrsDemandInfo (b:bs) ds+ | isTyVar b = b : setBndrsDemandInfo bs ds+setBndrsDemandInfo (b:bs) (d:ds) =+ let !new_info = setIdDemandInfo b d+ !vars = setBndrsDemandInfo bs ds+ in new_info : vars+setBndrsDemandInfo [] ds = assert (null ds) []+setBndrsDemandInfo bs _ = pprPanic "setBndrsDemandInfo" (ppr bs)++annotateLamIdBndr :: AnalEnv+ -> DmdType -- Demand type of body+ -> Id -- Lambda binder+ -> WithDmdType Id -- Demand type of lambda+ -- and binder annotated with demand++annotateLamIdBndr env dmd_ty id+-- For lambdas we add the demand to the argument demands+-- Only called for Ids+ = assert (isId id) $+ -- pprTrace "annLamBndr" (vcat [ppr id, ppr dmd_ty, ppr final_ty]) $+ WithDmdType main_ty new_id+ where+ new_id = setIdDemandInfo id dmd+ main_ty = addDemand dmd dmd_ty'+ WithDmdType dmd_ty' dmd = findBndrDmd env dmd_ty id++{- Note [NOINLINE and strictness]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+At one point we disabled strictness for NOINLINE functions, on the+grounds that they should be entirely opaque. But that lost lots of+useful semantic strictness information, so now we analyse them like+any other function, and pin strictness information on them.++That in turn forces us to worker/wrapper them; see+Note [Worker/wrapper for NOINLINE functions] in GHC.Core.Opt.WorkWrap.+++Note [Lazy and unleashable free variables]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+We put the strict and once-used FVs in the DmdType of the Id, so+that at its call sites we unleash demands on its strict fvs.+An example is 'roll' in imaginary/wheel-sieve2+Something like this:+ roll x = letrec+ go y = if ... then roll (x-1) else x+1+ in+ go ms+We want to see that roll is strict in x, which is because+go is called. So we put the DmdEnv for x in go's DmdType.++Another example:++ f :: Int -> Int -> Int+ f x y = let t = x+1+ h z = if z==0 then t else+ if z==1 then x+1 else+ x + h (z-1)+ in h y++Calling h does indeed evaluate x, but we can only see+that if we unleash a demand on x at the call site for t.++Incidentally, here's a place where lambda-lifting h would+lose the cigar --- we couldn't see the joint strictness in t/x++ ON THE OTHER HAND++We don't want to put *all* the fv's from the RHS into the+DmdType. Because++ * it makes the strictness signatures larger, and hence slows down fixpointing++and++ * it is useless information at the call site anyways:+ For lazy, used-many times fv's we will never get any better result than+ that, no matter how good the actual demand on the function at the call site+ is (unless it is always absent, but then the whole binder is useless).++Therefore we exclude lazy multiple-used fv's from the environment in the+DmdType.++But now the signature lies! (Missing variables are assumed to be absent.) To+make up for this, the code that analyses the binding keeps the demand on those+variable separate (usually called "lazy_fv") and adds it to the demand of the+whole binding later.++What if we decide _not_ to store a strictness signature for a binding at all, as+we do when aborting a fixed-point iteration? The we risk losing the information+that the strict variables are being used. In that case, we take all free variables+mentioned in the (unsound) strictness signature, conservatively approximate the+demand put on them (topDmd), and add that to the "lazy_fv" returned by "dmdFix".+++************************************************************************+* *+\subsection{Strictness signatures}+* *+************************************************************************+-}+++data AnalEnv = AE+ { ae_opts :: !DmdAnalOpts+ -- ^ Analysis options+ , ae_sigs :: !SigEnv+ , ae_virgin :: !Bool+ -- ^ True on first iteration only. See Note [Initialising strictness]+ , ae_fam_envs :: !FamInstEnvs+ , ae_rec_dc :: DataCon -> IsRecDataConResult+ -- ^ Memoised result of 'GHC.Core.Opt.WorkWrap.Utils.isRecDataCon'+ }++ -- We use the se_env to tell us whether to+ -- record info about a variable in the DmdEnv+ -- We do so if it's a LocalId, but not top-level+ --+ -- The DmdEnv gives the demand on the free vars of the function+ -- when it is given enough args to satisfy the strictness signature++type SigEnv = VarEnv (DmdSig, TopLevelFlag)++instance Outputable AnalEnv where+ ppr env = text "AE" <+> braces (vcat+ [ text "ae_virgin =" <+> ppr (ae_virgin env)+ , text "ae_sigs =" <+> ppr (ae_sigs env)+ ])++emptyAnalEnv :: DmdAnalOpts -> FamInstEnvs -> AnalEnv+emptyAnalEnv opts fam_envs+ = AE { ae_opts = opts+ , ae_sigs = emptySigEnv+ , ae_virgin = True+ , ae_fam_envs = fam_envs+ , ae_rec_dc = memoiseUniqueFun (isRecDataCon fam_envs 3)+ }++-- | Unset the 'dmd_strict_dicts' flag if any of the given bindings is a DFun+-- binding. Part of the mechanism that detects+-- Note [Do not strictify a DFun's parameter dictionaries].+enterDFun :: CoreBind -> AnalEnv -> AnalEnv+enterDFun bind env+ | any isDFunId (bindersOf bind)+ = env { ae_opts = (ae_opts env) { dmd_strict_dicts = False } }+ | otherwise+ = env++emptySigEnv :: SigEnv+emptySigEnv = emptyVarEnv++-- | Extend an environment with the strictness sigs attached to the Ids+extendAnalEnvs :: TopLevelFlag -> AnalEnv -> [Id] -> AnalEnv+extendAnalEnvs top_lvl env vars+ = env { ae_sigs = extendSigEnvs top_lvl (ae_sigs env) vars }++extendSigEnvs :: TopLevelFlag -> SigEnv -> [Id] -> SigEnv+extendSigEnvs top_lvl sigs vars+ = extendVarEnvList sigs [ (var, (idDmdSig var, top_lvl)) | var <- vars]++extendAnalEnv :: TopLevelFlag -> AnalEnv -> Id -> DmdSig -> AnalEnv+extendAnalEnv top_lvl env var sig+ = env { ae_sigs = extendSigEnv top_lvl (ae_sigs env) var sig }++extendSigEnv :: TopLevelFlag -> SigEnv -> Id -> DmdSig -> SigEnv+extendSigEnv top_lvl sigs var sig = extendVarEnv sigs var (sig, top_lvl)++lookupSigEnv :: AnalEnv -> Id -> Maybe (DmdSig, TopLevelFlag)+lookupSigEnv env id = lookupVarEnv (ae_sigs env) id++addInScopeAnalEnv :: AnalEnv -> Var -> AnalEnv+addInScopeAnalEnv env id = env { ae_sigs = delVarEnv (ae_sigs env) id }++addInScopeAnalEnvs :: AnalEnv -> [Var] -> AnalEnv+addInScopeAnalEnvs env ids = env { ae_sigs = delVarEnvList (ae_sigs env) ids }++nonVirgin :: AnalEnv -> AnalEnv+nonVirgin env = env { ae_virgin = False }++findBndrsDmds :: AnalEnv -> DmdType -> [Var] -> WithDmdType [Demand]+-- Return the demands on the Ids in the [Var]+findBndrsDmds env dmd_ty bndrs+ = go dmd_ty bndrs+ where+ go dmd_ty [] = WithDmdType dmd_ty []+ go dmd_ty (b:bs)+ | isId b = let WithDmdType dmd_ty1 dmds = go dmd_ty bs+ WithDmdType dmd_ty2 dmd = findBndrDmd env dmd_ty1 b+ in WithDmdType dmd_ty2 (dmd : dmds)+ | otherwise = go dmd_ty bs++findBndrDmd :: AnalEnv -> DmdType -> Id -> WithDmdType Demand+-- See Note [Trimming a demand to a type]+findBndrDmd env dmd_ty id+ = -- pprTrace "findBndrDmd" (ppr id $$ ppr dmd_ty $$ ppr starting_dmd $$ ppr dmd') $+ WithDmdType dmd_ty' dmd'+ where+ dmd' = strictify $+ trimToType starting_dmd (findTypeShape fam_envs id_ty)++ (dmd_ty', starting_dmd) = peelFV dmd_ty id++ id_ty = idType id++ strictify dmd+ -- See Note [Making dictionary parameters strict]+ -- and Note [Do not strictify a DFun's parameter dictionaries]+ | dmd_strict_dicts (ae_opts env)+ = strictifyDictDmd id_ty dmd+ | otherwise+ = dmd++ fam_envs = ae_fam_envs env++{- Note [Bringing a new variable into scope]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider+ f x = blah+ g = ...(\f. ...f...)...++In the body of the '\f', any occurrence of `f` refers to the lambda-bound `f`,+not the top-level `f` (which will be in `ae_sigs`). So it's very important+to delete `f` from `ae_sigs` when we pass a lambda/case/let-up binding of `f`.+Otherwise chaos results (#22718).++Note [Making dictionary parameters strict]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+The Opt_DictsStrict flag makes GHC use call-by-value for dictionaries. Why?++* Generally CBV is more efficient.++* A datatype dictionary is always non-bottom and never takes much work to+ compute. E.g. a DFun from an instance decl always returns a dictionary+ record immediately. See DFunUnfolding in CoreSyn.+ See also Note [Recursive superclasses] in TcInstDcls.++See #17758 for more background and perf numbers.++Wrinkles:++* A newtype dictionary is *not* always non-bottom. E.g.+ class C a where op :: a -> a+ instance C Int where op = error "urk"+ Now a value of type (C Int) is just a newtype wrapper (a cast) around+ the error thunk. Don't strictify these!++* Strictifying DFuns risks destroying the invariant that DFuns never take much+ work to compute, so we don't do it.+ See Note [Do not strictify a DFun's parameter dictionaries] for details.++* Although worker/wrapper *could* unbox strictly used dictionaries, we do not do+ so; see Note [Do not unbox class dictionaries].++The implementation is extremely simple: just make the strictness+analyser strictify the demand on a dictionary binder in+'findBndrDmd' if the binder does not belong to a DFun.++Note [Do not strictify a DFun's parameter dictionaries]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+The typechecker can tie recursive knots involving (non-recursive) DFuns, so+we must not strictify a DFun's parameter dictionaries (#22549).+T22549 has an example involving undecidable instances that <<loop>>s when we+strictify the DFun of, e.g., `$fEqSeqT`:++ Main.$fEqSeqT+ = \@m @a ($dEq :: Eq (m (ViewT m a))) ($dMonad :: Monad m) ->+ GHC.Classes.C:Eq @(SeqT m a) ($c== @m @a $dEq $dMonad)+ ($c/= @m @a $dEq $dMonad)++ Rec {+ $dEq_a = Main.$fEqSeqT @Identity @Int $dEq_b Main.$fMonadIdentity+ $dEq_b = ... $dEq_a ... <another strict context due to DFun>+ }++If we make `$fEqSeqT` strict in `$dEq`, we'll collapse the Rec group into a+giant, <<loop>>ing thunk.++To prevent that, we never strictify dictionary params when inside a DFun.+That is implemented by unsetting 'dmd_strict_dicts' when entering a DFun.++See also Note [Speculative evaluation] in GHC.CoreToStg.Prep which has a rather+similar example in #20836. We may never speculate *arguments* of (recursive)+DFun calls, likewise we should not mark *formal parameters* of recursive DFuns+as strict.++Note [Initialising strictness]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+See section 9.2 (Finding fixpoints) of the paper.++Our basic plan is to initialise the strictness of each Id in a+recursive group to "bottom", and find a fixpoint from there. However,+this group B might be inside an *enclosing* recursive group A, in+which case we'll do the entire fixpoint shebang on for each iteration+of A. This can be illustrated by the following example:++Example:++ f [] = []+ f (x:xs) = let g [] = f xs+ g (y:ys) = y+1 : g ys+ in g (h x)++At each iteration of the fixpoint for f, the analyser has to find a+fixpoint for the enclosed function g. In the meantime, the demand+values for g at each iteration for f are *greater* than those we+encountered in the previous iteration for f. Therefore, we can begin+the fixpoint for g not with the bottom value but rather with the+result of the previous analysis. I.e., when beginning the fixpoint+process for g, we can start from the demand signature computed for g+previously and attached to the binding occurrence of g.++To speed things up, we initialise each iteration of A (the enclosing+one) from the result of the last one, which is neatly recorded in each+binder. That way we make use of earlier iterations of the fixpoint+algorithm. (Cunning plan.)++But on the *first* iteration we want to *ignore* the current strictness+of the Id, and start from "bottom". Nowadays the Id can have a current+strictness, because interface files record strictness for nested bindings.+To know when we are in the first iteration, we look at the ae_virgin+field of the AnalEnv.+++Note [Final Demand Analyser run]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Some of the information that the demand analyser determines is not always+preserved by the simplifier. For example, the simplifier will happily rewrite+ \y [Demand=MU] let x = y in x + x+to+ \y [Demand=MU] y + y+which is quite a lie: Now y occurs more than just once.++The once-used information is (currently) only used by the code+generator, though. So:++ * We zap the used-once info in the worker-wrapper;+ see Note [Zapping Used Once info in WorkWrap] in+ GHC.Core.Opt.WorkWrap.+ If it's not reliable, it's better not to have it at all.++ * Just before TidyCore, we add a pass of the demand analyser,+ but WITHOUT subsequent worker/wrapper and simplifier,+ right before TidyCore. See SimplCore.getCoreToDo.++ This way, correct information finds its way into the module interface+ (strictness signatures!) and the code generator (single-entry thunks!)++Note that, in contrast, the single-call information (C(M,..)) /can/ be relied upon, as the simplifier tends to be very careful about not duplicating actual function calls.
compiler/GHC/Core/Opt/Exitify.hs view
@@ -62,7 +62,7 @@ goTopLvl (Rec pairs) = Rec (map (second (go in_scope_toplvl)) pairs) -- Top-level bindings are never join points - in_scope_toplvl = emptyInScopeSet `extendInScopeSetList` bindersOfBinds binds+ in_scope_toplvl = emptyInScopeSet `extendInScopeSetBndrs` binds go :: InScopeSet -> CoreExpr -> CoreExpr go _ e@(Var{}) = e@@ -94,7 +94,7 @@ | otherwise = Let (Rec pairs') body' where is_join_rec = any (isJoinId . fst) pairs- in_scope' = in_scope `extendInScopeSetList` bindersOf (Rec pairs)+ in_scope' = in_scope `extendInScopeSetBind` (Rec pairs) pairs' = mapSnd (go in_scope') pairs body' = go in_scope' body @@ -265,7 +265,7 @@ `extendInScopeSet` exit_id_tmpl -- just cosmetics return (uniqAway avoid exit_id_tmpl) where- exit_id_tmpl = mkSysLocal (fsLit "exit") initExitJoinUnique Many ty+ exit_id_tmpl = mkSysLocal (fsLit "exit") initExitJoinUnique ManyTy ty `asJoinId` join_arity addExit :: InScopeSet -> JoinArity -> CoreExpr -> ExitifyM JoinId@@ -306,7 +306,7 @@ in … where the floated expression `x+x` is a bit more complicated, but still not-intersting.+interesting. Expressions are interesting when they move an occurrence of a variable outside the recursive `go` that can benefit from being obviously called once, for example:@@ -315,7 +315,7 @@ see that it is called at most once, and hence improve the function’s strictness signature -So we only hoist an exit expression out if it mentiones at least one free,+So we only hoist an exit expression out if it mentions at least one free, non-imported variable. Note [Jumps can be interesting]@@ -430,7 +430,7 @@ To prevent this, we need to recognize exit join points, and then disable inlining. -Exit join points, recognizeable using `isExitJoinId` are join points with an+Exit join points, recognizable using `isExitJoinId` are join points with an occurrence in a recursive group, and can be recognized (after the occurrence analyzer ran!) using `isExitJoinId`. This function detects joinpoints with `occ_in_lam (idOccinfo id) == True`,
compiler/GHC/Core/Opt/FloatIn.hs view
@@ -22,13 +22,14 @@ import GHC.Platform import GHC.Core+import GHC.Core.Opt.Arity( isOneShotBndr ) import GHC.Core.Make hiding ( wrapFloats ) import GHC.Core.Utils import GHC.Core.FVs import GHC.Core.Type import GHC.Types.Basic ( RecFlag(..), isRec, Levity(Unlifted) )-import GHC.Types.Id ( isOneShotBndr, idType, isJoinId, isJoinId_maybe )+import GHC.Types.Id ( idType, isJoinId, isJoinId_maybe ) import GHC.Types.Tickish import GHC.Types.Var import GHC.Types.Var.Set@@ -198,13 +199,10 @@ -- useless since the simplifier will immediately float it back out.) add_arg :: FreeVarSet -> CoreExprWithFVs -> (FreeVarSet,FreeVarSet)- add_arg here_fvs (arg_fvs, AnnType _)- = (here_fvs, arg_fvs)+ -- We can't float into some arguments, so put them into the here_fvs add_arg here_fvs (arg_fvs, arg)- | noFloatIntoArg arg arg_ty = (here_fvs `unionDVarSet` arg_fvs, emptyDVarSet)+ | noFloatIntoArg arg = (here_fvs `unionDVarSet` arg_fvs, emptyDVarSet) | otherwise = (here_fvs, arg_fvs)- where- arg_ty = exprType $ deAnnotate' arg {- Note [Dead bindings] ~~~~~~~~~~~~~~~~~~~~~~~@@ -215,15 +213,6 @@ But, while this may be unusual it is not actually wrong, and it did once happen (#15696). -Note [Do not destroy the let/app invariant]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Watch out for- f (x +# y)-We don't want to float bindings into here- f (case ... of { x -> x +# y })-because that might destroy the let/app invariant, which requires-unlifted function arguments to be ok-for-speculation.- Note [Join points] ~~~~~~~~~~~~~~~~~~ Generally, we don't need to worry about join points - there are places we're@@ -629,14 +618,14 @@ | isJoinId bndr = isRec is_rec -- Joins are one-shot iff non-recursive - | otherwise- = noFloatIntoArg rhs (idType bndr)+ | Just Unlifted <- typeLevity_maybe (idType bndr)+ = True -- Preserve let-can-float invariant, see Note [noFloatInto considerations] -noFloatIntoArg :: CoreExprWithFVs' -> Type -> Bool-noFloatIntoArg expr expr_ty- | Just Unlifted <- typeLevity_maybe expr_ty- = True -- See Note [Do not destroy the let/app invariant]+ | otherwise+ = noFloatIntoArg rhs +noFloatIntoArg :: CoreExprWithFVs' -> Bool+noFloatIntoArg expr | AnnLam bndr e <- expr , (bndrs, _) <- collectAnnBndrs e = noFloatIntoLam (bndr:bndrs) -- Wrinkle 1 (a)@@ -651,11 +640,11 @@ {- Note [noFloatInto considerations] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When do we want to float bindings into- - noFloatIntoRHs: the RHS of a let-binding+ - noFloatIntoRhs: the RHS of a let-binding - noFloatIntoArg: the argument of a function application -Definitely don't float in if it has unlifted type; that-would destroy the let/app invariant.+Definitely don't float into RHS if it has unlifted type;+that would destroy the let-can-float invariant. * Wrinkle 1: do not float in if (a) any non-one-shot value lambdas
compiler/GHC/Core/Opt/FloatOut.hs view
@@ -15,12 +15,13 @@ import GHC.Core import GHC.Core.Utils import GHC.Core.Make-import GHC.Core.Opt.Arity ( exprArity, etaExpand )+-- import GHC.Core.Opt.Arity ( exprArity, etaExpand ) import GHC.Core.Opt.Monad ( FloatOutSwitches(..) ) -import GHC.Driver.Session+import GHC.Driver.Flags ( DumpFlag (..) ) import GHC.Utils.Logger-import GHC.Types.Id ( Id, idArity, idType, isDeadEndId,+import GHC.Types.Id ( Id, idType,+-- idArity, isDeadEndId, isJoinId, isJoinId_maybe ) import GHC.Types.Tickish import GHC.Core.Opt.SetLevels@@ -218,14 +219,7 @@ -- See Note [Floating out of Rec rhss] for why things get arranged this way. floatBind (NonRec (TB var _) rhs) = case (floatRhs var rhs) of { (fs, rhs_floats, rhs') ->-- -- A tiresome hack:- -- see Note [Bottoming floats: eta expansion] in GHC.Core.Opt.SetLevels- let rhs'' | isDeadEndId var- , exprArity rhs' < idArity var = etaExpand (idArity var) rhs'- | otherwise = rhs'-- in (fs, rhs_floats, [NonRec var rhs'']) }+ (fs, rhs_floats, [NonRec var rhs']) } floatBind (Rec pairs) = case floatList do_pair pairs of { (fs, rhs_floats, new_pairs) ->@@ -349,7 +343,7 @@ We may have a *nested* binding whose destination level is (FloatMe tOP_LEVEL), thus letrec { foo <0,0> = .... (let bar<0,0> = .. in ..) .... } The binding for bar will be in the "tops" part of the floating binds,-and thus not partioned by floatBody.+and thus not partitioned by floatBody. We could perhaps get rid of the 'tops' component of the floating binds, but this case works just as well.
compiler/GHC/Core/Opt/LiberateCase.hs view
@@ -5,11 +5,13 @@ -} -module GHC.Core.Opt.LiberateCase ( liberateCase ) where+module GHC.Core.Opt.LiberateCase+ ( LibCaseOpts(..)+ , liberateCase+ ) where import GHC.Prelude -import GHC.Driver.Session import GHC.Core import GHC.Core.Unfold import GHC.Builtin.Types ( unitDataConId )@@ -101,19 +103,17 @@ ************************************************************************ -} -liberateCase :: DynFlags -> CoreProgram -> CoreProgram-liberateCase dflags binds = do_prog (initLiberateCaseEnv dflags) binds+liberateCase :: LibCaseOpts -> CoreProgram -> CoreProgram+liberateCase opts binds = do_prog (initLiberateCaseEnv opts) binds where do_prog _ [] = [] do_prog env (bind:binds) = bind' : do_prog env' binds where (env', bind') = libCaseBind env bind --initLiberateCaseEnv :: DynFlags -> LibCaseEnv-initLiberateCaseEnv dflags = LibCaseEnv- { lc_threshold = liberateCaseThreshold dflags- , lc_uf_opts = unfoldingOpts dflags+initLiberateCaseEnv :: LibCaseOpts -> LibCaseEnv+initLiberateCaseEnv opts = LibCaseEnv+ { lc_opts = opts , lc_lvl = 0 , lc_lvl_env = emptyVarEnv , lc_rec_env = emptyVarEnv@@ -173,7 +173,7 @@ {- Note [Not bottoming Ids] ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Do not specialise error-functions (this is unusual, but I once saw it,-(actually in Data.Typable.Internal)+(actually in Data.Typeable.Internal) Note [Only functions!] ~~~~~~~~~~~~~~~~~~~~~~@@ -388,6 +388,22 @@ {- ************************************************************************ * *+ Options+* *+************************************************************************+-}++-- | Options for the liberate case pass.+data LibCaseOpts = LibCaseOpts+ { -- | Bomb-out size for deciding if potential liberatees are too big.+ lco_threshold :: !(Maybe Int)+ -- | Unfolding options+ , lco_unfolding_opts :: !UnfoldingOpts+ }++{-+************************************************************************+* * The environment * * ************************************************************************@@ -398,14 +414,16 @@ topLevel :: LibCaseLevel topLevel = 0 +lc_threshold :: LibCaseEnv -> Maybe Int+lc_threshold = lco_threshold . lc_opts++lc_uf_opts :: LibCaseEnv -> UnfoldingOpts+lc_uf_opts = lco_unfolding_opts . lc_opts+ data LibCaseEnv = LibCaseEnv {- lc_threshold :: Maybe Int,- -- ^ Bomb-out size for deciding if potential liberatees are too- -- big.-- lc_uf_opts :: UnfoldingOpts,- -- ^ Unfolding options+ lc_opts :: !LibCaseOpts,+ -- ^ liberate case options lc_lvl :: LibCaseLevel, -- ^ Current level -- The level is incremented when (and only when) going
compiler/GHC/Core/Opt/Pipeline.hs view
@@ -13,24 +13,24 @@ import GHC.Driver.Session import GHC.Driver.Plugins ( withPlugins, installCoreToDos ) import GHC.Driver.Env+import GHC.Driver.Config.Core.Lint ( endPass )+import GHC.Driver.Config.Core.Opt.LiberateCase ( initLiberateCaseOpts )+import GHC.Driver.Config.Core.Opt.Simplify ( initSimplifyOpts, initSimplMode, initGentleSimplMode )+import GHC.Driver.Config.Core.Opt.WorkWrap ( initWorkWrapOpts )+import GHC.Driver.Config.Core.Rules ( initRuleOpts ) import GHC.Platform.Ways ( hasWay, Way(WayProf) ) import GHC.Core import GHC.Core.Opt.CSE ( cseProgram )-import GHC.Core.Rules ( mkRuleBase,- extendRuleBaseList, ruleCheckProgram, addRuleInfo,- getRules, initRuleOpts )-import GHC.Core.Ppr ( pprCoreBindings, pprCoreExpr )-import GHC.Core.Opt.OccurAnal ( occurAnalysePgm, occurAnalyseExpr )-import GHC.Core.Stats ( coreBindsSize, coreBindsStats, exprSize )-import GHC.Core.Utils ( mkTicks, stripTicksTop, dumpIdInfoOfProgram )-import GHC.Core.Lint ( endPass, lintPassResult, dumpPassResult,- lintAnnots )-import GHC.Core.Opt.Simplify ( simplTopBinds, simplExpr, simplImpRules )-import GHC.Core.Opt.Simplify.Utils ( simplEnvForGHCi, activeRule, activeUnfolding )-import GHC.Core.Opt.Simplify.Env+import GHC.Core.Rules ( RuleBase, mkRuleBase, ruleCheckProgram, getRules )+import GHC.Core.Ppr ( pprCoreBindings )+import GHC.Core.Utils ( dumpIdInfoOfProgram )+import GHC.Core.Lint ( lintAnnots )+import GHC.Core.Lint.Interactive ( interactiveInScope )+import GHC.Core.Opt.Simplify ( simplifyExpr, simplifyPgm ) import GHC.Core.Opt.Simplify.Monad import GHC.Core.Opt.Monad+import GHC.Core.Opt.Pipeline.Types import GHC.Core.Opt.FloatIn ( floatInwards ) import GHC.Core.Opt.FloatOut ( floatOutwards ) import GHC.Core.Opt.LiberateCase ( liberateCase )@@ -51,29 +51,19 @@ import GHC.Utils.Logger as Logger import GHC.Utils.Outputable import GHC.Utils.Panic-import GHC.Utils.Constants (debugIsOn)-import GHC.Utils.Trace -import GHC.Unit.External-import GHC.Unit.Module.Env import GHC.Unit.Module.ModGuts-import GHC.Unit.Module.Deps -import GHC.Runtime.Context--import GHC.Types.Id import GHC.Types.Id.Info import GHC.Types.Basic import GHC.Types.Demand ( zapDmdEnvSig )-import GHC.Types.Var.Set-import GHC.Types.Var.Env-import GHC.Types.Tickish-import GHC.Types.Unique.FM import GHC.Types.Name.Ppr+import GHC.Types.Var ( Var ) import Control.Monad import qualified GHC.LanguageExtensions as LangExt import GHC.Unit.Module+ {- ************************************************************************ * *@@ -85,14 +75,12 @@ core2core :: HscEnv -> ModGuts -> IO ModGuts core2core hsc_env guts@(ModGuts { mg_module = mod , mg_loc = loc- , mg_deps = deps , mg_rdr_env = rdr_env })- = do { let builtin_passes = getCoreToDo logger dflags- orph_mods = mkModuleSet (mod : dep_orphs deps)+ = do { let builtin_passes = getCoreToDo dflags hpt_rule_base extra_vars uniq_mask = 's'- ;+ ; (guts2, stats) <- runCoreM hsc_env hpt_rule_base uniq_mask mod- orph_mods print_unqual loc $+ name_ppr_ctx loc $ do { hsc_env' <- getHscEnv ; all_passes <- withPlugins (hsc_plugins hsc_env') installCoreToDos@@ -106,12 +94,14 @@ ; return guts2 } where- logger = hsc_logger hsc_env dflags = hsc_dflags hsc_env+ logger = hsc_logger hsc_env+ extra_vars = interactiveInScope (hsc_IC hsc_env) home_pkg_rules = hptRules hsc_env (moduleUnitId mod) (GWIB { gwib_mod = moduleName mod , gwib_isBoot = NotBoot }) hpt_rule_base = mkRuleBase home_pkg_rules- print_unqual = mkPrintUnqualified (hsc_unit_env hsc_env) rdr_env+ name_ppr_ctx = mkNamePprCtx ptc (hsc_unit_env hsc_env) rdr_env+ ptc = initPromotionTickContext dflags -- mod: get the module out of the current HscEnv so we can retrieve it from the monad. -- This is very convienent for the users of the monad (e.g. plugins do not have to -- consume the ModGuts to find the module) but somewhat ugly because mg_module may@@ -126,8 +116,9 @@ ************************************************************************ -} -getCoreToDo :: Logger -> DynFlags -> [CoreToDo]-getCoreToDo logger dflags+getCoreToDo :: DynFlags -> RuleBase -> [Var] -> [CoreToDo]+-- This function builds the pipeline of optimisations+getCoreToDo dflags hpt_rule_base extra_vars = flatten_todos core_todo where phases = simplPhases dflags@@ -147,8 +138,6 @@ late_specialise = gopt Opt_LateSpecialise dflags static_args = gopt Opt_StaticArgumentTransformation dflags rules_on = gopt Opt_EnableRewriteRules dflags- eta_expand_on = gopt Opt_DoLambdaEtaExpansion dflags- pre_inline_on = gopt Opt_SimplPreInlining dflags ww_on = gopt Opt_WorkerWrapper dflags static_ptrs = xopt LangExt.StaticPointers dflags profiling = ways dflags `hasWay` WayProf@@ -159,30 +148,15 @@ maybe_rule_check phase = runMaybe rule_check (CoreDoRuleCheck phase) maybe_strictness_before (Phase phase)- | phase `elem` strictnessBefore dflags = CoreDoDemand+ | phase `elem` strictnessBefore dflags = CoreDoDemand False maybe_strictness_before _ = CoreDoNothing - base_mode = SimplMode { sm_phase = panic "base_mode"- , sm_names = []- , sm_dflags = dflags- , sm_logger = logger- , sm_uf_opts = unfoldingOpts dflags- , sm_rules = rules_on- , sm_eta_expand = eta_expand_on- , sm_cast_swizzle = True- , sm_inline = True- , sm_case_case = True- , sm_pre_inline = pre_inline_on- }- simpl_phase phase name iter = CoreDoPasses $ [ maybe_strictness_before phase- , CoreDoSimplify iter- (base_mode { sm_phase = phase- , sm_names = [name] })-+ , CoreDoSimplify $ initSimplifyOpts dflags extra_vars iter+ (initSimplMode dflags phase name) hpt_rule_base , maybe_rule_check phase ] -- Run GHC's internal simplification phase, after all rules have run.@@ -190,18 +164,13 @@ simplify name = simpl_phase FinalPhase name max_iter -- initial simplify: mk specialiser happy: minimum effort please- simpl_gently = CoreDoSimplify max_iter- (base_mode { sm_phase = InitialPhase- , sm_names = ["Gentle"]- , sm_rules = rules_on -- Note [RULEs enabled in InitialPhase]- , sm_inline = True- -- See Note [Inline in InitialPhase]- , sm_case_case = False })- -- Don't do case-of-case transformations.- -- This makes full laziness work better+ -- See Note [Inline in InitialPhase]+ -- See Note [RULEs enabled in InitialPhase]+ simpl_gently = CoreDoSimplify $ initSimplifyOpts dflags extra_vars max_iter+ (initGentleSimplMode dflags) hpt_rule_base - dmd_cpr_ww = if ww_on then [CoreDoDemand,CoreDoCpr,CoreDoWorkerWrapper]- else [CoreDoDemand,CoreDoCpr]+ dmd_cpr_ww = if ww_on then [CoreDoDemand True,CoreDoCpr,CoreDoWorkerWrapper]+ else [CoreDoDemand False] -- NB: No CPR! See Note [Don't change boxity without worker/wrapper] demand_analyser = (CoreDoPasses (@@ -273,7 +242,7 @@ -- GHC.Iface.Tidy.StaticPtrTable. static_ptrs_float_outwards, - -- Run the simplier phases 2,1,0 to allow rewrite rules to fire+ -- Run the simplifier phases 2,1,0 to allow rewrite rules to fire runWhen do_simpl3 (CoreDoPasses $ [ simpl_phase (Phase phase) "main" max_iter | phase <- [phases, phases-1 .. 1] ] ++@@ -369,7 +338,7 @@ -- has run at all. See Note [Final Demand Analyser run] in GHC.Core.Opt.DmdAnal -- It is EXTREMELY IMPORTANT to run this pass, otherwise execution -- can become /exponentially/ more expensive. See #11731, #12996.- runWhen (strictness || late_dmd_anal) CoreDoDemand,+ runWhen (strictness || late_dmd_anal) (CoreDoDemand False), maybe_rule_check FinalPhase, @@ -384,6 +353,15 @@ flatten_todos passes ++ flatten_todos rest flatten_todos (todo : rest) = todo : flatten_todos rest +-- The core-to-core pass ordering is derived from the DynFlags:+runWhen :: Bool -> CoreToDo -> CoreToDo+runWhen True do_this = do_this+runWhen False _ = CoreDoNothing++runMaybe :: Maybe a -> (a -> CoreToDo) -> CoreToDo+runMaybe (Just x) f = f x+runMaybe Nothing _ = CoreDoNothing+ {- Note [Inline in InitialPhase] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In GHC 8 and earlier we did not inline anything in the InitialPhase. But that is@@ -437,7 +415,7 @@ with -O2 -flate-specialise 1. I found that running late-Specialise after SpecConstr, with no- simplification in between meant that the carefullly constructed+ simplification in between meant that the carefully constructed SpecConstr rule never got to fire. (It was something like lvl = f a -- Arity 1 ....g lvl....@@ -477,6 +455,7 @@ doCorePass :: CoreToDo -> ModGuts -> CoreM ModGuts doCorePass pass guts = do logger <- getLogger+ hsc_env <- getHscEnv dflags <- getDynFlags us <- getUniqueSupplyM p_fam_env <- getPackageFamInstEnv@@ -484,16 +463,21 @@ let fam_envs = (p_fam_env, mg_fam_inst_env guts) let updateBinds f = return $ guts { mg_binds = f (mg_binds guts) } let updateBindsM f = f (mg_binds guts) >>= \b' -> return $ guts { mg_binds = b' }+ let name_ppr_ctx =+ mkNamePprCtx+ (initPromotionTickContext dflags)+ (hsc_unit_env hsc_env)+ (mg_rdr_env guts) case pass of- CoreDoSimplify {} -> {-# SCC "Simplify" #-}- simplifyPgm pass guts+ CoreDoSimplify opts -> {-# SCC "Simplify" #-}+ liftIOWithCount $ simplifyPgm logger (hsc_unit_env hsc_env) name_ppr_ctx opts guts CoreCSE -> {-# SCC "CommonSubExpr" #-} updateBinds cseProgram CoreLiberateCase -> {-# SCC "LiberateCase" #-}- updateBinds (liberateCase dflags)+ updateBinds (liberateCase (initLiberateCaseOpts dflags)) CoreDoFloatInwards -> {-# SCC "FloatInwards" #-} updateBinds (floatInwards platform)@@ -510,14 +494,16 @@ CoreDoExitify -> {-# SCC "Exitify" #-} updateBinds exitifyProgram - CoreDoDemand -> {-# SCC "DmdAnal" #-}- updateBindsM (liftIO . dmdAnal logger dflags fam_envs (mg_rules guts))+ CoreDoDemand before_ww -> {-# SCC "DmdAnal" #-}+ updateBindsM (liftIO . dmdAnal logger before_ww dflags fam_envs (mg_rules guts)) CoreDoCpr -> {-# SCC "CprAnal" #-} updateBindsM (liftIO . cprAnalProgram logger fam_envs) CoreDoWorkerWrapper -> {-# SCC "WorkWrap" #-}- updateBinds (wwTopBinds (mg_module guts) dflags fam_envs us)+ updateBinds (wwTopBinds+ (initWorkWrapOpts (mg_module guts) dflags fam_envs)+ us) CoreDoSpecialising -> {-# SCC "Specialise" #-} specProgram guts@@ -545,7 +531,6 @@ CoreDesugarOpt -> pprPanic "doCorePass" (ppr pass) CoreTidy -> pprPanic "doCorePass" (ppr pass) CorePrep -> pprPanic "doCorePass" (ppr pass)- CoreOccurAnal -> pprPanic "doCorePass" (ppr pass) {- ************************************************************************@@ -565,516 +550,19 @@ logger <- getLogger withTiming logger (text "RuleCheck"<+>brackets (ppr $ mg_module guts)) (const ()) $ do- rb <- getRuleBase- vis_orphs <- getVisibleOrphanMods- let rule_fn fn = getRules (RuleEnv [rb] vis_orphs) fn- ++ (mg_rules guts)- let ropts = initRuleOpts dflags+ rule_env <- initRuleEnv guts+ let rule_fn fn = getRules rule_env fn+ ropts = initRuleOpts dflags liftIO $ logDumpMsg logger "Rule check" (ruleCheckProgram ropts current_phase pat rule_fn (mg_binds guts)) return guts -{--************************************************************************-* *- Gentle simplification-* *-************************************************************************--}--simplifyExpr :: HscEnv -- includes spec of what core-to-core passes to do- -> CoreExpr- -> IO CoreExpr--- simplifyExpr is called by the driver to simplify an--- expression typed in at the interactive prompt-simplifyExpr hsc_env expr- = withTiming logger (text "Simplify [expr]") (const ()) $- do { eps <- hscEPS hsc_env ;- ; let fi_env = ( eps_fam_inst_env eps- , extendFamInstEnvList emptyFamInstEnv $- snd $ ic_instances $ hsc_IC hsc_env )- simpl_env = simplEnvForGHCi logger dflags-- ; let sz = exprSize expr-- ; (expr', counts) <- initSmpl logger dflags (eps_rule_base <$> hscEPS hsc_env) emptyRuleEnv fi_env sz $- simplExprGently simpl_env expr-- ; Logger.putDumpFileMaybe logger Opt_D_dump_simpl_stats- "Simplifier statistics" FormatText (pprSimplCount counts)-- ; Logger.putDumpFileMaybe logger Opt_D_dump_simpl "Simplified expression"- FormatCore- (pprCoreExpr expr')-- ; return expr'- }- where- dflags = hsc_dflags hsc_env- logger = hsc_logger hsc_env--simplExprGently :: SimplEnv -> CoreExpr -> SimplM CoreExpr--- Simplifies an expression--- does occurrence analysis, then simplification--- and repeats (twice currently) because one pass--- alone leaves tons of crud.--- Used (a) for user expressions typed in at the interactive prompt--- (b) the LHS and RHS of a RULE--- (c) Template Haskell splices------ The name 'Gently' suggests that the SimplMode is InitialPhase,--- and in fact that is so.... but the 'Gently' in simplExprGently doesn't--- enforce that; it just simplifies the expression twice---- It's important that simplExprGently does eta reduction; see--- Note [Simplify rule LHS] above. The--- simplifier does indeed do eta reduction (it's in GHC.Core.Opt.Simplify.completeLam)--- but only if -O is on.--simplExprGently env expr = do- expr1 <- simplExpr env (occurAnalyseExpr expr)- simplExpr env (occurAnalyseExpr expr1)--{--************************************************************************-* *-\subsection{The driver for the simplifier}-* *-************************************************************************--}--simplifyPgm :: CoreToDo -> ModGuts -> CoreM ModGuts-simplifyPgm pass guts- = do { hsc_env <- getHscEnv- ; rb <- getRuleBase- ; liftIOWithCount $- simplifyPgmIO pass hsc_env rb guts }--simplifyPgmIO :: CoreToDo- -> HscEnv- -> RuleBase- -> ModGuts- -> IO (SimplCount, ModGuts) -- New bindings--simplifyPgmIO pass@(CoreDoSimplify max_iterations mode)- hsc_env hpt_rule_base- guts@(ModGuts { mg_module = this_mod- , mg_rdr_env = rdr_env- , mg_deps = deps- , mg_binds = binds, mg_rules = rules- , mg_fam_inst_env = fam_inst_env })- = do { (termination_msg, it_count, counts_out, guts')- <- do_iteration 1 [] binds rules-- ; when (logHasDumpFlag logger Opt_D_verbose_core2core- && logHasDumpFlag logger Opt_D_dump_simpl_stats) $- logDumpMsg logger- "Simplifier statistics for following pass"- (vcat [text termination_msg <+> text "after" <+> ppr it_count- <+> text "iterations",- blankLine,- pprSimplCount counts_out])-- ; return (counts_out, guts')- }- where- dflags = hsc_dflags hsc_env- logger = hsc_logger hsc_env- print_unqual = mkPrintUnqualified (hsc_unit_env hsc_env) rdr_env- simpl_env = mkSimplEnv mode- active_rule = activeRule mode- active_unf = activeUnfolding mode-- do_iteration :: Int --UniqSupply- -- -> Int -- Counts iterations- -> [SimplCount] -- Counts from earlier iterations, reversed- -> CoreProgram -- Bindings in- -> [CoreRule] -- and orphan rules- -> IO (String, Int, SimplCount, ModGuts)-- do_iteration iteration_no counts_so_far binds rules- -- iteration_no is the number of the iteration we are- -- about to begin, with '1' for the first- | iteration_no > max_iterations -- Stop if we've run out of iterations- = warnPprTrace (debugIsOn && (max_iterations > 2))- "Simplifier bailing out"- ( hang (ppr this_mod <> text ", after"- <+> int max_iterations <+> text "iterations"- <+> (brackets $ hsep $ punctuate comma $- map (int . simplCountN) (reverse counts_so_far)))- 2 (text "Size =" <+> ppr (coreBindsStats binds))) $-- -- Subtract 1 from iteration_no to get the- -- number of iterations we actually completed- return ( "Simplifier baled out", iteration_no - 1- , totalise counts_so_far- , guts { mg_binds = binds, mg_rules = rules } )-- -- Try and force thunks off the binds; significantly reduces- -- space usage, especially with -O. JRS, 000620.- | let sz = coreBindsSize binds- , () <- sz `seq` () -- Force it- = do {- -- Occurrence analysis- let { tagged_binds = {-# SCC "OccAnal" #-}- occurAnalysePgm this_mod active_unf active_rule rules- binds- } ;- Logger.putDumpFileMaybe logger Opt_D_dump_occur_anal "Occurrence analysis"- FormatCore- (pprCoreBindings tagged_binds);-- -- read_eps_rules:- -- We need to read rules from the EPS regularly because simplification can- -- poke on IdInfo thunks, which in turn brings in new rules- -- behind the scenes. Otherwise there's a danger we'll simply- -- miss the rules for Ids hidden inside imported inlinings- -- Hence just before attempting to match rules we read on the EPS- -- value and then combine it when the existing rule base.- -- See `GHC.Core.Opt.Simplify.Monad.getSimplRules`.- eps <- hscEPS hsc_env ;- let { read_eps_rules = eps_rule_base <$> hscEPS hsc_env- ; rule_base = extendRuleBaseList hpt_rule_base rules- ; fam_envs = (eps_fam_inst_env eps, fam_inst_env)- ; vis_orphs = this_mod : dep_orphs deps } ;-- -- Simplify the program- ((binds1, rules1), counts1) <-- initSmpl logger dflags read_eps_rules (mkRuleEnv rule_base vis_orphs) fam_envs sz $- do { (floats, env1) <- {-# SCC "SimplTopBinds" #-}- simplTopBinds simpl_env tagged_binds-- -- Apply the substitution to rules defined in this module- -- for imported Ids. Eg RULE map my_f = blah- -- If we have a substitution my_f :-> other_f, we'd better- -- apply it to the rule to, or it'll never match- ; rules1 <- simplImpRules env1 rules-- ; return (getTopFloatBinds floats, rules1) } ;-- -- Stop if nothing happened; don't dump output- -- See Note [Which transformations are innocuous] in GHC.Core.Opt.Monad- if isZeroSimplCount counts1 then- return ( "Simplifier reached fixed point", iteration_no- , totalise (counts1 : counts_so_far) -- Include "free" ticks- , guts { mg_binds = binds1, mg_rules = rules1 } )- else do {- -- Short out indirections- -- We do this *after* at least one run of the simplifier- -- because indirection-shorting uses the export flag on *occurrences*- -- and that isn't guaranteed to be ok until after the first run propagates- -- stuff from the binding site to its occurrences- --- -- ToDo: alas, this means that indirection-shorting does not happen at all- -- if the simplifier does nothing (not common, I know, but unsavoury)- let { binds2 = {-# SCC "ZapInd" #-} shortOutIndirections binds1 } ;-- -- Dump the result of this iteration- let { dump_core_sizes = not (gopt Opt_SuppressCoreSizes dflags) } ;- dump_end_iteration logger dump_core_sizes print_unqual iteration_no counts1 binds2 rules1 ;- lintPassResult hsc_env pass binds2 ;-- -- Loop- do_iteration (iteration_no + 1) (counts1:counts_so_far) binds2 rules1- } }-#if __GLASGOW_HASKELL__ <= 810- | otherwise = panic "do_iteration"-#endif- where- -- Remember the counts_so_far are reversed- totalise :: [SimplCount] -> SimplCount- totalise = foldr (\c acc -> acc `plusSimplCount` c)- (zeroSimplCount dflags)--simplifyPgmIO _ _ _ _ = panic "simplifyPgmIO"----------------------dump_end_iteration :: Logger -> Bool -> PrintUnqualified -> Int- -> SimplCount -> CoreProgram -> [CoreRule] -> IO ()-dump_end_iteration logger dump_core_sizes print_unqual iteration_no counts binds rules- = dumpPassResult logger dump_core_sizes print_unqual mb_flag hdr pp_counts binds rules- where- mb_flag | logHasDumpFlag logger Opt_D_dump_simpl_iterations = Just Opt_D_dump_simpl_iterations- | otherwise = Nothing- -- Show details if Opt_D_dump_simpl_iterations is on-- hdr = "Simplifier iteration=" ++ show iteration_no- pp_counts = vcat [ text "---- Simplifier counts for" <+> text hdr- , pprSimplCount counts- , text "---- End of simplifier counts for" <+> text hdr ]--{--************************************************************************-* *- Shorting out indirections-* *-************************************************************************--If we have this:-- x_local = <expression>- ...bindings...- x_exported = x_local--where x_exported is exported, and x_local is not, then we replace it with this:-- x_exported = <expression>- x_local = x_exported- ...bindings...--Without this we never get rid of the x_exported = x_local thing. This-save a gratuitous jump (from \tr{x_exported} to \tr{x_local}), and-makes strictness information propagate better. This used to happen in-the final phase, but it's tidier to do it here.--Note [Messing up the exported Id's RULES]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We must be careful about discarding (obviously) or even merging the-RULES on the exported Id. The example that went bad on me at one stage-was this one:-- iterate :: (a -> a) -> a -> [a]- [Exported]- iterate = iterateList-- iterateFB c f x = x `c` iterateFB c f (f x)- iterateList f x = x : iterateList f (f x)- [Not exported]-- {-# RULES- "iterate" forall f x. iterate f x = build (\c _n -> iterateFB c f x)- "iterateFB" iterateFB (:) = iterateList- #-}--This got shorted out to:-- iterateList :: (a -> a) -> a -> [a]- iterateList = iterate-- iterateFB c f x = x `c` iterateFB c f (f x)- iterate f x = x : iterate f (f x)-- {-# RULES- "iterate" forall f x. iterate f x = build (\c _n -> iterateFB c f x)- "iterateFB" iterateFB (:) = iterate- #-}--And now we get an infinite loop in the rule system- iterate f x -> build (\cn -> iterateFB c f x)- -> iterateFB (:) f x- -> iterate f x--Old "solution":- use rule switching-off pragmas to get rid- of iterateList in the first place--But in principle the user *might* want rules that only apply to the Id-they say. And inline pragmas are similar- {-# NOINLINE f #-}- f = local- local = <stuff>-Then we do not want to get rid of the NOINLINE.--Hence hasShortableIdinfo.---Note [Rules and indirection-zapping]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Problem: what if x_exported has a RULE that mentions something in ...bindings...?-Then the things mentioned can be out of scope! Solution- a) Make sure that in this pass the usage-info from x_exported is- available for ...bindings...- b) If there are any such RULES, rec-ify the entire top-level.- It'll get sorted out next time round--Other remarks-~~~~~~~~~~~~~-If more than one exported thing is equal to a local thing (i.e., the-local thing really is shared), then we do one only:-\begin{verbatim}- x_local = ....- x_exported1 = x_local- x_exported2 = x_local-==>- x_exported1 = ....-- x_exported2 = x_exported1-\end{verbatim}--We rely on prior eta reduction to simplify things like-\begin{verbatim}- x_exported = /\ tyvars -> x_local tyvars-==>- x_exported = x_local-\end{verbatim}-Hence,there's a possibility of leaving unchanged something like this:-\begin{verbatim}- x_local = ....- x_exported1 = x_local Int-\end{verbatim}-By the time we've thrown away the types in STG land this-could be eliminated. But I don't think it's very common-and it's dangerous to do this fiddling in STG land-because we might eliminate a binding that's mentioned in the-unfolding for something.--Note [Indirection zapping and ticks]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Unfortunately this is another place where we need a special case for-ticks. The following happens quite regularly:-- x_local = <expression>- x_exported = tick<x> x_local--Which we want to become:-- x_exported = tick<x> <expression>--As it makes no sense to keep the tick and the expression on separate-bindings. Note however that this might increase the ticks scoping-over the execution of x_local, so we can only do this for floatable-ticks. More often than not, other references will be unfoldings of-x_exported, and therefore carry the tick anyway.--}--type IndEnv = IdEnv (Id, [CoreTickish]) -- Maps local_id -> exported_id, ticks--shortOutIndirections :: CoreProgram -> CoreProgram-shortOutIndirections binds- | isEmptyVarEnv ind_env = binds- | no_need_to_flatten = binds' -- See Note [Rules and indirection-zapping]- | otherwise = [Rec (flattenBinds binds')] -- for this no_need_to_flatten stuff- where- ind_env = makeIndEnv binds- -- These exported Ids are the subjects of the indirection-elimination- exp_ids = map fst $ nonDetEltsUFM ind_env- -- It's OK to use nonDetEltsUFM here because we forget the ordering- -- by immediately converting to a set or check if all the elements- -- satisfy a predicate.- exp_id_set = mkVarSet exp_ids- no_need_to_flatten = all (null . ruleInfoRules . idSpecialisation) exp_ids- binds' = concatMap zap binds-- zap (NonRec bndr rhs) = [NonRec b r | (b,r) <- zapPair (bndr,rhs)]- zap (Rec pairs) = [Rec (concatMap zapPair pairs)]-- zapPair (bndr, rhs)- | bndr `elemVarSet` exp_id_set- = [] -- Kill the exported-id binding-- | Just (exp_id, ticks) <- lookupVarEnv ind_env bndr- , (exp_id', lcl_id') <- transferIdInfo exp_id bndr- = -- Turn a local-id binding into two bindings- -- exp_id = rhs; lcl_id = exp_id- [ (exp_id', mkTicks ticks rhs),- (lcl_id', Var exp_id') ]-- | otherwise- = [(bndr,rhs)]--makeIndEnv :: [CoreBind] -> IndEnv-makeIndEnv binds- = foldl' add_bind emptyVarEnv binds- where- add_bind :: IndEnv -> CoreBind -> IndEnv- add_bind env (NonRec exported_id rhs) = add_pair env (exported_id, rhs)- add_bind env (Rec pairs) = foldl' add_pair env pairs-- add_pair :: IndEnv -> (Id,CoreExpr) -> IndEnv- add_pair env (exported_id, exported)- | (ticks, Var local_id) <- stripTicksTop tickishFloatable exported- , shortMeOut env exported_id local_id- = extendVarEnv env local_id (exported_id, ticks)- add_pair env _ = env--------------------shortMeOut :: IndEnv -> Id -> Id -> Bool-shortMeOut ind_env exported_id local_id--- The if-then-else stuff is just so I can get a pprTrace to see--- how often I don't get shorting out because of IdInfo stuff- = if isExportedId exported_id && -- Only if this is exported-- isLocalId local_id && -- Only if this one is defined in this- -- module, so that we *can* change its- -- binding to be the exported thing!-- not (isExportedId local_id) && -- Only if this one is not itself exported,- -- since the transformation will nuke it-- not (local_id `elemVarEnv` ind_env) -- Only if not already substituted for- then- if hasShortableIdInfo exported_id- then True -- See Note [Messing up the exported Id's RULES]- else warnPprTrace True "Not shorting out" (ppr exported_id) False- else- False--------------------hasShortableIdInfo :: Id -> Bool--- True if there is no user-attached IdInfo on exported_id,--- so we can safely discard it--- See Note [Messing up the exported Id's RULES]-hasShortableIdInfo id- = isEmptyRuleInfo (ruleInfo info)- && isDefaultInlinePragma (inlinePragInfo info)- && not (isStableUnfolding (realUnfoldingInfo info))- where- info = idInfo id--------------------{- Note [Transferring IdInfo]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-If we have- lcl_id = e; exp_id = lcl_id--and lcl_id has useful IdInfo, we don't want to discard it by going- gbl_id = e; lcl_id = gbl_id--Instead, transfer IdInfo from lcl_id to exp_id, specifically-* (Stable) unfolding-* Strictness-* Rules-* Inline pragma--Overwriting, rather than merging, seems to work ok.--For the lcl_id we--* Zap the InlinePragma. It might originally have had a NOINLINE, which- we have now transferred; and we really want the lcl_id to inline now- that its RHS is trivial!--* Zap any Stable unfolding. agian, we want lcl_id = gbl_id to inline,- replacing lcl_id by gbl_id. That won't happen if lcl_id has its original- great big Stable unfolding--}--transferIdInfo :: Id -> Id -> (Id, Id)--- See Note [Transferring IdInfo]-transferIdInfo exported_id local_id- = ( modifyIdInfo transfer exported_id- , modifyIdInfo zap_info local_id )- where- local_info = idInfo local_id- transfer exp_info = exp_info `setDmdSigInfo` dmdSigInfo local_info- `setCprSigInfo` cprSigInfo local_info- `setUnfoldingInfo` realUnfoldingInfo local_info- `setInlinePragInfo` inlinePragInfo local_info- `setRuleInfo` addRuleInfo (ruleInfo exp_info) new_info- new_info = setRuleInfoHead (idName exported_id)- (ruleInfo local_info)- -- Remember to set the function-name field of the- -- rules as we transfer them from one function to another-- zap_info lcl_info = lcl_info `setInlinePragInfo` defaultInlinePragma- `setUnfoldingInfo` noUnfolding---dmdAnal :: Logger -> DynFlags -> FamInstEnvs -> [CoreRule] -> CoreProgram -> IO CoreProgram-dmdAnal logger dflags fam_envs rules binds = do+dmdAnal :: Logger -> Bool -> DynFlags -> FamInstEnvs -> [CoreRule] -> CoreProgram -> IO CoreProgram+dmdAnal logger before_ww dflags fam_envs rules binds = do let !opts = DmdAnalOpts { dmd_strict_dicts = gopt Opt_DictsStrict dflags+ , dmd_do_boxity = before_ww -- only run Boxity Analysis immediately preceding WW , dmd_unbox_width = dmdUnboxWidth dflags , dmd_max_worker_args = maxWorkerArgs dflags }
compiler/GHC/Core/Opt/SetLevels.hs view
@@ -1,4 +1,3 @@- {-# LANGUAGE PatternSynonyms #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}@@ -52,17 +51,6 @@ The simplifier tries to get rid of occurrences of x, in favour of wild, in the hope that there will only be one remaining occurrence of x, namely the scrutinee of the case, and we can inline it.-- This can only work if @wild@ is an unrestricted binder. Indeed, even with the- extended typing rule (in the linter) for case expressions, if- case x of wild % 1 { p -> e}- is well-typed, then- case x of wild % 1 { p -> e[wild\x] }- is only well-typed if @e[wild\x] = e@ (that is, if @wild@ is not used in @e@- at all). In which case, it is, of course, pointless to do the substitution- anyway. So for a linear binder (and really anything which isn't unrestricted),- doing this substitution would either produce ill-typed terms or be the- identity. -} module GHC.Core.Opt.SetLevels (@@ -83,18 +71,17 @@ , exprOkForSpeculation , exprIsTopLevelBindable , collectMakeStaticArgs- , mkLamTypes+ , mkLamTypes, extendInScopeSetBndrs )-import GHC.Core.Opt.Arity ( exprBotStrictness_maybe )+import GHC.Core.Opt.Arity ( exprBotStrictness_maybe, isOneShotBndr ) import GHC.Core.FVs -- all of it import GHC.Core.Subst import GHC.Core.Make ( sortQuantVars )-import GHC.Core.Type ( Type, splitTyConApp_maybe, tyCoVarsOfType+import GHC.Core.Type ( Type, tyCoVarsOfType , mightBeUnliftedType, closeOverKindsDSet , typeHasFixedRuntimeRep )-import GHC.Core.Multiplicity ( pattern Many )-import GHC.Core.DataCon ( dataConOrigResTy )+import GHC.Core.Multiplicity ( pattern ManyTy ) import GHC.Types.Id import GHC.Types.Id.Info@@ -104,10 +91,10 @@ import GHC.Types.Unique.DSet ( getUniqDSet ) import GHC.Types.Var.Env import GHC.Types.Literal ( litIsTrivial )-import GHC.Types.Demand ( DmdSig, Demand, isStrUsedDmd, splitDmdSig, prependArgsDmdSig )-import GHC.Types.Cpr ( mkCprSig, botCpr )+import GHC.Types.Demand ( DmdSig, prependArgsDmdSig )+import GHC.Types.Cpr ( CprSig, prependArgsCprSig ) import GHC.Types.Name ( getOccName, mkSystemVarName )-import GHC.Types.Name.Occurrence ( occNameString )+import GHC.Types.Name.Occurrence ( occNameFS ) import GHC.Types.Unique ( hasKey ) import GHC.Types.Tickish ( tickishIsCode ) import GHC.Types.Unique.Supply@@ -120,12 +107,10 @@ import GHC.Data.FastString import GHC.Utils.FV-import GHC.Utils.Monad ( mapAccumLM ) import GHC.Utils.Misc import GHC.Utils.Outputable import GHC.Utils.Panic import GHC.Utils.Panic.Plain-import GHC.Utils.Trace import Data.Maybe @@ -364,7 +349,7 @@ If there were another lambda in @r@'s rhs, it would get level-2 as well. -} -lvlExpr env (_, AnnType ty) = return (Type (GHC.Core.Subst.substTy (le_subst env) ty))+lvlExpr env (_, AnnType ty) = return (Type (substTyUnchecked (le_subst env) ty)) lvlExpr env (_, AnnCoercion co) = return (Coercion (substCo (le_subst env) co)) lvlExpr env (_, AnnVar v) = return (lookupVar env v) lvlExpr _ (_, AnnLit lit) = return (Lit lit)@@ -440,21 +425,13 @@ ; return (foldl' App lapp' rargs') } | otherwise- = do { (_, args') <- mapAccumLM lvl_arg stricts args- -- Take account of argument strictness; see- -- Note [Floating to the top]+ = do { args' <- mapM (lvlMFE env False) args+ -- False: see "Arguments" in Note [Floating to the top] ; return (foldl' App (lookupVar env fn) args') } where n_val_args = count (isValArg . deAnnotate) args arity = idArity fn - stricts :: [Demand] -- True for strict /value/ arguments- stricts = case splitDmdSig (idDmdSig fn) of- (arg_ds, _) | arg_ds `lengthExceeds` n_val_args- -> []- | otherwise- -> arg_ds- -- Separate out the PAP that we are floating from the extra -- arguments, by traversing the spine until we have collected -- (n_val_args - arity) value arguments.@@ -466,19 +443,6 @@ | otherwise = left n f (a:rargs) left _ _ _ = panic "GHC.Core.Opt.SetLevels.lvlExpr.left" - is_val_arg :: CoreExprWithFVs -> Bool- is_val_arg (_, AnnType {}) = False- is_val_arg _ = True-- lvl_arg :: [Demand] -> CoreExprWithFVs -> LvlM ([Demand], LevelledExpr)- lvl_arg strs arg | (str1 : strs') <- strs- , is_val_arg arg- = do { arg' <- lvlMFE env (isStrUsedDmd str1) arg- ; return (strs', arg') }- | otherwise- = do { arg' <- lvlMFE env False arg- ; return (strs, arg') }- lvlApp env _ (fun, args) = -- No PAPs that we can float: just carry on with the -- arguments and the function.@@ -499,7 +463,7 @@ , exprIsHNF (deTagExpr scrut') -- See Note [Check the output scrutinee for exprIsHNF] , not (isTopLvl dest_lvl) -- Can't have top-level cases , not (floatTopLvlOnly env) -- Can float anywhere- , Many <- idMult case_bndr -- See Note [Floating linear case]+ , ManyTy <- idMult case_bndr -- See Note [Floating linear case] = -- Always float the case if possible -- Unlike lets we don't insist that it escapes a value lambda do { (env1, (case_bndr' : bs')) <- cloneCaseBndrs env dest_lvl (case_bndr : bs)@@ -514,7 +478,7 @@ ; alts' <- mapM (lvl_alt alts_env) alts ; return (Case scrut' case_bndr' ty' alts') } where- ty' = substTy (le_subst env) ty+ ty' = substTyUnchecked (le_subst env) ty incd_lvl = incMinorLvl (le_ctxt_lvl env) dest_lvl = maxFvLevel (const True) env scrut_fvs@@ -645,7 +609,7 @@ -- the expression, so that it can itself be floated. lvlMFE env _ (_, AnnType ty)- = return (Type (GHC.Core.Subst.substTy (le_subst env) ty))+ = return (Type (substTyUnchecked (le_subst env) ty)) -- No point in floating out an expression wrapped in a coercion or note -- If we do we'll transform lvl = e |> co@@ -682,9 +646,7 @@ -- No wrapping needed if the type is lifted, or is a literal string -- or if we are wrapping it in one or more value lambdas = do { expr1 <- lvlFloatRhs abs_vars dest_lvl rhs_env NonRecursive- (isJust mb_bot_str)- join_arity_maybe- ann_expr+ is_bot_lam join_arity_maybe ann_expr -- Treat the expr just like a right-hand side ; var <- newLvlVar expr1 join_arity_maybe is_mk_static ; let var2 = annotateBotStr var float_n_lams mb_bot_str@@ -698,21 +660,20 @@ | escapes_value_lam , not expr_ok_for_spec -- Boxing/unboxing isn't worth it for cheap expressions -- See Note [Test cheapness with exprOkForSpeculation]- , Just (tc, _) <- splitTyConApp_maybe expr_ty- , Just dc <- boxingDataCon_maybe tc- , let dc_res_ty = dataConOrigResTy dc -- No free type variables- [bx_bndr, ubx_bndr] = mkTemplateLocals [dc_res_ty, expr_ty]+ , BI_Box { bi_data_con = box_dc, bi_inst_con = boxing_expr+ , bi_boxed_type = box_ty } <- boxingDataCon expr_ty+ , let [bx_bndr, ubx_bndr] = mkTemplateLocals [box_ty, expr_ty] = do { expr1 <- lvlExpr rhs_env ann_expr ; let l1r = incMinorLvlFrom rhs_env float_rhs = mkLams abs_vars_w_lvls $- Case expr1 (stayPut l1r ubx_bndr) dc_res_ty- [Alt DEFAULT [] (mkConApp dc [Var ubx_bndr])]+ Case expr1 (stayPut l1r ubx_bndr) box_ty+ [Alt DEFAULT [] (App boxing_expr (Var ubx_bndr))] ; var <- newLvlVar float_rhs Nothing is_mk_static ; let l1u = incMinorLvlFrom env use_expr = Case (mkVarApps (Var var) abs_vars) (stayPut l1u bx_bndr) expr_ty- [Alt (DataAlt dc) [stayPut l1u ubx_bndr] (Var ubx_bndr)]+ [Alt (DataAlt box_dc) [stayPut l1u ubx_bndr] (Var ubx_bndr)] ; return (Let (NonRec (TB var (FloatMe dest_lvl)) float_rhs) use_expr) } @@ -724,14 +685,16 @@ expr_ty = exprType expr fvs = freeVarsOf ann_expr fvs_ty = tyCoVarsOfType expr_ty- is_bot = isBottomThunk mb_bot_str+ is_bot_lam = isJust mb_bot_str -- True of bottoming thunks too! is_function = isFunction ann_expr mb_bot_str = exprBotStrictness_maybe expr -- See Note [Bottoming floats] -- esp Bottoming floats (2) expr_ok_for_spec = exprOkForSpeculation expr- dest_lvl = destLevel env fvs fvs_ty is_function is_bot False- abs_vars = abstractVars dest_lvl env fvs+ abs_vars = abstractVars dest_lvl env fvs+ dest_lvl = destLevel env fvs fvs_ty is_function is_bot_lam False+ -- NB: is_bot_lam not is_bot; see (3) in+ -- Note [Bottoming floats] -- float_is_new_lam: the floated thing will be a new value lambda -- replacing, say (g (x+4)) by (lvl x). No work is saved, nor is@@ -763,7 +726,9 @@ -- See Note [Floating to the top] saves_alloc = isTopLvl dest_lvl && floatConsts env- && (not strict_ctxt || is_bot || exprIsHNF expr)+ && ( not strict_ctxt -- (a)+ || exprIsHNF expr -- (b)+ || (is_bot_lam && escapes_value_lam)) -- (c) hasFreeJoin :: LevelEnv -> DVarSet -> Bool -- Has a free join point which is not being floated to top level.@@ -773,61 +738,72 @@ hasFreeJoin env fvs = not (maxFvLevel isJoinId env fvs == tOP_LEVEL) -isBottomThunk :: Maybe (Arity, s) -> Bool--- See Note [Bottoming floats] (2)-isBottomThunk (Just (0, _)) = True -- Zero arity-isBottomThunk _ = False- {- Note [Floating to the top] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We are keen to float something to the top level, even if it does not-escape a value lambda (and hence save work), for two reasons:-- * Doing so makes the function smaller, by floating out- bottoming expressions, or integer or string literals. That in- turn makes it easier to inline, with less duplication.-- * (Minor) Doing so may turn a dynamic allocation (done by machine- instructions) into a static one. Minor because we are assuming- we are not escaping a value lambda.+Suppose saves_work is False, i.e.+ - 'e' does not escape a value lambda (escapes_value_lam), or+ - 'e' would have added value lambdas if floated (float_is_new_lam)+Then we may still be keen to float a sub-expression 'e' to the top level,+for two reasons: -But do not so if:- - the context is a strict, and- - the expression is not a HNF, and- - the expression is not bottoming+ (i) Doing so makes the function smaller, by floating out+ bottoming expressions, or integer or string literals. That in+ turn makes it easier to inline, with less duplication.+ This only matters if the floated sub-expression is inside a+ value-lambda, which in turn may be easier to inline. -Exammples:+ (ii) (Minor) Doing so may turn a dynamic allocation (done by machine+ instructions) into a static one. Minor because we are assuming+ we are not escaping a value lambda. -* Bottoming- f x = case x of- 0 -> error <big thing>- _ -> x+1- Here we want to float (error <big thing>) to top level, abstracting- over 'x', so as to make f's RHS smaller.+But only do so if (saves_alloc):+ (a) the context is lazy (so we get allocation), or+ (b) the expression is a HNF (so we get allocation), or+ (c) the expression is bottoming and (i) applies+ (NB: if the expression is a lambda, (b) will apply;+ so this case only catches bottoming thunks) -* HNF- f = case y of- True -> p:q- False -> blah- We may as well float the (p:q) so it becomes a static data structure.+Examples: -* Case scrutinee+* (a) Strict. Case scrutinee f = case g True of .... Don't float (g True) to top level; then we have the admin of a top-level thunk to worry about, with zero gain. -* Case alternative+* (a) Strict. Case alternative h = case y of True -> g True False -> False Don't float (g True) to the top level +* (b) HNF+ f = case y of+ True -> p:q+ False -> blah+ We may as well float the (p:q) so it becomes a static data structure.++* (c) Bottoming expressions; see also Note [Bottoming floats]+ f x = case x of+ 0 -> error <big thing>+ _ -> x+1+ Here we want to float (error <big thing>) to top level, abstracting+ over 'x', so as to make f's RHS smaller.++ But (#22494) if it's more like+ foo = case error <thing> of { ... }+ then there is no point in floating; we are never going to inline+ 'foo' anyway. So float bottoming things only if they escape+ a lambda.+ * Arguments t = f (g True)- If f is lazy, we /do/ float (g True) because then we can allocate- the thunk statically rather than dynamically. But if f is strict- we don't (see the use of idDmdSig in lvlApp). It's not clear- if this test is worth the bother: it's only about CAFs!+ Prior to Apr 22 we didn't float (g True) to the top if f was strict.+ But (a) this only affected CAFs, because if it escapes a value lambda+ we'll definitely float it; so the complication of working out+ argument strictness doesn't seem worth it.+ (b) floating to the top helps SpecContr; see GHC.Core.Opt.SpecConstr+ Note [Specialising on dictionaries].+ So now we don't use strictness to affect argument floating. It's controlled by a flag (floatConsts), because doing this too early loses opportunities for RULES which (needless to say) are@@ -912,15 +888,15 @@ We don't want to float very cheap expressions by boxing and unboxing. But we use exprOkForSpeculation for the test, not exprIsCheap. Why? Because it's important /not/ to transform- f (a /# 3)+ let x = a /# 3 to- f (case bx of I# a -> a /# 3)-and float bx = I# (a /# 3), because the application of f no-longer obeys the let/app invariant. But (a /# 3) is ok-for-spec+ let x = case bx of I# a -> a /# 3+because the let binding no+longer obeys the let-can-float invariant. But (a /# 3) is ok-for-spec due to a special hack that says division operators can't fail when the denominator is definitely non-zero. And yet that same expression says False to exprIsCheap. Simplest way to-guarantee the let/app invariant is to use the same function!+guarantee the let-can-float invariant is to use the same function! If an expression is okay for speculation, we could also float it out *without* boxing and unboxing, since evaluating it early is okay.@@ -947,7 +923,7 @@ (1) We want to float a bottoming expression even if it has free variables: f = \x. g (let v = h x in error ("urk" ++ v))- Then we'd like to abstract over 'x' can float the whole arg of g:+ Then we'd like to abstract over 'x', and float the whole arg of g: lvl = \x. let v = h x in error ("urk" ++ v) f = \x. g (lvl x) To achieve this we pass is_bot to destLevel@@ -956,6 +932,12 @@ bottom. Instead we treat the /body/ of such a function specially, via point (1). For example: f = \x. ....(\y z. if x then error y else error z)....+ If we float the whole lambda thus+ lvl = \x. \y z. if x then error y else error z+ f = \x. ...(lvl x)...+ we may well end up eta-expanding that PAP to+ f = \x. ...(\y z. lvl x y z)...+ ===> lvl = \x z y. if b then error y else error z f = \x. ...(\y z. lvl x z y)...@@ -966,7 +948,7 @@ Example: ... let { v = \y. error (show x ++ show y) } in ... We want to abstract over x and float the whole thing to top:- lvl = \xy. errror (show x ++ show y)+ lvl = \xy. error (show x ++ show y) ...let {v = lvl x} in ... Then of course we don't want to separately float the body (error ...)@@ -997,16 +979,6 @@ thing is based on the cheap-and-cheerful exprIsDeadEnd, I'm not sure that it'll nail all such cases. -Note [Bottoming floats: eta expansion] c.f Note [Bottoming floats]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Tiresomely, though, the simplifier has an invariant that the manifest-arity of the RHS should be the same as the arity; but we can't call-etaExpand during GHC.Core.Opt.SetLevels because it works over a decorated form of-CoreExpr. So we do the eta expansion later, in GHC.Core.Opt.FloatOut.-But we should only eta-expand if the RHS doesn't already have the right-exprArity, otherwise we get unnecessary top-level bindings if the RHS was-trivial after the next run of the Simplifier.- Note [Case MFEs] ~~~~~~~~~~~~~~~~ We don't float a case expression as an MFE from a strict context. Why not?@@ -1028,17 +1000,18 @@ -} -annotateBotStr :: Id -> Arity -> Maybe (Arity, DmdSig) -> Id+annotateBotStr :: Id -> Arity -> Maybe (Arity, DmdSig, CprSig) -> Id -- See Note [Bottoming floats] for why we want to add -- bottoming information right now -- -- n_extra are the number of extra value arguments added during floating-annotateBotStr id n_extra mb_str- = case mb_str of- Nothing -> id- Just (arity, sig) -> id `setIdArity` (arity + n_extra)- `setIdDmdSig` (prependArgsDmdSig n_extra sig)- `setIdCprSig` mkCprSig (arity + n_extra) botCpr+annotateBotStr id n_extra mb_bot_str+ | Just (arity, str_sig, cpr_sig) <- mb_bot_str+ = id `setIdArity` (arity + n_extra)+ `setIdDmdSig` prependArgsDmdSig n_extra str_sig+ `setIdCprSig` prependArgsCprSig n_extra cpr_sig+ | otherwise+ = id notWorthFloating :: CoreExpr -> [Var] -> Bool -- Returns True if the expression would be replaced by@@ -1147,7 +1120,7 @@ -- bit brutal, but unlifted bindings aren't expensive either = -- No float- do { rhs' <- lvlRhs env NonRecursive is_bot mb_join_arity rhs+ do { rhs' <- lvlRhs env NonRecursive is_bot_lam mb_join_arity rhs ; let bind_lvl = incMinorLvl (le_ctxt_lvl env) (env', [bndr']) = substAndLvlBndrs NonRecursive env bind_lvl [bndr] ; return (NonRec bndr' rhs', env') }@@ -1156,7 +1129,7 @@ | null abs_vars = do { -- No type abstraction; clone existing binder rhs' <- lvlFloatRhs [] dest_lvl env NonRecursive- is_bot mb_join_arity rhs+ is_bot_lam mb_join_arity rhs ; (env', [bndr']) <- cloneLetVars NonRecursive env dest_lvl [bndr] ; let bndr2 = annotateBotStr bndr' 0 mb_bot_str ; return (NonRec (TB bndr2 (FloatMe dest_lvl)) rhs', env') }@@ -1164,7 +1137,7 @@ | otherwise = do { -- Yes, type abstraction; create a new binder, extend substitution, etc rhs' <- lvlFloatRhs abs_vars dest_lvl env NonRecursive- is_bot mb_join_arity rhs+ is_bot_lam mb_join_arity rhs ; (env', [bndr']) <- newPolyBndrs dest_lvl env abs_vars [bndr] ; let bndr2 = annotateBotStr bndr' n_extra mb_bot_str ; return (NonRec (TB bndr2 (FloatMe dest_lvl)) rhs', env') }@@ -1175,11 +1148,12 @@ rhs_fvs = freeVarsOf rhs bind_fvs = rhs_fvs `unionDVarSet` dIdFreeVars bndr abs_vars = abstractVars dest_lvl env bind_fvs- dest_lvl = destLevel env bind_fvs ty_fvs (isFunction rhs) is_bot is_join+ dest_lvl = destLevel env bind_fvs ty_fvs (isFunction rhs) is_bot_lam is_join deann_rhs = deAnnotate rhs mb_bot_str = exprBotStrictness_maybe deann_rhs- is_bot = isJust mb_bot_str+ is_bot_lam = isJust mb_bot_str+ -- is_bot_lam: looks like (\xy. bot), maybe zero lams -- NB: not isBottomThunk! See Note [Bottoming floats] point (3) n_extra = count isId abs_vars@@ -1281,7 +1255,7 @@ profitableFloat :: LevelEnv -> Level -> Bool profitableFloat env dest_lvl = (dest_lvl `ltMajLvl` le_ctxt_lvl env) -- Escapes a value lambda- || isTopLvl dest_lvl -- Going all the way to top level+ || (isTopLvl dest_lvl && floatConsts env) -- Going all the way to top level ----------------------------------------------------@@ -1403,9 +1377,11 @@ new_lvl | any is_major bndrs = incMajorLvl lvl | otherwise = incMinorLvl lvl - is_major bndr = isId bndr && not (isProbablyOneShotLambda bndr)- -- The "probably" part says "don't float things out of a- -- probable one-shot lambda"+ is_major bndr = not (isOneShotBndr bndr)+ -- Only non-one-shot lambdas bump a major level, which in+ -- turn triggers floating. NB: isOneShotBndr is always+ -- true of a type variable -- there is no point in floating+ -- out of a big lambda. -- See Note [Computing one-shot info] in GHC.Types.Demand lvlJoinBndrs :: LevelEnv -> Level -> RecFlag -> [OutVar]@@ -1443,7 +1419,7 @@ -> TyCoVarSet -- Free in the /type/ of the term -- (a subset of the previous argument) -> Bool -- True <=> is function- -> Bool -- True <=> is bottom+ -> Bool -- True <=> looks like \x1..xn.bottom (n>=0) -> Bool -- True <=> is a join point -> Level -- INVARIANT: if is_join=True then result >= join_ceiling@@ -1460,7 +1436,7 @@ | is_bot -- Send bottoming bindings to the top = as_far_as_poss -- regardless; see Note [Bottoming floats]- -- Esp Bottoming floats (1)+ -- Esp Bottoming floats (1) and (3) | Just n_args <- floatLams env , n_args > 0 -- n=0 case handled uniformly by the 'otherwise' case@@ -1593,7 +1569,7 @@ , le_subst = mkEmptySubst in_scope_toplvl , le_env = emptyVarEnv } where- in_scope_toplvl = emptyInScopeSet `extendInScopeSetList` bindersOfBinds binds+ in_scope_toplvl = emptyInScopeSet `extendInScopeSetBndrs` binds -- The Simplifier (see Note [Glomming] in GHC.Core.Opt.OccurAnal) and -- the specialiser (see Note [Top level scope] in GHC.Core.Opt.Specialise) -- may both produce top-level bindings where an early binding refers@@ -1629,7 +1605,9 @@ -> LevelEnv extendCaseBndrEnv le@(LE { le_subst = subst, le_env = id_env }) case_bndr (Var scrut_var)- | Many <- varMult case_bndr+ -- We could use OccurAnal. scrutBinderSwap_maybe here, and perhaps+ -- get a bit more floating. But we didn't in the past and it's+ -- an unforced change, so I'm leaving it. = le { le_subst = extendSubstWithVar subst case_bndr scrut_var , le_env = add_id id_env (case_bndr, scrut_var) } extendCaseBndrEnv env _ _ = env@@ -1733,10 +1711,10 @@ mk_poly_bndr bndr uniq = transferPolyIdInfo bndr abs_vars $ -- Note [transferPolyIdInfo] in GHC.Types.Id transfer_join_info bndr $- mkSysLocal (mkFastString str) uniq (idMult bndr) poly_ty+ mkSysLocal str uniq (idMult bndr) poly_ty where- str = "poly_" ++ occNameString (getOccName bndr)- poly_ty = mkLamTypes abs_vars (GHC.Core.Subst.substTy subst (idType bndr))+ str = fsLit "poly_" `appendFS` occNameFS (getOccName bndr)+ poly_ty = mkLamTypes abs_vars (substTyUnchecked subst (idType bndr)) -- If we are floating a join point to top level, it stops being -- a join point. Otherwise it continues to be a join point,@@ -1768,7 +1746,7 @@ = mkExportedVanillaId (mkSystemVarName uniq (mkFastString "static_ptr")) rhs_ty | otherwise- = mkSysLocal (mkFastString "lvl") uniq Many rhs_ty+ = mkSysLocal (mkFastString "lvl") uniq ManyTy rhs_ty -- | Clone the binders bound by a single-alternative case. cloneCaseBndrs :: LevelEnv -> Level -> [Var] -> LvlM (LevelEnv, [Var])
− compiler/GHC/Core/Opt/Simplify.hs
@@ -1,4317 +0,0 @@-{--(c) The AQUA Project, Glasgow University, 1993-1998--\section[Simplify]{The main module of the simplifier}--}---{-# LANGUAGE TypeFamilies #-}--{-# OPTIONS_GHC -Wno-incomplete-record-updates -Wno-incomplete-uni-patterns #-}-module GHC.Core.Opt.Simplify ( simplTopBinds, simplExpr, simplImpRules ) where--import GHC.Prelude--import GHC.Platform--import GHC.Driver.Session--import GHC.Core-import GHC.Core.Opt.Simplify.Monad-import GHC.Core.Type hiding ( substTy, substTyVar, extendTvSubst, extendCvSubst )-import GHC.Core.Opt.Simplify.Env-import GHC.Core.Opt.Simplify.Utils-import GHC.Core.Opt.OccurAnal ( occurAnalyseExpr, zapLambdaBndrs )-import GHC.Core.Make ( FloatBind, mkImpossibleExpr, castBottomExpr )-import qualified GHC.Core.Make-import GHC.Core.Coercion hiding ( substCo, substCoVar )-import GHC.Core.Reduction-import GHC.Core.Coercion.Opt ( optCoercion )-import GHC.Core.FamInstEnv ( FamInstEnv, topNormaliseType_maybe )-import GHC.Core.DataCon- ( DataCon, dataConWorkId, dataConRepStrictness- , dataConRepArgTys, isUnboxedTupleDataCon- , StrictnessMark (..) )-import GHC.Core.Opt.Monad ( Tick(..), SimplMode(..) )-import GHC.Core.Ppr ( pprCoreExpr )-import GHC.Core.Unfold-import GHC.Core.Unfold.Make-import GHC.Core.Utils-import GHC.Core.Opt.Arity ( ArityType(..)- , pushCoTyArg, pushCoValArg- , etaExpandAT )-import GHC.Core.SimpleOpt ( exprIsConApp_maybe, joinPointBinding_maybe, joinPointBindings_maybe )-import GHC.Core.FVs ( mkRuleInfo )-import GHC.Core.Rules ( lookupRule, getRules, initRuleOpts )-import GHC.Core.Multiplicity--import GHC.Types.Literal ( litIsLifted ) --, mkLitInt ) -- temporarily commented out. See #8326-import GHC.Types.SourceText-import GHC.Types.Id-import GHC.Types.Id.Make ( seqId )-import GHC.Types.Id.Info-import GHC.Types.Name ( mkSystemVarName, isExternalName, getOccFS )-import GHC.Types.Demand-import GHC.Types.Cpr ( mkCprSig, botCpr )-import GHC.Types.Unique ( hasKey )-import GHC.Types.Basic-import GHC.Types.Tickish-import GHC.Types.Var ( isTyCoVar )-import GHC.Builtin.PrimOps ( PrimOp (SeqOp) )-import GHC.Builtin.Types.Prim( realWorldStatePrimTy )-import GHC.Builtin.Names( runRWKey )--import GHC.Data.Maybe ( isNothing, orElse )-import GHC.Data.FastString-import GHC.Unit.Module ( moduleName, pprModuleName )-import GHC.Utils.Misc-import GHC.Utils.Outputable-import GHC.Utils.Panic-import GHC.Utils.Panic.Plain-import GHC.Utils.Constants (debugIsOn)-import GHC.Utils.Trace-import GHC.Utils.Monad ( mapAccumLM, liftIO )-import GHC.Utils.Logger--import Control.Monad--{--The guts of the simplifier is in this module, but the driver loop for-the simplifier is in GHC.Core.Opt.Pipeline--Note [The big picture]-~~~~~~~~~~~~~~~~~~~~~~-The general shape of the simplifier is this:-- simplExpr :: SimplEnv -> InExpr -> SimplCont -> SimplM (SimplFloats, OutExpr)- simplBind :: SimplEnv -> InBind -> SimplM (SimplFloats, SimplEnv)-- * SimplEnv contains- - Simplifier mode (which includes DynFlags for convenience)- - Ambient substitution- - InScopeSet-- * SimplFloats contains- - Let-floats (which includes ok-for-spec case-floats)- - Join floats- - InScopeSet (including all the floats)-- * Expressions- simplExpr :: SimplEnv -> InExpr -> SimplCont- -> SimplM (SimplFloats, OutExpr)- The result of simplifying an /expression/ is (floats, expr)- - A bunch of floats (let bindings, join bindings)- - A simplified expression.- The overall result is effectively (let floats in expr)-- * Bindings- simplBind :: SimplEnv -> InBind -> SimplM (SimplFloats, SimplEnv)- The result of simplifying a binding is- - A bunch of floats, the last of which is the simplified binding- There may be auxiliary bindings too; see prepareRhs- - An environment suitable for simplifying the scope of the binding-- The floats may also be empty, if the binding is inlined unconditionally;- in that case the returned SimplEnv will have an augmented substitution.-- The returned floats and env both have an in-scope set, and they are- guaranteed to be the same.---Note [Shadowing]-~~~~~~~~~~~~~~~~-The simplifier used to guarantee that the output had no shadowing, but-it does not do so any more. (Actually, it never did!) The reason is-documented with simplifyArgs.---Eta expansion-~~~~~~~~~~~~~~-For eta expansion, we want to catch things like-- case e of (a,b) -> \x -> case a of (p,q) -> \y -> r--If the \x was on the RHS of a let, we'd eta expand to bring the two-lambdas together. And in general that's a good thing to do. Perhaps-we should eta expand wherever we find a (value) lambda? Then the eta-expansion at a let RHS can concentrate solely on the PAP case.--Note [In-scope set as a substitution]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-As per Note [Lookups in in-scope set], an in-scope set can act as-a substitution. Specifically, it acts as a substitution from variable to-variables /with the same unique/.--Why do we need this? Well, during the course of the simplifier, we may want to-adjust inessential properties of a variable. For instance, when performing a-beta-reduction, we change-- (\x. e) u ==> let x = u in e--We typically want to add an unfolding to `x` so that it inlines to (the-simplification of) `u`.--We do that by adding the unfolding to the binder `x`, which is added to the-in-scope set. When simplifying occurrences of `x` (every occurrence!), they are-replaced by their “updated” version from the in-scope set, hence inherit the-unfolding. This happens in `SimplEnv.substId`.--Another example. Consider-- case x of y { Node a b -> ...y...- ; Leaf v -> ...y... }--In the Node branch want y's unfolding to be (Node a b); in the Leaf branch we-want y's unfolding to be (Leaf v). We achieve this by adding the appropriate-unfolding to y, and re-adding it to the in-scope set. See the calls to-`addBinderUnfolding` in `Simplify.addAltUnfoldings` and elsewhere.--It's quite convenient. This way we don't need to manipulate the substitution all-the time: every update to a binder is automatically reflected to its bound-occurrences.--Note [Bangs in the Simplifier]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Both SimplFloats and SimplEnv do *not* generally benefit from making-their fields strict. I don't know if this is because of good use of-laziness or unintended side effects like closures capturing more variables-after WW has run.--But the end result is that we keep these lazy, but force them in some places-where we know it's beneficial to the compiler.--Similarly environments returned from functions aren't *always* beneficial to-force. In some places they would never be demanded so forcing them early-increases allocation. In other places they almost always get demanded so-it's worthwhile to force them early.--Would it be better to through every allocation of e.g. SimplEnv and decide-wether or not to make this one strict? Absolutely! Would be a good use of-someones time? Absolutely not! I made these strict that showed up during-a profiled build or which I noticed while looking at core for one reason-or another.--The result sadly is that we end up with "random" bangs in the simplifier-where we sometimes force e.g. the returned environment from a function and-sometimes we don't for the same function. Depending on the context around-the call. The treatment is also not very consistent. I only added bangs-where I saw it making a difference either in the core or benchmarks. Some-patterns where it would be beneficial aren't convered as a consequence as-I neither have the time to go through all of the core and some cases are-too small to show up in benchmarks.----************************************************************************-* *-\subsection{Bindings}-* *-************************************************************************--}--simplTopBinds :: SimplEnv -> [InBind] -> SimplM (SimplFloats, SimplEnv)--- See Note [The big picture]-simplTopBinds env0 binds0- = do { -- Put all the top-level binders into scope at the start- -- so that if a rewrite rule has unexpectedly brought- -- anything into scope, then we don't get a complaint about that.- -- It's rather as if the top-level binders were imported.- -- See Note [Glomming] in "GHC.Core.Opt.OccurAnal".- -- See Note [Bangs in the Simplifier]- ; !env1 <- {-#SCC "simplTopBinds-simplRecBndrs" #-} simplRecBndrs env0 (bindersOfBinds binds0)- ; (floats, env2) <- {-#SCC "simplTopBinds-simpl_binds" #-} simpl_binds env1 binds0- ; freeTick SimplifierDone- ; return (floats, env2) }- where- -- We need to track the zapped top-level binders, because- -- they should have their fragile IdInfo zapped (notably occurrence info)- -- That's why we run down binds and bndrs' simultaneously.- --- simpl_binds :: SimplEnv -> [InBind] -> SimplM (SimplFloats, SimplEnv)- simpl_binds env [] = return (emptyFloats env, env)- simpl_binds env (bind:binds) = do { (float, env1) <- simpl_bind env bind- ; (floats, env2) <- simpl_binds env1 binds- -- See Note [Bangs in the Simplifier]- ; let !floats1 = float `addFloats` floats- ; return (floats1, env2) }-- simpl_bind env (Rec pairs)- = simplRecBind env (BC_Let TopLevel Recursive) pairs- simpl_bind env (NonRec b r)- = do { let bind_cxt = BC_Let TopLevel NonRecursive- ; (env', b') <- addBndrRules env b (lookupRecBndr env b) bind_cxt- ; simplRecOrTopPair env' bind_cxt b b' r }--{--************************************************************************-* *- Lazy bindings-* *-************************************************************************--simplRecBind is used for- * recursive bindings only--}--simplRecBind :: SimplEnv -> BindContext- -> [(InId, InExpr)]- -> SimplM (SimplFloats, SimplEnv)-simplRecBind env0 bind_cxt pairs0- = do { (env1, triples) <- mapAccumLM add_rules env0 pairs0- ; let new_bndrs = map sndOf3 triples- ; (rec_floats, env2) <- enterRecGroupRHSs env1 new_bndrs $ \env ->- go env triples- ; return (mkRecFloats rec_floats, env2) }- where- add_rules :: SimplEnv -> (InBndr,InExpr) -> SimplM (SimplEnv, (InBndr, OutBndr, InExpr))- -- Add the (substituted) rules to the binder- add_rules env (bndr, rhs)- = do { (env', bndr') <- addBndrRules env bndr (lookupRecBndr env bndr) bind_cxt- ; return (env', (bndr, bndr', rhs)) }-- go env [] = return (emptyFloats env, env)-- go env ((old_bndr, new_bndr, rhs) : pairs)- = do { (float, env1) <- simplRecOrTopPair env bind_cxt- old_bndr new_bndr rhs- ; (floats, env2) <- go env1 pairs- ; return (float `addFloats` floats, env2) }--{--simplOrTopPair is used for- * recursive bindings (whether top level or not)- * top-level non-recursive bindings--It assumes the binder has already been simplified, but not its IdInfo.--}--simplRecOrTopPair :: SimplEnv- -> BindContext- -> InId -> OutBndr -> InExpr -- Binder and rhs- -> SimplM (SimplFloats, SimplEnv)--simplRecOrTopPair env bind_cxt old_bndr new_bndr rhs- | Just env' <- preInlineUnconditionally env (bindContextLevel bind_cxt)- old_bndr rhs env- = {-#SCC "simplRecOrTopPair-pre-inline-uncond" #-}- simplTrace env "SimplBindr:inline-uncond" (ppr old_bndr) $- do { tick (PreInlineUnconditionally old_bndr)- ; return ( emptyFloats env, env' ) }-- | otherwise- = case bind_cxt of- BC_Join cont -> simplTrace env "SimplBind:join" (ppr old_bndr) $- simplJoinBind env cont old_bndr new_bndr rhs env-- BC_Let top_lvl is_rec -> simplTrace env "SimplBind:normal" (ppr old_bndr) $- simplLazyBind env top_lvl is_rec old_bndr new_bndr rhs env--simplTrace :: SimplEnv -> String -> SDoc -> a -> a-simplTrace env herald doc thing_inside- | not (logHasDumpFlag logger Opt_D_verbose_core2core)- = thing_inside- | otherwise- = logTraceMsg logger herald doc thing_inside- where- logger = seLogger env-----------------------------simplLazyBind :: SimplEnv- -> TopLevelFlag -> RecFlag- -> InId -> OutId -- Binder, both pre-and post simpl- -- Not a JoinId- -- The OutId has IdInfo, except arity, unfolding- -- Ids only, no TyVars- -> InExpr -> SimplEnv -- The RHS and its environment- -> SimplM (SimplFloats, SimplEnv)--- Precondition: the OutId is already in the InScopeSet of the incoming 'env'--- Precondition: not a JoinId--- Precondition: rhs obeys the let/app invariant--- NOT used for JoinIds-simplLazyBind env top_lvl is_rec bndr bndr1 rhs rhs_se- = assert (isId bndr )- assertPpr (not (isJoinId bndr)) (ppr bndr) $- -- pprTrace "simplLazyBind" ((ppr bndr <+> ppr bndr1) $$ ppr rhs $$ ppr (seIdSubst rhs_se)) $- do { let !rhs_env = rhs_se `setInScopeFromE` env -- See Note [Bangs in the Simplifier]- (tvs, body) = case collectTyAndValBinders rhs of- (tvs, [], body)- | surely_not_lam body -> (tvs, body)- _ -> ([], rhs)-- surely_not_lam (Lam {}) = False- surely_not_lam (Tick t e)- | not (tickishFloatable t) = surely_not_lam e- -- eta-reduction could float- surely_not_lam _ = True- -- Do not do the "abstract tyvar" thing if there's- -- a lambda inside, because it defeats eta-reduction- -- f = /\a. \x. g a x- -- should eta-reduce.-- ; (body_env, tvs') <- {-#SCC "simplBinders" #-} simplBinders rhs_env tvs- -- See Note [Floating and type abstraction] in GHC.Core.Opt.Simplify.Utils-- -- Simplify the RHS- ; let rhs_cont = mkRhsStop (substTy body_env (exprType body))- ; (body_floats0, body0) <- {-#SCC "simplExprF" #-} simplExprF body_env body rhs_cont-- -- ANF-ise a constructor or PAP rhs- ; (body_floats2, body2) <- {-#SCC "prepareBinding" #-}- prepareBinding env top_lvl is_rec- False -- Not strict; this is simplLazyBind- bndr1 body_floats0 body0- -- Subtle point: we do not need or want tvs' in the InScope set- -- of body_floats2, so we pass in 'env' not 'body_env'.- -- Don't want: if tvs' are in-scope in the scope of this let-binding, we may do- -- more renaming than necessary => extra work (see !7777 and test T16577).- -- Don't need: we wrap tvs' around the RHS anyway.-- ; (rhs_floats, body3)- <- if isEmptyFloats body_floats2 || null tvs then -- Simple floating- {-#SCC "simplLazyBind-simple-floating" #-}- return (body_floats2, body2)-- else -- Non-empty floats, and non-empty tyvars: do type-abstraction first- {-#SCC "simplLazyBind-type-abstraction-first" #-}- do { (poly_binds, body3) <- abstractFloats (seUnfoldingOpts env) top_lvl- tvs' body_floats2 body2- ; let floats = foldl' extendFloats (emptyFloats env) poly_binds- ; return (floats, body3) }-- ; let env' = env `setInScopeFromF` rhs_floats- ; rhs' <- mkLam env' tvs' body3 rhs_cont- ; (bind_float, env2) <- completeBind env' (BC_Let top_lvl is_rec) bndr bndr1 rhs'- ; return (rhs_floats `addFloats` bind_float, env2) }-----------------------------simplJoinBind :: SimplEnv- -> SimplCont- -> InId -> OutId -- Binder, both pre-and post simpl- -- The OutId has IdInfo, except arity,- -- unfolding- -> InExpr -> SimplEnv -- The right hand side and its env- -> SimplM (SimplFloats, SimplEnv)-simplJoinBind env cont old_bndr new_bndr rhs rhs_se- = do { let rhs_env = rhs_se `setInScopeFromE` env- ; rhs' <- simplJoinRhs rhs_env old_bndr rhs cont- ; completeBind env (BC_Join cont) old_bndr new_bndr rhs' }-----------------------------simplNonRecX :: SimplEnv- -> InId -- Old binder; not a JoinId- -> OutExpr -- Simplified RHS- -> SimplM (SimplFloats, SimplEnv)--- A specialised variant of simplNonRec used when the RHS is already--- simplified, notably in knownCon. It uses case-binding where necessary.------ Precondition: rhs satisfies the let/app invariant--simplNonRecX env bndr new_rhs- | assertPpr (not (isJoinId bndr)) (ppr bndr) $- isDeadBinder bndr -- Not uncommon; e.g. case (a,b) of c { (p,q) -> p }- = return (emptyFloats env, env) -- Here c is dead, and we avoid- -- creating the binding c = (a,b)-- | Coercion co <- new_rhs- = return (emptyFloats env, extendCvSubst env bndr co)-- | exprIsTrivial new_rhs -- Short-cut for let x = y in ...- -- This case would ultimately land in postInlineUnconditionally- -- but it seems not uncommon, and avoids a lot of faff to do it here- = return (emptyFloats env- , extendIdSubst env bndr (DoneEx new_rhs Nothing))-- | otherwise- = do { (env1, new_bndr) <- simplBinder env bndr- ; let is_strict = isStrictId new_bndr- -- isStrictId: use new_bndr because the InId bndr might not have- -- a fixed runtime representation, which isStrictId doesn't expect- -- c.f. Note [Dark corner with representation polymorphism]-- ; (rhs_floats, rhs1) <- prepareBinding env NotTopLevel NonRecursive is_strict- new_bndr (emptyFloats env) new_rhs- -- NB: it makes a surprisingly big difference (5% in compiler allocation- -- in T9630) to pass 'env' rather than 'env1'. It's fine to pass 'env',- -- because this is simplNonRecX, so bndr is not in scope in the RHS.-- ; (bind_float, env2) <- completeBind (env1 `setInScopeFromF` rhs_floats)- (BC_Let NotTopLevel NonRecursive)- bndr new_bndr rhs1- -- Must pass env1 to completeBind in case simplBinder had to clone,- -- and extended the substitution with [bndr :-> new_bndr]-- ; return (rhs_floats `addFloats` bind_float, env2) }---{- *********************************************************************-* *- Cast worker/wrapper-* *-************************************************************************--Note [Cast worker/wrapper]-~~~~~~~~~~~~~~~~~~~~~~~~~~-When we have a binding- x = e |> co-we want to do something very similar to worker/wrapper:- $wx = e- x = $wx |> co--We call this making a cast worker/wrapper in tryCastWorkerWrapper.--The main motivaiton is that x can be inlined freely. There's a chance-that e will be a constructor application or function, or something-like that, so moving the coercion to the usage site may well cancel-the coercions and lead to further optimisation. Example:-- data family T a :: *- data instance T Int = T Int-- foo :: Int -> Int -> Int- foo m n = ...- where- t = T m- go 0 = 0- go n = case t of { T m -> go (n-m) }- -- This case should optimise--A second reason for doing cast worker/wrapper is that the worker/wrapper-pass after strictness analysis can't deal with RHSs like- f = (\ a b c. blah) |> co-Instead, it relies on cast worker/wrapper to get rid of the cast,-leaving a simpler job for demand-analysis worker/wrapper. See #19874.--Wrinkles--1. We must /not/ do cast w/w on- f = g |> co- otherwise it'll just keep repeating forever! You might think this- is avoided because the call to tryCastWorkerWrapper is guarded by- preInlineUnconditinally, but I'm worried that a loop-breaker or an- exported Id might say False to preInlineUnonditionally.--2. We need to be careful with inline/noinline pragmas:- rec { {-# NOINLINE f #-}- f = (...g...) |> co- ; g = ...f... }- This is legitimate -- it tells GHC to use f as the loop breaker- rather than g. Now we do the cast thing, to get something like- rec { $wf = ...g...- ; f = $wf |> co- ; g = ...f... }- Where should the NOINLINE pragma go? If we leave it on f we'll get- rec { $wf = ...g...- ; {-# NOINLINE f #-}- f = $wf |> co- ; g = ...f... }- and that is bad: the whole point is that we want to inline that- cast! We want to transfer the pagma to $wf:- rec { {-# NOINLINE $wf #-}- $wf = ...g...- ; f = $wf |> co- ; g = ...f... }- c.f. Note [Worker/wrapper for NOINLINE functions] in GHC.Core.Opt.WorkWrap.--3. We should still do cast w/w even if `f` is INLINEABLE. E.g.- {- f: Stable unfolding = <stable-big> -}- f = (\xy. <big-body>) |> co- Then we want to w/w to- {- $wf: Stable unfolding = <stable-big> |> sym co -}- $wf = \xy. <big-body>- f = $wf |> co- Notice that the stable unfolding moves to the worker! Now demand analysis- will work fine on $wf, whereas it has trouble with the original f.- c.f. Note [Worker/wrapper for INLINABLE functions] in GHC.Core.Opt.WorkWrap.- This point also applies to strong loopbreakers with INLINE pragmas, see- wrinkle (4).--4. We should /not/ do cast w/w for non-loop-breaker INLINE functions (hence- hasInlineUnfolding in tryCastWorkerWrapper, which responds False to- loop-breakers) because they'll definitely be inlined anyway, cast and- all. And if we do cast w/w for an INLINE function with arity zero, we get- something really silly: we inline that "worker" right back into the wrapper!- Worse than a no-op, because we have then lost the stable unfolding.--All these wrinkles are exactly like worker/wrapper for strictness analysis:- f is the wrapper and must inline like crazy- $wf is the worker and must carry f's original pragma-See Note [Worker/wrapper for INLINABLE functions]-and Note [Worker/wrapper for NOINLINE functions] in GHC.Core.Opt.WorkWrap.--See #17673, #18093, #18078, #19890.--Note [Preserve strictness in cast w/w]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-In the Note [Cast worker/wrapper] transformation, keep the strictness info.-Eg- f = e `cast` co -- f has strictness SSL-When we transform to- f' = e -- f' also has strictness SSL- f = f' `cast` co -- f still has strictness SSL--Its not wrong to drop it on the floor, but better to keep it.--Note [Preserve RuntimeRep info in cast w/w]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We must not do cast w/w when the presence of the coercion is needed in order-to determine the runtime representation.--Example:-- Suppose we have a type family:-- type F :: RuntimeRep- type family F where- F = LiftedRep-- together with a type `ty :: TYPE F` and a top-level binding-- a :: ty |> TYPE F[0]-- The kind of `ty |> TYPE F[0]` is `LiftedRep`, so `a` is a top-level lazy binding.- However, were we to apply cast w/w, we would get:-- b :: ty- b = ...-- a :: ty |> TYPE F[0]- a = b `cast` GRefl (TYPE F[0])-- Now we are in trouble because `ty :: TYPE F` does not have a known runtime- representation, because we need to be able to reduce the nullary type family- application `F` to find that out.--Conclusion: only do cast w/w when doing so would not lose the RuntimeRep-information. That is, when handling `Cast rhs co`, don't attempt cast w/w-unless the kind of the type of rhs is concrete, in the sense of-Note [Concrete types] in GHC.Tc.Utils.Concrete.--}--tryCastWorkerWrapper :: SimplEnv -> BindContext- -> InId -> OccInfo- -> OutId -> OutExpr- -> SimplM (SimplFloats, SimplEnv)--- See Note [Cast worker/wrapper]-tryCastWorkerWrapper env bind_cxt old_bndr occ_info bndr (Cast rhs co)- | BC_Let top_lvl is_rec <- bind_cxt -- Not join points- , not (isDFunId bndr) -- nor DFuns; cast w/w is no help, and we can't transform- -- a DFunUnfolding in mk_worker_unfolding- , not (exprIsTrivial rhs) -- Not x = y |> co; Wrinkle 1- , not (hasInlineUnfolding info) -- Not INLINE things: Wrinkle 4- , isConcrete (typeKind rhs_ty) -- Don't peel off a cast if doing so would- -- lose the underlying runtime representation.- -- See Note [Preserve RuntimeRep info in cast w/w]- , not (isOpaquePragma (idInlinePragma old_bndr)) -- Not for OPAQUE bindings- -- See Note [OPAQUE pragma]- = do { uniq <- getUniqueM- ; let work_name = mkSystemVarName uniq occ_fs- work_id = mkLocalIdWithInfo work_name Many rhs_ty worker_info- is_strict = isStrictId bndr-- ; (rhs_floats, work_rhs) <- prepareBinding env top_lvl is_rec is_strict- work_id (emptyFloats env) rhs-- ; work_unf <- mk_worker_unfolding top_lvl work_id work_rhs- ; let work_id_w_unf = work_id `setIdUnfolding` work_unf- floats = rhs_floats `addLetFloats`- unitLetFloat (NonRec work_id_w_unf work_rhs)-- triv_rhs = Cast (Var work_id_w_unf) co-- ; if postInlineUnconditionally env bind_cxt bndr occ_info triv_rhs- -- Almost always True, because the RHS is trivial- -- In that case we want to eliminate the binding fast- -- We conservatively use postInlineUnconditionally so that we- -- check all the right things- then do { tick (PostInlineUnconditionally bndr)- ; return ( floats- , extendIdSubst (setInScopeFromF env floats) old_bndr $- DoneEx triv_rhs Nothing ) }-- else do { wrap_unf <- mkLetUnfolding (sm_uf_opts mode) top_lvl InlineRhs bndr triv_rhs- ; let bndr' = bndr `setInlinePragma` mkCastWrapperInlinePrag (idInlinePragma bndr)- `setIdUnfolding` wrap_unf- floats' = floats `extendFloats` NonRec bndr' triv_rhs- ; return ( floats', setInScopeFromF env floats' ) } }- where- mode = getMode env- occ_fs = getOccFS bndr- rhs_ty = coercionLKind co- info = idInfo bndr-- worker_info = vanillaIdInfo `setDmdSigInfo` dmdSigInfo info- `setCprSigInfo` cprSigInfo info- `setDemandInfo` demandInfo info- `setInlinePragInfo` inlinePragInfo info- `setArityInfo` arityInfo info- -- We do /not/ want to transfer OccInfo, Rules- -- Note [Preserve strictness in cast w/w]- -- and Wrinkle 2 of Note [Cast worker/wrapper]-- ----------- Worker unfolding ------------ -- Stable case: if there is a stable unfolding we have to compose with (Sym co);- -- the next round of simplification will do the job- -- Non-stable case: use work_rhs- -- Wrinkle 3 of Note [Cast worker/wrapper]- mk_worker_unfolding top_lvl work_id work_rhs- = case realUnfoldingInfo info of -- NB: the real one, even for loop-breakers- unf@(CoreUnfolding { uf_tmpl = unf_rhs, uf_src = src })- | isStableSource src -> return (unf { uf_tmpl = mkCast unf_rhs (mkSymCo co) })- _ -> mkLetUnfolding (sm_uf_opts mode) top_lvl InlineRhs work_id work_rhs--tryCastWorkerWrapper env _ _ _ bndr rhs -- All other bindings- = return (mkFloatBind env (NonRec bndr rhs))--mkCastWrapperInlinePrag :: InlinePragma -> InlinePragma--- See Note [Cast worker/wrapper]-mkCastWrapperInlinePrag (InlinePragma { inl_act = act, inl_rule = rule_info })- = InlinePragma { inl_src = SourceText "{-# INLINE"- , inl_inline = NoUserInlinePrag -- See Note [Wrapper NoUserInlinePrag]- , inl_sat = Nothing -- in GHC.Core.Opt.WorkWrap- , inl_act = wrap_act -- See Note [Wrapper activation]- , inl_rule = rule_info } -- in GHC.Core.Opt.WorkWrap- -- RuleMatchInfo is (and must be) unaffected- where- -- See Note [Wrapper activation] in GHC.Core.Opt.WorkWrap- -- But simpler, because we don't need to disable during InitialPhase- wrap_act | isNeverActive act = activateDuringFinal- | otherwise = act---{- *********************************************************************-* *- prepareBinding, prepareRhs, makeTrivial-* *-********************************************************************* -}--prepareBinding :: SimplEnv -> TopLevelFlag -> RecFlag -> Bool- -> Id -- Used only for its OccName; can be InId or OutId- -> SimplFloats -> OutExpr- -> SimplM (SimplFloats, OutExpr)--- In (prepareBinding ... bndr floats rhs), the binding is really just--- bndr = let floats in rhs--- Maybe we can ANF-ise this binding and float out; e.g.--- bndr = let a = f x in K a a (g x)--- we could float out to give--- a = f x--- tmp = g x--- bndr = K a a tmp--- That's what prepareBinding does--- Precondition: binder is not a JoinId-prepareBinding env top_lvl is_rec strict_bind bndr rhs_floats rhs- = do { -- Never float join-floats out of a non-join let-binding (which this is)- -- So wrap the body in the join-floats right now- -- Hence: rhs_floats1 consists only of let-floats- let (rhs_floats1, rhs1) = wrapJoinFloatsX rhs_floats rhs-- -- rhs_env: add to in-scope set the binders from rhs_floats- -- so that prepareRhs knows what is in scope in rhs- ; let rhs_env = env `setInScopeFromF` rhs_floats1-- -- Now ANF-ise the remaining rhs- ; (anf_floats, rhs2) <- prepareRhs rhs_env top_lvl (getOccFS bndr) rhs1-- -- Finally, decide whether or not to float- ; let all_floats = rhs_floats1 `addLetFloats` anf_floats- ; if doFloatFromRhs top_lvl is_rec strict_bind all_floats rhs2- then -- Float!- do { tick LetFloatFromLet- ; return (all_floats, rhs2) }-- else -- Abandon floating altogether; revert to original rhs- -- Since we have already built rhs1, we just need to add- -- rhs_floats1 to it- return (emptyFloats env, wrapFloats rhs_floats1 rhs1) }--{- Note [prepareRhs]-~~~~~~~~~~~~~~~~~~~~-prepareRhs takes a putative RHS, checks whether it's a PAP or-constructor application and, if so, converts it to ANF, so that the-resulting thing can be inlined more easily. Thus- x = (f a, g b)-becomes- t1 = f a- t2 = g b- x = (t1,t2)--We also want to deal well cases like this- v = (f e1 `cast` co) e2-Here we want to make e1,e2 trivial and get- x1 = e1; x2 = e2; v = (f x1 `cast` co) v2-That's what the 'go' loop in prepareRhs does--}--prepareRhs :: SimplEnv -> TopLevelFlag- -> FastString -- Base for any new variables- -> OutExpr- -> SimplM (LetFloats, OutExpr)--- Transforms a RHS into a better RHS by ANF'ing args--- for expandable RHSs: constructors and PAPs--- e.g x = Just e--- becomes a = e -- 'a' is fresh--- x = Just a--- See Note [prepareRhs]-prepareRhs env top_lvl occ rhs0- = do { (_is_exp, floats, rhs1) <- go 0 rhs0- ; return (floats, rhs1) }- where- go :: Int -> OutExpr -> SimplM (Bool, LetFloats, OutExpr)- go n_val_args (Cast rhs co)- = do { (is_exp, floats, rhs') <- go n_val_args rhs- ; return (is_exp, floats, Cast rhs' co) }- go n_val_args (App fun (Type ty))- = do { (is_exp, floats, rhs') <- go n_val_args fun- ; return (is_exp, floats, App rhs' (Type ty)) }- go n_val_args (App fun arg)- = do { (is_exp, floats1, fun') <- go (n_val_args+1) fun- ; case is_exp of- False -> return (False, emptyLetFloats, App fun arg)- True -> do { (floats2, arg') <- makeTrivial env top_lvl topDmd occ arg- ; return (True, floats1 `addLetFlts` floats2, App fun' arg') } }- go n_val_args (Var fun)- = return (is_exp, emptyLetFloats, Var fun)- where- is_exp = isExpandableApp fun n_val_args -- The fun a constructor or PAP- -- See Note [CONLIKE pragma] in GHC.Types.Basic- -- The definition of is_exp should match that in- -- 'GHC.Core.Opt.OccurAnal.occAnalApp'-- go n_val_args (Tick t rhs)- -- We want to be able to float bindings past this- -- tick. Non-scoping ticks don't care.- | tickishScoped t == NoScope- = do { (is_exp, floats, rhs') <- go n_val_args rhs- ; return (is_exp, floats, Tick t rhs') }-- -- On the other hand, for scoping ticks we need to be able to- -- copy them on the floats, which in turn is only allowed if- -- we can obtain non-counting ticks.- | (not (tickishCounts t) || tickishCanSplit t)- = do { (is_exp, floats, rhs') <- go n_val_args rhs- ; let tickIt (id, expr) = (id, mkTick (mkNoCount t) expr)- floats' = mapLetFloats floats tickIt- ; return (is_exp, floats', Tick t rhs') }-- go _ other- = return (False, emptyLetFloats, other)--makeTrivialArg :: SimplEnv -> ArgSpec -> SimplM (LetFloats, ArgSpec)-makeTrivialArg env arg@(ValArg { as_arg = e, as_dmd = dmd })- = do { (floats, e') <- makeTrivial env NotTopLevel dmd (fsLit "arg") e- ; return (floats, arg { as_arg = e' }) }-makeTrivialArg _ arg- = return (emptyLetFloats, arg) -- CastBy, TyArg--makeTrivial :: SimplEnv -> TopLevelFlag -> Demand- -> FastString -- ^ A "friendly name" to build the new binder from- -> OutExpr -- ^ This expression satisfies the let/app invariant- -> SimplM (LetFloats, OutExpr)--- Binds the expression to a variable, if it's not trivial, returning the variable--- For the Demand argument, see Note [Keeping demand info in StrictArg Plan A]-makeTrivial env top_lvl dmd occ_fs expr- | exprIsTrivial expr -- Already trivial- || not (bindingOk top_lvl expr expr_ty) -- Cannot trivialise- -- See Note [Cannot trivialise]- = return (emptyLetFloats, expr)-- | Cast expr' co <- expr- = do { (floats, triv_expr) <- makeTrivial env top_lvl dmd occ_fs expr'- ; return (floats, Cast triv_expr co) }-- | otherwise- = do { (floats, new_id) <- makeTrivialBinding env top_lvl occ_fs- id_info expr expr_ty- ; return (floats, Var new_id) }- where- id_info = vanillaIdInfo `setDemandInfo` dmd- expr_ty = exprType expr--makeTrivialBinding :: SimplEnv -> TopLevelFlag- -> FastString -- ^ a "friendly name" to build the new binder from- -> IdInfo- -> OutExpr -- ^ This expression satisfies the let/app invariant- -> OutType -- Type of the expression- -> SimplM (LetFloats, OutId)-makeTrivialBinding env top_lvl occ_fs info expr expr_ty- = do { (floats, expr1) <- prepareRhs env top_lvl occ_fs expr- ; uniq <- getUniqueM- ; let name = mkSystemVarName uniq occ_fs- var = mkLocalIdWithInfo name Many expr_ty info-- -- Now something very like completeBind,- -- but without the postInlineUnconditionally part- ; (arity_type, expr2) <- tryEtaExpandRhs env var expr1- -- Technically we should extend the in-scope set in 'env' with- -- the 'floats' from prepareRHS; but they are all fresh, so there is- -- no danger of introducing name shadowig in eta expansion-- ; unf <- mkLetUnfolding (sm_uf_opts mode) top_lvl InlineRhs var expr2-- ; let final_id = addLetBndrInfo var arity_type unf- bind = NonRec final_id expr2-- ; return ( floats `addLetFlts` unitLetFloat bind, final_id ) }- where- mode = getMode env--bindingOk :: TopLevelFlag -> CoreExpr -> Type -> Bool--- True iff we can have a binding of this expression at this level--- Precondition: the type is the type of the expression-bindingOk top_lvl expr expr_ty- | isTopLevel top_lvl = exprIsTopLevelBindable expr expr_ty- | otherwise = True--{- Note [Cannot trivialise]-~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider:- f :: Int -> Addr#-- foo :: Bar- foo = Bar (f 3)--Then we can't ANF-ise foo, even though we'd like to, because-we can't make a top-level binding for the Addr# (f 3). And if-so we don't want to turn it into- foo = let x = f 3 in Bar x-because we'll just end up inlining x back, and that makes the-simplifier loop. Better not to ANF-ise it at all.--Literal strings are an exception.-- foo = Ptr "blob"#--We want to turn this into:-- foo1 = "blob"#- foo = Ptr foo1--See Note [Core top-level string literals] in GHC.Core.--************************************************************************-* *- Completing a lazy binding-* *-************************************************************************--completeBind- * deals only with Ids, not TyVars- * takes an already-simplified binder and RHS- * is used for both recursive and non-recursive bindings- * is used for both top-level and non-top-level bindings--It does the following:- - tries discarding a dead binding- - tries PostInlineUnconditionally- - add unfolding [this is the only place we add an unfolding]- - add arity- - extend the InScopeSet of the SimplEnv--It does *not* attempt to do let-to-case. Why? Because it is used for- - top-level bindings (when let-to-case is impossible)- - many situations where the "rhs" is known to be a WHNF- (so let-to-case is inappropriate).--Nor does it do the atomic-argument thing--}--completeBind :: SimplEnv- -> BindContext- -> InId -- Old binder- -> OutId -- New binder; can be a JoinId- -> OutExpr -- New RHS- -> SimplM (SimplFloats, SimplEnv)--- completeBind may choose to do its work--- * by extending the substitution (e.g. let x = y in ...)--- * or by adding to the floats in the envt------ Binder /can/ be a JoinId--- Precondition: rhs obeys the let/app invariant-completeBind env bind_cxt old_bndr new_bndr new_rhs- | isCoVar old_bndr- = case new_rhs of- Coercion co -> return (emptyFloats env, extendCvSubst env old_bndr co)- _ -> return (mkFloatBind env (NonRec new_bndr new_rhs))-- | otherwise- = assert (isId new_bndr) $- do { let old_info = idInfo old_bndr- old_unf = realUnfoldingInfo old_info- occ_info = occInfo old_info-- -- Do eta-expansion on the RHS of the binding- -- See Note [Eta-expanding at let bindings] in GHC.Core.Opt.Simplify.Utils- ; (new_arity, eta_rhs) <- tryEtaExpandRhs env new_bndr new_rhs-- -- Simplify the unfolding- ; new_unfolding <- simplLetUnfolding env bind_cxt old_bndr- eta_rhs (idType new_bndr) new_arity old_unf-- ; let new_bndr_w_info = addLetBndrInfo new_bndr new_arity new_unfolding- -- See Note [In-scope set as a substitution]-- ; if postInlineUnconditionally env bind_cxt new_bndr_w_info occ_info eta_rhs-- then -- Inline and discard the binding- do { tick (PostInlineUnconditionally old_bndr)- ; let unf_rhs = maybeUnfoldingTemplate new_unfolding `orElse` eta_rhs- -- See Note [Use occ-anald RHS in postInlineUnconditionally]- ; simplTrace env "PostInlineUnconditionally" (ppr new_bndr <+> ppr unf_rhs) $- return ( emptyFloats env- , extendIdSubst env old_bndr $- DoneEx unf_rhs (isJoinId_maybe new_bndr)) }- -- Use the substitution to make quite, quite sure that the- -- substitution will happen, since we are going to discard the binding-- else -- Keep the binding; do cast worker/wrapper- -- pprTrace "Binding" (ppr new_bndr <+> ppr new_unfolding) $- tryCastWorkerWrapper env bind_cxt old_bndr occ_info new_bndr_w_info eta_rhs }--addLetBndrInfo :: OutId -> ArityType -> Unfolding -> OutId-addLetBndrInfo new_bndr new_arity_type new_unf- = new_bndr `setIdInfo` info5- where- AT oss div = new_arity_type- new_arity = length oss-- info1 = idInfo new_bndr `setArityInfo` new_arity-- -- Unfolding info: Note [Setting the new unfolding]- info2 = info1 `setUnfoldingInfo` new_unf-- -- Demand info: Note [Setting the demand info]- info3 | isEvaldUnfolding new_unf- = zapDemandInfo info2 `orElse` info2- | otherwise- = info2-- -- Bottoming bindings: see Note [Bottoming bindings]- info4 | isDeadEndDiv div = info3 `setDmdSigInfo` bot_sig- `setCprSigInfo` bot_cpr- | otherwise = info3-- bot_sig = mkClosedDmdSig (replicate new_arity topDmd) div- bot_cpr = mkCprSig new_arity botCpr-- -- Zap call arity info. We have used it by now (via- -- `tryEtaExpandRhs`), and the simplifier can invalidate this- -- information, leading to broken code later (e.g. #13479)- info5 = zapCallArityInfo info4---{- Note [Bottoming bindings]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Suppose we have- let x = error "urk"- in ...(case x of <alts>)...-or- let f = \x. error (x ++ "urk")- in ...(case f "foo" of <alts>)...--Then we'd like to drop the dead <alts> immediately. So it's good to-propagate the info that x's RHS is bottom to x's IdInfo as rapidly as-possible.--We use tryEtaExpandRhs on every binding, and it turns out that the-arity computation it performs (via GHC.Core.Opt.Arity.findRhsArity) already-does a simple bottoming-expression analysis. So all we need to do-is propagate that info to the binder's IdInfo.--This showed up in #12150; see comment:16.--Note [Setting the demand info]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-If the unfolding is a value, the demand info may-go pear-shaped, so we nuke it. Example:- let x = (a,b) in- case x of (p,q) -> h p q x-Here x is certainly demanded. But after we've nuked-the case, we'll get just- let x = (a,b) in h a b x-and now x is not demanded (I'm assuming h is lazy)-This really happens. Similarly- let f = \x -> e in ...f..f...-After inlining f at some of its call sites the original binding may-(for example) be no longer strictly demanded.-The solution here is a bit ad hoc...--Note [Use occ-anald RHS in postInlineUnconditionally]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Suppose we postInlineUnconditionally 'f in- let f = \x -> x True in ...(f blah)...-then we'd like to inline the /occ-anald/ RHS for 'f'. If we-use the non-occ-anald version, we'll end up with a- ...(let x = blah in x True)...-and hence an extra Simplifier iteration.--We already /have/ the occ-anald version in the Unfolding for-the Id. Well, maybe not /quite/ always. If the binder is Dead,-postInlineUnconditionally will return True, but we may not have an-unfolding because it's too big. Hence the belt-and-braces `orElse`-in the defn of unf_rhs. The Nothing case probably never happens.---************************************************************************-* *-\subsection[Simplify-simplExpr]{The main function: simplExpr}-* *-************************************************************************--The reason for this OutExprStuff stuff is that we want to float *after*-simplifying a RHS, not before. If we do so naively we get quadratic-behaviour as things float out.--To see why it's important to do it after, consider this (real) example:-- let t = f x- in fst t-==>- let t = let a = e1- b = e2- in (a,b)- in fst t-==>- let a = e1- b = e2- t = (a,b)- in- a -- Can't inline a this round, cos it appears twice-==>- e1--Each of the ==> steps is a round of simplification. We'd save a-whole round if we float first. This can cascade. Consider-- let f = g d- in \x -> ...f...-==>- let f = let d1 = ..d.. in \y -> e- in \x -> ...f...-==>- let d1 = ..d..- in \x -> ...(\y ->e)...--Only in this second round can the \y be applied, and it-might do the same again.--}--simplExpr :: SimplEnv -> CoreExpr -> SimplM CoreExpr-simplExpr !env (Type ty) -- See Note [Bangs in the Simplifier]- = do { ty' <- simplType env ty -- See Note [Avoiding space leaks in OutType]- ; return (Type ty') }--simplExpr env expr- = simplExprC env expr (mkBoringStop expr_out_ty)- where- expr_out_ty :: OutType- expr_out_ty = substTy env (exprType expr)- -- NB: Since 'expr' is term-valued, not (Type ty), this call- -- to exprType will succeed. exprType fails on (Type ty).--simplExprC :: SimplEnv- -> InExpr -- A term-valued expression, never (Type ty)- -> SimplCont- -> SimplM OutExpr- -- Simplify an expression, given a continuation-simplExprC env expr cont- = -- pprTrace "simplExprC" (ppr expr $$ ppr cont) $- do { (floats, expr') <- simplExprF env expr cont- ; -- pprTrace "simplExprC ret" (ppr expr $$ ppr expr') $- -- pprTrace "simplExprC ret3" (ppr (seInScope env')) $- -- pprTrace "simplExprC ret4" (ppr (seLetFloats env')) $- return (wrapFloats floats expr') }-----------------------------------------------------simplExprF :: SimplEnv- -> InExpr -- A term-valued expression, never (Type ty)- -> SimplCont- -> SimplM (SimplFloats, OutExpr)--simplExprF !env e !cont -- See Note [Bangs in the Simplifier]- = {- pprTrace "simplExprF" (vcat- [ ppr e- , text "cont =" <+> ppr cont- , text "inscope =" <+> ppr (seInScope env)- , text "tvsubst =" <+> ppr (seTvSubst env)- , text "idsubst =" <+> ppr (seIdSubst env)- , text "cvsubst =" <+> ppr (seCvSubst env)- ]) $ -}- simplExprF1 env e cont--simplExprF1 :: SimplEnv -> InExpr -> SimplCont- -> SimplM (SimplFloats, OutExpr)--simplExprF1 _ (Type ty) cont- = pprPanic "simplExprF: type" (ppr ty <+> text"cont: " <+> ppr cont)- -- simplExprF does only with term-valued expressions- -- The (Type ty) case is handled separately by simplExpr- -- and by the other callers of simplExprF--simplExprF1 env (Var v) cont = {-#SCC "simplIdF" #-} simplIdF env v cont-simplExprF1 env (Lit lit) cont = {-#SCC "rebuild" #-} rebuild env (Lit lit) cont-simplExprF1 env (Tick t expr) cont = {-#SCC "simplTick" #-} simplTick env t expr cont-simplExprF1 env (Cast body co) cont = {-#SCC "simplCast" #-} simplCast env body co cont-simplExprF1 env (Coercion co) cont = {-#SCC "simplCoercionF" #-} simplCoercionF env co cont--simplExprF1 env (App fun arg) cont- = {-#SCC "simplExprF1-App" #-} case arg of- Type ty -> do { -- The argument type will (almost) certainly be used- -- in the output program, so just force it now.- -- See Note [Avoiding space leaks in OutType]- arg' <- simplType env ty-- -- But use substTy, not simplType, to avoid forcing- -- the hole type; it will likely not be needed.- -- See Note [The hole type in ApplyToTy]- ; let hole' = substTy env (exprType fun)-- ; simplExprF env fun $- ApplyToTy { sc_arg_ty = arg'- , sc_hole_ty = hole'- , sc_cont = cont } }- _ ->- -- Crucially, sc_hole_ty is a /lazy/ binding. It will- -- be forced only if we need to run contHoleType.- -- When these are forced, we might get quadratic behavior;- -- this quadratic blowup could be avoided by drilling down- -- to the function and getting its multiplicities all at once- -- (instead of one-at-a-time). But in practice, we have not- -- observed the quadratic behavior, so this extra entanglement- -- seems not worthwhile.- simplExprF env fun $- ApplyToVal { sc_arg = arg, sc_env = env- , sc_hole_ty = substTy env (exprType fun)- , sc_dup = NoDup, sc_cont = cont }--simplExprF1 env expr@(Lam {}) cont- = {-#SCC "simplExprF1-Lam" #-}- simplLam env (zapLambdaBndrs expr n_args) cont- -- zapLambdaBndrs: the issue here is under-saturated lambdas- -- (\x1. \x2. e) arg1- -- Here x1 might have "occurs-once" occ-info, because occ-info- -- is computed assuming that a group of lambdas is applied- -- all at once. If there are too few args, we must zap the- -- occ-info, UNLESS the remaining binders are one-shot- where- n_args = countArgs cont- -- NB: countArgs counts all the args (incl type args)- -- and likewise drop counts all binders (incl type lambdas)--simplExprF1 env (Case scrut bndr _ alts) cont- = {-#SCC "simplExprF1-Case" #-}- simplExprF env scrut (Select { sc_dup = NoDup, sc_bndr = bndr- , sc_alts = alts- , sc_env = env, sc_cont = cont })--simplExprF1 env (Let (Rec pairs) body) cont- | Just pairs' <- joinPointBindings_maybe pairs- = {-#SCC "simplRecJoinPoin" #-} simplRecJoinPoint env pairs' body cont-- | otherwise- = {-#SCC "simplRecE" #-} simplRecE env pairs body cont--simplExprF1 env (Let (NonRec bndr rhs) body) cont- | Type ty <- rhs -- First deal with type lets (let a = Type ty in e)- = {-#SCC "simplExprF1-NonRecLet-Type" #-}- assert (isTyVar bndr) $- do { ty' <- simplType env ty- ; simplExprF (extendTvSubst env bndr ty') body cont }-- | Just (bndr', rhs') <- joinPointBinding_maybe bndr rhs- = {-#SCC "simplNonRecJoinPoint" #-} simplNonRecJoinPoint env bndr' rhs' body cont-- | otherwise- = {-#SCC "simplNonRecE" #-} simplNonRecE env bndr (rhs, env) body cont--{- Note [Avoiding space leaks in OutType]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Since the simplifier is run for multiple iterations, we need to ensure-that any thunks in the output of one simplifier iteration are forced-by the evaluation of the next simplifier iteration. Otherwise we may-retain multiple copies of the Core program and leak a terrible amount-of memory (as in #13426).--The simplifier is naturally strict in the entire "Expr part" of the-input Core program, because any expression may contain binders, which-we must find in order to extend the SimplEnv accordingly. But types-do not contain binders and so it is tempting to write things like-- simplExpr env (Type ty) = return (Type (substTy env ty)) -- Bad!--This is Bad because the result includes a thunk (substTy env ty) which-retains a reference to the whole simplifier environment; and the next-simplifier iteration will not force this thunk either, because the-line above is not strict in ty.--So instead our strategy is for the simplifier to fully evaluate-OutTypes when it emits them into the output Core program, for example-- simplExpr env (Type ty) = do { ty' <- simplType env ty -- Good- ; return (Type ty') }--where the only difference from above is that simplType calls seqType-on the result of substTy.--However, SimplCont can also contain OutTypes and it's not necessarily-a good idea to force types on the way in to SimplCont, because they-may end up not being used and forcing them could be a lot of wasted-work. T5631 is a good example of this.--- For ApplyToTy's sc_arg_ty, we force the type on the way in because- the type will almost certainly appear as a type argument in the- output program.--- For the hole types in Stop and ApplyToTy, we force the type when we- emit it into the output program, after obtaining it from- contResultType. (The hole type in ApplyToTy is only directly used- to form the result type in a new Stop continuation.)--}-------------------------------------- Simplify a join point, adding the context.--- Context goes *inside* the lambdas. IOW, if the join point has arity n, we do:--- \x1 .. xn -> e => \x1 .. xn -> E[e]--- Note that we need the arity of the join point, since e may be a lambda--- (though this is unlikely). See Note [Join points and case-of-case].-simplJoinRhs :: SimplEnv -> InId -> InExpr -> SimplCont- -> SimplM OutExpr-simplJoinRhs env bndr expr cont- | Just arity <- isJoinId_maybe bndr- = do { let (join_bndrs, join_body) = collectNBinders arity expr- mult = contHoleScaling cont- ; (env', join_bndrs') <- simplLamBndrs env (map (scaleVarBy mult) join_bndrs)- ; join_body' <- simplExprC env' join_body cont- ; return $ mkLams join_bndrs' join_body' }-- | otherwise- = pprPanic "simplJoinRhs" (ppr bndr)------------------------------------simplType :: SimplEnv -> InType -> SimplM OutType- -- Kept monadic just so we can do the seqType- -- See Note [Avoiding space leaks in OutType]-simplType env ty- = -- pprTrace "simplType" (ppr ty $$ ppr (seTvSubst env)) $- seqType new_ty `seq` return new_ty- where- new_ty = substTy env ty------------------------------------simplCoercionF :: SimplEnv -> InCoercion -> SimplCont- -> SimplM (SimplFloats, OutExpr)-simplCoercionF env co cont- = do { co' <- simplCoercion env co- ; rebuild env (Coercion co') cont }--simplCoercion :: SimplEnv -> InCoercion -> SimplM OutCoercion-simplCoercion env co- = do { opts <- getOptCoercionOpts- ; let opt_co = optCoercion opts (getTCvSubst env) co- ; seqCo opt_co `seq` return opt_co }---------------------------------------- | Push a TickIt context outwards past applications and cases, as--- long as this is a non-scoping tick, to let case and application--- optimisations apply.--simplTick :: SimplEnv -> CoreTickish -> InExpr -> SimplCont- -> SimplM (SimplFloats, OutExpr)-simplTick env tickish expr cont- -- A scoped tick turns into a continuation, so that we can spot- -- (scc t (\x . e)) in simplLam and eliminate the scc. If we didn't do- -- it this way, then it would take two passes of the simplifier to- -- reduce ((scc t (\x . e)) e').- -- NB, don't do this with counting ticks, because if the expr is- -- bottom, then rebuildCall will discard the continuation.---- XXX: we cannot do this, because the simplifier assumes that--- the context can be pushed into a case with a single branch. e.g.--- scc<f> case expensive of p -> e--- becomes--- case expensive of p -> scc<f> e------ So I'm disabling this for now. It just means we will do more--- simplifier iterations that necessary in some cases.---- | tickishScoped tickish && not (tickishCounts tickish)--- = simplExprF env expr (TickIt tickish cont)-- -- For unscoped or soft-scoped ticks, we are allowed to float in new- -- cost, so we simply push the continuation inside the tick. This- -- has the effect of moving the tick to the outside of a case or- -- application context, allowing the normal case and application- -- optimisations to fire.- | tickish `tickishScopesLike` SoftScope- = do { (floats, expr') <- simplExprF env expr cont- ; return (floats, mkTick tickish expr')- }-- -- Push tick inside if the context looks like this will allow us to- -- do a case-of-case - see Note [case-of-scc-of-case]- | Select {} <- cont, Just expr' <- push_tick_inside- = simplExprF env expr' cont-- -- We don't want to move the tick, but we might still want to allow- -- floats to pass through with appropriate wrapping (or not, see- -- wrap_floats below)- --- | not (tickishCounts tickish) || tickishCanSplit tickish- -- = wrap_floats-- | otherwise- = no_floating_past_tick-- where-- -- Try to push tick inside a case, see Note [case-of-scc-of-case].- push_tick_inside =- case expr0 of- Case scrut bndr ty alts- -> Just $ Case (tickScrut scrut) bndr ty (map tickAlt alts)- _other -> Nothing- where (ticks, expr0) = stripTicksTop movable (Tick tickish expr)- movable t = not (tickishCounts t) ||- t `tickishScopesLike` NoScope ||- tickishCanSplit t- tickScrut e = foldr mkTick e ticks- -- Alternatives get annotated with all ticks that scope in some way,- -- but we don't want to count entries.- tickAlt (Alt c bs e) = Alt c bs (foldr mkTick e ts_scope)- ts_scope = map mkNoCount $- filter (not . (`tickishScopesLike` NoScope)) ticks-- no_floating_past_tick =- do { let (inc,outc) = splitCont cont- ; (floats, expr1) <- simplExprF env expr inc- ; let expr2 = wrapFloats floats expr1- tickish' = simplTickish env tickish- ; rebuild env (mkTick tickish' expr2) outc- }---- Alternative version that wraps outgoing floats with the tick. This--- results in ticks being duplicated, as we don't make any attempt to--- eliminate the tick if we re-inline the binding (because the tick--- semantics allows unrestricted inlining of HNFs), so I'm not doing--- this any more. FloatOut will catch any real opportunities for--- floating.------ wrap_floats =--- do { let (inc,outc) = splitCont cont--- ; (env', expr') <- simplExprF (zapFloats env) expr inc--- ; let tickish' = simplTickish env tickish--- ; let wrap_float (b,rhs) = (zapIdDmdSig (setIdArity b 0),--- mkTick (mkNoCount tickish') rhs)--- -- when wrapping a float with mkTick, we better zap the Id's--- -- strictness info and arity, because it might be wrong now.--- ; let env'' = addFloats env (mapFloats env' wrap_float)--- ; rebuild env'' expr' (TickIt tickish' outc)--- }--- simplTickish env tickish- | Breakpoint ext n ids <- tickish- = Breakpoint ext n (map (getDoneId . substId env) ids)- | otherwise = tickish-- -- Push type application and coercion inside a tick- splitCont :: SimplCont -> (SimplCont, SimplCont)- splitCont cont@(ApplyToTy { sc_cont = tail }) = (cont { sc_cont = inc }, outc)- where (inc,outc) = splitCont tail- splitCont (CastIt co c) = (CastIt co inc, outc)- where (inc,outc) = splitCont c- splitCont other = (mkBoringStop (contHoleType other), other)-- getDoneId (DoneId id) = id- getDoneId (DoneEx e _) = getIdFromTrivialExpr e -- Note [substTickish] in GHC.Core.Subst- getDoneId other = pprPanic "getDoneId" (ppr other)---- Note [case-of-scc-of-case]--- ~~~~~~~~~~~~~~~~~~~~~~~~~~--- It's pretty important to be able to transform case-of-case when--- there's an SCC in the way. For example, the following comes up--- in nofib/real/compress/Encode.hs:------ case scctick<code_string.r1>--- case $wcode_string_r13s wild_XC w1_s137 w2_s138 l_aje--- of _ { (# ww1_s13f, ww2_s13g, ww3_s13h #) ->--- (ww1_s13f, ww2_s13g, ww3_s13h)--- }--- of _ { (ww_s12Y, ww1_s12Z, ww2_s130) ->--- tick<code_string.f1>--- (ww_s12Y,--- ww1_s12Z,--- PTTrees.PT--- @ GHC.Types.Char @ GHC.Types.Int wild2_Xj ww2_s130 r_ajf)--- }------ We really want this case-of-case to fire, because then the 3-tuple--- will go away (indeed, the CPR optimisation is relying on this--- happening). But the scctick is in the way - we need to push it--- inside to expose the case-of-case. So we perform this--- transformation on the inner case:------ scctick c (case e of { p1 -> e1; ...; pn -> en })--- ==>--- case (scctick c e) of { p1 -> scc c e1; ...; pn -> scc c en }------ So we've moved a constant amount of work out of the scc to expose--- the case. We only do this when the continuation is interesting: in--- for now, it has to be another Case (maybe generalise this later).--{--************************************************************************-* *-\subsection{The main rebuilder}-* *-************************************************************************--}--rebuild :: SimplEnv -> OutExpr -> SimplCont -> SimplM (SimplFloats, OutExpr)--- At this point the substitution in the SimplEnv should be irrelevant;--- only the in-scope set matters-rebuild env expr cont- = case cont of- Stop {} -> return (emptyFloats env, expr)- TickIt t cont -> rebuild env (mkTick t expr) cont- CastIt co cont -> rebuild env (mkCast expr co) cont- -- NB: mkCast implements the (Coercion co |> g) optimisation-- Select { sc_bndr = bndr, sc_alts = alts, sc_env = se, sc_cont = cont }- -> rebuildCase (se `setInScopeFromE` env) expr bndr alts cont-- StrictArg { sc_fun = fun, sc_cont = cont, sc_fun_ty = fun_ty }- -> rebuildCall env (addValArgTo fun expr fun_ty ) cont- StrictBind { sc_bndr = b, sc_body = body- , sc_env = se, sc_cont = cont }- -> do { (floats1, env') <- simplNonRecX (se `setInScopeFromE` env) b expr- -- expr satisfies let/app since it started life- -- in a call to simplNonRecE- ; (floats2, expr') <- simplLam env' body cont- ; return (floats1 `addFloats` floats2, expr') }-- ApplyToTy { sc_arg_ty = ty, sc_cont = cont}- -> rebuild env (App expr (Type ty)) cont-- ApplyToVal { sc_arg = arg, sc_env = se, sc_dup = dup_flag, sc_cont = cont}- -- See Note [Avoid redundant simplification]- -> do { (_, _, arg') <- simplArg env dup_flag se arg- ; rebuild env (App expr arg') cont }--{--************************************************************************-* *-\subsection{Lambdas}-* *-************************************************************************--}--{- Note [Optimising reflexivity]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-It's important (for compiler performance) to get rid of reflexivity as soon-as it appears. See #11735, #14737, and #15019.--In particular, we want to behave well on-- * e |> co1 |> co2- where the two happen to cancel out entirely. That is quite common;- e.g. a newtype wrapping and unwrapping cancel.--- * (f |> co) @t1 @t2 ... @tn x1 .. xm- Here we will use pushCoTyArg and pushCoValArg successively, which- build up NthCo stacks. Silly to do that if co is reflexive.--However, we don't want to call isReflexiveCo too much, because it uses-type equality which is expensive on big types (#14737 comment:7).--A good compromise (determined experimentally) seems to be to call-isReflexiveCo- * when composing casts, and- * at the end--In investigating this I saw missed opportunities for on-the-fly-coercion shrinkage. See #15090.--}---simplCast :: SimplEnv -> InExpr -> Coercion -> SimplCont- -> SimplM (SimplFloats, OutExpr)-simplCast env body co0 cont0- = do { co1 <- {-#SCC "simplCast-simplCoercion" #-} simplCoercion env co0- ; cont1 <- {-#SCC "simplCast-addCoerce" #-}- if isReflCo co1- then return cont0 -- See Note [Optimising reflexivity]- else addCoerce co1 cont0- ; {-#SCC "simplCast-simplExprF" #-} simplExprF env body cont1 }- where- -- If the first parameter is MRefl, then simplifying revealed a- -- reflexive coercion. Omit.- addCoerceM :: MOutCoercion -> SimplCont -> SimplM SimplCont- addCoerceM MRefl cont = return cont- addCoerceM (MCo co) cont = addCoerce co cont-- addCoerce :: OutCoercion -> SimplCont -> SimplM SimplCont- addCoerce co1 (CastIt co2 cont) -- See Note [Optimising reflexivity]- | isReflexiveCo co' = return cont- | otherwise = addCoerce co' cont- where- co' = mkTransCo co1 co2-- addCoerce co (ApplyToTy { sc_arg_ty = arg_ty, sc_cont = tail })- | Just (arg_ty', m_co') <- pushCoTyArg co arg_ty- = {-#SCC "addCoerce-pushCoTyArg" #-}- do { tail' <- addCoerceM m_co' tail- ; return (ApplyToTy { sc_arg_ty = arg_ty'- , sc_cont = tail'- , sc_hole_ty = coercionLKind co }) }- -- NB! As the cast goes past, the- -- type of the hole changes (#16312)-- -- (f |> co) e ===> (f (e |> co1)) |> co2- -- where co :: (s1->s2) ~ (t1->t2)- -- co1 :: t1 ~ s1- -- co2 :: s2 ~ t2- addCoerce co cont@(ApplyToVal { sc_arg = arg, sc_env = arg_se- , sc_dup = dup, sc_cont = tail })- | Just (m_co1, m_co2) <- pushCoValArg co- , fixed_rep m_co1- = {-#SCC "addCoerce-pushCoValArg" #-}- do { tail' <- addCoerceM m_co2 tail- ; case m_co1 of {- MRefl -> return (cont { sc_cont = tail'- , sc_hole_ty = coercionLKind co }) ;- -- Avoid simplifying if possible;- -- See Note [Avoiding exponential behaviour]-- MCo co1 ->- do { (dup', arg_se', arg') <- simplArg env dup arg_se arg- -- When we build the ApplyTo we can't mix the OutCoercion- -- 'co' with the InExpr 'arg', so we simplify- -- to make it all consistent. It's a bit messy.- -- But it isn't a common case.- -- Example of use: #995- ; return (ApplyToVal { sc_arg = mkCast arg' co1- , sc_env = arg_se'- , sc_dup = dup'- , sc_cont = tail'- , sc_hole_ty = coercionLKind co }) } } }-- addCoerce co cont- | isReflexiveCo co = return cont -- Having this at the end makes a huge- -- difference in T12227, for some reason- -- See Note [Optimising reflexivity]- | otherwise = return (CastIt co cont)-- fixed_rep :: MCoercionR -> Bool- fixed_rep MRefl = True- fixed_rep (MCo co) = typeHasFixedRuntimeRep $ coercionRKind co- -- Without this check, we can get an argument which does not- -- have a fixed runtime representation.- -- See Note [Representation polymorphism invariants] in GHC.Core- -- test: typecheck/should_run/EtaExpandLevPoly--simplArg :: SimplEnv -> DupFlag -> StaticEnv -> CoreExpr- -> SimplM (DupFlag, StaticEnv, OutExpr)-simplArg env dup_flag arg_env arg- | isSimplified dup_flag- = return (dup_flag, arg_env, arg)- | otherwise- = do { let arg_env' = arg_env `setInScopeFromE` env- ; arg' <- simplExpr arg_env' arg- ; return (Simplified, zapSubstEnv arg_env', arg') }- -- Return a StaticEnv that includes the in-scope set from 'env',- -- because arg' may well mention those variables (#20639)--{--************************************************************************-* *-\subsection{Lambdas}-* *-************************************************************************--}--simplLam :: SimplEnv -> InExpr -> SimplCont- -> SimplM (SimplFloats, OutExpr)--simplLam env (Lam bndr body) cont = simpl_lam env bndr body cont-simplLam env expr cont = simplExprF env expr cont--simpl_lam :: SimplEnv -> InBndr -> InExpr -> SimplCont- -> SimplM (SimplFloats, OutExpr)---- Type beta-reduction-simpl_lam env bndr body (ApplyToTy { sc_arg_ty = arg_ty, sc_cont = cont })- = do { tick (BetaReduction bndr)- ; simplLam (extendTvSubst env bndr arg_ty) body cont }---- Value beta-reduction-simpl_lam env bndr body (ApplyToVal { sc_arg = arg, sc_env = arg_se- , sc_cont = cont, sc_dup = dup })- | isSimplified dup -- Don't re-simplify if we've simplified it once- -- See Note [Avoiding exponential behaviour]- = do { tick (BetaReduction bndr)- ; (floats1, env') <- simplNonRecX env bndr arg- ; (floats2, expr') <- simplLam env' body cont- ; return (floats1 `addFloats` floats2, expr') }-- | otherwise- = do { tick (BetaReduction bndr)- ; simplNonRecE env bndr (arg, arg_se) body cont }---- Discard a non-counting tick on a lambda. This may change the--- cost attribution slightly (moving the allocation of the--- lambda elsewhere), but we don't care: optimisation changes--- cost attribution all the time.-simpl_lam env bndr body (TickIt tickish cont)- | not (tickishCounts tickish)- = simpl_lam env bndr body cont---- Not enough args, so there are real lambdas left to put in the result-simpl_lam env bndr body cont- = do { let (inner_bndrs, inner_body) = collectBinders body- ; (env', bndrs') <- simplLamBndrs env (bndr:inner_bndrs)- ; body' <- simplExpr env' inner_body- ; new_lam <- mkLam env' bndrs' body' cont- ; rebuild env' new_lam cont }----------------simplLamBndr :: SimplEnv -> InBndr -> SimplM (SimplEnv, OutBndr)--- Historically this had a special case for when a lambda-binder--- could have a stable unfolding;--- see Historical Note [Case binders and join points]--- But now it is much simpler! We now only remove unfoldings.--- See Note [Never put `OtherCon` unfoldings on lambda binders]-simplLamBndr env bndr = simplBinder env (zapIdUnfolding bndr)--simplLamBndrs :: SimplEnv -> [InBndr] -> SimplM (SimplEnv, [OutBndr])-simplLamBndrs env bndrs = mapAccumLM simplLamBndr env bndrs---------------------simplNonRecE :: SimplEnv- -> InId -- The binder, always an Id- -- Never a join point- -> (InExpr, SimplEnv) -- Rhs of binding (or arg of lambda)- -> InExpr -- Body of the let/lambda- -> SimplCont- -> SimplM (SimplFloats, OutExpr)---- simplNonRecE is used for--- * non-top-level non-recursive non-join-point lets in expressions--- * beta reduction------ simplNonRec env b (rhs, rhs_se) body k--- = let env in--- cont< let b = rhs_se(rhs) in body >------ It deals with strict bindings, via the StrictBind continuation,--- which may abort the whole process------ Precondition: rhs satisfies the let/app invariant--- Note [Core let/app invariant] in GHC.Core--simplNonRecE env bndr (rhs, rhs_se) body cont- | assert (isId bndr && not (isJoinId bndr) ) True- , Just env' <- preInlineUnconditionally env NotTopLevel bndr rhs rhs_se- = do { tick (PreInlineUnconditionally bndr)- ; -- pprTrace "preInlineUncond" (ppr bndr <+> ppr rhs) $- simplLam env' body cont }-- | otherwise- = do { (env1, bndr1) <- simplNonRecBndr env bndr-- -- Deal with strict bindings- -- See Note [Dark corner with representation polymorphism]- ; if isStrictId bndr1 && sm_case_case (getMode env)- then simplExprF (rhs_se `setInScopeFromE` env) rhs- (StrictBind { sc_bndr = bndr, sc_body = body- , sc_env = env, sc_cont = cont, sc_dup = NoDup })-- -- Deal with lazy bindings- else do- { (env2, bndr2) <- addBndrRules env1 bndr bndr1 (BC_Let NotTopLevel NonRecursive)- ; (floats1, env3) <- simplLazyBind env2 NotTopLevel NonRecursive bndr bndr2 rhs rhs_se- ; (floats2, expr') <- simplLam env3 body cont- ; return (floats1 `addFloats` floats2, expr') } }---------------------simplRecE :: SimplEnv- -> [(InId, InExpr)]- -> InExpr- -> SimplCont- -> SimplM (SimplFloats, OutExpr)---- simplRecE is used for--- * non-top-level recursive lets in expressions--- Precondition: not a join-point binding-simplRecE env pairs body cont- = do { let bndrs = map fst pairs- ; massert (all (not . isJoinId) bndrs)- ; env1 <- simplRecBndrs env bndrs- -- NB: bndrs' don't have unfoldings or rules- -- We add them as we go down- ; (floats1, env2) <- simplRecBind env1 (BC_Let NotTopLevel Recursive) pairs- ; (floats2, expr') <- simplExprF env2 body cont- ; return (floats1 `addFloats` floats2, expr') }--{- Note [Dark corner with representation polymorphism]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-In `simplNonRecE`, the call to `isStrictId` will fail if the binder-does not have a fixed runtime representation, e.g. if it is of kind (TYPE r).-So we are careful to call `isStrictId` on the OutId, not the InId, in case we have- ((\(r::RuntimeRep) \(x::TYPE r). blah) Lifted arg)-That will lead to `simplNonRecE env (x::TYPE r) arg`, and we can't tell-if x is lifted or unlifted from that.--We only get such redexes from the compulsory inlining of a wired-in,-representation-polymorphic function like `rightSection` (see-GHC.Types.Id.Make). Mind you, SimpleOpt should probably have inlined-such compulsory inlinings already, but belt and braces does no harm.--Plus, it turns out that GHC.Driver.Main.hscCompileCoreExpr calls the-Simplifier without first calling SimpleOpt, so anything involving-GHCi or TH and operator sections will fall over if we don't take-care here.--Note [Avoiding exponential behaviour]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-One way in which we can get exponential behaviour is if we simplify a-big expression, and the re-simplify it -- and then this happens in a-deeply-nested way. So we must be jolly careful about re-simplifying-an expression. That is why simplNonRecX does not try-preInlineUnconditionally (unlike simplNonRecE).--Example:- f BIG, where f has a RULE-Then- * We simplify BIG before trying the rule; but the rule does not fire- * We inline f = \x. x True- * So if we did preInlineUnconditionally we'd re-simplify (BIG True)--However, if BIG has /not/ already been simplified, we'd /like/ to-simplify BIG True; maybe good things happen. That is why--* simplLam has- - a case for (isSimplified dup), which goes via simplNonRecX, and- - a case for the un-simplified case, which goes via simplNonRecE--* We go to some efforts to avoid unnecessarily simplifying ApplyToVal,- in at least two places- - In simplCast/addCoerce, where we check for isReflCo- - In rebuildCall we avoid simplifying arguments before we have to- (see Note [Trying rewrite rules])---************************************************************************-* *- Join points-* *-********************************************************************* -}--{- Note [Rules and unfolding for join points]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Suppose we have-- simplExpr (join j x = rhs ) cont- ( {- RULE j (p:ps) = blah -} )- ( {- StableUnfolding j = blah -} )- (in blah )--Then we will push 'cont' into the rhs of 'j'. But we should *also* push-'cont' into the RHS of- * Any RULEs for j, e.g. generated by SpecConstr- * Any stable unfolding for j, e.g. the result of an INLINE pragma--Simplifying rules and stable-unfoldings happens a bit after-simplifying the right-hand side, so we remember whether or not it-is a join point, and what 'cont' is, in a value of type MaybeJoinCont--#13900 was caused by forgetting to push 'cont' into the RHS-of a SpecConstr-generated RULE for a join point.--}--simplNonRecJoinPoint :: SimplEnv -> InId -> InExpr- -> InExpr -> SimplCont- -> SimplM (SimplFloats, OutExpr)-simplNonRecJoinPoint env bndr rhs body cont- | assert (isJoinId bndr ) True- , Just env' <- preInlineUnconditionally env NotTopLevel bndr rhs env- = do { tick (PreInlineUnconditionally bndr)- ; simplExprF env' body cont }-- | otherwise- = wrapJoinCont env cont $ \ env cont ->- do { -- We push join_cont into the join RHS and the body;- -- and wrap wrap_cont around the whole thing- ; let mult = contHoleScaling cont- res_ty = contResultType cont- ; (env1, bndr1) <- simplNonRecJoinBndr env bndr mult res_ty- ; (env2, bndr2) <- addBndrRules env1 bndr bndr1 (BC_Join cont)- ; (floats1, env3) <- simplJoinBind env2 cont bndr bndr2 rhs env- ; (floats2, body') <- simplExprF env3 body cont- ; return (floats1 `addFloats` floats2, body') }----------------------simplRecJoinPoint :: SimplEnv -> [(InId, InExpr)]- -> InExpr -> SimplCont- -> SimplM (SimplFloats, OutExpr)-simplRecJoinPoint env pairs body cont- = wrapJoinCont env cont $ \ env cont ->- do { let bndrs = map fst pairs- mult = contHoleScaling cont- res_ty = contResultType cont- ; env1 <- simplRecJoinBndrs env bndrs mult res_ty- -- NB: bndrs' don't have unfoldings or rules- -- We add them as we go down- ; (floats1, env2) <- simplRecBind env1 (BC_Join cont) pairs- ; (floats2, body') <- simplExprF env2 body cont- ; return (floats1 `addFloats` floats2, body') }-----------------------wrapJoinCont :: SimplEnv -> SimplCont- -> (SimplEnv -> SimplCont -> SimplM (SimplFloats, OutExpr))- -> SimplM (SimplFloats, OutExpr)--- Deal with making the continuation duplicable if necessary,--- and with the no-case-of-case situation.-wrapJoinCont env cont thing_inside- | contIsStop cont -- Common case; no need for fancy footwork- = thing_inside env cont-- | not (sm_case_case (getMode env))- -- See Note [Join points with -fno-case-of-case]- = do { (floats1, expr1) <- thing_inside env (mkBoringStop (contHoleType cont))- ; let (floats2, expr2) = wrapJoinFloatsX floats1 expr1- ; (floats3, expr3) <- rebuild (env `setInScopeFromF` floats2) expr2 cont- ; return (floats2 `addFloats` floats3, expr3) }-- | otherwise- -- Normal case; see Note [Join points and case-of-case]- = do { (floats1, cont') <- mkDupableCont env cont- ; (floats2, result) <- thing_inside (env `setInScopeFromF` floats1) cont'- ; return (floats1 `addFloats` floats2, result) }------------------------trimJoinCont :: Id -> Maybe JoinArity -> SimplCont -> SimplCont--- Drop outer context from join point invocation (jump)--- See Note [Join points and case-of-case]--trimJoinCont _ Nothing cont- = cont -- Not a jump-trimJoinCont var (Just arity) cont- = trim arity cont- where- trim 0 cont@(Stop {})- = cont- trim 0 cont- = mkBoringStop (contResultType cont)- trim n cont@(ApplyToVal { sc_cont = k })- = cont { sc_cont = trim (n-1) k }- trim n cont@(ApplyToTy { sc_cont = k })- = cont { sc_cont = trim (n-1) k } -- join arity counts types!- trim _ cont- = pprPanic "completeCall" $ ppr var $$ ppr cont---{- Note [Join points and case-of-case]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-When we perform the case-of-case transform (or otherwise push continuations-inward), we want to treat join points specially. Since they're always-tail-called and we want to maintain this invariant, we can do this (for any-evaluation context E):-- E[join j = e- in case ... of- A -> jump j 1- B -> jump j 2- C -> f 3]-- -->-- join j = E[e]- in case ... of- A -> jump j 1- B -> jump j 2- C -> E[f 3]--As is evident from the example, there are two components to this behavior:-- 1. When entering the RHS of a join point, copy the context inside.- 2. When a join point is invoked, discard the outer context.--We need to be very careful here to remain consistent---neither part is-optional!--We need do make the continuation E duplicable (since we are duplicating it)-with mkDupableCont.---Note [Join points with -fno-case-of-case]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Supose case-of-case is switched off, and we are simplifying-- case (join j x = <j-rhs> in- case y of- A -> j 1- B -> j 2- C -> e) of <outer-alts>--Usually, we'd push the outer continuation (case . of <outer-alts>) into-both the RHS and the body of the join point j. But since we aren't doing-case-of-case we may then end up with this totally bogus result-- join x = case <j-rhs> of <outer-alts> in- case (case y of- A -> j 1- B -> j 2- C -> e) of <outer-alts>--This would be OK in the language of the paper, but not in GHC: j is no longer-a join point. We can only do the "push continuation into the RHS of the-join point j" if we also push the continuation right down to the /jumps/ to-j, so that it can evaporate there. If we are doing case-of-case, we'll get to-- join x = case <j-rhs> of <outer-alts> in- case y of- A -> j 1- B -> j 2- C -> case e of <outer-alts>--which is great.--Bottom line: if case-of-case is off, we must stop pushing the continuation-inwards altogether at any join point. Instead simplify the (join ... in ...)-with a Stop continuation, and wrap the original continuation around the-outside. Surprisingly tricky!---************************************************************************-* *- Variables-* *-************************************************************************--}--simplVar :: SimplEnv -> InVar -> SimplM OutExpr--- Look up an InVar in the environment-simplVar env var- -- Why $! ? See Note [Bangs in the Simplifier]- | isTyVar var = return $! Type $! (substTyVar env var)- | isCoVar var = return $! Coercion $! (substCoVar env var)- | otherwise- = case substId env var of- ContEx tvs cvs ids e -> let env' = setSubstEnv env tvs cvs ids- in simplExpr env' e- DoneId var1 -> return (Var var1)- DoneEx e _ -> return e--simplIdF :: SimplEnv -> InId -> SimplCont -> SimplM (SimplFloats, OutExpr)-simplIdF env var cont- = case substId env var of- ContEx tvs cvs ids e ->- let env' = setSubstEnv env tvs cvs ids- in simplExprF env' e cont- -- Don't trim; haven't already simplified e,- -- so the cont is not embodied in e-- DoneId var1 ->- let cont' = trimJoinCont var (isJoinId_maybe var1) cont- in completeCall env var1 cont'-- DoneEx e mb_join ->- let env' = zapSubstEnv env- cont' = trimJoinCont var mb_join cont- in simplExprF env' e cont'- -- Note [zapSubstEnv]- -- ~~~~~~~~~~~~~~~~~~- -- The template is already simplified, so don't re-substitute.- -- This is VITAL. Consider- -- let x = e in- -- let y = \z -> ...x... in- -- \ x -> ...y...- -- We'll clone the inner \x, adding x->x' in the id_subst- -- Then when we inline y, we must *not* replace x by x' in- -- the inlined copy!!-------------------------------------------------------------- Dealing with a call site--completeCall :: SimplEnv -> OutId -> SimplCont -> SimplM (SimplFloats, OutExpr)-completeCall env var cont- | Just expr <- callSiteInline logger uf_opts case_depth var active_unf- lone_variable arg_infos interesting_cont- -- Inline the variable's RHS- = do { checkedTick (UnfoldingDone var)- ; dump_inline expr cont- ; let env1 = zapSubstEnv env- ; simplExprF env1 expr cont }-- | otherwise- -- Don't inline; instead rebuild the call- = do { rule_base <- getSimplRules- ; let rules = getRules rule_base var- info = mkArgInfo env var rules- n_val_args call_cont- ; rebuildCall env info cont }-- where- uf_opts = seUnfoldingOpts env- case_depth = seCaseDepth env- logger = seLogger env- (lone_variable, arg_infos, call_cont) = contArgs cont- n_val_args = length arg_infos- interesting_cont = interestingCallContext env call_cont- active_unf = activeUnfolding (getMode env) var-- log_inlining doc- = liftIO $ logDumpFile logger (mkDumpStyle alwaysQualify)- Opt_D_dump_inlinings- "" FormatText doc-- dump_inline unfolding cont- | not (logHasDumpFlag logger Opt_D_dump_inlinings) = return ()- | not (logHasDumpFlag logger Opt_D_verbose_core2core)- = when (isExternalName (idName var)) $- log_inlining $- sep [text "Inlining done:", nest 4 (ppr var)]- | otherwise- = log_inlining $- sep [text "Inlining done: " <> ppr var,- nest 4 (vcat [text "Inlined fn: " <+> nest 2 (ppr unfolding),- text "Cont: " <+> ppr cont])]--rebuildCall :: SimplEnv- -> ArgInfo- -> SimplCont- -> SimplM (SimplFloats, OutExpr)--- We decided not to inline, so--- - simplify the arguments--- - try rewrite rules--- - and rebuild------------ Bottoming applications ---------------rebuildCall env (ArgInfo { ai_fun = fun, ai_args = rev_args, ai_dmds = [] }) cont- -- When we run out of strictness args, it means- -- that the call is definitely bottom; see GHC.Core.Opt.Simplify.Utils.mkArgInfo- -- Then we want to discard the entire strict continuation. E.g.- -- * case (error "hello") of { ... }- -- * (error "Hello") arg- -- * f (error "Hello") where f is strict- -- etc- -- Then, especially in the first of these cases, we'd like to discard- -- the continuation, leaving just the bottoming expression. But the- -- type might not be right, so we may have to add a coerce.- | not (contIsTrivial cont) -- Only do this if there is a non-trivial- -- continuation to discard, else we do it- -- again and again!- = seqType cont_ty `seq` -- See Note [Avoiding space leaks in OutType]- return (emptyFloats env, castBottomExpr res cont_ty)- where- res = argInfoExpr fun rev_args- cont_ty = contResultType cont------------ Try rewrite RULES ----------------- See Note [Trying rewrite rules]-rebuildCall env info@(ArgInfo { ai_fun = fun, ai_args = rev_args- , ai_rules = Just (nr_wanted, rules) }) cont- | nr_wanted == 0 || no_more_args- , let info' = info { ai_rules = Nothing }- = -- We've accumulated a simplified call in <fun,rev_args>- -- so try rewrite rules; see Note [RULES apply to simplified arguments]- -- See also Note [Rules for recursive functions]- do { mb_match <- tryRules env rules fun (reverse rev_args) cont- ; case mb_match of- Just (env', rhs, cont') -> simplExprF env' rhs cont'- Nothing -> rebuildCall env info' cont }- where- no_more_args = case cont of- ApplyToTy {} -> False- ApplyToVal {} -> False- _ -> True------------- Simplify applications and casts ---------------rebuildCall env info (CastIt co cont)- = rebuildCall env (addCastTo info co) cont--rebuildCall env info (ApplyToTy { sc_arg_ty = arg_ty, sc_hole_ty = hole_ty, sc_cont = cont })- = rebuildCall env (addTyArgTo info arg_ty hole_ty) cont------------ The runRW# rule. Do this after absorbing all arguments --------- See Note [Simplification of runRW#] in GHC.CoreToSTG.Prep.------ runRW# :: forall (r :: RuntimeRep) (o :: TYPE r). (State# RealWorld -> o) -> o--- K[ runRW# rr ty body ] --> runRW rr' ty' (\s. K[ body s ])-rebuildCall env (ArgInfo { ai_fun = fun_id, ai_args = rev_args })- (ApplyToVal { sc_arg = arg, sc_env = arg_se- , sc_cont = cont, sc_hole_ty = fun_ty })- | fun_id `hasKey` runRWKey- , [ TyArg {}, TyArg {} ] <- rev_args- -- Do this even if (contIsStop cont)- -- See Note [No eta-expansion in runRW#]- = do { let arg_env = arg_se `setInScopeFromE` env- ty' = contResultType cont-- -- If the argument is a literal lambda already, take a short cut- -- This isn't just efficiency; if we don't do this we get a beta-redex- -- every time, so the simplifier keeps doing more iterations.- ; arg' <- case arg of- Lam s body -> do { (env', s') <- simplBinder arg_env s- ; body' <- simplExprC env' body cont- ; return (Lam s' body') }- -- Important: do not try to eta-expand this lambda- -- See Note [No eta-expansion in runRW#]- _ -> do { s' <- newId (fsLit "s") Many realWorldStatePrimTy- ; let (m,_,_) = splitFunTy fun_ty- env' = arg_env `addNewInScopeIds` [s']- cont' = ApplyToVal { sc_dup = Simplified, sc_arg = Var s'- , sc_env = env', sc_cont = cont- , sc_hole_ty = mkVisFunTy m realWorldStatePrimTy ty' }- -- cont' applies to s', then K- ; body' <- simplExprC env' arg cont'- ; return (Lam s' body') }-- ; let rr' = getRuntimeRep ty'- call' = mkApps (Var fun_id) [mkTyArg rr', mkTyArg ty', arg']- ; return (emptyFloats env, call') }--rebuildCall env fun_info- (ApplyToVal { sc_arg = arg, sc_env = arg_se- , sc_dup = dup_flag, sc_hole_ty = fun_ty- , sc_cont = cont })- -- Argument is already simplified- | isSimplified dup_flag -- See Note [Avoid redundant simplification]- = rebuildCall env (addValArgTo fun_info arg fun_ty) cont-- -- Strict arguments- | isStrictArgInfo fun_info- , sm_case_case (getMode env)- = -- pprTrace "Strict Arg" (ppr arg $$ ppr (seIdSubst env) $$ ppr (seInScope env)) $- simplExprF (arg_se `setInScopeFromE` env) arg- (StrictArg { sc_fun = fun_info, sc_fun_ty = fun_ty- , sc_dup = Simplified- , sc_cont = cont })- -- Note [Shadowing]-- -- Lazy arguments- | otherwise- -- DO NOT float anything outside, hence simplExprC- -- There is no benefit (unlike in a let-binding), and we'd- -- have to be very careful about bogus strictness through- -- floating a demanded let.- = do { arg' <- simplExprC (arg_se `setInScopeFromE` env) arg- (mkLazyArgStop arg_ty (lazyArgContext fun_info))- ; rebuildCall env (addValArgTo fun_info arg' fun_ty) cont }- where- arg_ty = funArgTy fun_ty------------- No further useful info, revert to generic rebuild -------------rebuildCall env (ArgInfo { ai_fun = fun, ai_args = rev_args }) cont- = rebuild env (argInfoExpr fun rev_args) cont--{- Note [Trying rewrite rules]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider an application (f e1 e2 e3) where the e1,e2,e3 are not yet-simplified. We want to simplify enough arguments to allow the rules-to apply, but it's more efficient to avoid simplifying e2,e3 if e1 alone-is sufficient. Example: class ops- (+) dNumInt e2 e3-If we rewrite ((+) dNumInt) to plusInt, we can take advantage of the-latter's strictness when simplifying e2, e3. Moreover, suppose we have- RULE f Int = \x. x True--Then given (f Int e1) we rewrite to- (\x. x True) e1-without simplifying e1. Now we can inline x into its unique call site,-and absorb the True into it all in the same pass. If we simplified-e1 first, we couldn't do that; see Note [Avoiding exponential behaviour].--So we try to apply rules if either- (a) no_more_args: we've run out of argument that the rules can "see"- (b) nr_wanted: none of the rules wants any more arguments---Note [RULES apply to simplified arguments]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-It's very desirable to try RULES once the arguments have been simplified, because-doing so ensures that rule cascades work in one pass. Consider- {-# RULES g (h x) = k x- f (k x) = x #-}- ...f (g (h x))...-Then we want to rewrite (g (h x)) to (k x) and only then try f's rules. If-we match f's rules against the un-simplified RHS, it won't match. This-makes a particularly big difference when superclass selectors are involved:- op ($p1 ($p2 (df d)))-We want all this to unravel in one sweep.--Note [Avoid redundant simplification]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Because RULES apply to simplified arguments, there's a danger of repeatedly-simplifying already-simplified arguments. An important example is that of- (>>=) d e1 e2-Here e1, e2 are simplified before the rule is applied, but don't really-participate in the rule firing. So we mark them as Simplified to avoid-re-simplifying them.--Note [Shadowing]-~~~~~~~~~~~~~~~~-This part of the simplifier may break the no-shadowing invariant-Consider- f (...(\a -> e)...) (case y of (a,b) -> e')-where f is strict in its second arg-If we simplify the innermost one first we get (...(\a -> e)...)-Simplifying the second arg makes us float the case out, so we end up with- case y of (a,b) -> f (...(\a -> e)...) e'-So the output does not have the no-shadowing invariant. However, there is-no danger of getting name-capture, because when the first arg was simplified-we used an in-scope set that at least mentioned all the variables free in its-static environment, and that is enough.--We can't just do innermost first, or we'd end up with a dual problem:- case x of (a,b) -> f e (...(\a -> e')...)--I spent hours trying to recover the no-shadowing invariant, but I just could-not think of an elegant way to do it. The simplifier is already knee-deep in-continuations. We have to keep the right in-scope set around; AND we have-to get the effect that finding (error "foo") in a strict arg position will-discard the entire application and replace it with (error "foo"). Getting-all this at once is TOO HARD!--Note [No eta-expansion in runRW#]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-When we see `runRW# (\s. blah)` we must not attempt to eta-expand that-lambda. Why not? Because-* `blah` can mention join points bound outside the runRW#-* eta-expansion uses arityType, and-* `arityType` cannot cope with free join Ids:--So the simplifier spots the literal lambda, and simplifies inside it.-It's a very special lambda, because it is the one the OccAnal spots and-allows join points bound /outside/ to be called /inside/.--See Note [No free join points in arityType] in GHC.Core.Opt.Arity--************************************************************************-* *- Rewrite rules-* *-************************************************************************--}--tryRules :: SimplEnv -> [CoreRule]- -> Id -> [ArgSpec]- -> SimplCont- -> SimplM (Maybe (SimplEnv, CoreExpr, SimplCont))--tryRules env rules fn args call_cont- | null rules- = return Nothing--{- Disabled until we fix #8326- | fn `hasKey` tagToEnumKey -- See Note [Optimising tagToEnum#]- , [_type_arg, val_arg] <- args- , Select dup bndr ((_,[],rhs1) : rest_alts) se cont <- call_cont- , isDeadBinder bndr- = do { let enum_to_tag :: CoreAlt -> CoreAlt- -- Takes K -> e into tagK# -> e- -- where tagK# is the tag of constructor K- enum_to_tag (DataAlt con, [], rhs)- = assert (isEnumerationTyCon (dataConTyCon con) )- (LitAlt tag, [], rhs)- where- tag = mkLitInt dflags (toInteger (dataConTag con - fIRST_TAG))- enum_to_tag alt = pprPanic "tryRules: tagToEnum" (ppr alt)-- new_alts = (DEFAULT, [], rhs1) : map enum_to_tag rest_alts- new_bndr = setIdType bndr intPrimTy- -- The binder is dead, but should have the right type- ; return (Just (val_arg, Select dup new_bndr new_alts se cont)) }--}-- | Just (rule, rule_rhs) <- lookupRule ropts (getUnfoldingInRuleMatch env)- (activeRule (getMode env)) fn- (argInfoAppArgs args) rules- -- Fire a rule for the function- = do { checkedTick (RuleFired (ruleName rule))- ; let cont' = pushSimplifiedArgs zapped_env- (drop (ruleArity rule) args)- call_cont- -- (ruleArity rule) says how- -- many args the rule consumed-- occ_anald_rhs = occurAnalyseExpr rule_rhs- -- See Note [Occurrence-analyse after rule firing]- ; dump rule rule_rhs- ; return (Just (zapped_env, occ_anald_rhs, cont')) }- -- The occ_anald_rhs and cont' are all Out things- -- hence zapping the environment-- | otherwise -- No rule fires- = do { nodump -- This ensures that an empty file is written- ; return Nothing }-- where- ropts = initRuleOpts dflags- dflags = seDynFlags env- logger = seLogger env- zapped_env = zapSubstEnv env -- See Note [zapSubstEnv]-- printRuleModule rule- = parens (maybe (text "BUILTIN")- (pprModuleName . moduleName)- (ruleModule rule))-- dump rule rule_rhs- | logHasDumpFlag logger Opt_D_dump_rule_rewrites- = log_rule Opt_D_dump_rule_rewrites "Rule fired" $ vcat- [ text "Rule:" <+> ftext (ruleName rule)- , text "Module:" <+> printRuleModule rule- , text "Before:" <+> hang (ppr fn) 2 (sep (map ppr args))- , text "After: " <+> hang (pprCoreExpr rule_rhs) 2- (sep $ map ppr $ drop (ruleArity rule) args)- , text "Cont: " <+> ppr call_cont ]-- | logHasDumpFlag logger Opt_D_dump_rule_firings- = log_rule Opt_D_dump_rule_firings "Rule fired:" $- ftext (ruleName rule)- <+> printRuleModule rule-- | otherwise- = return ()-- nodump- | logHasDumpFlag logger Opt_D_dump_rule_rewrites- = liftIO $- touchDumpFile logger Opt_D_dump_rule_rewrites-- | logHasDumpFlag logger Opt_D_dump_rule_firings- = liftIO $- touchDumpFile logger Opt_D_dump_rule_firings-- | otherwise- = return ()-- log_rule flag hdr details- = liftIO $ logDumpFile logger (mkDumpStyle alwaysQualify) flag "" FormatText- $ sep [text hdr, nest 4 details]--trySeqRules :: SimplEnv- -> OutExpr -> InExpr -- Scrutinee and RHS- -> SimplCont- -> SimplM (Maybe (SimplEnv, CoreExpr, SimplCont))--- See Note [User-defined RULES for seq]-trySeqRules in_env scrut rhs cont- = do { rule_base <- getSimplRules- ; tryRules in_env (getRules rule_base seqId) seqId out_args rule_cont }- where- no_cast_scrut = drop_casts scrut- scrut_ty = exprType no_cast_scrut- seq_id_ty = idType seqId -- forall r a (b::TYPE r). a -> b -> b- res1_ty = piResultTy seq_id_ty rhs_rep -- forall a (b::TYPE rhs_rep). a -> b -> b- res2_ty = piResultTy res1_ty scrut_ty -- forall (b::TYPE rhs_rep). scrut_ty -> b -> b- res3_ty = piResultTy res2_ty rhs_ty -- scrut_ty -> rhs_ty -> rhs_ty- res4_ty = funResultTy res3_ty -- rhs_ty -> rhs_ty- rhs_ty = substTy in_env (exprType rhs)- rhs_rep = getRuntimeRep rhs_ty- out_args = [ TyArg { as_arg_ty = rhs_rep- , as_hole_ty = seq_id_ty }- , TyArg { as_arg_ty = scrut_ty- , as_hole_ty = res1_ty }- , TyArg { as_arg_ty = rhs_ty- , as_hole_ty = res2_ty }- , ValArg { as_arg = no_cast_scrut- , as_dmd = seqDmd- , as_hole_ty = res3_ty } ]- rule_cont = ApplyToVal { sc_dup = NoDup, sc_arg = rhs- , sc_env = in_env, sc_cont = cont- , sc_hole_ty = res4_ty }-- -- Lazily evaluated, so we don't do most of this-- drop_casts (Cast e _) = drop_casts e- drop_casts e = e--{- Note [User-defined RULES for seq]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Given- case (scrut |> co) of _ -> rhs-look for rules that match the expression- seq @t1 @t2 scrut-where scrut :: t1- rhs :: t2--If you find a match, rewrite it, and apply to 'rhs'.--Notice that we can simply drop casts on the fly here, which-makes it more likely that a rule will match.--See Note [User-defined RULES for seq] in GHC.Types.Id.Make.--Note [Occurrence-analyse after rule firing]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-After firing a rule, we occurrence-analyse the instantiated RHS before-simplifying it. Usually this doesn't make much difference, but it can-be huge. Here's an example (simplCore/should_compile/T7785)-- map f (map f (map f xs)--= -- Use build/fold form of map, twice- map f (build (\cn. foldr (mapFB c f) n- (build (\cn. foldr (mapFB c f) n xs))))--= -- Apply fold/build rule- map f (build (\cn. (\cn. foldr (mapFB c f) n xs) (mapFB c f) n))--= -- Beta-reduce- -- Alas we have no occurrence-analysed, so we don't know- -- that c is used exactly once- map f (build (\cn. let c1 = mapFB c f in- foldr (mapFB c1 f) n xs))--= -- Use mapFB rule: mapFB (mapFB c f) g = mapFB c (f.g)- -- We can do this because (mapFB c n) is a PAP and hence expandable- map f (build (\cn. let c1 = mapFB c n in- foldr (mapFB c (f.f)) n x))--This is not too bad. But now do the same with the outer map, and-we get another use of mapFB, and t can interact with /both/ remaining-mapFB calls in the above expression. This is stupid because actually-that 'c1' binding is dead. The outer map introduces another c2. If-there is a deep stack of maps we get lots of dead bindings, and lots-of redundant work as we repeatedly simplify the result of firing rules.--The easy thing to do is simply to occurrence analyse the result of-the rule firing. Note that this occ-anals not only the RHS of the-rule, but also the function arguments, which by now are OutExprs.-E.g.- RULE f (g x) = x+1--Call f (g BIG) --> (\x. x+1) BIG--The rule binders are lambda-bound and applied to the OutExpr arguments-(here BIG) which lack all internal occurrence info.--Is this inefficient? Not really: we are about to walk over the result-of the rule firing to simplify it, so occurrence analysis is at most-a constant factor.--Possible improvement: occ-anal the rules when putting them in the-database; and in the simplifier just occ-anal the OutExpr arguments.-But that's more complicated and the rule RHS is usually tiny; so I'm-just doing the simple thing.--Historical note: previously we did occ-anal the rules in Rule.hs,-but failed to occ-anal the OutExpr arguments, which led to the-nasty performance problem described above.---Note [Optimising tagToEnum#]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~-If we have an enumeration data type:-- data Foo = A | B | C--Then we want to transform-- case tagToEnum# x of ==> case x of- A -> e1 DEFAULT -> e1- B -> e2 1# -> e2- C -> e3 2# -> e3--thereby getting rid of the tagToEnum# altogether. If there was a DEFAULT-alternative we retain it (remember it comes first). If not the case must-be exhaustive, and we reflect that in the transformed version by adding-a DEFAULT. Otherwise Lint complains that the new case is not exhaustive.-See #8317.--Note [Rules for recursive functions]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-You might think that we shouldn't apply rules for a loop breaker:-doing so might give rise to an infinite loop, because a RULE is-rather like an extra equation for the function:- RULE: f (g x) y = x+y- Eqn: f a y = a-y--But it's too drastic to disable rules for loop breakers.-Even the foldr/build rule would be disabled, because foldr-is recursive, and hence a loop breaker:- foldr k z (build g) = g k z-So it's up to the programmer: rules can cause divergence---************************************************************************-* *- Rebuilding a case expression-* *-************************************************************************--Note [Case elimination]-~~~~~~~~~~~~~~~~~~~~~~~-The case-elimination transformation discards redundant case expressions.-Start with a simple situation:-- case x# of ===> let y# = x# in e- y# -> e--(when x#, y# are of primitive type, of course). We can't (in general)-do this for algebraic cases, because we might turn bottom into-non-bottom!--The code in GHC.Core.Opt.Simplify.Utils.prepareAlts has the effect of generalise-this idea to look for a case where we're scrutinising a variable, and we know-that only the default case can match. For example:-- case x of- 0# -> ...- DEFAULT -> ...(case x of- 0# -> ...- DEFAULT -> ...) ...--Here the inner case is first trimmed to have only one alternative, the-DEFAULT, after which it's an instance of the previous case. This-really only shows up in eliminating error-checking code.--Note that GHC.Core.Opt.Simplify.Utils.mkCase combines identical RHSs. So-- case e of ===> case e of DEFAULT -> r- True -> r- False -> r--Now again the case may be eliminated by the CaseElim transformation.-This includes things like (==# a# b#)::Bool so that we simplify- case ==# a# b# of { True -> x; False -> x }-to just- x-This particular example shows up in default methods for-comparison operations (e.g. in (>=) for Int.Int32)--Note [Case to let transformation]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-If a case over a lifted type has a single alternative, and is being-used as a strict 'let' (all isDeadBinder bndrs), we may want to do-this transformation:-- case e of r ===> let r = e in ...r...- _ -> ...r...--We treat the unlifted and lifted cases separately:--* Unlifted case: 'e' satisfies exprOkForSpeculation- (ok-for-spec is needed to satisfy the let/app invariant).- This turns case a +# b of r -> ...r...- into let r = a +# b in ...r...- and thence .....(a +# b)....-- However, if we have- case indexArray# a i of r -> ...r...- we might like to do the same, and inline the (indexArray# a i).- But indexArray# is not okForSpeculation, so we don't build a let- in rebuildCase (lest it get floated *out*), so the inlining doesn't- happen either. Annoying.--* Lifted case: we need to be sure that the expression is already- evaluated (exprIsHNF). If it's not already evaluated- - we risk losing exceptions, divergence or- user-specified thunk-forcing- - even if 'e' is guaranteed to converge, we don't want to- create a thunk (call by need) instead of evaluating it- right away (call by value)-- However, we can turn the case into a /strict/ let if the 'r' is- used strictly in the body. Then we won't lose divergence; and- we won't build a thunk because the let is strict.- See also Note [Case-to-let for strictly-used binders]--Note [Case-to-let for strictly-used binders]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-If we have this:- case <scrut> of r { _ -> ..r.. }--where 'r' is used strictly in (..r..), we can safely transform to- let r = <scrut> in ...r...--This is a Good Thing, because 'r' might be dead (if the body just-calls error), or might be used just once (in which case it can be-inlined); or we might be able to float the let-binding up or down.-E.g. #15631 has an example.--Note that this can change the error behaviour. For example, we might-transform- case x of { _ -> error "bad" }- --> error "bad"-which is might be puzzling if 'x' currently lambda-bound, but later gets-let-bound to (error "good").--Nevertheless, the paper "A semantics for imprecise exceptions" allows-this transformation. If you want to fix the evaluation order, use-'pseq'. See #8900 for an example where the loss of this-transformation bit us in practice.--See also Note [Empty case alternatives] in GHC.Core.--Historical notes--There have been various earlier versions of this patch:--* By Sept 18 the code looked like this:- || scrut_is_demanded_var scrut-- scrut_is_demanded_var :: CoreExpr -> Bool- scrut_is_demanded_var (Cast s _) = scrut_is_demanded_var s- scrut_is_demanded_var (Var _) = isStrUsedDmd (idDemandInfo case_bndr)- scrut_is_demanded_var _ = False-- This only fired if the scrutinee was a /variable/, which seems- an unnecessary restriction. So in #15631 I relaxed it to allow- arbitrary scrutinees. Less code, less to explain -- but the change- had 0.00% effect on nofib.--* Previously, in Jan 13 the code looked like this:- || case_bndr_evald_next rhs-- case_bndr_evald_next :: CoreExpr -> Bool- -- See Note [Case binder next]- case_bndr_evald_next (Var v) = v == case_bndr- case_bndr_evald_next (Cast e _) = case_bndr_evald_next e- case_bndr_evald_next (App e _) = case_bndr_evald_next e- case_bndr_evald_next (Case e _ _ _) = case_bndr_evald_next e- case_bndr_evald_next _ = False-- This patch was part of fixing #7542. See also- Note [Eta reduction of an eval'd function] in GHC.Core.Utils.)---Further notes about case elimination-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider: test :: Integer -> IO ()- test = print--Turns out that this compiles to:- Print.test- = \ eta :: Integer- eta1 :: Void# ->- case PrelNum.< eta PrelNum.zeroInteger of wild { __DEFAULT ->- case hPutStr stdout- (PrelNum.jtos eta ($w[] @ Char))- eta1- of wild1 { (# new_s, a4 #) -> PrelIO.lvl23 new_s }}--Notice the strange '<' which has no effect at all. This is a funny one.-It started like this:--f x y = if x < 0 then jtos x- else if y==0 then "" else jtos x--At a particular call site we have (f v 1). So we inline to get-- if v < 0 then jtos x- else if 1==0 then "" else jtos x--Now simplify the 1==0 conditional:-- if v<0 then jtos v else jtos v--Now common-up the two branches of the case:-- case (v<0) of DEFAULT -> jtos v--Why don't we drop the case? Because it's strict in v. It's technically-wrong to drop even unnecessary evaluations, and in practice they-may be a result of 'seq' so we *definitely* don't want to drop those.-I don't really know how to improve this situation.---Note [FloatBinds from constructor wrappers]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-If we have FloatBinds coming from the constructor wrapper-(as in Note [exprIsConApp_maybe on data constructors with wrappers]),-we cannot float past them. We'd need to float the FloatBind-together with the simplify floats, unfortunately the-simplifier doesn't have case-floats. The simplest thing we can-do is to wrap all the floats here. The next iteration of the-simplifier will take care of all these cases and lets.--Given data T = MkT !Bool, this allows us to simplify-case $WMkT b of { MkT x -> f x }-to-case b of { b' -> f b' }.--We could try and be more clever (like maybe wfloats only contain-let binders, so we could float them). But the need for the-extra complication is not clear.--Note [Do not duplicate constructor applications]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider this (#20125)- let x = (a,b)- in ...(case x of x' -> blah)...x...x...--We want that `case` to vanish (since `x` is bound to a data con) leaving- let x = (a,b)- in ...(let x'=x in blah)...x..x...--In rebuildCase, `exprIsConApp_maybe` will succeed on the scrutinee `x`,-since is bound to (a,b). But in eliminating the case, if the scrutinee-is trivial, we want to bind the case-binder to the scrutinee, /not/ to-the constructor application. Hence the case_bndr_rhs in rebuildCase.--This applies equally to a non-DEFAULT case alternative, say- let x = (a,b) in ...(case x of x' { (p,q) -> blah })...-This variant is handled by bind_case_bndr in knownCon.--We want to bind x' to x, and not to a duplicated (a,b)).--}-------------------------------------------------------------- Eliminate the case if possible--rebuildCase, reallyRebuildCase- :: SimplEnv- -> OutExpr -- Scrutinee- -> InId -- Case binder- -> [InAlt] -- Alternatives (increasing order)- -> SimplCont- -> SimplM (SimplFloats, OutExpr)------------------------------------------------------- 1. Eliminate the case if there's a known constructor-----------------------------------------------------rebuildCase env scrut case_bndr alts cont- | Lit lit <- scrut -- No need for same treatment as constructors- -- because literals are inlined more vigorously- , not (litIsLifted lit)- = do { tick (KnownBranch case_bndr)- ; case findAlt (LitAlt lit) alts of- Nothing -> missingAlt env case_bndr alts cont- Just (Alt _ bs rhs) -> simple_rhs env [] scrut bs rhs }-- | Just (in_scope', wfloats, con, ty_args, other_args)- <- exprIsConApp_maybe (getUnfoldingInRuleMatch env) scrut- -- Works when the scrutinee is a variable with a known unfolding- -- as well as when it's an explicit constructor application- , let env0 = setInScopeSet env in_scope'- = do { tick (KnownBranch case_bndr)- ; let scaled_wfloats = map scale_float wfloats- -- case_bndr_unf: see Note [Do not duplicate constructor applications]- case_bndr_rhs | exprIsTrivial scrut = scrut- | otherwise = con_app- con_app = Var (dataConWorkId con) `mkTyApps` ty_args- `mkApps` other_args- ; case findAlt (DataAlt con) alts of- Nothing -> missingAlt env0 case_bndr alts cont- Just (Alt DEFAULT bs rhs) -> simple_rhs env0 scaled_wfloats case_bndr_rhs bs rhs- Just (Alt _ bs rhs) -> knownCon env0 scrut scaled_wfloats con ty_args- other_args case_bndr bs rhs cont- }- where- simple_rhs env wfloats case_bndr_rhs bs rhs =- assert (null bs) $- do { (floats1, env') <- simplNonRecX env case_bndr case_bndr_rhs- -- scrut is a constructor application,- -- hence satisfies let/app invariant- ; (floats2, expr') <- simplExprF env' rhs cont- ; case wfloats of- [] -> return (floats1 `addFloats` floats2, expr')- _ -> return- -- See Note [FloatBinds from constructor wrappers]- ( emptyFloats env,- GHC.Core.Make.wrapFloats wfloats $- wrapFloats (floats1 `addFloats` floats2) expr' )}-- -- This scales case floats by the multiplicity of the continuation hole (see- -- Note [Scaling in case-of-case]). Let floats are _not_ scaled, because- -- they are aliases anyway.- scale_float (GHC.Core.Make.FloatCase scrut case_bndr con vars) =- let- scale_id id = scaleVarBy holeScaling id- in- GHC.Core.Make.FloatCase scrut (scale_id case_bndr) con (map scale_id vars)- scale_float f = f-- holeScaling = contHoleScaling cont `mkMultMul` idMult case_bndr- -- We are in the following situation- -- case[p] case[q] u of { D x -> C v } of { C x -> w }- -- And we are producing case[??] u of { D x -> w[x\v]}- --- -- What should the multiplicity `??` be? In order to preserve the usage of- -- variables in `u`, it needs to be `pq`.- --- -- As an illustration, consider the following- -- case[Many] case[1] of { C x -> C x } of { C x -> (x, x) }- -- Where C :: A %1 -> T is linear- -- If we were to produce a case[1], like the inner case, we would get- -- case[1] of { C x -> (x, x) }- -- Which is ill-typed with respect to linearity. So it needs to be a- -- case[Many].------------------------------------------------------- 2. Eliminate the case if scrutinee is evaluated-----------------------------------------------------rebuildCase env scrut case_bndr alts@[Alt _ bndrs rhs] cont- -- See if we can get rid of the case altogether- -- See Note [Case elimination]- -- mkCase made sure that if all the alternatives are equal,- -- then there is now only one (DEFAULT) rhs-- -- 2a. Dropping the case altogether, if- -- a) it binds nothing (so it's really just a 'seq')- -- b) evaluating the scrutinee has no side effects- | is_plain_seq- , exprOkForSideEffects scrut- -- The entire case is dead, so we can drop it- -- if the scrutinee converges without having imperative- -- side effects or raising a Haskell exception- -- See Note [PrimOp can_fail and has_side_effects] in GHC.Builtin.PrimOps- = simplExprF env rhs cont-- -- 2b. Turn the case into a let, if- -- a) it binds only the case-binder- -- b) unlifted case: the scrutinee is ok-for-speculation- -- lifted case: the scrutinee is in HNF (or will later be demanded)- -- See Note [Case to let transformation]- | all_dead_bndrs- , doCaseToLet scrut case_bndr- = do { tick (CaseElim case_bndr)- ; (floats1, env') <- simplNonRecX env case_bndr scrut- ; (floats2, expr') <- simplExprF env' rhs cont- ; return (floats1 `addFloats` floats2, expr') }-- -- 2c. Try the seq rules if- -- a) it binds only the case binder- -- b) a rule for seq applies- -- See Note [User-defined RULES for seq] in GHC.Types.Id.Make- | is_plain_seq- = do { mb_rule <- trySeqRules env scrut rhs cont- ; case mb_rule of- Just (env', rule_rhs, cont') -> simplExprF env' rule_rhs cont'- Nothing -> reallyRebuildCase env scrut case_bndr alts cont }- where- all_dead_bndrs = all isDeadBinder bndrs -- bndrs are [InId]- is_plain_seq = all_dead_bndrs && isDeadBinder case_bndr -- Evaluation *only* for effect--rebuildCase env scrut case_bndr alts cont- = reallyRebuildCase env scrut case_bndr alts cont---doCaseToLet :: OutExpr -- Scrutinee- -> InId -- Case binder- -> Bool--- The situation is case scrut of b { DEFAULT -> body }--- Can we transform thus? let { b = scrut } in body-doCaseToLet scrut case_bndr- | isTyCoVar case_bndr -- Respect GHC.Core- = isTyCoArg scrut -- Note [Core type and coercion invariant]-- | isUnliftedType (idType case_bndr)- -- OK to call isUnliftedType: scrutinees always have a fixed RuntimeRep (see FRRCase)- = exprOkForSpeculation scrut-- | otherwise -- Scrut has a lifted type- = exprIsHNF scrut- || isStrUsedDmd (idDemandInfo case_bndr)- -- See Note [Case-to-let for strictly-used binders]------------------------------------------------------- 3. Catch-all case-----------------------------------------------------reallyRebuildCase env scrut case_bndr alts cont- | not (sm_case_case (getMode env))- = do { case_expr <- simplAlts env scrut case_bndr alts- (mkBoringStop (contHoleType cont))- ; rebuild env case_expr cont }-- | otherwise- = do { (floats, env', cont') <- mkDupableCaseCont env alts cont- ; case_expr <- simplAlts env' scrut- (scaleIdBy holeScaling case_bndr)- (scaleAltsBy holeScaling alts)- cont'- ; return (floats, case_expr) }- where- holeScaling = contHoleScaling cont- -- Note [Scaling in case-of-case]--{--simplCaseBinder checks whether the scrutinee is a variable, v. If so,-try to eliminate uses of v in the RHSs in favour of case_bndr; that-way, there's a chance that v will now only be used once, and hence-inlined.--Historical note: we use to do the "case binder swap" in the Simplifier-so there were additional complications if the scrutinee was a variable.-Now the binder-swap stuff is done in the occurrence analyser; see-"GHC.Core.Opt.OccurAnal" Note [Binder swap].--Note [knownCon occ info]-~~~~~~~~~~~~~~~~~~~~~~~~-If the case binder is not dead, then neither are the pattern bound-variables:- case <any> of x { (a,b) ->- case x of { (p,q) -> p } }-Here (a,b) both look dead, but come alive after the inner case is eliminated.-The point is that we bring into the envt a binding- let x = (a,b)-after the outer case, and that makes (a,b) alive. At least we do unless-the case binder is guaranteed dead.--Note [Case alternative occ info]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-When we are simply reconstructing a case (the common case), we always-zap the occurrence info on the binders in the alternatives. Even-if the case binder is dead, the scrutinee is usually a variable, and *that*-can bring the case-alternative binders back to life.-See Note [Add unfolding for scrutinee]--Note [Improving seq]-~~~~~~~~~~~~~~~~~~~-Consider- type family F :: * -> *- type instance F Int = Int--We'd like to transform- case e of (x :: F Int) { DEFAULT -> rhs }-===>- case e `cast` co of (x'::Int)- I# x# -> let x = x' `cast` sym co- in rhs--so that 'rhs' can take advantage of the form of x'. Notice that Note-[Case of cast] (in OccurAnal) may then apply to the result.--We'd also like to eliminate empty types (#13468). So if-- data Void- type instance F Bool = Void--then we'd like to transform- case (x :: F Bool) of { _ -> error "urk" }-===>- case (x |> co) of (x' :: Void) of {}--Nota Bene: we used to have a built-in rule for 'seq' that dropped-casts, so that- case (x |> co) of { _ -> blah }-dropped the cast; in order to improve the chances of trySeqRules-firing. But that works in the /opposite/ direction to Note [Improving-seq] so there's a danger of flip/flopping. Better to make trySeqRules-insensitive to the cast, which is now is.--The need for [Improving seq] showed up in Roman's experiments. Example:- foo :: F Int -> Int -> Int- foo t n = t `seq` bar n- where- bar 0 = 0- bar n = bar (n - case t of TI i -> i)-Here we'd like to avoid repeated evaluating t inside the loop, by-taking advantage of the `seq`.--At one point I did transformation in LiberateCase, but it's more-robust here. (Otherwise, there's a danger that we'll simply drop the-'seq' altogether, before LiberateCase gets to see it.)--Note [Scaling in case-of-case]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--When two cases commute, if done naively, the multiplicities will be wrong:-- case (case u of w[1] { (x[1], y[1]) } -> f x y) of w'[Many]- { (z[Many], t[Many]) -> z- }--The multiplicities here, are correct, but if I perform a case of case:-- case u of w[1]- { (x[1], y[1]) -> case f x y of w'[Many] of { (z[Many], t[Many]) -> z }- }--This is wrong! Using `f x y` inside a `case … of w'[Many]` means that `x` and-`y` must have multiplicities `Many` not `1`! The correct solution is to make-all the `1`-s be `Many`-s instead:-- case u of w[Many]- { (x[Many], y[Many]) -> case f x y of w'[Many] of { (z[Many], t[Many]) -> z }- }--In general, when commuting two cases, the rule has to be:-- case (case … of x[p] {…}) of y[q] { … }- ===> case … of x[p*q] { … case … of y[q] { … } }--This is materialised, in the simplifier, by the fact that every time we simplify-case alternatives with a continuation (the surrounded case (or more!)), we must-scale the entire case we are simplifying, by a scaling factor which can be-computed in the continuation (with function `contHoleScaling`).--}--simplAlts :: SimplEnv- -> OutExpr -- Scrutinee- -> InId -- Case binder- -> [InAlt] -- Non-empty- -> SimplCont- -> SimplM OutExpr -- Returns the complete simplified case expression--simplAlts env0 scrut case_bndr alts cont'- = do { traceSmpl "simplAlts" (vcat [ ppr case_bndr- , text "cont':" <+> ppr cont'- , text "in_scope" <+> ppr (seInScope env0) ])- ; (env1, case_bndr1) <- simplBinder env0 case_bndr- ; let case_bndr2 = case_bndr1 `setIdUnfolding` evaldUnfolding- env2 = modifyInScope env1 case_bndr2- -- See Note [Case binder evaluated-ness]-- ; fam_envs <- getFamEnvs- ; (alt_env', scrut', case_bndr') <- improveSeq fam_envs env2 scrut- case_bndr case_bndr2 alts-- ; (imposs_deflt_cons, in_alts) <- prepareAlts scrut' case_bndr alts- -- NB: it's possible that the returned in_alts is empty: this is handled- -- by the caller (rebuildCase) in the missingAlt function- -- NB: pass case_bndr::InId, not case_bndr' :: OutId, to prepareAlts- -- See Note [Shadowing in prepareAlts] in GHC.Core.Opt.Simplify.Utils-- ; alts' <- mapM (simplAlt alt_env' (Just scrut') imposs_deflt_cons case_bndr' cont') in_alts--- ; pprTrace "simplAlts" (ppr case_bndr $$ ppr alts $$ ppr cont') $ return ()-- ; let alts_ty' = contResultType cont'- -- See Note [Avoiding space leaks in OutType]- ; seqType alts_ty' `seq`- mkCase (seDynFlags env0) scrut' case_bndr' alts_ty' alts' }----------------------------------------improveSeq :: (FamInstEnv, FamInstEnv) -> SimplEnv- -> OutExpr -> InId -> OutId -> [InAlt]- -> SimplM (SimplEnv, OutExpr, OutId)--- Note [Improving seq]-improveSeq fam_envs env scrut case_bndr case_bndr1 [Alt DEFAULT _ _]- | Just (Reduction co ty2) <- topNormaliseType_maybe fam_envs (idType case_bndr1)- = do { case_bndr2 <- newId (fsLit "nt") Many ty2- ; let rhs = DoneEx (Var case_bndr2 `Cast` mkSymCo co) Nothing- env2 = extendIdSubst env case_bndr rhs- ; return (env2, scrut `Cast` co, case_bndr2) }--improveSeq _ env scrut _ case_bndr1 _- = return (env, scrut, case_bndr1)----------------------------------------simplAlt :: SimplEnv- -> Maybe OutExpr -- The scrutinee- -> [AltCon] -- These constructors can't be present when- -- matching the DEFAULT alternative- -> OutId -- The case binder- -> SimplCont- -> InAlt- -> SimplM OutAlt--simplAlt env _ imposs_deflt_cons case_bndr' cont' (Alt DEFAULT bndrs rhs)- = assert (null bndrs) $- do { let env' = addBinderUnfolding env case_bndr'- (mkOtherCon imposs_deflt_cons)- -- Record the constructors that the case-binder *can't* be.- ; rhs' <- simplExprC env' rhs cont'- ; return (Alt DEFAULT [] rhs') }--simplAlt env scrut' _ case_bndr' cont' (Alt (LitAlt lit) bndrs rhs)- = assert (null bndrs) $- do { env' <- addAltUnfoldings env scrut' case_bndr' (Lit lit)- ; rhs' <- simplExprC env' rhs cont'- ; return (Alt (LitAlt lit) [] rhs') }--simplAlt env scrut' _ case_bndr' cont' (Alt (DataAlt con) vs rhs)- = do { -- See Note [Adding evaluatedness info to pattern-bound variables]- let vs_with_evals = addEvals scrut' con vs- ; (env', vs') <- simplBinders env vs_with_evals-- -- Bind the case-binder to (con args)- ; let inst_tys' = tyConAppArgs (idType case_bndr')- con_app :: OutExpr- con_app = mkConApp2 con inst_tys' vs'-- ; env'' <- addAltUnfoldings env' scrut' case_bndr' con_app- ; rhs' <- simplExprC env'' rhs cont'- ; return (Alt (DataAlt con) vs' rhs') }--{- Note [Adding evaluatedness info to pattern-bound variables]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-addEvals records the evaluated-ness of the bound variables of-a case pattern. This is *important*. Consider-- data T = T !Int !Int-- case x of { T a b -> T (a+1) b }--We really must record that b is already evaluated so that we don't-go and re-evaluate it when constructing the result.-See Note [Data-con worker strictness] in GHC.Core.DataCon--NB: simplLamBndrs preserves this eval info--In addition to handling data constructor fields with !s, addEvals-also records the fact that the result of seq# is always in WHNF.-See Note [seq# magic] in GHC.Core.Opt.ConstantFold. Example (#15226):-- case seq# v s of- (# s', v' #) -> E--we want the compiler to be aware that v' is in WHNF in E.--Open problem: we don't record that v itself is in WHNF (and we can't-do it here). The right thing is to do some kind of binder-swap;-see #15226 for discussion.--}--addEvals :: Maybe OutExpr -> DataCon -> [Id] -> [Id]--- See Note [Adding evaluatedness info to pattern-bound variables]-addEvals scrut con vs- -- Deal with seq# applications- | Just scr <- scrut- , isUnboxedTupleDataCon con- , [s,x] <- vs- -- Use stripNArgs rather than collectArgsTicks to avoid building- -- a list of arguments only to throw it away immediately.- , Just (Var f) <- stripNArgs 4 scr- , Just SeqOp <- isPrimOpId_maybe f- , let x' = zapIdOccInfoAndSetEvald MarkedStrict x- = [s, x']-- -- Deal with banged datacon fields-addEvals _scrut con vs = go vs the_strs- where- the_strs = dataConRepStrictness con-- go [] [] = []- go (v:vs') strs | isTyVar v = v : go vs' strs- go (v:vs') (str:strs) = zapIdOccInfoAndSetEvald str v : go vs' strs- go _ _ = pprPanic "Simplify.addEvals"- (ppr con $$- ppr vs $$- ppr_with_length (map strdisp the_strs) $$- ppr_with_length (dataConRepArgTys con) $$- ppr_with_length (dataConRepStrictness con))- where- ppr_with_length list- = ppr list <+> parens (text "length =" <+> ppr (length list))- strdisp MarkedStrict = text "MarkedStrict"- strdisp NotMarkedStrict = text "NotMarkedStrict"--zapIdOccInfoAndSetEvald :: StrictnessMark -> Id -> Id-zapIdOccInfoAndSetEvald str v =- setCaseBndrEvald str $ -- Add eval'dness info- zapIdOccInfo v -- And kill occ info;- -- see Note [Case alternative occ info]--addAltUnfoldings :: SimplEnv -> Maybe OutExpr -> OutId -> OutExpr -> SimplM SimplEnv-addAltUnfoldings env scrut case_bndr con_app- = do { let con_app_unf = mk_simple_unf con_app- env1 = addBinderUnfolding env case_bndr con_app_unf-- -- See Note [Add unfolding for scrutinee]- env2 | Many <- idMult case_bndr = case scrut of- Just (Var v) -> addBinderUnfolding env1 v con_app_unf- Just (Cast (Var v) co) -> addBinderUnfolding env1 v $- mk_simple_unf (Cast con_app (mkSymCo co))- _ -> env1- | otherwise = env1-- ; traceSmpl "addAltUnf" (vcat [ppr case_bndr <+> ppr scrut, ppr con_app])- ; return env2 }- where- -- Force the opts, so that the whole SimplEnv isn't retained- !opts = seUnfoldingOpts env- mk_simple_unf = mkSimpleUnfolding opts--addBinderUnfolding :: SimplEnv -> Id -> Unfolding -> SimplEnv-addBinderUnfolding env bndr unf- | debugIsOn, Just tmpl <- maybeUnfoldingTemplate unf- = warnPprTrace (not (eqType (idType bndr) (exprType tmpl)))- "unfolding type mismatch"- (ppr bndr $$ ppr (idType bndr) $$ ppr tmpl $$ ppr (exprType tmpl)) $- modifyInScope env (bndr `setIdUnfolding` unf)-- | otherwise- = modifyInScope env (bndr `setIdUnfolding` unf)--zapBndrOccInfo :: Bool -> Id -> Id--- Consider case e of b { (a,b) -> ... }--- Then if we bind b to (a,b) in "...", and b is not dead,--- then we must zap the deadness info on a,b-zapBndrOccInfo keep_occ_info pat_id- | keep_occ_info = pat_id- | otherwise = zapIdOccInfo pat_id--{- Note [Case binder evaluated-ness]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We pin on a (OtherCon []) unfolding to the case-binder of a Case,-even though it'll be over-ridden in every case alternative with a more-informative unfolding. Why? Because suppose a later, less clever, pass-simply replaces all occurrences of the case binder with the binder itself;-then Lint may complain about the let/app invariant. Example- case e of b { DEFAULT -> let v = reallyUnsafePtrEquality# b y in ....- ; K -> blah }--The let/app invariant requires that y is evaluated in the call to-reallyUnsafePtrEquality#, which it is. But we still want that to be true if we-propagate binders to occurrences.--This showed up in #13027.--Note [Add unfolding for scrutinee]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-In general it's unlikely that a variable scrutinee will appear-in the case alternatives case x of { ...x unlikely to appear... }-because the binder-swap in OccurAnal has got rid of all such occurrences-See Note [Binder swap] in "GHC.Core.Opt.OccurAnal".--BUT it is still VERY IMPORTANT to add a suitable unfolding for a-variable scrutinee, in simplAlt. Here's why- case x of y- (a,b) -> case b of c- I# v -> ...(f y)...-There is no occurrence of 'b' in the (...(f y)...). But y gets-the unfolding (a,b), and *that* mentions b. If f has a RULE- RULE f (p, I# q) = ...-we want that rule to match, so we must extend the in-scope env with a-suitable unfolding for 'y'. It's *essential* for rule matching; but-it's also good for case-elimination -- suppose that 'f' was inlined-and did multi-level case analysis, then we'd solve it in one-simplifier sweep instead of two.--Exactly the same issue arises in GHC.Core.Opt.SpecConstr;-see Note [Add scrutinee to ValueEnv too] in GHC.Core.Opt.SpecConstr--HOWEVER, given- case x of y { Just a -> r1; Nothing -> r2 }-we do not want to add the unfolding x -> y to 'x', which might seem cool,-since 'y' itself has different unfoldings in r1 and r2. Reason: if we-did that, we'd have to zap y's deadness info and that is a very useful-piece of information.--So instead we add the unfolding x -> Just a, and x -> Nothing in the-respective RHSs.--Since this transformation is tantamount to a binder swap, the same caveat as in-Note [Suppressing binder-swaps on linear case] in OccurAnal apply.---************************************************************************-* *-\subsection{Known constructor}-* *-************************************************************************--We are a bit careful with occurrence info. Here's an example-- (\x* -> case x of (a*, b) -> f a) (h v, e)--where the * means "occurs once". This effectively becomes- case (h v, e) of (a*, b) -> f a)-and then- let a* = h v; b = e in f a-and then- f (h v)--All this should happen in one sweep.--}--knownCon :: SimplEnv- -> OutExpr -- The scrutinee- -> [FloatBind] -> DataCon -> [OutType] -> [OutExpr] -- The scrutinee (in pieces)- -> InId -> [InBndr] -> InExpr -- The alternative- -> SimplCont- -> SimplM (SimplFloats, OutExpr)--knownCon env scrut dc_floats dc dc_ty_args dc_args bndr bs rhs cont- = do { (floats1, env1) <- bind_args env bs dc_args- ; (floats2, env2) <- bind_case_bndr env1- ; (floats3, expr') <- simplExprF env2 rhs cont- ; case dc_floats of- [] ->- return (floats1 `addFloats` floats2 `addFloats` floats3, expr')- _ ->- return ( emptyFloats env- -- See Note [FloatBinds from constructor wrappers]- , GHC.Core.Make.wrapFloats dc_floats $- wrapFloats (floats1 `addFloats` floats2 `addFloats` floats3) expr') }- where- zap_occ = zapBndrOccInfo (isDeadBinder bndr) -- bndr is an InId-- -- Ugh!- bind_args env' [] _ = return (emptyFloats env', env')-- bind_args env' (b:bs') (Type ty : args)- = assert (isTyVar b )- bind_args (extendTvSubst env' b ty) bs' args-- bind_args env' (b:bs') (Coercion co : args)- = assert (isCoVar b )- bind_args (extendCvSubst env' b co) bs' args-- bind_args env' (b:bs') (arg : args)- = assert (isId b) $- do { let b' = zap_occ b- -- Note that the binder might be "dead", because it doesn't- -- occur in the RHS; and simplNonRecX may therefore discard- -- it via postInlineUnconditionally.- -- Nevertheless we must keep it if the case-binder is alive,- -- because it may be used in the con_app. See Note [knownCon occ info]- ; (floats1, env2) <- simplNonRecX env' b' arg -- arg satisfies let/app invariant- ; (floats2, env3) <- bind_args env2 bs' args- ; return (floats1 `addFloats` floats2, env3) }-- bind_args _ _ _ =- pprPanic "bind_args" $ ppr dc $$ ppr bs $$ ppr dc_args $$- text "scrut:" <+> ppr scrut-- -- It's useful to bind bndr to scrut, rather than to a fresh- -- binding x = Con arg1 .. argn- -- because very often the scrut is a variable, so we avoid- -- creating, and then subsequently eliminating, a let-binding- -- BUT, if scrut is a not a variable, we must be careful- -- about duplicating the arg redexes; in that case, make- -- a new con-app from the args- bind_case_bndr env- | isDeadBinder bndr = return (emptyFloats env, env)- | exprIsTrivial scrut = return (emptyFloats env- , extendIdSubst env bndr (DoneEx scrut Nothing))- -- See Note [Do not duplicate constructor applications]- | otherwise = do { dc_args <- mapM (simplVar env) bs- -- dc_ty_args are already OutTypes,- -- but bs are InBndrs- ; let con_app = Var (dataConWorkId dc)- `mkTyApps` dc_ty_args- `mkApps` dc_args- ; simplNonRecX env bndr con_app }----------------------missingAlt :: SimplEnv -> Id -> [InAlt] -> SimplCont- -> SimplM (SimplFloats, OutExpr)- -- This isn't strictly an error, although it is unusual.- -- It's possible that the simplifier might "see" that- -- an inner case has no accessible alternatives before- -- it "sees" that the entire branch of an outer case is- -- inaccessible. So we simply put an error case here instead.-missingAlt env case_bndr _ cont- = warnPprTrace True "missingAlt" (ppr case_bndr) $- -- See Note [Avoiding space leaks in OutType]- let cont_ty = contResultType cont- in seqType cont_ty `seq`- return (emptyFloats env, mkImpossibleExpr cont_ty)--{--************************************************************************-* *-\subsection{Duplicating continuations}-* *-************************************************************************--Consider- let x* = case e of { True -> e1; False -> e2 }- in b-where x* is a strict binding. Then mkDupableCont will be given-the continuation- case [] of { True -> e1; False -> e2 } ; let x* = [] in b ; stop-and will split it into- dupable: case [] of { True -> $j1; False -> $j2 } ; stop- join floats: $j1 = e1, $j2 = e2- non_dupable: let x* = [] in b; stop--Putting this back together would give- let x* = let { $j1 = e1; $j2 = e2 } in- case e of { True -> $j1; False -> $j2 }- in b-(Of course we only do this if 'e' wants to duplicate that continuation.)-Note how important it is that the new join points wrap around the-inner expression, and not around the whole thing.--In contrast, any let-bindings introduced by mkDupableCont can wrap-around the entire thing.--Note [Bottom alternatives]-~~~~~~~~~~~~~~~~~~~~~~~~~~-When we have- case (case x of { A -> error .. ; B -> e; C -> error ..)- of alts-then we can just duplicate those alts because the A and C cases-will disappear immediately. This is more direct than creating-join points and inlining them away. See #4930.--}-----------------------mkDupableCaseCont :: SimplEnv -> [InAlt] -> SimplCont- -> SimplM ( SimplFloats -- Join points (if any)- , SimplEnv -- Use this for the alts- , SimplCont)-mkDupableCaseCont env alts cont- | altsWouldDup alts = do { (floats, cont) <- mkDupableCont env cont- ; let env' = bumpCaseDepth $- env `setInScopeFromF` floats- ; return (floats, env', cont) }- | otherwise = return (emptyFloats env, env, cont)--altsWouldDup :: [InAlt] -> Bool -- True iff strictly > 1 non-bottom alternative-altsWouldDup [] = False -- See Note [Bottom alternatives]-altsWouldDup [_] = False-altsWouldDup (alt:alts)- | is_bot_alt alt = altsWouldDup alts- | otherwise = not (all is_bot_alt alts)- -- otherwise case: first alt is non-bot, so all the rest must be bot- where- is_bot_alt (Alt _ _ rhs) = exprIsDeadEnd rhs----------------------------mkDupableCont :: SimplEnv- -> SimplCont- -> SimplM ( SimplFloats -- Incoming SimplEnv augmented with- -- extra let/join-floats and in-scope variables- , SimplCont) -- dup_cont: duplicable continuation-mkDupableCont env cont- = mkDupableContWithDmds env (repeat topDmd) cont--mkDupableContWithDmds- :: SimplEnv -> [Demand] -- Demands on arguments; always infinite- -> SimplCont -> SimplM ( SimplFloats, SimplCont)--mkDupableContWithDmds env _ cont- | contIsDupable cont- = return (emptyFloats env, cont)--mkDupableContWithDmds _ _ (Stop {}) = panic "mkDupableCont" -- Handled by previous eqn--mkDupableContWithDmds env dmds (CastIt ty cont)- = do { (floats, cont') <- mkDupableContWithDmds env dmds cont- ; return (floats, CastIt ty cont') }---- Duplicating ticks for now, not sure if this is good or not-mkDupableContWithDmds env dmds (TickIt t cont)- = do { (floats, cont') <- mkDupableContWithDmds env dmds cont- ; return (floats, TickIt t cont') }--mkDupableContWithDmds env _- (StrictBind { sc_bndr = bndr, sc_body = body- , sc_env = se, sc_cont = cont})--- See Note [Duplicating StrictBind]--- K[ let x = <> in b ] --> join j x = K[ b ]--- j <>- = do { let sb_env = se `setInScopeFromE` env- ; (sb_env1, bndr') <- simplBinder sb_env bndr- ; (floats1, join_inner) <- simplLam sb_env1 body cont- -- No need to use mkDupableCont before simplLam; we- -- use cont once here, and then share the result if necessary-- ; let join_body = wrapFloats floats1 join_inner- res_ty = contResultType cont-- ; mkDupableStrictBind env bndr' join_body res_ty }--mkDupableContWithDmds env _- (StrictArg { sc_fun = fun, sc_cont = cont- , sc_fun_ty = fun_ty })- -- NB: sc_dup /= OkToDup; that is caught earlier by contIsDupable- | isNothing (isDataConId_maybe (ai_fun fun))- , thumbsUpPlanA cont -- See point (3) of Note [Duplicating join points]- = -- Use Plan A of Note [Duplicating StrictArg]- do { let (_ : dmds) = ai_dmds fun- ; (floats1, cont') <- mkDupableContWithDmds env dmds cont- -- Use the demands from the function to add the right- -- demand info on any bindings we make for further args- ; (floats_s, args') <- mapAndUnzipM (makeTrivialArg env)- (ai_args fun)- ; return ( foldl' addLetFloats floats1 floats_s- , StrictArg { sc_fun = fun { ai_args = args' }- , sc_cont = cont'- , sc_fun_ty = fun_ty- , sc_dup = OkToDup} ) }-- | otherwise- = -- Use Plan B of Note [Duplicating StrictArg]- -- K[ f a b <> ] --> join j x = K[ f a b x ]- -- j <>- do { let rhs_ty = contResultType cont- (m,arg_ty,_) = splitFunTy fun_ty- ; arg_bndr <- newId (fsLit "arg") m arg_ty- ; let env' = env `addNewInScopeIds` [arg_bndr]- ; (floats, join_rhs) <- rebuildCall env' (addValArgTo fun (Var arg_bndr) fun_ty) cont- ; mkDupableStrictBind env' arg_bndr (wrapFloats floats join_rhs) rhs_ty }- where- thumbsUpPlanA (StrictArg {}) = False- thumbsUpPlanA (CastIt _ k) = thumbsUpPlanA k- thumbsUpPlanA (TickIt _ k) = thumbsUpPlanA k- thumbsUpPlanA (ApplyToVal { sc_cont = k }) = thumbsUpPlanA k- thumbsUpPlanA (ApplyToTy { sc_cont = k }) = thumbsUpPlanA k- thumbsUpPlanA (Select {}) = True- thumbsUpPlanA (StrictBind {}) = True- thumbsUpPlanA (Stop {}) = True--mkDupableContWithDmds env dmds- (ApplyToTy { sc_cont = cont, sc_arg_ty = arg_ty, sc_hole_ty = hole_ty })- = do { (floats, cont') <- mkDupableContWithDmds env dmds cont- ; return (floats, ApplyToTy { sc_cont = cont'- , sc_arg_ty = arg_ty, sc_hole_ty = hole_ty }) }--mkDupableContWithDmds env dmds- (ApplyToVal { sc_arg = arg, sc_dup = dup, sc_env = se- , sc_cont = cont, sc_hole_ty = hole_ty })- = -- e.g. [...hole...] (...arg...)- -- ==>- -- let a = ...arg...- -- in [...hole...] a- -- NB: sc_dup /= OkToDup; that is caught earlier by contIsDupable- do { let (dmd:cont_dmds) = dmds -- Never fails- ; (floats1, cont') <- mkDupableContWithDmds env cont_dmds cont- ; let env' = env `setInScopeFromF` floats1- ; (_, se', arg') <- simplArg env' dup se arg- ; (let_floats2, arg'') <- makeTrivial env NotTopLevel dmd (fsLit "karg") arg'- ; let all_floats = floats1 `addLetFloats` let_floats2- ; return ( all_floats- , ApplyToVal { sc_arg = arg''- , sc_env = se' `setInScopeFromF` all_floats- -- Ensure that sc_env includes the free vars of- -- arg'' in its in-scope set, even if makeTrivial- -- has turned arg'' into a fresh variable- -- See Note [StaticEnv invariant] in GHC.Core.Opt.Simplify.Utils- , sc_dup = OkToDup, sc_cont = cont'- , sc_hole_ty = hole_ty }) }--mkDupableContWithDmds env _- (Select { sc_bndr = case_bndr, sc_alts = alts, sc_env = se, sc_cont = cont })- = -- e.g. (case [...hole...] of { pi -> ei })- -- ===>- -- let ji = \xij -> ei- -- in case [...hole...] of { pi -> ji xij }- -- NB: sc_dup /= OkToDup; that is caught earlier by contIsDupable- do { tick (CaseOfCase case_bndr)- ; (floats, alt_env, alt_cont) <- mkDupableCaseCont (se `setInScopeFromE` env) alts cont- -- NB: We call mkDupableCaseCont here to make cont duplicable- -- (if necessary, depending on the number of alts)- -- And this is important: see Note [Fusing case continuations]-- ; let cont_scaling = contHoleScaling cont- -- See Note [Scaling in case-of-case]- ; (alt_env', case_bndr') <- simplBinder alt_env (scaleIdBy cont_scaling case_bndr)- ; alts' <- mapM (simplAlt alt_env' Nothing [] case_bndr' alt_cont) (scaleAltsBy cont_scaling alts)- -- Safe to say that there are no handled-cons for the DEFAULT case- -- NB: simplBinder does not zap deadness occ-info, so- -- a dead case_bndr' will still advertise its deadness- -- This is really important because in- -- case e of b { (# p,q #) -> ... }- -- b is always dead, and indeed we are not allowed to bind b to (# p,q #),- -- which might happen if e was an explicit unboxed pair and b wasn't marked dead.- -- In the new alts we build, we have the new case binder, so it must retain- -- its deadness.- -- NB: we don't use alt_env further; it has the substEnv for- -- the alternatives, and we don't want that-- ; (join_floats, alts'') <- mapAccumLM (mkDupableAlt (targetPlatform (seDynFlags env)) case_bndr')- emptyJoinFloats alts'-- ; let all_floats = floats `addJoinFloats` join_floats- -- Note [Duplicated env]- ; return (all_floats- , Select { sc_dup = OkToDup- , sc_bndr = case_bndr'- , sc_alts = alts''- , sc_env = zapSubstEnv se `setInScopeFromF` all_floats- -- See Note [StaticEnv invariant] in GHC.Core.Opt.Simplify.Utils- , sc_cont = mkBoringStop (contResultType cont) } ) }--mkDupableStrictBind :: SimplEnv -> OutId -> OutExpr -> OutType- -> SimplM (SimplFloats, SimplCont)-mkDupableStrictBind env arg_bndr join_rhs res_ty- | exprIsTrivial join_rhs -- See point (2) of Note [Duplicating join points]- = return (emptyFloats env- , StrictBind { sc_bndr = arg_bndr- , sc_body = join_rhs- , sc_env = zapSubstEnv env- -- See Note [StaticEnv invariant] in GHC.Core.Opt.Simplify.Utils- , sc_dup = OkToDup- , sc_cont = mkBoringStop res_ty } )- | otherwise- = do { join_bndr <- newJoinId [arg_bndr] res_ty- ; let arg_info = ArgInfo { ai_fun = join_bndr- , ai_rules = Nothing, ai_args = []- , ai_encl = False, ai_dmds = repeat topDmd- , ai_discs = repeat 0 }- ; return ( addJoinFloats (emptyFloats env) $- unitJoinFloat $- NonRec join_bndr $- Lam (setOneShotLambda arg_bndr) join_rhs- , StrictArg { sc_dup = OkToDup- , sc_fun = arg_info- , sc_fun_ty = idType join_bndr- , sc_cont = mkBoringStop res_ty- } ) }--mkDupableAlt :: Platform -> OutId- -> JoinFloats -> OutAlt- -> SimplM (JoinFloats, OutAlt)-mkDupableAlt _platform case_bndr jfloats (Alt con alt_bndrs alt_rhs_in)- | exprIsTrivial alt_rhs_in -- See point (2) of Note [Duplicating join points]- = return (jfloats, Alt con alt_bndrs alt_rhs_in)-- | otherwise- = do { let rhs_ty' = exprType alt_rhs_in-- bangs- | DataAlt c <- con- = dataConRepStrictness c- | otherwise = []-- abstracted_binders = abstract_binders alt_bndrs bangs-- abstract_binders :: [Var] -> [StrictnessMark] -> [(Id,StrictnessMark)]- abstract_binders [] []- -- Abstract over the case binder too if it's used.- | isDeadBinder case_bndr = []- | otherwise = [(case_bndr,MarkedStrict)]- abstract_binders (alt_bndr:alt_bndrs) marks- -- Abstract over all type variables just in case- | isTyVar alt_bndr = (alt_bndr,NotMarkedStrict) : abstract_binders alt_bndrs marks- abstract_binders (alt_bndr:alt_bndrs) (mark:marks)- -- The deadness info on the new Ids is preserved by simplBinders- -- We don't abstract over dead ids here.- | isDeadBinder alt_bndr = abstract_binders alt_bndrs marks- | otherwise = (alt_bndr,mark) : abstract_binders alt_bndrs marks- abstract_binders _ _ = pprPanic "abstrict_binders - failed to abstract" (ppr $ Alt con alt_bndrs alt_rhs_in)-- filtered_binders = map fst abstracted_binders- -- We want to make any binder with an evaldUnfolding strict in the rhs.- -- See Note [Call-by-value for worker args] (which also applies to join points)- (rhs_with_seqs) = mkStrictFieldSeqs abstracted_binders alt_rhs_in-- final_args = varsToCoreExprs filtered_binders- -- Note [Join point abstraction]-- -- We make the lambdas into one-shot-lambdas. The- -- join point is sure to be applied at most once, and doing so- -- prevents the body of the join point being floated out by- -- the full laziness pass- final_bndrs = map one_shot filtered_binders- one_shot v | isId v = setOneShotLambda v- | otherwise = v-- -- No lambda binder has an unfolding, but (currently) case binders can,- -- so we must zap them here.- join_rhs = mkLams (map zapIdUnfolding final_bndrs) rhs_with_seqs-- ; join_bndr <- newJoinId filtered_binders rhs_ty'-- ; let join_call = mkApps (Var join_bndr) final_args- alt' = Alt con alt_bndrs join_call-- ; return ( jfloats `addJoinFlts` unitJoinFloat (NonRec join_bndr join_rhs)- , alt') }- -- See Note [Duplicated env]--{--Note [Fusing case continuations]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-It's important to fuse two successive case continuations when the-first has one alternative. That's why we call prepareCaseCont here.-Consider this, which arises from thunk splitting (see Note [Thunk-splitting] in GHC.Core.Opt.WorkWrap):-- let- x* = case (case v of {pn -> rn}) of- I# a -> I# a- in body--The simplifier will find- (Var v) with continuation- Select (pn -> rn) (- Select [I# a -> I# a] (- StrictBind body Stop--So we'll call mkDupableCont on- Select [I# a -> I# a] (StrictBind body Stop)-There is just one alternative in the first Select, so we want to-simplify the rhs (I# a) with continuation (StrictBind body Stop)-Supposing that body is big, we end up with- let $j a = <let x = I# a in body>- in case v of { pn -> case rn of- I# a -> $j a }-This is just what we want because the rn produces a box that-the case rn cancels with.--See #4957 a fuller example.--Note [Duplicating join points]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-IN #19996 we discovered that we want to be really careful about-inlining join points. Consider- case (join $j x = K f x )- (in case v of )- ( p1 -> $j x1 ) of- ( p2 -> $j x2 )- ( p3 -> $j x3 )- K g y -> blah[g,y]--Here the join-point RHS is very small, just a constructor-application (K x y). So we might inline it to get- case (case v of )- ( p1 -> K f x1 ) of- ( p2 -> K f x2 )- ( p3 -> K f x3 )- K g y -> blah[g,y]--But now we have to make `blah` into a join point, /abstracted/-over `g` and `y`. In contrast, if we /don't/ inline $j we-don't need a join point for `blah` and we'll get- join $j x = let g=f, y=x in blah[g,y]- in case v of- p1 -> $j x1- p2 -> $j x2- p3 -> $j x3--This can make a /massive/ difference, because `blah` can see-what `f` is, instead of lambda-abstracting over it.--To achieve this:--1. Do not postInlineUnconditionally a join point, until the Final- phase. (The Final phase is still quite early, so we might consider- delaying still more.)--2. In mkDupableAlt and mkDupableStrictBind, generate an alterative for- all alternatives, except for exprIsTrival RHSs. Previously we used- exprIsDupable. This generates a lot more join points, but makes- them much more case-of-case friendly.-- It is definitely worth checking for exprIsTrivial, otherwise we get- an extra Simplifier iteration, because it is inlined in the next- round.--3. By the same token we want to use Plan B in- Note [Duplicating StrictArg] when the RHS of the new join point- is a data constructor application. That same Note explains why we- want Plan A when the RHS of the new join point would be a- non-data-constructor application--4. You might worry that $j will be inlined by the call-site inliner,- but it won't because the call-site context for a join is usually- extremely boring (the arguments come from the pattern match).- And if not, then perhaps inlining it would be a good idea.-- You might also wonder if we get UnfWhen, because the RHS of the- join point is no bigger than the call. But in the cases we care- about it will be a little bigger, because of that free `f` in- $j x = K f x- So for now we don't do anything special in callSiteInline--There is a bit of tension between (2) and (3). Do we want to retain-the join point only when the RHS is-* a constructor application? or-* just non-trivial?-Currently, a bit ad-hoc, but we definitely want to retain the join-point for data constructors in mkDupalbleALt (point 2); that is the-whole point of #19996 described above.--Historical Note [Case binders and join points]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-NB: this entire Note is now irrelevant. In Jun 21 we stopped-adding unfoldings to lambda binders (#17530). It was always a-hack and bit us in multiple small and not-so-small ways--Consider this- case (case .. ) of c {- I# c# -> ....c....--If we make a join point with c but not c# we get- $j = \c -> ....c....--But if later inlining scrutinises the c, thus-- $j = \c -> ... case c of { I# y -> ... } ...--we won't see that 'c' has already been scrutinised. This actually-happens in the 'tabulate' function in wave4main, and makes a significant-difference to allocation.--An alternative plan is this:-- $j = \c# -> let c = I# c# in ...c....--but that is bad if 'c' is *not* later scrutinised.--So instead we do both: we pass 'c' and 'c#' , and record in c's inlining-(a stable unfolding) that it's really I# c#, thus-- $j = \c# -> \c[=I# c#] -> ...c....--Absence analysis may later discard 'c'.--NB: take great care when doing strictness analysis;- see Note [Lambda-bound unfoldings] in GHC.Core.Opt.DmdAnal.--Also note that we can still end up passing stuff that isn't used. Before-strictness analysis we have- let $j x y c{=(x,y)} = (h c, ...)- in ...-After strictness analysis we see that h is strict, we end up with- let $j x y c{=(x,y)} = ($wh x y, ...)-and c is unused.--Note [Duplicated env]-~~~~~~~~~~~~~~~~~~~~~-Some of the alternatives are simplified, but have not been turned into a join point-So they *must* have a zapped subst-env. So we can't use completeNonRecX to-bind the join point, because it might to do PostInlineUnconditionally, and-we'd lose that when zapping the subst-env. We could have a per-alt subst-env,-but zapping it (as we do in mkDupableCont, the Select case) is safe, and-at worst delays the join-point inlining.--Note [Funky mkLamTypes]-~~~~~~~~~~~~~~~~~~~~~~-Notice the funky mkLamTypes. If the constructor has existentials-it's possible that the join point will be abstracted over-type variables as well as term variables.- Example: Suppose we have- data T = forall t. C [t]- Then faced with- case (case e of ...) of- C t xs::[t] -> rhs- We get the join point- let j :: forall t. [t] -> ...- j = /\t \xs::[t] -> rhs- in- case (case e of ...) of- C t xs::[t] -> j t xs--Note [Duplicating StrictArg]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Dealing with making a StrictArg continuation duplicable has turned out-to be one of the trickiest corners of the simplifier, giving rise-to several cases in which the simplier expanded the program's size-*exponentially*. They include- #13253 exponential inlining- #10421 ditto- #18140 strict constructors- #18282 another nested-function call case--Suppose we have a call- f e1 (case x of { True -> r1; False -> r2 }) e3-and f is strict in its second argument. Then we end up in-mkDupableCont with a StrictArg continuation for (f e1 <> e3).-There are two ways to make it duplicable.--* Plan A: move the entire call inwards, being careful not- to duplicate e1 or e3, thus:- let a1 = e1- a3 = e3- in case x of { True -> f a1 r1 a3- ; False -> f a1 r2 a3 }--* Plan B: make a join point:- join $j x = f e1 x e3- in case x of { True -> jump $j r1- ; False -> jump $j r2 }-- Notice that Plan B is very like the way we handle strict bindings;- see Note [Duplicating StrictBind]. And Plan B is exactly what we'd- get if we turned use a case expression to evaluate the strict arg:-- case (case x of { True -> r1; False -> r2 }) of- r -> f e1 r e3-- So, looking at Note [Duplicating join points], we also want Plan B- when `f` is a data constructor.--Plan A is often good. Here's an example from #3116- go (n+1) (case l of- 1 -> bs'- _ -> Chunk p fpc (o+1) (l-1) bs')--If we pushed the entire call for 'go' inside the case, we get-call-pattern specialisation for 'go', which is *crucial* for-this particular program.--Here is another example.- && E (case x of { T -> F; F -> T })--Pushing the call inward (being careful not to duplicate E)- let a = E- in case x of { T -> && a F; F -> && a T }--and now the (&& a F) etc can optimise. Moreover there might-be a RULE for the function that can fire when it "sees" the-particular case alternative.--But Plan A can have terrible, terrible behaviour. Here is a classic-case:- f (f (f (f (f True))))--Suppose f is strict, and has a body that is small enough to inline.-The innermost call inlines (seeing the True) to give- f (f (f (f (case v of { True -> e1; False -> e2 }))))--Now, suppose we naively push the entire continuation into both-case branches (it doesn't look large, just f.f.f.f). We get- case v of- True -> f (f (f (f e1)))- False -> f (f (f (f e2)))--And now the process repeats, so we end up with an exponentially large-number of copies of f. No good!--CONCLUSION: we want Plan A in general, but do Plan B is there a-danger of this nested call behaviour. The function that decides-this is called thumbsUpPlanA.--Note [Keeping demand info in StrictArg Plan A]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Following on from Note [Duplicating StrictArg], another common code-pattern that can go bad is this:- f (case x1 of { T -> F; F -> T })- (case x2 of { T -> F; F -> T })- ...etc...-when f is strict in all its arguments. (It might, for example, be a-strict data constructor whose wrapper has not yet been inlined.)--We use Plan A (because there is no nesting) giving- let a2 = case x2 of ...- a3 = case x3 of ...- in case x1 of { T -> f F a2 a3 ... ; F -> f T a2 a3 ... }--Now we must be careful! a2 and a3 are small, and the OneOcc code in-postInlineUnconditionally may inline them both at both sites; see Note-Note [Inline small things to avoid creating a thunk] in-Simplify.Utils. But if we do inline them, the entire process will-repeat -- back to exponential behaviour.--So we are careful to keep the demand-info on a2 and a3. Then they'll-be /strict/ let-bindings, which will be dealt with by StrictBind.-That's why contIsDupableWithDmds is careful to propagage demand-info to the auxiliary bindings it creates. See the Demand argument-to makeTrivial.--Note [Duplicating StrictBind]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We make a StrictBind duplicable in a very similar way to-that for case expressions. After all,- let x* = e in b is similar to case e of x -> b--So we potentially make a join-point for the body, thus:- let x = <> in b ==> join j x = b- in j <>--Just like StrictArg in fact -- and indeed they share code.--Note [Join point abstraction] Historical note-~~~~~~~~~~~~~~~~~~~~~~~~~~~~-NB: This note is now historical, describing how (in the past) we used-to add a void argument to nullary join points. But now that "join-point" is not a fuzzy concept but a formal syntactic construct (as-distinguished by the JoinId constructor of IdDetails), each of these-concerns is handled separately, with no need for a vestigial extra-argument.--Join points always have at least one value argument,-for several reasons--* If we try to lift a primitive-typed something out- for let-binding-purposes, we will *caseify* it (!),- with potentially-disastrous strictness results. So- instead we turn it into a function: \v -> e- where v::Void#. The value passed to this function is void,- which generates (almost) no code.--* CPR. We used to say "&& isUnliftedType rhs_ty'" here, but now- we make the join point into a function whenever used_bndrs'- is empty. This makes the join-point more CPR friendly.- Consider: let j = if .. then I# 3 else I# 4- in case .. of { A -> j; B -> j; C -> ... }-- Now CPR doesn't w/w j because it's a thunk, so- that means that the enclosing function can't w/w either,- which is a lose. Here's the example that happened in practice:- kgmod :: Int -> Int -> Int- kgmod x y = if x > 0 && y < 0 || x < 0 && y > 0- then 78- else 5--* Let-no-escape. We want a join point to turn into a let-no-escape- so that it is implemented as a jump, and one of the conditions- for LNE is that it's not updatable. In CoreToStg, see- Note [What is a non-escaping let]--* Floating. Since a join point will be entered once, no sharing is- gained by floating out, but something might be lost by doing- so because it might be allocated.--I have seen a case alternative like this:- True -> \v -> ...-It's a bit silly to add the realWorld dummy arg in this case, making- $j = \s v -> ...- True -> $j s-(the \v alone is enough to make CPR happy) but I think it's rare--There's a slight infelicity here: we pass the overall-case_bndr to all the join points if it's used in *any* RHS,-because we don't know its usage in each RHS separately----************************************************************************-* *- Unfoldings-* *-************************************************************************--}--simplLetUnfolding :: SimplEnv- -> BindContext- -> InId- -> OutExpr -> OutType -> ArityType- -> Unfolding -> SimplM Unfolding-simplLetUnfolding env bind_cxt id new_rhs rhs_ty arity unf- | isStableUnfolding unf- = simplStableUnfolding env bind_cxt id rhs_ty arity unf- | isExitJoinId id- = return noUnfolding -- See Note [Do not inline exit join points] in GHC.Core.Opt.Exitify- | otherwise- = -- Otherwise, we end up retaining all the SimpleEnv- let !opts = seUnfoldingOpts env- in mkLetUnfolding opts (bindContextLevel bind_cxt) InlineRhs id new_rhs----------------------mkLetUnfolding :: UnfoldingOpts -> TopLevelFlag -> UnfoldingSource- -> InId -> OutExpr -> SimplM Unfolding-mkLetUnfolding !uf_opts top_lvl src id new_rhs- = return (mkUnfolding uf_opts src is_top_lvl is_bottoming new_rhs Nothing)- -- We make an unfolding *even for loop-breakers*.- -- Reason: (a) It might be useful to know that they are WHNF- -- (b) In GHC.Iface.Tidy we currently assume that, if we want to- -- expose the unfolding then indeed we *have* an unfolding- -- to expose. (We could instead use the RHS, but currently- -- we don't.) The simple thing is always to have one.- where- -- Might as well force this, profiles indicate up to 0.5MB of thunks- -- just from this site.- !is_top_lvl = isTopLevel top_lvl- -- See Note [Force bottoming field]- !is_bottoming = isDeadEndId id----------------------simplStableUnfolding :: SimplEnv -> BindContext- -> InId- -> OutType- -> ArityType -- Used to eta expand, but only for non-join-points- -> Unfolding- ->SimplM Unfolding--- Note [Setting the new unfolding]-simplStableUnfolding env bind_cxt id rhs_ty id_arity unf- = case unf of- NoUnfolding -> return unf- BootUnfolding -> return unf- OtherCon {} -> return unf-- DFunUnfolding { df_bndrs = bndrs, df_con = con, df_args = args }- -> do { (env', bndrs') <- simplBinders unf_env bndrs- ; args' <- mapM (simplExpr env') args- ; return (mkDFunUnfolding bndrs' con args') }-- CoreUnfolding { uf_tmpl = expr, uf_src = src, uf_guidance = guide }- | isStableSource src- -> do { expr' <- case bind_cxt of- BC_Join cont -> -- Binder is a join point- -- See Note [Rules and unfolding for join points]- simplJoinRhs unf_env id expr cont- BC_Let {} -> -- Binder is not a join point- do { expr' <- simplExprC unf_env expr (mkBoringStop rhs_ty)- ; return (eta_expand expr') }- ; case guide of- UnfWhen { ug_arity = arity- , ug_unsat_ok = sat_ok- , ug_boring_ok = boring_ok- }- -- Happens for INLINE things- -- Really important to force new_boring_ok as otherwise- -- `ug_boring_ok` is a thunk chain of- -- inlineBoringExprOk expr0- -- || inlineBoringExprOk expr1 || ...- -- See #20134- -> let !new_boring_ok = boring_ok || inlineBoringOk expr'- guide' =- UnfWhen { ug_arity = arity- , ug_unsat_ok = sat_ok- , ug_boring_ok = new_boring_ok-- }- -- Refresh the boring-ok flag, in case expr'- -- has got small. This happens, notably in the inlinings- -- for dfuns for single-method classes; see- -- Note [Single-method classes] in GHC.Tc.TyCl.Instance.- -- A test case is #4138- -- But retain a previous boring_ok of True; e.g. see- -- the way it is set in calcUnfoldingGuidanceWithArity- in return (mkCoreUnfolding src is_top_lvl expr' Nothing guide')- -- See Note [Top-level flag on inline rules] in GHC.Core.Unfold-- _other -- Happens for INLINABLE things- -> mkLetUnfolding uf_opts top_lvl src id expr' }- -- If the guidance is UnfIfGoodArgs, this is an INLINABLE- -- unfolding, and we need to make sure the guidance is kept up- -- to date with respect to any changes in the unfolding.-- | otherwise -> return noUnfolding -- Discard unstable unfoldings- where- uf_opts = seUnfoldingOpts env- -- Forcing this can save about 0.5MB of max residency and the result- -- is small and easy to compute so might as well force it.- top_lvl = bindContextLevel bind_cxt- !is_top_lvl = isTopLevel top_lvl- act = idInlineActivation id- unf_env = updMode (updModeForStableUnfoldings act) env- -- See Note [Simplifying inside stable unfoldings] in GHC.Core.Opt.Simplify.Utils-- -- See Note [Eta-expand stable unfoldings]- eta_expand expr- | not eta_on = expr- | exprIsTrivial expr = expr- | otherwise = etaExpandAT (getInScope env) id_arity expr- eta_on = sm_eta_expand (getMode env)--{- Note [Eta-expand stable unfoldings]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-For INLINE/INLINABLE things (which get stable unfoldings) there's a danger-of getting- f :: Int -> Int -> Int -> Blah- [ Arity = 3 -- Good arity- , Unf=Stable (\xy. blah) -- Less good arity, only 2- f = \pqr. e--This can happen because f's RHS is optimised more vigorously than-its stable unfolding. Now suppose we have a call- g = f x-Because f has arity=3, g will have arity=2. But if we inline f (using-its stable unfolding) g's arity will reduce to 1, because <blah>-hasn't been optimised yet. This happened in the 'parsec' library,-for Text.Pasec.Char.string.--Generally, if we know that 'f' has arity N, it seems sensible to-eta-expand the stable unfolding to arity N too. Simple and consistent.--Wrinkles--* See Note [Eta-expansion in stable unfoldings] in- GHC.Core.Opt.Simplify.Utils--* Don't eta-expand a trivial expr, else each pass will eta-reduce it,- and then eta-expand again. See Note [Do not eta-expand trivial expressions]- in GHC.Core.Opt.Simplify.Utils.--* Don't eta-expand join points; see Note [Do not eta-expand join points]- in GHC.Core.Opt.Simplify.Utils. We uphold this because the join-point- case (bind_cxt = BC_Join _) doesn't use eta_expand.--Note [Force bottoming field]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We need to force bottoming, or the new unfolding holds-on to the old unfolding (which is part of the id).--Note [Setting the new unfolding]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-* If there's an INLINE pragma, we simplify the RHS gently. Maybe we- should do nothing at all, but simplifying gently might get rid of- more crap.--* If not, we make an unfolding from the new RHS. But *only* for- non-loop-breakers. Making loop breakers not have an unfolding at all- means that we can avoid tests in exprIsConApp, for example. This is- important: if exprIsConApp says 'yes' for a recursive thing, then we- can get into an infinite loop--If there's a stable unfolding on a loop breaker (which happens for-INLINABLE), we hang on to the inlining. It's pretty dodgy, but the-user did say 'INLINE'. May need to revisit this choice.--************************************************************************-* *- Rules-* *-************************************************************************--Note [Rules in a letrec]-~~~~~~~~~~~~~~~~~~~~~~~~-After creating fresh binders for the binders of a letrec, we-substitute the RULES and add them back onto the binders; this is done-*before* processing any of the RHSs. This is important. Manuel found-cases where he really, really wanted a RULE for a recursive function-to apply in that function's own right-hand side.--See Note [Forming Rec groups] in "GHC.Core.Opt.OccurAnal"--}--addBndrRules :: SimplEnv -> InBndr -> OutBndr- -> BindContext- -> SimplM (SimplEnv, OutBndr)--- Rules are added back into the bin-addBndrRules env in_id out_id bind_cxt- | null old_rules- = return (env, out_id)- | otherwise- = do { new_rules <- simplRules env (Just out_id) old_rules bind_cxt- ; let final_id = out_id `setIdSpecialisation` mkRuleInfo new_rules- ; return (modifyInScope env final_id, final_id) }- where- old_rules = ruleInfoRules (idSpecialisation in_id)--simplImpRules :: SimplEnv -> [CoreRule] -> SimplM [CoreRule]--- Simplify local rules for imported Ids-simplImpRules env rules- = simplRules env Nothing rules (BC_Let TopLevel NonRecursive)--simplRules :: SimplEnv -> Maybe OutId -> [CoreRule]- -> BindContext -> SimplM [CoreRule]-simplRules env mb_new_id rules bind_cxt- = mapM simpl_rule rules- where- simpl_rule rule@(BuiltinRule {})- = return rule-- simpl_rule rule@(Rule { ru_bndrs = bndrs, ru_args = args- , ru_fn = fn_name, ru_rhs = rhs- , ru_act = act })- = do { (env', bndrs') <- simplBinders env bndrs- ; let rhs_ty = substTy env' (exprType rhs)- rhs_cont = case bind_cxt of -- See Note [Rules and unfolding for join points]- BC_Let {} -> mkBoringStop rhs_ty- BC_Join cont -> assertPpr join_ok bad_join_msg cont- lhs_env = updMode updModeForRules env'- rhs_env = updMode (updModeForStableUnfoldings act) env'- -- See Note [Simplifying the RHS of a RULE]- fn_name' = case mb_new_id of- Just id -> idName id- Nothing -> fn_name-- -- join_ok is an assertion check that the join-arity of the- -- binder matches that of the rule, so that pushing the- -- continuation into the RHS makes sense- join_ok = case mb_new_id of- Just id | Just join_arity <- isJoinId_maybe id- -> length args == join_arity- _ -> False- bad_join_msg = vcat [ ppr mb_new_id, ppr rule- , ppr (fmap isJoinId_maybe mb_new_id) ]-- ; args' <- mapM (simplExpr lhs_env) args- ; rhs' <- simplExprC rhs_env rhs rhs_cont- ; return (rule { ru_bndrs = bndrs'- , ru_fn = fn_name'- , ru_args = args'- , ru_rhs = occurAnalyseExpr rhs' }) }- -- Remember to occ-analyse, to drop dead code.- -- See Note [OccInfo in unfoldings and rules] in GHC.Core--{- Note [Simplifying the RHS of a RULE]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We can simplify the RHS of a RULE much as we do the RHS of a stable-unfolding. We used to use the much more conservative updModeForRules-for the RHS as well as the LHS, but that seems more conservative-than necesary. Allowing some inlining might, for example, eliminate-a binding.--}--
− compiler/GHC/Core/Opt/Simplify/Env.hs
@@ -1,1069 +0,0 @@-{--(c) The AQUA Project, Glasgow University, 1993-1998--\section[GHC.Core.Opt.Simplify.Monad]{The simplifier Monad}--}----module GHC.Core.Opt.Simplify.Env (- -- * The simplifier mode- setMode, getMode, updMode, seDynFlags, seUnfoldingOpts, seLogger,-- -- * Environments- SimplEnv(..), pprSimplEnv, -- Temp not abstract- mkSimplEnv, extendIdSubst,- extendTvSubst, extendCvSubst,- zapSubstEnv, setSubstEnv, bumpCaseDepth,- getInScope, setInScopeFromE, setInScopeFromF,- setInScopeSet, modifyInScope, addNewInScopeIds,- getSimplRules, enterRecGroupRHSs,-- -- * Substitution results- SimplSR(..), mkContEx, substId, lookupRecBndr, refineFromInScope,-- -- * Simplifying 'Id' binders- simplNonRecBndr, simplNonRecJoinBndr, simplRecBndrs, simplRecJoinBndrs,- simplBinder, simplBinders,- substTy, substTyVar, getTCvSubst,- substCo, substCoVar,-- -- * Floats- SimplFloats(..), emptyFloats, isEmptyFloats, mkRecFloats,- mkFloatBind, addLetFloats, addJoinFloats, addFloats,- extendFloats, wrapFloats,- doFloatFromRhs, getTopFloatBinds,-- -- * LetFloats- LetFloats, letFloatBinds, emptyLetFloats, unitLetFloat,- addLetFlts, mapLetFloats,-- -- * JoinFloats- JoinFloat, JoinFloats, emptyJoinFloats,- wrapJoinFloats, wrapJoinFloatsX, unitJoinFloat, addJoinFlts- ) where--import GHC.Prelude--import GHC.Core.Opt.Simplify.Monad-import GHC.Core.Opt.Monad ( SimplMode(..) )-import GHC.Core-import GHC.Core.Utils-import GHC.Core.Multiplicity ( scaleScaled )-import GHC.Core.Unfold-import GHC.Types.Var-import GHC.Types.Var.Env-import GHC.Types.Var.Set-import GHC.Data.OrdList-import GHC.Data.Graph.UnVar-import GHC.Types.Id as Id-import GHC.Core.Make ( mkWildValBinder )-import GHC.Driver.Session ( DynFlags )-import GHC.Builtin.Types-import GHC.Core.TyCo.Rep ( TyCoBinder(..) )-import qualified GHC.Core.Type as Type-import GHC.Core.Type hiding ( substTy, substTyVar, substTyVarBndr, extendTvSubst, extendCvSubst )-import qualified GHC.Core.Coercion as Coercion-import GHC.Core.Coercion hiding ( substCo, substCoVar, substCoVarBndr )-import GHC.Types.Basic-import GHC.Utils.Monad-import GHC.Utils.Outputable-import GHC.Utils.Panic-import GHC.Utils.Panic.Plain-import GHC.Utils.Misc-import GHC.Utils.Logger-import GHC.Types.Unique.FM ( pprUniqFM )--import Data.List (mapAccumL)--{--************************************************************************-* *-\subsubsection{The @SimplEnv@ type}-* *-************************************************************************--}--data SimplEnv- = SimplEnv {- ----------- Static part of the environment ------------ -- Static in the sense of lexically scoped,- -- wrt the original expression-- seMode :: !SimplMode-- -- The current substitution- , seTvSubst :: TvSubstEnv -- InTyVar |--> OutType- , seCvSubst :: CvSubstEnv -- InCoVar |--> OutCoercion- , seIdSubst :: SimplIdSubst -- InId |--> OutExpr-- -- | Fast OutVarSet tracking which recursive RHSs we are analysing.- -- See Note [Eta reduction in recursive RHSs] in GHC.Core.Opt.Arity.- , seRecIds :: !UnVarSet-- ----------- Dynamic part of the environment ------------ -- Dynamic in the sense of describing the setup where- -- the expression finally ends up-- -- The current set of in-scope variables- -- They are all OutVars, and all bound in this module- , seInScope :: !InScopeSet -- OutVars only-- , seCaseDepth :: !Int -- Depth of multi-branch case alternatives- }--data SimplFloats- = SimplFloats- { -- Ordinary let bindings- sfLetFloats :: LetFloats- -- See Note [LetFloats]-- -- Join points- , sfJoinFloats :: JoinFloats- -- Handled separately; they don't go very far- -- We consider these to be /inside/ sfLetFloats- -- because join points can refer to ordinary bindings,- -- but not vice versa-- -- Includes all variables bound by sfLetFloats and- -- sfJoinFloats, plus at least whatever is in scope where- -- these bindings land up.- , sfInScope :: InScopeSet -- All OutVars- }--instance Outputable SimplFloats where- ppr (SimplFloats { sfLetFloats = lf, sfJoinFloats = jf, sfInScope = is })- = text "SimplFloats"- <+> braces (vcat [ text "lets: " <+> ppr lf- , text "joins:" <+> ppr jf- , text "in_scope:" <+> ppr is ])--emptyFloats :: SimplEnv -> SimplFloats-emptyFloats env- = SimplFloats { sfLetFloats = emptyLetFloats- , sfJoinFloats = emptyJoinFloats- , sfInScope = seInScope env }--isEmptyFloats :: SimplFloats -> Bool--- Precondition: used only when sfJoinFloats is empty-isEmptyFloats (SimplFloats { sfLetFloats = LetFloats fs _- , sfJoinFloats = js })- = assertPpr (isNilOL js) (ppr js ) $- isNilOL fs--pprSimplEnv :: SimplEnv -> SDoc--- Used for debugging; selective-pprSimplEnv env- = vcat [text "TvSubst:" <+> ppr (seTvSubst env),- text "CvSubst:" <+> ppr (seCvSubst env),- text "IdSubst:" <+> id_subst_doc,- text "InScope:" <+> in_scope_vars_doc- ]- where- id_subst_doc = pprUniqFM ppr (seIdSubst env)- in_scope_vars_doc = pprVarSet (getInScopeVars (seInScope env))- (vcat . map ppr_one)- ppr_one v | isId v = ppr v <+> ppr (idUnfolding v)- | otherwise = ppr v--type SimplIdSubst = IdEnv SimplSR -- IdId |--> OutExpr- -- See Note [Extending the Subst] in GHC.Core.Subst---- | A substitution result.-data SimplSR- = DoneEx OutExpr (Maybe JoinArity)- -- If x :-> DoneEx e ja is in the SimplIdSubst- -- then replace occurrences of x by e- -- and ja = Just a <=> x is a join-point of arity a- -- See Note [Join arity in SimplIdSubst]--- | DoneId OutId- -- If x :-> DoneId v is in the SimplIdSubst- -- then replace occurrences of x by v- -- and v is a join-point of arity a- -- <=> x is a join-point of arity a-- | ContEx TvSubstEnv -- A suspended substitution- CvSubstEnv- SimplIdSubst- InExpr- -- If x :-> ContEx tv cv id e is in the SimplISubst- -- then replace occurrences of x by (subst (tv,cv,id) e)--instance Outputable SimplSR where- ppr (DoneId v) = text "DoneId" <+> ppr v- ppr (DoneEx e mj) = text "DoneEx" <> pp_mj <+> ppr e- where- pp_mj = case mj of- Nothing -> empty- Just n -> parens (int n)-- ppr (ContEx _tv _cv _id e) = vcat [text "ContEx" <+> ppr e {-,- ppr (filter_env tv), ppr (filter_env id) -}]- -- where- -- fvs = exprFreeVars e- -- filter_env env = filterVarEnv_Directly keep env- -- keep uniq _ = uniq `elemUFM_Directly` fvs--{--Note [SimplEnv invariants]-~~~~~~~~~~~~~~~~~~~~~~~~~~-seInScope:- The in-scope part of Subst includes *all* in-scope TyVars and Ids- The elements of the set may have better IdInfo than the- occurrences of in-scope Ids, and (more important) they will- have a correctly-substituted type. So we use a lookup in this- set to replace occurrences-- The Ids in the InScopeSet are replete with their Rules,- and as we gather info about the unfolding of an Id, we replace- it in the in-scope set.-- The in-scope set is actually a mapping OutVar -> OutVar, and- in case expressions we sometimes bind--seIdSubst:- The substitution is *apply-once* only, because InIds and OutIds- can overlap.- For example, we generally omit mappings- a77 -> a77- from the substitution, when we decide not to clone a77, but it's quite- legitimate to put the mapping in the substitution anyway.-- Furthermore, consider- let x = case k of I# x77 -> ... in- let y = case k of I# x77 -> ... in ...- and suppose the body is strict in both x and y. Then the simplifier- will pull the first (case k) to the top; so the second (case k) will- cancel out, mapping x77 to, well, x77! But one is an in-Id and the- other is an out-Id.-- Of course, the substitution *must* applied! Things in its domain- simply aren't necessarily bound in the result.--* substId adds a binding (DoneId new_id) to the substitution if- the Id's unique has changed-- Note, though that the substitution isn't necessarily extended- if the type of the Id changes. Why not? Because of the next point:--* We *always, always* finish by looking up in the in-scope set- any variable that doesn't get a DoneEx or DoneVar hit in the substitution.- Reason: so that we never finish up with a "old" Id in the result.- An old Id might point to an old unfolding and so on... which gives a space- leak.-- [The DoneEx and DoneVar hits map to "new" stuff.]--* It follows that substExpr must not do a no-op if the substitution is empty.- substType is free to do so, however.--* When we come to a let-binding (say) we generate new IdInfo, including an- unfolding, attach it to the binder, and add this newly adorned binder to- the in-scope set. So all subsequent occurrences of the binder will get- mapped to the full-adorned binder, which is also the one put in the- binding site.--* The in-scope "set" usually maps x->x; we use it simply for its domain.- But sometimes we have two in-scope Ids that are synomyms, and should- map to the same target: x->x, y->x. Notably:- case y of x { ... }- That's why the "set" is actually a VarEnv Var--Note [Join arity in SimplIdSubst]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We have to remember which incoming variables are join points: the occurrences-may not be marked correctly yet, and we're in change of propagating the change if-OccurAnal makes something a join point).--Normally the in-scope set is where we keep the latest information, but-the in-scope set tracks only OutVars; if a binding is unconditionally-inlined (via DoneEx), it never makes it into the in-scope set, and we-need to know at the occurrence site that the variable is a join point-so that we know to drop the context. Thus we remember which join-points we're substituting. -}--mkSimplEnv :: SimplMode -> SimplEnv-mkSimplEnv mode- = SimplEnv { seMode = mode- , seInScope = init_in_scope- , seTvSubst = emptyVarEnv- , seCvSubst = emptyVarEnv- , seIdSubst = emptyVarEnv- , seRecIds = emptyUnVarSet- , seCaseDepth = 0 }- -- The top level "enclosing CC" is "SUBSUMED".--init_in_scope :: InScopeSet-init_in_scope = mkInScopeSet (unitVarSet (mkWildValBinder Many unitTy))- -- See Note [WildCard binders]--{--Note [WildCard binders]-~~~~~~~~~~~~~~~~~~~~~~~-The program to be simplified may have wild binders- case e of wild { p -> ... }-We want to *rename* them away, so that there are no-occurrences of 'wild-id' (with wildCardKey). The easy-way to do that is to start of with a representative-Id in the in-scope set--There can be *occurrences* of wild-id. For example,-GHC.Core.Make.mkCoreApp transforms- e (a /# b) --> case (a /# b) of wild { DEFAULT -> e wild }-This is ok provided 'wild' isn't free in 'e', and that's the delicate-thing. Generally, you want to run the simplifier to get rid of the-wild-ids before doing much else.--It's a very dark corner of GHC. Maybe it should be cleaned up.--}--getMode :: SimplEnv -> SimplMode-getMode env = seMode env--seDynFlags :: SimplEnv -> DynFlags-seDynFlags env = sm_dflags (seMode env)--seLogger :: SimplEnv -> Logger-seLogger env = sm_logger (seMode env)---seUnfoldingOpts :: SimplEnv -> UnfoldingOpts-seUnfoldingOpts env = sm_uf_opts (seMode env)---setMode :: SimplMode -> SimplEnv -> SimplEnv-setMode mode env = env { seMode = mode }--updMode :: (SimplMode -> SimplMode) -> SimplEnv -> SimplEnv-updMode upd env- = -- Avoid keeping env alive in case inlining fails.- let mode = upd $! (seMode env)- in env { seMode = mode }--bumpCaseDepth :: SimplEnv -> SimplEnv-bumpCaseDepth env = env { seCaseDepth = seCaseDepth env + 1 }------------------------extendIdSubst :: SimplEnv -> Id -> SimplSR -> SimplEnv-extendIdSubst env@(SimplEnv {seIdSubst = subst}) var res- = assertPpr (isId var && not (isCoVar var)) (ppr var) $- env { seIdSubst = extendVarEnv subst var res }--extendTvSubst :: SimplEnv -> TyVar -> Type -> SimplEnv-extendTvSubst env@(SimplEnv {seTvSubst = tsubst}) var res- = assertPpr (isTyVar var) (ppr var $$ ppr res) $- env {seTvSubst = extendVarEnv tsubst var res}--extendCvSubst :: SimplEnv -> CoVar -> Coercion -> SimplEnv-extendCvSubst env@(SimplEnv {seCvSubst = csubst}) var co- = assert (isCoVar var) $- env {seCvSubst = extendVarEnv csubst var co}------------------------getInScope :: SimplEnv -> InScopeSet-getInScope env = seInScope env--setInScopeSet :: SimplEnv -> InScopeSet -> SimplEnv-setInScopeSet env in_scope = env {seInScope = in_scope}--setInScopeFromE :: SimplEnv -> SimplEnv -> SimplEnv--- See Note [Setting the right in-scope set]-setInScopeFromE rhs_env here_env = rhs_env { seInScope = seInScope here_env }--setInScopeFromF :: SimplEnv -> SimplFloats -> SimplEnv-setInScopeFromF env floats = env { seInScope = sfInScope floats }--addNewInScopeIds :: SimplEnv -> [CoreBndr] -> SimplEnv- -- The new Ids are guaranteed to be freshly allocated-addNewInScopeIds env@(SimplEnv { seInScope = in_scope, seIdSubst = id_subst }) vs--- See Note [Bangs in the Simplifier]- = let !in_scope1 = in_scope `extendInScopeSetList` vs- !id_subst1 = id_subst `delVarEnvList` vs- in- env { seInScope = in_scope1,- seIdSubst = id_subst1 }- -- Why delete? Consider- -- let x = a*b in (x, \x -> x+3)- -- We add [x |-> a*b] to the substitution, but we must- -- _delete_ it from the substitution when going inside- -- the (\x -> ...)!--modifyInScope :: SimplEnv -> CoreBndr -> SimplEnv--- The variable should already be in scope, but--- replace the existing version with this new one--- which has more information-modifyInScope env@(SimplEnv {seInScope = in_scope}) v- = env {seInScope = extendInScopeSet in_scope v}--enterRecGroupRHSs :: SimplEnv -> [OutBndr] -> (SimplEnv -> SimplM (r, SimplEnv))- -> SimplM (r, SimplEnv)-enterRecGroupRHSs env bndrs k = do- --pprTraceM "enterRecGroupRHSs" (ppr bndrs)- (r, env'') <- k env{seRecIds = extendUnVarSetList bndrs (seRecIds env)}- return (r, env''{seRecIds = seRecIds env})--{- Note [Setting the right in-scope set]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider- \x. (let x = e in b) arg[x]-where the let shadows the lambda. Really this means something like- \x1. (let x2 = e in b) arg[x1]--- When we capture the 'arg' in an ApplyToVal continuation, we capture- the environment, which says what 'x' is bound to, namely x1--- Then that continuation gets pushed under the let--- Finally we simplify 'arg'. We want- - the static, lexical environment binding x :-> x1- - the in-scopeset from "here", under the 'let' which includes- both x1 and x2--It's important to have the right in-scope set, else we may rename a-variable to one that is already in scope. So we must pick up the-in-scope set from "here", but otherwise use the environment we-captured along with 'arg'. This transfer of in-scope set is done by-setInScopeFromE.--}------------------------zapSubstEnv :: SimplEnv -> SimplEnv-zapSubstEnv env = env {seTvSubst = emptyVarEnv, seCvSubst = emptyVarEnv, seIdSubst = emptyVarEnv}--setSubstEnv :: SimplEnv -> TvSubstEnv -> CvSubstEnv -> SimplIdSubst -> SimplEnv-setSubstEnv env tvs cvs ids = env { seTvSubst = tvs, seCvSubst = cvs, seIdSubst = ids }--mkContEx :: SimplEnv -> InExpr -> SimplSR-mkContEx (SimplEnv { seTvSubst = tvs, seCvSubst = cvs, seIdSubst = ids }) e = ContEx tvs cvs ids e--{--************************************************************************-* *-\subsection{LetFloats}-* *-************************************************************************--Note [LetFloats]-~~~~~~~~~~~~~~~~-The LetFloats is a bunch of bindings, classified by a FloatFlag.--* All of them satisfy the let/app invariant--Examples-- NonRec x (y:ys) FltLifted- Rec [(x,rhs)] FltLifted-- NonRec x* (p:q) FltOKSpec -- RHS is WHNF. Question: why not FltLifted?- NonRec x# (y +# 3) FltOkSpec -- Unboxed, but ok-for-spec'n-- NonRec x* (f y) FltCareful -- Strict binding; might fail or diverge--Can't happen:- NonRec x# (a /# b) -- Might fail; does not satisfy let/app- NonRec x# (f y) -- Might diverge; does not satisfy let/app--}--data LetFloats = LetFloats (OrdList OutBind) FloatFlag- -- See Note [LetFloats]--type JoinFloat = OutBind-type JoinFloats = OrdList JoinFloat--data FloatFlag- = FltLifted -- All bindings are lifted and lazy *or*- -- consist of a single primitive string literal- -- Hence ok to float to top level, or recursive-- | FltOkSpec -- All bindings are FltLifted *or*- -- strict (perhaps because unlifted,- -- perhaps because of a strict binder),- -- *and* ok-for-speculation- -- Hence ok to float out of the RHS- -- of a lazy non-recursive let binding- -- (but not to top level, or into a rec group)-- | FltCareful -- At least one binding is strict (or unlifted)- -- and not guaranteed cheap- -- Do not float these bindings out of a lazy let--instance Outputable LetFloats where- ppr (LetFloats binds ff) = ppr ff $$ ppr (fromOL binds)--instance Outputable FloatFlag where- ppr FltLifted = text "FltLifted"- ppr FltOkSpec = text "FltOkSpec"- ppr FltCareful = text "FltCareful"--andFF :: FloatFlag -> FloatFlag -> FloatFlag-andFF FltCareful _ = FltCareful-andFF FltOkSpec FltCareful = FltCareful-andFF FltOkSpec _ = FltOkSpec-andFF FltLifted flt = flt--doFloatFromRhs :: TopLevelFlag -> RecFlag -> Bool -> SimplFloats -> OutExpr -> Bool--- If you change this function look also at FloatIn.noFloatFromRhs-doFloatFromRhs lvl rec strict_bind (SimplFloats { sfLetFloats = LetFloats fs ff }) rhs- = not (isNilOL fs) && want_to_float && can_float- where- want_to_float = isTopLevel lvl || exprIsCheap rhs || exprIsExpandable rhs- -- See Note [Float when cheap or expandable]- can_float = case ff of- FltLifted -> True- FltOkSpec -> isNotTopLevel lvl && isNonRec rec- FltCareful -> isNotTopLevel lvl && isNonRec rec && strict_bind--{--Note [Float when cheap or expandable]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We want to float a let from a let if the residual RHS is- a) cheap, such as (\x. blah)- b) expandable, such as (f b) if f is CONLIKE-But there are- - cheap things that are not expandable (eg \x. expensive)- - expandable things that are not cheap (eg (f b) where b is CONLIKE)-so we must take the 'or' of the two.--}--emptyLetFloats :: LetFloats-emptyLetFloats = LetFloats nilOL FltLifted--emptyJoinFloats :: JoinFloats-emptyJoinFloats = nilOL--unitLetFloat :: OutBind -> LetFloats--- This key function constructs a singleton float with the right form-unitLetFloat bind = assert (all (not . isJoinId) (bindersOf bind)) $- LetFloats (unitOL bind) (flag bind)- where- flag (Rec {}) = FltLifted- flag (NonRec bndr rhs)- | not (isStrictId bndr) = FltLifted- | exprIsTickedString rhs = FltLifted- -- String literals can be floated freely.- -- See Note [Core top-level string literals] in GHC.Core.- | exprOkForSpeculation rhs = FltOkSpec -- Unlifted, and lifted but ok-for-spec (eg HNF)- | otherwise = assertPpr (not (isUnliftedType (idType bndr))) (ppr bndr)- -- NB: binders always have a fixed RuntimeRep, so calling- -- isUnliftedType is OK here- FltCareful- -- Unlifted binders can only be let-bound if exprOkForSpeculation holds--unitJoinFloat :: OutBind -> JoinFloats-unitJoinFloat bind = assert (all isJoinId (bindersOf bind)) $- unitOL bind--mkFloatBind :: SimplEnv -> OutBind -> (SimplFloats, SimplEnv)--- Make a singleton SimplFloats, and--- extend the incoming SimplEnv's in-scope set with its binders--- These binders may already be in the in-scope set,--- but may have by now been augmented with more IdInfo-mkFloatBind env bind- = (floats, env { seInScope = in_scope' })- where- floats- | isJoinBind bind- = SimplFloats { sfLetFloats = emptyLetFloats- , sfJoinFloats = unitJoinFloat bind- , sfInScope = in_scope' }- | otherwise- = SimplFloats { sfLetFloats = unitLetFloat bind- , sfJoinFloats = emptyJoinFloats- , sfInScope = in_scope' }- -- See Note [Bangs in the Simplifier]- !in_scope' = seInScope env `extendInScopeSetBind` bind--extendFloats :: SimplFloats -> OutBind -> SimplFloats--- Add this binding to the floats, and extend the in-scope env too-extendFloats (SimplFloats { sfLetFloats = floats- , sfJoinFloats = jfloats- , sfInScope = in_scope })- bind- | isJoinBind bind- = SimplFloats { sfInScope = in_scope'- , sfLetFloats = floats- , sfJoinFloats = jfloats' }- | otherwise- = SimplFloats { sfInScope = in_scope'- , sfLetFloats = floats'- , sfJoinFloats = jfloats }- where- in_scope' = in_scope `extendInScopeSetBind` bind- floats' = floats `addLetFlts` unitLetFloat bind- jfloats' = jfloats `addJoinFlts` unitJoinFloat bind--addLetFloats :: SimplFloats -> LetFloats -> SimplFloats--- Add the let-floats for env2 to env1;--- *plus* the in-scope set for env2, which is bigger--- than that for env1-addLetFloats floats let_floats- = floats { sfLetFloats = sfLetFloats floats `addLetFlts` let_floats- , sfInScope = sfInScope floats `extendInScopeFromLF` let_floats }--extendInScopeFromLF :: InScopeSet -> LetFloats -> InScopeSet-extendInScopeFromLF in_scope (LetFloats binds _)- = foldlOL extendInScopeSetBind in_scope binds--addJoinFloats :: SimplFloats -> JoinFloats -> SimplFloats-addJoinFloats floats join_floats- = floats { sfJoinFloats = sfJoinFloats floats `addJoinFlts` join_floats- , sfInScope = foldlOL extendInScopeSetBind- (sfInScope floats) join_floats }--extendInScopeSetBind :: InScopeSet -> CoreBind -> InScopeSet-extendInScopeSetBind in_scope bind- = extendInScopeSetList in_scope (bindersOf bind)--addFloats :: SimplFloats -> SimplFloats -> SimplFloats--- Add both let-floats and join-floats for env2 to env1;--- *plus* the in-scope set for env2, which is bigger--- than that for env1-addFloats (SimplFloats { sfLetFloats = lf1, sfJoinFloats = jf1 })- (SimplFloats { sfLetFloats = lf2, sfJoinFloats = jf2, sfInScope = in_scope })- = SimplFloats { sfLetFloats = lf1 `addLetFlts` lf2- , sfJoinFloats = jf1 `addJoinFlts` jf2- , sfInScope = in_scope }--addLetFlts :: LetFloats -> LetFloats -> LetFloats-addLetFlts (LetFloats bs1 l1) (LetFloats bs2 l2)- = LetFloats (bs1 `appOL` bs2) (l1 `andFF` l2)--letFloatBinds :: LetFloats -> [CoreBind]-letFloatBinds (LetFloats bs _) = fromOL bs--addJoinFlts :: JoinFloats -> JoinFloats -> JoinFloats-addJoinFlts = appOL--mkRecFloats :: SimplFloats -> SimplFloats--- Flattens the floats into a single Rec group,--- They must either all be lifted LetFloats or all JoinFloats-mkRecFloats floats@(SimplFloats { sfLetFloats = LetFloats bs _ff- , sfJoinFloats = jbs- , sfInScope = in_scope })- = assertPpr (isNilOL bs || isNilOL jbs) (ppr floats) $- SimplFloats { sfLetFloats = floats'- , sfJoinFloats = jfloats'- , sfInScope = in_scope }- where- -- See Note [Bangs in the Simplifier]- !floats' | isNilOL bs = emptyLetFloats- | otherwise = unitLetFloat (Rec (flattenBinds (fromOL bs)))- !jfloats' | isNilOL jbs = emptyJoinFloats- | otherwise = unitJoinFloat (Rec (flattenBinds (fromOL jbs)))--wrapFloats :: SimplFloats -> OutExpr -> OutExpr--- Wrap the floats around the expression; they should all--- satisfy the let/app invariant, so mkLets should do the job just fine-wrapFloats (SimplFloats { sfLetFloats = LetFloats bs _- , sfJoinFloats = jbs }) body- = foldrOL Let (wrapJoinFloats jbs body) bs- -- Note: Always safe to put the joins on the inside- -- since the values can't refer to them--wrapJoinFloatsX :: SimplFloats -> OutExpr -> (SimplFloats, OutExpr)--- Wrap the sfJoinFloats of the env around the expression,--- and take them out of the SimplEnv-wrapJoinFloatsX floats body- = ( floats { sfJoinFloats = emptyJoinFloats }- , wrapJoinFloats (sfJoinFloats floats) body )--wrapJoinFloats :: JoinFloats -> OutExpr -> OutExpr--- Wrap the sfJoinFloats of the env around the expression,--- and take them out of the SimplEnv-wrapJoinFloats join_floats body- = foldrOL Let body join_floats--getTopFloatBinds :: SimplFloats -> [CoreBind]-getTopFloatBinds (SimplFloats { sfLetFloats = lbs- , sfJoinFloats = jbs})- = assert (isNilOL jbs) $ -- Can't be any top-level join bindings- letFloatBinds lbs--{-# INLINE mapLetFloats #-}-mapLetFloats :: LetFloats -> ((Id,CoreExpr) -> (Id,CoreExpr)) -> LetFloats-mapLetFloats (LetFloats fs ff) fun- = LetFloats fs1 ff- where- app (NonRec b e) = case fun (b,e) of (b',e') -> NonRec b' e'- app (Rec bs) = Rec (strictMap fun bs)- !fs1 = (mapOL' app fs) -- See Note [Bangs in the Simplifier]--{--************************************************************************-* *- Substitution of Vars-* *-************************************************************************--Note [Global Ids in the substitution]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We look up even a global (eg imported) Id in the substitution. Consider- case X.g_34 of b { (a,b) -> ... case X.g_34 of { (p,q) -> ...} ... }-The binder-swap in the occurrence analyser will add a binding-for a LocalId version of g (with the same unique though):- case X.g_34 of b { (a,b) -> let g_34 = b in- ... case X.g_34 of { (p,q) -> ...} ... }-So we want to look up the inner X.g_34 in the substitution, where we'll-find that it has been substituted by b. (Or conceivably cloned.)--}--substId :: SimplEnv -> InId -> SimplSR--- Returns DoneEx only on a non-Var expression-substId (SimplEnv { seInScope = in_scope, seIdSubst = ids }) v- = case lookupVarEnv ids v of -- Note [Global Ids in the substitution]- Nothing -> DoneId (refineFromInScope in_scope v)- Just (DoneId v) -> DoneId (refineFromInScope in_scope v)- Just res -> res -- DoneEx non-var, or ContEx-- -- Get the most up-to-date thing from the in-scope set- -- Even though it isn't in the substitution, it may be in- -- the in-scope set with better IdInfo.- --- -- See also Note [In-scope set as a substitution] in GHC.Core.Opt.Simplify.--refineFromInScope :: InScopeSet -> Var -> Var-refineFromInScope in_scope v- | isLocalId v = case lookupInScope in_scope v of- Just v' -> v'- Nothing -> pprPanic "refineFromInScope" (ppr in_scope $$ ppr v)- -- c.f #19074 for a subtle place where this went wrong- | otherwise = v--lookupRecBndr :: SimplEnv -> InId -> OutId--- Look up an Id which has been put into the envt by simplRecBndrs,--- but where we have not yet done its RHS-lookupRecBndr (SimplEnv { seInScope = in_scope, seIdSubst = ids }) v- = case lookupVarEnv ids v of- Just (DoneId v) -> v- Just _ -> pprPanic "lookupRecBndr" (ppr v)- Nothing -> refineFromInScope in_scope v--{--************************************************************************-* *-\section{Substituting an Id binder}-* *-************************************************************************---These functions are in the monad only so that they can be made strict via seq.--Note [Return type for join points]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider-- (join j :: Char -> Int -> Int) 77- ( j x = \y. y + ord x )- (in case v of )- ( A -> j 'x' )- ( B -> j 'y' )- ( C -> <blah> )--The simplifier pushes the "apply to 77" continuation inwards to give-- join j :: Char -> Int- j x = (\y. y + ord x) 77- in case v of- A -> j 'x'- B -> j 'y'- C -> <blah> 77--Notice that the "apply to 77" continuation went into the RHS of the-join point. And that meant that the return type of the join point-changed!!--That's why we pass res_ty into simplNonRecJoinBndr, and substIdBndr-takes a (Just res_ty) argument so that it knows to do the type-changing-thing.--See also Note [Scaling join point arguments].--}--simplBinders :: SimplEnv -> [InBndr] -> SimplM (SimplEnv, [OutBndr])-simplBinders !env bndrs = mapAccumLM simplBinder env bndrs----------------simplBinder :: SimplEnv -> InBndr -> SimplM (SimplEnv, OutBndr)--- Used for lambda and case-bound variables--- Clone Id if necessary, substitute type--- Return with IdInfo already substituted, but (fragile) occurrence info zapped--- The substitution is extended only if the variable is cloned, because--- we *don't* need to use it to track occurrence info.-simplBinder !env bndr- | isTyVar bndr = do { let (env', tv) = substTyVarBndr env bndr- ; seqTyVar tv `seq` return (env', tv) }- | otherwise = do { let (env', id) = substIdBndr env bndr- ; seqId id `seq` return (env', id) }------------------simplNonRecBndr :: SimplEnv -> InBndr -> SimplM (SimplEnv, OutBndr)--- A non-recursive let binder-simplNonRecBndr !env id- -- See Note [Bangs in the Simplifier]- = do { let (!env1, id1) = substIdBndr env id- ; seqId id1 `seq` return (env1, id1) }------------------simplRecBndrs :: SimplEnv -> [InBndr] -> SimplM SimplEnv--- Recursive let binders-simplRecBndrs env@(SimplEnv {}) ids- -- See Note [Bangs in the Simplifier]- = assert (all (not . isJoinId) ids) $- do { let (!env1, ids1) = mapAccumL substIdBndr env ids- ; seqIds ids1 `seq` return env1 }-------------------substIdBndr :: SimplEnv -> InBndr -> (SimplEnv, OutBndr)--- Might be a coercion variable-substIdBndr env bndr- | isCoVar bndr = substCoVarBndr env bndr- | otherwise = substNonCoVarIdBndr env bndr------------------substNonCoVarIdBndr- :: SimplEnv- -> InBndr -- Env and binder to transform- -> (SimplEnv, OutBndr)--- Clone Id if necessary, substitute its type--- Return an Id with its--- * Type substituted--- * UnfoldingInfo, Rules, WorkerInfo zapped--- * Fragile OccInfo (only) zapped: Note [Robust OccInfo]--- * Robust info, retained especially arity and demand info,--- so that they are available to occurrences that occur in an--- earlier binding of a letrec------ For the robust info, see Note [Arity robustness]------ Augment the substitution if the unique changed--- Extend the in-scope set with the new Id------ Similar to GHC.Core.Subst.substIdBndr, except that--- the type of id_subst differs--- all fragile info is zapped-substNonCoVarIdBndr env id = subst_id_bndr env id (\x -> x)---- Inline to make the (OutId -> OutId) function a known call.--- This is especially important for `substNonCoVarIdBndr` which--- passes an identity lambda.-{-# INLINE subst_id_bndr #-}-subst_id_bndr :: SimplEnv- -> InBndr -- Env and binder to transform- -> (OutId -> OutId) -- Adjust the type- -> (SimplEnv, OutBndr)-subst_id_bndr env@(SimplEnv { seInScope = in_scope, seIdSubst = id_subst })- old_id adjust_type- = assertPpr (not (isCoVar old_id)) (ppr old_id)- (env { seInScope = new_in_scope,- seIdSubst = new_subst }, new_id)- -- It's important that both seInScope and seIdSubst are updated with- -- the new_id, /after/ applying adjust_type. That's why adjust_type- -- is done here. If we did adjust_type in simplJoinBndr (the only- -- place that gives a non-identity adjust_type) we'd have to fiddle- -- afresh with both seInScope and seIdSubst- where- -- See Note [Bangs in the Simplifier]- !id1 = uniqAway in_scope old_id- !id2 = substIdType env id1- !id3 = zapFragileIdInfo id2 -- Zaps rules, worker-info, unfolding- -- and fragile OccInfo- !new_id = adjust_type id3-- -- Extend the substitution if the unique has changed,- -- or there's some useful occurrence information- -- See the notes with substTyVarBndr for the delSubstEnv- !new_subst | new_id /= old_id- = extendVarEnv id_subst old_id (DoneId new_id)- | otherwise- = delVarEnv id_subst old_id-- !new_in_scope = in_scope `extendInScopeSet` new_id---------------------------------------seqTyVar :: TyVar -> ()-seqTyVar b = b `seq` ()--seqId :: Id -> ()-seqId id = seqType (idType id) `seq`- idInfo id `seq`- ()--seqIds :: [Id] -> ()-seqIds [] = ()-seqIds (id:ids) = seqId id `seq` seqIds ids--{--Note [Arity robustness]-~~~~~~~~~~~~~~~~~~~~~~~-We *do* transfer the arity from the in_id of a let binding to the-out_id so that its arity is visible in its RHS. Examples:-- * f = \x y. let g = \p q. f (p+q) in Just (...g..g...)- Here we want to give `g` arity 3 and eta-expand. `findRhsArity` will have a- hard time figuring that out when `f` only has arity 0 in its own RHS.- * f = \x y. ....(f `seq` blah)....- We want to drop the seq.- * f = \x. g (\y. f y)- You'd think we could eta-reduce `\y. f y` to `f` here. And indeed, that is true.- Unfortunately, it is not sound in general to eta-reduce in f's RHS.- Example: `f = \x. f x`. See Note [Eta reduction in recursive RHSs] for how- we prevent that.--Note [Robust OccInfo]-~~~~~~~~~~~~~~~~~~~~~-It's important that we *do* retain the loop-breaker OccInfo, because-that's what stops the Id getting inlined infinitely, in the body of-the letrec.--}---{- *********************************************************************-* *- Join points-* *-********************************************************************* -}--simplNonRecJoinBndr :: SimplEnv -> InBndr- -> Mult -> OutType- -> SimplM (SimplEnv, OutBndr)---- A non-recursive let binder for a join point;--- context being pushed inward may change the type--- See Note [Return type for join points]-simplNonRecJoinBndr env id mult res_ty- = do { let (env1, id1) = simplJoinBndr mult res_ty env id- ; seqId id1 `seq` return (env1, id1) }--simplRecJoinBndrs :: SimplEnv -> [InBndr]- -> Mult -> OutType- -> SimplM SimplEnv--- Recursive let binders for join points;--- context being pushed inward may change types--- See Note [Return type for join points]-simplRecJoinBndrs env@(SimplEnv {}) ids mult res_ty- = assert (all isJoinId ids) $- do { let (env1, ids1) = mapAccumL (simplJoinBndr mult res_ty) env ids- ; seqIds ids1 `seq` return env1 }------------------simplJoinBndr :: Mult -> OutType- -> SimplEnv -> InBndr- -> (SimplEnv, OutBndr)-simplJoinBndr mult res_ty env id- = subst_id_bndr env id (adjustJoinPointType mult res_ty)------------------adjustJoinPointType :: Mult- -> Type -- New result type- -> Id -- Old join-point Id- -> Id -- Adjusted jont-point Id--- (adjustJoinPointType mult new_res_ty join_id) does two things:------ 1. Set the return type of the join_id to new_res_ty--- See Note [Return type for join points]------ 2. Adjust the multiplicity of arrows in join_id's type, as--- directed by 'mult'. See Note [Scaling join point arguments]------ INVARIANT: If any of the first n binders are foralls, those tyvars--- cannot appear in the original result type. See isValidJoinPointType.-adjustJoinPointType mult new_res_ty join_id- = assert (isJoinId join_id) $- setIdType join_id new_join_ty- where- orig_ar = idJoinArity join_id- orig_ty = idType join_id-- new_join_ty = go orig_ar orig_ty :: Type-- go 0 _ = new_res_ty- go n ty | Just (arg_bndr, res_ty) <- splitPiTy_maybe ty- = mkPiTy (scale_bndr arg_bndr) $- go (n-1) res_ty- | otherwise- = pprPanic "adjustJoinPointType" (ppr orig_ar <+> ppr orig_ty)-- -- See Note [Bangs in the Simplifier]- scale_bndr (Anon af t) = Anon af $! (scaleScaled mult t)- scale_bndr b@(Named _) = b--{- Note [Scaling join point arguments]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider a join point which is linear in its variable, in some context E:--E[join j :: a %1 -> a- j x = x- in case v of- A -> j 'x'- B -> <blah>]--The simplifier changes to:--join j :: a %1 -> a- j x = E[x]-in case v of- A -> j 'x'- B -> E[<blah>]--If E uses its argument in a nonlinear way (e.g. a case['Many]), then-this is wrong: the join point has to change its type to a -> a.-Otherwise, we'd get a linearity error.--See also Note [Return type for join points] and Note [Join points and case-of-case].--}--{--************************************************************************-* *- Impedance matching to type substitution-* *-************************************************************************--}--getTCvSubst :: SimplEnv -> TCvSubst-getTCvSubst (SimplEnv { seInScope = in_scope, seTvSubst = tv_env- , seCvSubst = cv_env })- = mkTCvSubst in_scope (tv_env, cv_env)--substTy :: SimplEnv -> Type -> Type-substTy env ty = Type.substTy (getTCvSubst env) ty--substTyVar :: SimplEnv -> TyVar -> Type-substTyVar env tv = Type.substTyVar (getTCvSubst env) tv--substTyVarBndr :: SimplEnv -> TyVar -> (SimplEnv, TyVar)-substTyVarBndr env tv- = case Type.substTyVarBndr (getTCvSubst env) tv of- (TCvSubst in_scope' tv_env' cv_env', tv')- -> (env { seInScope = in_scope', seTvSubst = tv_env', seCvSubst = cv_env' }, tv')--substCoVar :: SimplEnv -> CoVar -> Coercion-substCoVar env tv = Coercion.substCoVar (getTCvSubst env) tv--substCoVarBndr :: SimplEnv -> CoVar -> (SimplEnv, CoVar)-substCoVarBndr env cv- = case Coercion.substCoVarBndr (getTCvSubst env) cv of- (TCvSubst in_scope' tv_env' cv_env', cv')- -> (env { seInScope = in_scope', seTvSubst = tv_env', seCvSubst = cv_env' }, cv')--substCo :: SimplEnv -> Coercion -> Coercion-substCo env co = Coercion.substCo (getTCvSubst env) co---------------------substIdType :: SimplEnv -> Id -> Id-substIdType (SimplEnv { seInScope = in_scope, seTvSubst = tv_env, seCvSubst = cv_env }) id- | (isEmptyVarEnv tv_env && isEmptyVarEnv cv_env)- || no_free_vars- = id- | otherwise = Id.updateIdTypeAndMult (Type.substTyUnchecked subst) id- -- The tyCoVarsOfType is cheaper than it looks- -- because we cache the free tyvars of the type- -- in a Note in the id's type itself- where- no_free_vars = noFreeVarsOfType old_ty && noFreeVarsOfType old_w- subst = TCvSubst in_scope tv_env cv_env- old_ty = idType id- old_w = varMult id
− compiler/GHC/Core/Opt/Simplify/Monad.hs
@@ -1,297 +0,0 @@-{-# LANGUAGE PatternSynonyms #-}-{--(c) The AQUA Project, Glasgow University, 1993-1998--\section[GHC.Core.Opt.Simplify.Monad]{The simplifier Monad}--}--module GHC.Core.Opt.Simplify.Monad (- -- The monad- SimplM,- initSmpl, traceSmpl,- getSimplRules, getFamEnvs, getOptCoercionOpts,-- -- Unique supply- MonadUnique(..), newId, newJoinId,-- -- Counting- SimplCount, tick, freeTick, checkedTick,- getSimplCount, zeroSimplCount, pprSimplCount,- plusSimplCount, isZeroSimplCount- ) where--import GHC.Prelude--import GHC.Types.Var ( Var, isId, mkLocalVar )-import GHC.Types.Name ( mkSystemVarName )-import GHC.Types.Id ( Id, mkSysLocalOrCoVar )-import GHC.Types.Id.Info ( IdDetails(..), vanillaIdInfo, setArityInfo )-import GHC.Core.Type ( Type, Mult )-import GHC.Core.FamInstEnv ( FamInstEnv )-import GHC.Core ( RuleEnv(..), RuleBase)-import GHC.Core.Rules-import GHC.Core.Utils ( mkLamTypes )-import GHC.Core.Coercion.Opt-import GHC.Types.Unique.Supply-import GHC.Driver.Session-import GHC.Driver.Config-import GHC.Core.Opt.Monad-import GHC.Utils.Outputable-import GHC.Data.FastString-import GHC.Utils.Monad-import GHC.Utils.Logger as Logger-import GHC.Utils.Misc ( count )-import GHC.Utils.Panic (throwGhcExceptionIO, GhcException (..))-import GHC.Types.Basic ( IntWithInf, treatZeroAsInf, mkIntWithInf )-import Control.Monad ( ap )-import GHC.Core.Multiplicity ( pattern Many )-import GHC.Exts( oneShot )--{--************************************************************************-* *-\subsection{Monad plumbing}-* *-************************************************************************--For the simplifier monad, we want to {\em thread} a unique supply and a counter.-(Command-line switches move around through the explicitly-passed SimplEnv.)--}--newtype SimplM result- = SM' { unSM :: SimplTopEnv -- Envt that does not change much- -> SimplCount- -> IO (result, SimplCount)}- -- We only need IO here for dump output, but since we already have it- -- we might as well use it for uniques.--pattern SM :: (SimplTopEnv -> SimplCount- -> IO (result, SimplCount))- -> SimplM result--- This pattern synonym makes the simplifier monad eta-expand,--- which as a very beneficial effect on compiler performance--- (worth a 1-2% reduction in bytes-allocated). See #18202.--- See Note [The one-shot state monad trick] in GHC.Utils.Monad-pattern SM m <- SM' m- where- SM m = SM' (oneShot $ \env -> oneShot $ \ct -> m env ct)--data SimplTopEnv- = STE { st_flags :: DynFlags- , st_logger :: !Logger- , st_max_ticks :: IntWithInf -- ^ Max #ticks in this simplifier run- , st_query_rulebase :: IO RuleBase- -- ^ The action to retrieve an up-to-date EPS RuleBase- -- See Note [Overall plumbing for rules]- , st_mod_rules :: RuleEnv- , st_fams :: (FamInstEnv, FamInstEnv)-- , st_co_opt_opts :: !OptCoercionOpts- -- ^ Coercion optimiser options- }--initSmpl :: Logger -> DynFlags -> IO RuleBase -> RuleEnv -> (FamInstEnv, FamInstEnv)- -> Int -- Size of the bindings, used to limit- -- the number of ticks we allow- -> SimplM a- -> IO (a, SimplCount)--initSmpl logger dflags qrb rules fam_envs size m- = do -- No init count; set to 0- let simplCount = zeroSimplCount dflags- (result, count) <- unSM m env simplCount- return (result, count)- where- env = STE { st_flags = dflags- , st_logger = logger- , st_query_rulebase = qrb- , st_mod_rules = rules- , st_max_ticks = computeMaxTicks dflags size- , st_fams = fam_envs- , st_co_opt_opts = initOptCoercionOpts dflags- }--computeMaxTicks :: DynFlags -> Int -> IntWithInf--- Compute the max simplifier ticks as--- (base-size + pgm-size) * magic-multiplier * tick-factor/100--- where--- magic-multiplier is a constant that gives reasonable results--- base-size is a constant to deal with size-zero programs-computeMaxTicks dflags size- = treatZeroAsInf $- fromInteger ((toInteger (size + base_size)- * toInteger (tick_factor * magic_multiplier))- `div` 100)- where- tick_factor = simplTickFactor dflags- base_size = 100- magic_multiplier = 40- -- MAGIC NUMBER, multiplies the simplTickFactor- -- We can afford to be generous; this is really- -- just checking for loops, and shouldn't usually fire- -- A figure of 20 was too small: see #5539.--{-# INLINE thenSmpl #-}-{-# INLINE thenSmpl_ #-}-{-# INLINE returnSmpl #-}-{-# INLINE mapSmpl #-}--instance Functor SimplM where- fmap = mapSmpl--instance Applicative SimplM where- pure = returnSmpl- (<*>) = ap- (*>) = thenSmpl_--instance Monad SimplM where- (>>) = (*>)- (>>=) = thenSmpl--mapSmpl :: (a -> b) -> SimplM a -> SimplM b-mapSmpl f m = thenSmpl m (returnSmpl . f)--returnSmpl :: a -> SimplM a-returnSmpl e = SM (\_st_env sc -> return (e, sc))--thenSmpl :: SimplM a -> (a -> SimplM b) -> SimplM b-thenSmpl_ :: SimplM a -> SimplM b -> SimplM b--thenSmpl m k- = SM $ \st_env sc0 -> do- (m_result, sc1) <- unSM m st_env sc0- unSM (k m_result) st_env sc1--thenSmpl_ m k- = SM $ \st_env sc0 -> do- (_, sc1) <- unSM m st_env sc0- unSM k st_env sc1---- TODO: this specializing is not allowed--- {-# SPECIALIZE mapM :: (a -> SimplM b) -> [a] -> SimplM [b] #-}--- {-# SPECIALIZE mapAndUnzipM :: (a -> SimplM (b, c)) -> [a] -> SimplM ([b],[c]) #-}--- {-# SPECIALIZE mapAccumLM :: (acc -> b -> SimplM (acc,c)) -> acc -> [b] -> SimplM (acc, [c]) #-}--traceSmpl :: String -> SDoc -> SimplM ()-traceSmpl herald doc- = do logger <- getLogger- liftIO $ Logger.putDumpFileMaybe logger Opt_D_dump_simpl_trace "Simpl Trace"- FormatText- (hang (text herald) 2 doc)-{-# INLINE traceSmpl #-} -- see Note [INLINE conditional tracing utilities]--{--************************************************************************-* *-\subsection{The unique supply}-* *-************************************************************************--}---- See Note [Uniques for wired-in prelude things and known masks] in GHC.Builtin.Uniques-simplMask :: Char-simplMask = 's'--instance MonadUnique SimplM where- getUniqueSupplyM = liftIO $ mkSplitUniqSupply simplMask- getUniqueM = liftIO $ uniqFromMask simplMask--instance HasDynFlags SimplM where- getDynFlags = SM (\st_env sc -> return (st_flags st_env, sc))--instance HasLogger SimplM where- getLogger = SM (\st_env sc -> return (st_logger st_env, sc))--instance MonadIO SimplM where- liftIO m = SM $ \_ sc -> do- x <- m- return (x, sc)--getSimplRules :: SimplM RuleEnv-getSimplRules = SM (\st_env sc -> do- eps_rules <- st_query_rulebase st_env- return (extendRuleEnv (st_mod_rules st_env) eps_rules, sc))--getFamEnvs :: SimplM (FamInstEnv, FamInstEnv)-getFamEnvs = SM (\st_env sc -> return (st_fams st_env, sc))--getOptCoercionOpts :: SimplM OptCoercionOpts-getOptCoercionOpts = SM (\st_env sc -> return (st_co_opt_opts st_env, sc))--newId :: FastString -> Mult -> Type -> SimplM Id-newId fs w ty = do uniq <- getUniqueM- return (mkSysLocalOrCoVar fs uniq w ty)---- | Make a join id with given type and arity but without call-by-value annotations.-newJoinId :: [Var] -> Type -> SimplM Id-newJoinId bndrs body_ty- = do { uniq <- getUniqueM- ; let name = mkSystemVarName uniq (fsLit "$j")- join_id_ty = mkLamTypes bndrs body_ty -- Note [Funky mkLamTypes]- arity = count isId bndrs- -- arity: See Note [Invariants on join points] invariant 2b, in GHC.Core- join_arity = length bndrs- details = JoinId join_arity Nothing- id_info = vanillaIdInfo `setArityInfo` arity--- `setOccInfo` strongLoopBreaker-- ; return (mkLocalVar details name Many join_id_ty id_info) }--{--************************************************************************-* *-\subsection{Counting up what we've done}-* *-************************************************************************--}--getSimplCount :: SimplM SimplCount-getSimplCount = SM (\_st_env sc -> return (sc, sc))--tick :: Tick -> SimplM ()-tick t = SM (\st_env sc -> let sc' = doSimplTick (st_flags st_env) t sc- in sc' `seq` return ((), sc'))--checkedTick :: Tick -> SimplM ()--- Try to take a tick, but fail if too many-checkedTick t- = SM (\st_env sc ->- if st_max_ticks st_env <= mkIntWithInf (simplCountN sc)- then throwGhcExceptionIO $- PprProgramError "Simplifier ticks exhausted" (msg sc)- else let sc' = doSimplTick (st_flags st_env) t sc- in sc' `seq` return ((), sc'))- where- msg sc = vcat- [ text "When trying" <+> ppr t- , text "To increase the limit, use -fsimpl-tick-factor=N (default 100)."- , space- , text "In addition try adjusting -funfolding-case-threshold=N and"- , text "-funfolding-case-scaling=N for the module in question."- , text "Using threshold=1 and scaling=5 should break most inlining loops."- , space- , text "If you need to increase the tick factor substantially, while also"- , text "adjusting unfolding parameters please file a bug report and"- , text "indicate the factor you needed."- , space- , text "If GHC was unable to complete compilation even"- <+> text "with a very large factor"- , text "(a thousand or more), please consult the"- <+> doubleQuotes (text "Known bugs or infelicities")- , text "section in the Users Guide before filing a report. There are a"- , text "few situations unlikely to occur in practical programs for which"- , text "simplifier non-termination has been judged acceptable."- , space- , pp_details sc- , pprSimplCount sc ]- pp_details sc- | hasDetailedCounts sc = empty- | otherwise = text "To see detailed counts use -ddump-simpl-stats"---freeTick :: Tick -> SimplM ()--- Record a tick, but don't add to the total tick count, which is--- used to decide when nothing further has happened-freeTick t- = SM (\_st_env sc -> let sc' = doFreeSimplTick t sc- in sc' `seq` return ((), sc'))
− compiler/GHC/Core/Opt/Simplify/Utils.hs
@@ -1,2600 +0,0 @@-{--(c) The AQUA Project, Glasgow University, 1993-1998--The simplifier utilities--}----module GHC.Core.Opt.Simplify.Utils (- -- Rebuilding- mkLam, mkCase, prepareAlts, tryEtaExpandRhs,-- -- Inlining,- preInlineUnconditionally, postInlineUnconditionally,- activeUnfolding, activeRule,- getUnfoldingInRuleMatch,- simplEnvForGHCi, updModeForStableUnfoldings, updModeForRules,-- -- The BindContext type- BindContext(..), bindContextLevel,-- -- The continuation type- SimplCont(..), DupFlag(..), StaticEnv,- isSimplified, contIsStop,- contIsDupable, contResultType, contHoleType, contHoleScaling,- contIsTrivial, contArgs,- countArgs,- mkBoringStop, mkRhsStop, mkLazyArgStop, contIsRhsOrArg,- interestingCallContext,-- -- ArgInfo- ArgInfo(..), ArgSpec(..), mkArgInfo,- addValArgTo, addCastTo, addTyArgTo,- argInfoExpr, argInfoAppArgs, pushSimplifiedArgs,- isStrictArgInfo, lazyArgContext,-- abstractFloats,-- -- Utilities- isExitJoinId- ) where--import GHC.Prelude--import GHC.Driver.Session--import GHC.Core-import GHC.Types.Literal ( isLitRubbish )-import GHC.Core.Opt.Simplify.Env-import GHC.Core.Opt.Monad ( SimplMode(..), Tick(..) )-import qualified GHC.Core.Subst-import GHC.Core.Ppr-import GHC.Core.TyCo.Ppr ( pprParendType )-import GHC.Core.FVs-import GHC.Core.Utils-import GHC.Core.Opt.Arity-import GHC.Core.Unfold-import GHC.Core.Unfold.Make-import GHC.Core.Opt.Simplify.Monad-import GHC.Core.Type hiding( substTy )-import GHC.Core.Coercion hiding( substCo )-import GHC.Core.DataCon ( dataConWorkId, isNullaryRepDataCon )-import GHC.Core.Multiplicity-import GHC.Core.Opt.ConstantFold--import GHC.Types.Name-import GHC.Types.Id-import GHC.Types.Id.Info-import GHC.Types.Tickish-import GHC.Types.Demand-import GHC.Types.Var.Set-import GHC.Types.Basic--import GHC.Data.OrdList ( isNilOL )-import GHC.Data.FastString ( fsLit )--import GHC.Utils.Misc-import GHC.Utils.Monad-import GHC.Utils.Outputable-import GHC.Utils.Logger-import GHC.Utils.Panic-import GHC.Utils.Panic.Plain-import GHC.Utils.Trace--import Control.Monad ( when )-import Data.List ( sortBy )--{- *********************************************************************-* *- The BindContext type-* *-********************************************************************* -}---- What sort of binding is this? A let-binding or a join-binding?-data BindContext- = BC_Let -- A regular let-binding- TopLevelFlag RecFlag-- | BC_Join -- A join point with continuation k- SimplCont -- See Note [Rules and unfolding for join points]- -- in GHC.Core.Opt.Simplify--bindContextLevel :: BindContext -> TopLevelFlag-bindContextLevel (BC_Let top_lvl _) = top_lvl-bindContextLevel (BC_Join {}) = NotTopLevel---{- *********************************************************************-* *- The SimplCont and DupFlag types-* *-************************************************************************--A SimplCont allows the simplifier to traverse the expression in a-zipper-like fashion. The SimplCont represents the rest of the expression,-"above" the point of interest.--You can also think of a SimplCont as an "evaluation context", using-that term in the way it is used for operational semantics. This is the-way I usually think of it, For example you'll often see a syntax for-evaluation context looking like- C ::= [] | C e | case C of alts | C `cast` co-That's the kind of thing we are doing here, and I use that syntax in-the comments.---Key points:- * A SimplCont describes a *strict* context (just like- evaluation contexts do). E.g. Just [] is not a SimplCont-- * A SimplCont describes a context that *does not* bind- any variables. E.g. \x. [] is not a SimplCont--}--data SimplCont- = Stop -- Stop[e] = e- OutType -- Type of the <hole>- CallCtxt -- Tells if there is something interesting about- -- the context, and hence the inliner- -- should be a bit keener (see interestingCallContext)- -- Specifically:- -- This is an argument of a function that has RULES- -- Inlining the call might allow the rule to fire- -- Never ValAppCxt (use ApplyToVal instead)- -- or CaseCtxt (use Select instead)-- | CastIt -- (CastIt co K)[e] = K[ e `cast` co ]- OutCoercion -- The coercion simplified- -- Invariant: never an identity coercion- SimplCont-- | ApplyToVal -- (ApplyToVal arg K)[e] = K[ e arg ]- { sc_dup :: DupFlag -- See Note [DupFlag invariants]- , sc_hole_ty :: OutType -- Type of the function, presumably (forall a. blah)- -- See Note [The hole type in ApplyToTy]- , sc_arg :: InExpr -- The argument,- , sc_env :: StaticEnv -- see Note [StaticEnv invariant]- , sc_cont :: SimplCont }-- | ApplyToTy -- (ApplyToTy ty K)[e] = K[ e ty ]- { sc_arg_ty :: OutType -- Argument type- , sc_hole_ty :: OutType -- Type of the function, presumably (forall a. blah)- -- See Note [The hole type in ApplyToTy]- , sc_cont :: SimplCont }-- | Select -- (Select alts K)[e] = K[ case e of alts ]- { sc_dup :: DupFlag -- See Note [DupFlag invariants]- , sc_bndr :: InId -- case binder- , sc_alts :: [InAlt] -- Alternatives- , sc_env :: StaticEnv -- See Note [StaticEnv invariant]- , sc_cont :: SimplCont }-- -- The two strict forms have no DupFlag, because we never duplicate them- | StrictBind -- (StrictBind x b K)[e] = let x = e in K[b]- -- or, equivalently, = K[ (\x.b) e ]- { sc_dup :: DupFlag -- See Note [DupFlag invariants]- , sc_bndr :: InId- , sc_body :: InExpr- , sc_env :: StaticEnv -- See Note [StaticEnv invariant]- , sc_cont :: SimplCont }-- | StrictArg -- (StrictArg (f e1 ..en) K)[e] = K[ f e1 .. en e ]- { sc_dup :: DupFlag -- Always Simplified or OkToDup- , sc_fun :: ArgInfo -- Specifies f, e1..en, Whether f has rules, etc- -- plus demands and discount flags for *this* arg- -- and further args- -- So ai_dmds and ai_discs are never empty- , sc_fun_ty :: OutType -- Type of the function (f e1 .. en),- -- presumably (arg_ty -> res_ty)- -- where res_ty is expected by sc_cont- , sc_cont :: SimplCont }-- | TickIt -- (TickIt t K)[e] = K[ tick t e ]- CoreTickish -- Tick tickish <hole>- SimplCont--type StaticEnv = SimplEnv -- Just the static part is relevant--data DupFlag = NoDup -- Unsimplified, might be big- | Simplified -- Simplified- | OkToDup -- Simplified and small--isSimplified :: DupFlag -> Bool-isSimplified NoDup = False-isSimplified _ = True -- Invariant: the subst-env is empty--perhapsSubstTy :: DupFlag -> StaticEnv -> Type -> Type-perhapsSubstTy dup env ty- | isSimplified dup = ty- | otherwise = substTy env ty--{- Note [StaticEnv invariant]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We pair up an InExpr or InAlts with a StaticEnv, which establishes the-lexical scope for that InExpr. When we simplify that InExpr/InAlts, we-use- - Its captured StaticEnv- - Overriding its InScopeSet with the larger one at the- simplification point.--Why override the InScopeSet? Example:- (let y = ey in f) ex-By the time we simplify ex, 'y' will be in scope.--However the InScopeSet in the StaticEnv is not irrelevant: it should-include all the free vars of applying the substitution to the InExpr.-Reason: contHoleType uses perhapsSubstTy to apply the substitution to-the expression, and that (rightly) gives ASSERT failures if the InScopeSet-isn't big enough.--Note [DupFlag invariants]-~~~~~~~~~~~~~~~~~~~~~~~~~-In both (ApplyToVal dup _ env k)- and (Select dup _ _ env k)-the following invariants hold-- (a) if dup = OkToDup, then continuation k is also ok-to-dup- (b) if dup = OkToDup or Simplified, the subst-env is empty- (and hence no need to re-simplify)--}--instance Outputable DupFlag where- ppr OkToDup = text "ok"- ppr NoDup = text "nodup"- ppr Simplified = text "simpl"--instance Outputable SimplCont where- ppr (Stop ty interesting) = text "Stop" <> brackets (ppr interesting) <+> ppr ty- ppr (CastIt co cont ) = (text "CastIt" <+> pprOptCo co) $$ ppr cont- ppr (TickIt t cont) = (text "TickIt" <+> ppr t) $$ ppr cont- ppr (ApplyToTy { sc_arg_ty = ty, sc_cont = cont })- = (text "ApplyToTy" <+> pprParendType ty) $$ ppr cont- ppr (ApplyToVal { sc_arg = arg, sc_dup = dup, sc_cont = cont, sc_hole_ty = hole_ty })- = (hang (text "ApplyToVal" <+> ppr dup <+> text "hole" <+> ppr hole_ty)- 2 (pprParendExpr arg))- $$ ppr cont- ppr (StrictBind { sc_bndr = b, sc_cont = cont })- = (text "StrictBind" <+> ppr b) $$ ppr cont- ppr (StrictArg { sc_fun = ai, sc_cont = cont })- = (text "StrictArg" <+> ppr (ai_fun ai)) $$ ppr cont- ppr (Select { sc_dup = dup, sc_bndr = bndr, sc_alts = alts, sc_env = se, sc_cont = cont })- = (text "Select" <+> ppr dup <+> ppr bndr) $$- whenPprDebug (nest 2 $ vcat [ppr (seTvSubst se), ppr alts]) $$ ppr cont---{- Note [The hole type in ApplyToTy]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-The sc_hole_ty field of ApplyToTy records the type of the "hole" in the-continuation. It is absolutely necessary to compute contHoleType, but it is-not used for anything else (and hence may not be evaluated).--Why is it necessary for contHoleType? Consider the continuation- ApplyToType Int (Stop Int)-corresponding to- (<hole> @Int) :: Int-What is the type of <hole>? It could be (forall a. Int) or (forall a. a),-and there is no way to know which, so we must record it.--In a chain of applications (f @t1 @t2 @t3) we'll lazily compute exprType-for (f @t1) and (f @t1 @t2), which is potentially non-linear; but it probably-doesn't matter because we'll never compute them all.--************************************************************************-* *- ArgInfo and ArgSpec-* *-************************************************************************--}--data ArgInfo- = ArgInfo {- ai_fun :: OutId, -- The function- ai_args :: [ArgSpec], -- ...applied to these args (which are in *reverse* order)-- ai_rules :: FunRules, -- Rules for this function-- ai_encl :: Bool, -- Flag saying whether this function- -- or an enclosing one has rules (recursively)- -- True => be keener to inline in all args-- ai_dmds :: [Demand], -- Demands on remaining value arguments (beyond ai_args)- -- Usually infinite, but if it is finite it guarantees- -- that the function diverges after being given- -- that number of args-- ai_discs :: [Int] -- Discounts for remaining value arguments (beyong ai_args)- -- non-zero => be keener to inline- -- Always infinite- }--data ArgSpec- = ValArg { as_dmd :: Demand -- Demand placed on this argument- , as_arg :: OutExpr -- Apply to this (coercion or value); c.f. ApplyToVal- , as_hole_ty :: OutType } -- Type of the function (presumably t1 -> t2)-- | TyArg { as_arg_ty :: OutType -- Apply to this type; c.f. ApplyToTy- , as_hole_ty :: OutType } -- Type of the function (presumably forall a. blah)-- | CastBy OutCoercion -- Cast by this; c.f. CastIt--instance Outputable ArgInfo where- ppr (ArgInfo { ai_fun = fun, ai_args = args, ai_dmds = dmds })- = text "ArgInfo" <+> braces- (sep [ text "fun =" <+> ppr fun- , text "dmds =" <+> ppr dmds- , text "args =" <+> ppr args ])--instance Outputable ArgSpec where- ppr (ValArg { as_arg = arg }) = text "ValArg" <+> ppr arg- ppr (TyArg { as_arg_ty = ty }) = text "TyArg" <+> ppr ty- ppr (CastBy c) = text "CastBy" <+> ppr c--addValArgTo :: ArgInfo -> OutExpr -> OutType -> ArgInfo-addValArgTo ai arg hole_ty- | ArgInfo { ai_dmds = dmd:dmds, ai_discs = _:discs, ai_rules = rules } <- ai- -- Pop the top demand and and discounts off- , let arg_spec = ValArg { as_arg = arg, as_hole_ty = hole_ty, as_dmd = dmd }- = ai { ai_args = arg_spec : ai_args ai- , ai_dmds = dmds- , ai_discs = discs- , ai_rules = decRules rules }- | otherwise- = pprPanic "addValArgTo" (ppr ai $$ ppr arg)- -- There should always be enough demands and discounts--addTyArgTo :: ArgInfo -> OutType -> OutType -> ArgInfo-addTyArgTo ai arg_ty hole_ty = ai { ai_args = arg_spec : ai_args ai- , ai_rules = decRules (ai_rules ai) }- where- arg_spec = TyArg { as_arg_ty = arg_ty, as_hole_ty = hole_ty }--addCastTo :: ArgInfo -> OutCoercion -> ArgInfo-addCastTo ai co = ai { ai_args = CastBy co : ai_args ai }--isStrictArgInfo :: ArgInfo -> Bool--- True if the function is strict in the next argument-isStrictArgInfo (ArgInfo { ai_dmds = dmds })- | dmd:_ <- dmds = isStrUsedDmd dmd- | otherwise = False--argInfoAppArgs :: [ArgSpec] -> [OutExpr]-argInfoAppArgs [] = []-argInfoAppArgs (CastBy {} : _) = [] -- Stop at a cast-argInfoAppArgs (ValArg { as_arg = arg } : as) = arg : argInfoAppArgs as-argInfoAppArgs (TyArg { as_arg_ty = ty } : as) = Type ty : argInfoAppArgs as--pushSimplifiedArgs :: SimplEnv -> [ArgSpec] -> SimplCont -> SimplCont-pushSimplifiedArgs _env [] k = k-pushSimplifiedArgs env (arg : args) k- = case arg of- TyArg { as_arg_ty = arg_ty, as_hole_ty = hole_ty }- -> ApplyToTy { sc_arg_ty = arg_ty, sc_hole_ty = hole_ty, sc_cont = rest }- ValArg { as_arg = arg, as_hole_ty = hole_ty }- -> ApplyToVal { sc_arg = arg, sc_env = env, sc_dup = Simplified- , sc_hole_ty = hole_ty, sc_cont = rest }- CastBy c -> CastIt c rest- where- rest = pushSimplifiedArgs env args k- -- The env has an empty SubstEnv--argInfoExpr :: OutId -> [ArgSpec] -> OutExpr--- NB: the [ArgSpec] is reversed so that the first arg--- in the list is the last one in the application-argInfoExpr fun rev_args- = go rev_args- where- go [] = Var fun- go (ValArg { as_arg = arg } : as) = go as `App` arg- go (TyArg { as_arg_ty = ty } : as) = go as `App` Type ty- go (CastBy co : as) = mkCast (go as) co---type FunRules = Maybe (Int, [CoreRule]) -- Remaining rules for this function- -- Nothing => No rules- -- Just (n, rules) => some rules, requiring at least n more type/value args--decRules :: FunRules -> FunRules-decRules (Just (n, rules)) = Just (n-1, rules)-decRules Nothing = Nothing--mkFunRules :: [CoreRule] -> FunRules-mkFunRules [] = Nothing-mkFunRules rs = Just (n_required, rs)- where- n_required = maximum (map ruleArity rs)--{--************************************************************************-* *- Functions on SimplCont-* *-************************************************************************--}--mkBoringStop :: OutType -> SimplCont-mkBoringStop ty = Stop ty BoringCtxt--mkRhsStop :: OutType -> SimplCont -- See Note [RHS of lets] in GHC.Core.Unfold-mkRhsStop ty = Stop ty RhsCtxt--mkLazyArgStop :: OutType -> CallCtxt -> SimplCont-mkLazyArgStop ty cci = Stop ty cci----------------------contIsRhsOrArg :: SimplCont -> Bool-contIsRhsOrArg (Stop {}) = True-contIsRhsOrArg (StrictBind {}) = True-contIsRhsOrArg (StrictArg {}) = True-contIsRhsOrArg _ = False--contIsRhs :: SimplCont -> Bool-contIsRhs (Stop _ RhsCtxt) = True-contIsRhs (CastIt _ k) = contIsRhs k -- For f = e |> co, treat e as Rhs context-contIsRhs _ = False----------------------contIsStop :: SimplCont -> Bool-contIsStop (Stop {}) = True-contIsStop _ = False--contIsDupable :: SimplCont -> Bool-contIsDupable (Stop {}) = True-contIsDupable (ApplyToTy { sc_cont = k }) = contIsDupable k-contIsDupable (ApplyToVal { sc_dup = OkToDup }) = True -- See Note [DupFlag invariants]-contIsDupable (Select { sc_dup = OkToDup }) = True -- ...ditto...-contIsDupable (StrictArg { sc_dup = OkToDup }) = True -- ...ditto...-contIsDupable (CastIt _ k) = contIsDupable k-contIsDupable _ = False----------------------contIsTrivial :: SimplCont -> Bool-contIsTrivial (Stop {}) = True-contIsTrivial (ApplyToTy { sc_cont = k }) = contIsTrivial k--- This one doesn't look right. A value application is not trivial--- contIsTrivial (ApplyToVal { sc_arg = Coercion _, sc_cont = k }) = contIsTrivial k-contIsTrivial (CastIt _ k) = contIsTrivial k-contIsTrivial _ = False----------------------contResultType :: SimplCont -> OutType-contResultType (Stop ty _) = ty-contResultType (CastIt _ k) = contResultType k-contResultType (StrictBind { sc_cont = k }) = contResultType k-contResultType (StrictArg { sc_cont = k }) = contResultType k-contResultType (Select { sc_cont = k }) = contResultType k-contResultType (ApplyToTy { sc_cont = k }) = contResultType k-contResultType (ApplyToVal { sc_cont = k }) = contResultType k-contResultType (TickIt _ k) = contResultType k--contHoleType :: SimplCont -> OutType-contHoleType (Stop ty _) = ty-contHoleType (TickIt _ k) = contHoleType k-contHoleType (CastIt co _) = coercionLKind co-contHoleType (StrictBind { sc_bndr = b, sc_dup = dup, sc_env = se })- = perhapsSubstTy dup se (idType b)-contHoleType (StrictArg { sc_fun_ty = ty }) = funArgTy ty-contHoleType (ApplyToTy { sc_hole_ty = ty }) = ty -- See Note [The hole type in ApplyToTy]-contHoleType (ApplyToVal { sc_hole_ty = ty }) = ty -- See Note [The hole type in ApplyToTy]-contHoleType (Select { sc_dup = d, sc_bndr = b, sc_env = se })- = perhapsSubstTy d se (idType b)----- Computes the multiplicity scaling factor at the hole. That is, in (case [] of--- x ::(p) _ { … }) (respectively for arguments of functions), the scaling--- factor is p. And in E[G[]], the scaling factor is the product of the scaling--- factor of E and that of G.------ The scaling factor at the hole of E[] is used to determine how a binder--- should be scaled if it commutes with E. This appears, in particular, in the--- case-of-case transformation.-contHoleScaling :: SimplCont -> Mult-contHoleScaling (Stop _ _) = One-contHoleScaling (CastIt _ k) = contHoleScaling k-contHoleScaling (StrictBind { sc_bndr = id, sc_cont = k })- = idMult id `mkMultMul` contHoleScaling k-contHoleScaling (Select { sc_bndr = id, sc_cont = k })- = idMult id `mkMultMul` contHoleScaling k-contHoleScaling (StrictArg { sc_fun_ty = fun_ty, sc_cont = k })- = w `mkMultMul` contHoleScaling k- where- (w, _, _) = splitFunTy fun_ty-contHoleScaling (ApplyToTy { sc_cont = k }) = contHoleScaling k-contHoleScaling (ApplyToVal { sc_cont = k }) = contHoleScaling k-contHoleScaling (TickIt _ k) = contHoleScaling k---------------------countArgs :: SimplCont -> Int--- Count all arguments, including types, coercions,--- and other values; skipping over casts.-countArgs (ApplyToTy { sc_cont = cont }) = 1 + countArgs cont-countArgs (ApplyToVal { sc_cont = cont }) = 1 + countArgs cont-countArgs (CastIt _ cont) = countArgs cont-countArgs _ = 0--contArgs :: SimplCont -> (Bool, [ArgSummary], SimplCont)--- Summarises value args, discards type args and coercions--- The returned continuation of the call is only used to--- answer questions like "are you interesting?"-contArgs cont- | lone cont = (True, [], cont)- | otherwise = go [] cont- where- lone (ApplyToTy {}) = False -- See Note [Lone variables] in GHC.Core.Unfold- lone (ApplyToVal {}) = False -- NB: even a type application or cast- lone (CastIt {}) = False -- stops it being "lone"- lone _ = True-- go args (ApplyToVal { sc_arg = arg, sc_env = se, sc_cont = k })- = go (is_interesting arg se : args) k- go args (ApplyToTy { sc_cont = k }) = go args k- go args (CastIt _ k) = go args k- go args k = (False, reverse args, k)-- is_interesting arg se = interestingArg se arg- -- Do *not* use short-cutting substitution here- -- because we want to get as much IdInfo as possible-----------------------mkArgInfo :: SimplEnv- -> Id- -> [CoreRule] -- Rules for function- -> Int -- Number of value args- -> SimplCont -- Context of the call- -> ArgInfo--mkArgInfo env fun rules n_val_args call_cont- | n_val_args < idArity fun -- Note [Unsaturated functions]- = ArgInfo { ai_fun = fun, ai_args = []- , ai_rules = fun_rules- , ai_encl = False- , ai_dmds = vanilla_dmds- , ai_discs = vanilla_discounts }- | otherwise- = ArgInfo { ai_fun = fun- , ai_args = []- , ai_rules = fun_rules- , ai_encl = interestingArgContext rules call_cont- , ai_dmds = add_type_strictness (idType fun) arg_dmds- , ai_discs = arg_discounts }- where- fun_rules = mkFunRules rules-- vanilla_discounts, arg_discounts :: [Int]- vanilla_discounts = repeat 0- arg_discounts = case idUnfolding fun of- CoreUnfolding {uf_guidance = UnfIfGoodArgs {ug_args = discounts}}- -> discounts ++ vanilla_discounts- _ -> vanilla_discounts-- vanilla_dmds, arg_dmds :: [Demand]- vanilla_dmds = repeat topDmd-- arg_dmds- | not (sm_inline (seMode env))- = vanilla_dmds -- See Note [Do not expose strictness if sm_inline=False]- | otherwise- = -- add_type_str fun_ty $- case splitDmdSig (idDmdSig fun) of- (demands, result_info)- | not (demands `lengthExceeds` n_val_args)- -> -- Enough args, use the strictness given.- -- For bottoming functions we used to pretend that the arg- -- is lazy, so that we don't treat the arg as an- -- interesting context. This avoids substituting- -- top-level bindings for (say) strings into- -- calls to error. But now we are more careful about- -- inlining lone variables, so its ok- -- (see GHC.Core.Op.Simplify.Utils.analyseCont)- if isDeadEndDiv result_info then- demands -- Finite => result is bottom- else- demands ++ vanilla_dmds- | otherwise- -> warnPprTrace True "More demands than arity" (ppr fun <+> ppr (idArity fun)- <+> ppr n_val_args <+> ppr demands) $- vanilla_dmds -- Not enough args, or no strictness-- add_type_strictness :: Type -> [Demand] -> [Demand]- -- If the function arg types are strict, record that in the 'strictness bits'- -- No need to instantiate because unboxed types (which dominate the strict- -- types) can't instantiate type variables.- -- add_type_strictness is done repeatedly (for each call);- -- might be better once-for-all in the function- -- But beware primops/datacons with no strictness-- add_type_strictness fun_ty dmds- | null dmds = []-- | Just (_, fun_ty') <- splitForAllTyCoVar_maybe fun_ty- = add_type_strictness fun_ty' dmds -- Look through foralls-- | Just (_, arg_ty, fun_ty') <- splitFunTy_maybe fun_ty -- Add strict-type info- , dmd : rest_dmds <- dmds- , let dmd'- | Just Unlifted <- typeLevity_maybe arg_ty- = strictifyDmd dmd- | otherwise- -- Something that's not definitely unlifted.- -- If the type is representation-polymorphic, we can't know whether- -- it's strict.- = dmd- = dmd' : add_type_strictness fun_ty' rest_dmds-- | otherwise- = dmds--{- Note [Unsaturated functions]- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider (test eyeball/inline4)- x = a:as- y = f x-where f has arity 2. Then we do not want to inline 'x', because-it'll just be floated out again. Even if f has lots of discounts-on its first argument -- it must be saturated for these to kick in--Note [Do not expose strictness if sm_inline=False]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-#15163 showed a case in which we had-- {-# INLINE [1] zip #-}- zip = undefined-- {-# RULES "foo" forall as bs. stream (zip as bs) = ..blah... #-}--If we expose zip's bottoming nature when simplifying the LHS of the-RULE we get- {-# RULES "foo" forall as bs.- stream (case zip of {}) = ..blah... #-}-discarding the arguments to zip. Usually this is fine, but on the-LHS of a rule it's not, because 'as' and 'bs' are now not bound on-the LHS.--This is a pretty pathological example, so I'm not losing sleep over-it, but the simplest solution was to check sm_inline; if it is False,-which it is on the LHS of a rule (see updModeForRules), then don't-make use of the strictness info for the function.--}---{--************************************************************************-* *- Interesting arguments-* *-************************************************************************--Note [Interesting call context]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We want to avoid inlining an expression where there can't possibly be-any gain, such as in an argument position. Hence, if the continuation-is interesting (eg. a case scrutinee, application etc.) then we-inline, otherwise we don't.--Previously some_benefit used to return True only if the variable was-applied to some value arguments. This didn't work:-- let x = _coerce_ (T Int) Int (I# 3) in- case _coerce_ Int (T Int) x of- I# y -> ....--we want to inline x, but can't see that it's a constructor in a case-scrutinee position, and some_benefit is False.--Another example:--dMonadST = _/\_ t -> :Monad (g1 _@_ t, g2 _@_ t, g3 _@_ t)--.... case dMonadST _@_ x0 of (a,b,c) -> ....--we'd really like to inline dMonadST here, but we *don't* want to-inline if the case expression is just-- case x of y { DEFAULT -> ... }--since we can just eliminate this case instead (x is in WHNF). Similar-applies when x is bound to a lambda expression. Hence-contIsInteresting looks for case expressions with just a single-default case.--Note [No case of case is boring]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-If we see- case f x of <alts>--we'd usually treat the context as interesting, to encourage 'f' to-inline. But if case-of-case is off, it's really not so interesting-after all, because we are unlikely to be able to push the case-expression into the branches of any case in f's unfolding. So, to-reduce unnecessary code expansion, we just make the context look boring.-This made a small compile-time perf improvement in perf/compiler/T6048,-and it looks plausible to me.--}--lazyArgContext :: ArgInfo -> CallCtxt--- Use this for lazy arguments-lazyArgContext (ArgInfo { ai_encl = encl_rules, ai_discs = discs })- | encl_rules = RuleArgCtxt- | disc:_ <- discs, disc > 0 = DiscArgCtxt -- Be keener here- | otherwise = BoringCtxt -- Nothing interesting--strictArgContext :: ArgInfo -> CallCtxt-strictArgContext (ArgInfo { ai_encl = encl_rules, ai_discs = discs })--- Use this for strict arguments- | encl_rules = RuleArgCtxt- | disc:_ <- discs, disc > 0 = DiscArgCtxt -- Be keener here- | otherwise = RhsCtxt- -- Why RhsCtxt? if we see f (g x) (h x), and f is strict, we- -- want to be a bit more eager to inline g, because it may- -- expose an eval (on x perhaps) that can be eliminated or- -- shared. I saw this in nofib 'boyer2', RewriteFuns.onewayunify1- -- It's worth an 18% improvement in allocation for this- -- particular benchmark; 5% on 'mate' and 1.3% on 'multiplier'--interestingCallContext :: SimplEnv -> SimplCont -> CallCtxt--- See Note [Interesting call context]-interestingCallContext env cont- = interesting cont- where- interesting (Select {})- | sm_case_case (getMode env) = CaseCtxt- | otherwise = BoringCtxt- -- See Note [No case of case is boring]-- interesting (ApplyToVal {}) = ValAppCtxt- -- Can happen if we have (f Int |> co) y- -- If f has an INLINE prag we need to give it some- -- motivation to inline. See Note [Cast then apply]- -- in GHC.Core.Unfold-- interesting (StrictArg { sc_fun = fun }) = strictArgContext fun- interesting (StrictBind {}) = BoringCtxt- interesting (Stop _ cci) = cci- interesting (TickIt _ k) = interesting k- interesting (ApplyToTy { sc_cont = k }) = interesting k- interesting (CastIt _ k) = interesting k- -- If this call is the arg of a strict function, the context- -- is a bit interesting. If we inline here, we may get useful- -- evaluation information to avoid repeated evals: e.g.- -- x + (y * z)- -- Here the contIsInteresting makes the '*' keener to inline,- -- which in turn exposes a constructor which makes the '+' inline.- -- Assuming that +,* aren't small enough to inline regardless.- --- -- It's also very important to inline in a strict context for things- -- like- -- foldr k z (f x)- -- Here, the context of (f x) is strict, and if f's unfolding is- -- a build it's *great* to inline it here. So we must ensure that- -- the context for (f x) is not totally uninteresting.--interestingArgContext :: [CoreRule] -> SimplCont -> Bool--- If the argument has form (f x y), where x,y are boring,--- and f is marked INLINE, then we don't want to inline f.--- But if the context of the argument is--- g (f x y)--- where g has rules, then we *do* want to inline f, in case it--- exposes a rule that might fire. Similarly, if the context is--- h (g (f x x))--- where h has rules, then we do want to inline f; hence the--- call_cont argument to interestingArgContext------ The ai-rules flag makes this happen; if it's--- set, the inliner gets just enough keener to inline f--- regardless of how boring f's arguments are, if it's marked INLINE------ The alternative would be to *always* inline an INLINE function,--- regardless of how boring its context is; but that seems overkill--- For example, it'd mean that wrapper functions were always inlined------ The call_cont passed to interestingArgContext is the context of--- the call itself, e.g. g <hole> in the example above-interestingArgContext rules call_cont- = notNull rules || enclosing_fn_has_rules- where- enclosing_fn_has_rules = go call_cont-- go (Select {}) = False- go (ApplyToVal {}) = False -- Shouldn't really happen- go (ApplyToTy {}) = False -- Ditto- go (StrictArg { sc_fun = fun }) = ai_encl fun- go (StrictBind {}) = False -- ??- go (CastIt _ c) = go c- go (Stop _ RuleArgCtxt) = True- go (Stop _ _) = False- go (TickIt _ c) = go c--{- Note [Interesting arguments]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-An argument is interesting if it deserves a discount for unfoldings-with a discount in that argument position. The idea is to avoid-unfolding a function that is applied only to variables that have no-unfolding (i.e. they are probably lambda bound): f x y z There is-little point in inlining f here.--Generally, *values* (like (C a b) and (\x.e)) deserve discounts. But-we must look through lets, eg (let x = e in C a b), because the let will-float, exposing the value, if we inline. That makes it different to-exprIsHNF.--Before 2009 we said it was interesting if the argument had *any* structure-at all; i.e. (hasSomeUnfolding v). But does too much inlining; see #3016.--But we don't regard (f x y) as interesting, unless f is unsaturated.-If it's saturated and f hasn't inlined, then it's probably not going-to now!--Note [Conlike is interesting]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider- f d = ...((*) d x y)...- ... f (df d')...-where df is con-like. Then we'd really like to inline 'f' so that the-rule for (*) (df d) can fire. To do this- a) we give a discount for being an argument of a class-op (eg (*) d)- b) we say that a con-like argument (eg (df d)) is interesting--}--interestingArg :: SimplEnv -> CoreExpr -> ArgSummary--- See Note [Interesting arguments]-interestingArg env e = go env 0 e- where- -- n is # value args to which the expression is applied- go env n (Var v)- = case substId env v of- DoneId v' -> go_var n v'- DoneEx e _ -> go (zapSubstEnv env) n e- ContEx tvs cvs ids e -> go (setSubstEnv env tvs cvs ids) n e-- go _ _ (Lit l)- | isLitRubbish l = TrivArg -- Leads to unproductive inlining in WWRec, #20035- | otherwise = ValueArg- go _ _ (Type _) = TrivArg- go _ _ (Coercion _) = TrivArg- go env n (App fn (Type _)) = go env n fn- go env n (App fn _) = go env (n+1) fn- go env n (Tick _ a) = go env n a- go env n (Cast e _) = go env n e- go env n (Lam v e)- | isTyVar v = go env n e- | n>0 = NonTrivArg -- (\x.b) e is NonTriv- | otherwise = ValueArg- go _ _ (Case {}) = NonTrivArg- go env n (Let b e) = case go env' n e of- ValueArg -> ValueArg- _ -> NonTrivArg- where- env' = env `addNewInScopeIds` bindersOf b-- go_var n v- | isConLikeId v = ValueArg -- Experimenting with 'conlike' rather that- -- data constructors here- | idArity v > n = ValueArg -- Catches (eg) primops with arity but no unfolding- | n > 0 = NonTrivArg -- Saturated or unknown call- | conlike_unfolding = ValueArg -- n==0; look for an interesting unfolding- -- See Note [Conlike is interesting]- | otherwise = TrivArg -- n==0, no useful unfolding- where- conlike_unfolding = isConLikeUnfolding (idUnfolding v)--{--************************************************************************-* *- SimplMode-* *-************************************************************************--The SimplMode controls several switches; see its definition in-GHC.Core.Opt.Monad- sm_rules :: Bool -- Whether RULES are enabled- sm_inline :: Bool -- Whether inlining is enabled- sm_case_case :: Bool -- Whether case-of-case is enabled- sm_eta_expand :: Bool -- Whether eta-expansion is enabled--}--simplEnvForGHCi :: Logger -> DynFlags -> SimplEnv-simplEnvForGHCi logger dflags- = mkSimplEnv $ SimplMode { sm_names = ["GHCi"]- , sm_phase = InitialPhase- , sm_logger = logger- , sm_dflags = dflags- , sm_uf_opts = uf_opts- , sm_rules = rules_on- , sm_inline = False- -- Do not do any inlining, in case we expose some- -- unboxed tuple stuff that confuses the bytecode- -- interpreter- , sm_eta_expand = eta_expand_on- , sm_cast_swizzle = True- , sm_case_case = True- , sm_pre_inline = pre_inline_on- }- where- rules_on = gopt Opt_EnableRewriteRules dflags- eta_expand_on = gopt Opt_DoLambdaEtaExpansion dflags- pre_inline_on = gopt Opt_SimplPreInlining dflags- uf_opts = unfoldingOpts dflags--updModeForStableUnfoldings :: Activation -> SimplMode -> SimplMode-updModeForStableUnfoldings unf_act current_mode- = current_mode { sm_phase = phaseFromActivation unf_act- , sm_eta_expand = False- , sm_inline = True }- -- sm_phase: see Note [Simplifying inside stable unfoldings]- -- sm_eta_expand: see Note [Eta-expansion in stable unfoldings]- -- sm_rules: just inherit; sm_rules might be "off"- -- because of -fno-enable-rewrite-rules- where- phaseFromActivation (ActiveAfter _ n) = Phase n- phaseFromActivation _ = InitialPhase--updModeForRules :: SimplMode -> SimplMode--- See Note [Simplifying rules]-updModeForRules current_mode- = current_mode { sm_phase = InitialPhase- , sm_inline = False- -- See Note [Do not expose strictness if sm_inline=False]- , sm_rules = False- , sm_cast_swizzle = False- -- See Note [Cast swizzling on rule LHSs]- , sm_eta_expand = False }--{- Note [Simplifying rules]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-When simplifying a rule LHS, refrain from /any/ inlining or applying-of other RULES.--Doing anything to the LHS is plain confusing, because it means that what the-rule matches is not what the user wrote. c.f. #10595, and #10528.-Moreover, inlining (or applying rules) on rule LHSs risks introducing-Ticks into the LHS, which makes matching trickier. #10665, #10745.--Doing this to either side confounds tools like HERMIT, which seek to reason-about and apply the RULES as originally written. See #10829.--There is, however, one case where we are pretty much /forced/ to transform the-LHS of a rule: postInlineUnconditionally. For instance, in the case of-- let f = g @Int in f--We very much want to inline f into the body of the let. However, to do so (and-be able to safely drop f's binding) we must inline into all occurrences of f,-including those in the LHS of rules.--This can cause somewhat surprising results; for instance, in #18162 we found-that a rule template contained ticks in its arguments, because-postInlineUnconditionally substituted in a trivial expression that contains-ticks. See Note [Tick annotations in RULE matching] in GHC.Core.Rules for-details.--Note [Cast swizzling on rule LHSs]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-In the LHS of a RULE we may have- (\x. blah |> CoVar cv)-where `cv` is a coercion variable. Critically, we really only want-coercion /variables/, not general coercions, on the LHS of a RULE. So-we don't want to swizzle this to- (\x. blah) |> (Refl xty `FunCo` CoVar cv)-So we switch off cast swizzling in updModeForRules.--Note [Eta-expansion in stable unfoldings]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We don't do eta-expansion inside stable unfoldings. It's extra work,-and can be expensive (the bizarre T18223 is a case in point).--See Note [Occurrence analysis for lambda binders] in GHC.Core.Opt.OccurAnal.--Historical note. There was /previously/ another reason not to do eta-expansion in stable unfoldings. If we have a stable unfolding-- f :: Ord a => a -> IO ()- -- Unfolding template- -- = /\a \(d:Ord a) (x:a). bla--we previously did not want to eta-expand to-- f :: Ord a => a -> IO ()- -- Unfolding template- -- = (/\a \(d:Ord a) (x:a) (eta:State#). bla eta) |> co--because not specialisation of the overloading didn't work properly (#9509).-But now it does: see Note [Account for casts in binding] in GHC.Core.Opt.Specialise---Note [Inlining in gentle mode]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Something is inlined if- (i) the sm_inline flag is on, AND- (ii) the thing has an INLINE pragma, AND- (iii) the thing is inlinable in the earliest phase.--Example of why (iii) is important:- {-# INLINE [~1] g #-}- g = ...-- {-# INLINE f #-}- f x = g (g x)--If we were to inline g into f's inlining, then an importing module would-never be able to do- f e --> g (g e) ---> RULE fires-because the stable unfolding for f has had g inlined into it.--On the other hand, it is bad not to do ANY inlining into an-stable unfolding, because then recursive knots in instance declarations-don't get unravelled.--However, *sometimes* SimplGently must do no call-site inlining at all-(hence sm_inline = False). Before full laziness we must be careful-not to inline wrappers, because doing so inhibits floating- e.g. ...(case f x of ...)...- ==> ...(case (case x of I# x# -> fw x#) of ...)...- ==> ...(case x of I# x# -> case fw x# of ...)...-and now the redex (f x) isn't floatable any more.--The no-inlining thing is also important for Template Haskell. You might be-compiling in one-shot mode with -O2; but when TH compiles a splice before-running it, we don't want to use -O2. Indeed, we don't want to inline-anything, because the byte-code interpreter might get confused about-unboxed tuples and suchlike.--Note [Simplifying inside stable unfoldings]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We must take care with simplification inside stable unfoldings (which come from-INLINE pragmas).--First, consider the following example- let f = \pq -> BIG- in- let g = \y -> f y y- {-# INLINE g #-}- in ...g...g...g...g...g...-Now, if that's the ONLY occurrence of f, it might be inlined inside g,-and thence copied multiple times when g is inlined. HENCE we treat-any occurrence in a stable unfolding as a multiple occurrence, not a single-one; see OccurAnal.addRuleUsage.--Second, we do want *do* to some modest rules/inlining stuff in stable-unfoldings, partly to eliminate senseless crap, and partly to break-the recursive knots generated by instance declarations.--However, suppose we have- {-# INLINE <act> f #-}- f = <rhs>-meaning "inline f in phases p where activation <act>(p) holds".-Then what inlinings/rules can we apply to the copy of <rhs> captured in-f's stable unfolding? Our model is that literally <rhs> is substituted for-f when it is inlined. So our conservative plan (implemented by-updModeForStableUnfoldings) is this:-- -------------------------------------------------------------- When simplifying the RHS of a stable unfolding, set the phase- to the phase in which the stable unfolding first becomes active- ---------------------------------------------------------------That ensures that-- a) Rules/inlinings that *cease* being active before p will- not apply to the stable unfolding, consistent with it being- inlined in its *original* form in phase p.-- b) Rules/inlinings that only become active *after* p will- not apply to the stable unfolding, again to be consistent with- inlining the *original* rhs in phase p.--For example,- {-# INLINE f #-}- f x = ...g...-- {-# NOINLINE [1] g #-}- g y = ...-- {-# RULE h g = ... #-}-Here we must not inline g into f's RHS, even when we get to phase 0,-because when f is later inlined into some other module we want the-rule for h to fire.--Similarly, consider- {-# INLINE f #-}- f x = ...g...-- g y = ...-and suppose that there are auto-generated specialisations and a strictness-wrapper for g. The specialisations get activation AlwaysActive, and the-strictness wrapper get activation (ActiveAfter 0). So the strictness-wrepper fails the test and won't be inlined into f's stable unfolding. That-means f can inline, expose the specialised call to g, so the specialisation-rules can fire.--A note about wrappers-~~~~~~~~~~~~~~~~~~~~~-It's also important not to inline a worker back into a wrapper.-A wrapper looks like- wraper = inline_me (\x -> ...worker... )-Normally, the inline_me prevents the worker getting inlined into-the wrapper (initially, the worker's only call site!). But,-if the wrapper is sure to be called, the strictness analyser will-mark it 'demanded', so when the RHS is simplified, it'll get an ArgOf-continuation.--}--activeUnfolding :: SimplMode -> Id -> Bool-activeUnfolding mode id- | isCompulsoryUnfolding (realIdUnfolding id)- = True -- Even sm_inline can't override compulsory unfoldings- | otherwise- = isActive (sm_phase mode) (idInlineActivation id)- && sm_inline mode- -- `or` isStableUnfolding (realIdUnfolding id)- -- Inline things when- -- (a) they are active- -- (b) sm_inline says so, except that for stable unfoldings- -- (ie pragmas) we inline anyway--getUnfoldingInRuleMatch :: SimplEnv -> InScopeEnv--- When matching in RULE, we want to "look through" an unfolding--- (to see a constructor) if *rules* are on, even if *inlinings*--- are not. A notable example is DFuns, which really we want to--- match in rules like (op dfun) in gentle mode. Another example--- is 'otherwise' which we want exprIsConApp_maybe to be able to--- see very early on-getUnfoldingInRuleMatch env- = (in_scope, id_unf)- where- in_scope = seInScope env- mode = getMode env- id_unf id | unf_is_active id = idUnfolding id- | otherwise = NoUnfolding- unf_is_active id = isActive (sm_phase mode) (idInlineActivation id)- -- When sm_rules was off we used to test for a /stable/ unfolding,- -- but that seems wrong (#20941)-------------------------activeRule :: SimplMode -> Activation -> Bool--- Nothing => No rules at all-activeRule mode- | not (sm_rules mode) = \_ -> False -- Rewriting is off- | otherwise = isActive (sm_phase mode)--{--************************************************************************-* *- preInlineUnconditionally-* *-************************************************************************--preInlineUnconditionally-~~~~~~~~~~~~~~~~~~~~~~~~-@preInlineUnconditionally@ examines a bndr to see if it is used just-once in a completely safe way, so that it is safe to discard the-binding inline its RHS at the (unique) usage site, REGARDLESS of how-big the RHS might be. If this is the case we don't simplify the RHS-first, but just inline it un-simplified.--This is much better than first simplifying a perhaps-huge RHS and then-inlining and re-simplifying it. Indeed, it can be at least quadratically-better. Consider-- x1 = e1- x2 = e2[x1]- x3 = e3[x2]- ...etc...- xN = eN[xN-1]--We may end up simplifying e1 N times, e2 N-1 times, e3 N-3 times etc.-This can happen with cascades of functions too:-- f1 = \x1.e1- f2 = \xs.e2[f1]- f3 = \xs.e3[f3]- ...etc...--THE MAIN INVARIANT is this:-- ---- preInlineUnconditionally invariant ------ IF preInlineUnconditionally chooses to inline x = <rhs>- THEN doing the inlining should not change the occurrence- info for the free vars of <rhs>- ------------------------------------------------For example, it's tempting to look at trivial binding like- x = y-and inline it unconditionally. But suppose x is used many times,-but this is the unique occurrence of y. Then inlining x would change-y's occurrence info, which breaks the invariant. It matters: y-might have a BIG rhs, which will now be dup'd at every occurrence of x.---Even RHSs labelled InlineMe aren't caught here, because there might be-no benefit from inlining at the call site.--[Sept 01] Don't unconditionally inline a top-level thing, because that-can simply make a static thing into something built dynamically. E.g.- x = (a,b)- main = \s -> h x--[Remember that we treat \s as a one-shot lambda.] No point in-inlining x unless there is something interesting about the call site.--But watch out: if you aren't careful, some useful foldr/build fusion-can be lost (most notably in spectral/hartel/parstof) because the-foldr didn't see the build. Doing the dynamic allocation isn't a big-deal, in fact, but losing the fusion can be. But the right thing here-seems to be to do a callSiteInline based on the fact that there is-something interesting about the call site (it's strict). Hmm. That-seems a bit fragile.--Conclusion: inline top level things gaily until FinalPhase (the last-phase), at which point don't.--Note [pre/postInlineUnconditionally in gentle mode]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Even in gentle mode we want to do preInlineUnconditionally. The-reason is that too little clean-up happens if you don't inline-use-once things. Also a bit of inlining is *good* for full laziness;-it can expose constant sub-expressions. Example in-spectral/mandel/Mandel.hs, where the mandelset function gets a useful-let-float if you inline windowToViewport--However, as usual for Gentle mode, do not inline things that are-inactive in the initial stages. See Note [Gentle mode].--Note [Stable unfoldings and preInlineUnconditionally]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Surprisingly, do not pre-inline-unconditionally Ids with INLINE pragmas!-Example-- {-# INLINE f #-}- f :: Eq a => a -> a- f x = ...-- fInt :: Int -> Int- fInt = f Int dEqInt-- ...fInt...fInt...fInt...--Here f occurs just once, in the RHS of fInt. But if we inline it there-it might make fInt look big, and we'll lose the opportunity to inline f-at each of fInt's call sites. The INLINE pragma will only inline when-the application is saturated for exactly this reason; and we don't-want PreInlineUnconditionally to second-guess it. A live example is #3736.- c.f. Note [Stable unfoldings and postInlineUnconditionally]--NB: this only applies for INLINE things. Do /not/ switch off-preInlineUnconditionally for--* INLINABLE. It just says to GHC "inline this if you like". If there- is a unique occurrence, we want to inline the stable unfolding, not- the RHS.--* NONLINE[n] just switches off inlining until phase n. We should- respect that, but after phase n, just behave as usual.--* NoUserInlinePrag. There is no pragma at all. This ends up on wrappers.- (See #18815.)--Note [Top-level bottoming Ids]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Don't inline top-level Ids that are bottoming, even if they are used just-once, because FloatOut has gone to some trouble to extract them out.-Inlining them won't make the program run faster!--Note [Do not inline CoVars unconditionally]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Coercion variables appear inside coercions, and the RHS of a let-binding-is a term (not a coercion) so we can't necessarily inline the latter in-the former.--}--preInlineUnconditionally- :: SimplEnv -> TopLevelFlag -> InId- -> InExpr -> StaticEnv -- These two go together- -> Maybe SimplEnv -- Returned env has extended substitution--- Precondition: rhs satisfies the let/app invariant--- See Note [Core let/app invariant] in GHC.Core--- Reason: we don't want to inline single uses, or discard dead bindings,--- for unlifted, side-effect-ful bindings-preInlineUnconditionally env top_lvl bndr rhs rhs_env- | not pre_inline_unconditionally = Nothing- | not active = Nothing- | isTopLevel top_lvl && isDeadEndId bndr = Nothing -- Note [Top-level bottoming Ids]- | isCoVar bndr = Nothing -- Note [Do not inline CoVars unconditionally]- | isExitJoinId bndr = Nothing -- Note [Do not inline exit join points]- -- in module Exitify- | not (one_occ (idOccInfo bndr)) = Nothing- | not (isStableUnfolding unf) = Just $! (extend_subst_with rhs)-- -- See Note [Stable unfoldings and preInlineUnconditionally]- | not (isInlinePragma inline_prag)- , Just inl <- maybeUnfoldingTemplate unf = Just $! (extend_subst_with inl)- | otherwise = Nothing- where- unf = idUnfolding bndr- extend_subst_with inl_rhs = extendIdSubst env bndr $! (mkContEx rhs_env inl_rhs)-- one_occ IAmDead = True -- Happens in ((\x.1) v)- one_occ OneOcc{ occ_n_br = 1- , occ_in_lam = NotInsideLam } = isNotTopLevel top_lvl || early_phase- one_occ OneOcc{ occ_n_br = 1- , occ_in_lam = IsInsideLam- , occ_int_cxt = IsInteresting } = canInlineInLam rhs- one_occ _ = False-- pre_inline_unconditionally = sm_pre_inline mode- mode = getMode env- active = isActive (sm_phase mode) (inlinePragmaActivation inline_prag)- -- See Note [pre/postInlineUnconditionally in gentle mode]- inline_prag = idInlinePragma bndr---- Be very careful before inlining inside a lambda, because (a) we must not--- invalidate occurrence information, and (b) we want to avoid pushing a--- single allocation (here) into multiple allocations (inside lambda).--- Inlining a *function* with a single *saturated* call would be ok, mind you.--- || (if is_cheap && not (canInlineInLam rhs) then pprTrace "preinline" (ppr bndr <+> ppr rhs) ok else ok)--- where--- is_cheap = exprIsCheap rhs--- ok = is_cheap && int_cxt-- -- int_cxt The context isn't totally boring- -- E.g. let f = \ab.BIG in \y. map f xs- -- Don't want to substitute for f, because then we allocate- -- its closure every time the \y is called- -- But: let f = \ab.BIG in \y. map (f y) xs- -- Now we do want to substitute for f, even though it's not- -- saturated, because we're going to allocate a closure for- -- (f y) every time round the loop anyhow.-- -- canInlineInLam => free vars of rhs are (Once in_lam) or Many,- -- so substituting rhs inside a lambda doesn't change the occ info.- -- Sadly, not quite the same as exprIsHNF.- canInlineInLam (Lit _) = True- canInlineInLam (Lam b e) = isRuntimeVar b || canInlineInLam e- canInlineInLam (Tick t e) = not (tickishIsCode t) && canInlineInLam e- canInlineInLam _ = False- -- not ticks. Counting ticks cannot be duplicated, and non-counting- -- ticks around a Lam will disappear anyway.-- early_phase = sm_phase mode /= FinalPhase- -- If we don't have this early_phase test, consider- -- x = length [1,2,3]- -- The full laziness pass carefully floats all the cons cells to- -- top level, and preInlineUnconditionally floats them all back in.- -- Result is (a) static allocation replaced by dynamic allocation- -- (b) many simplifier iterations because this tickles- -- a related problem; only one inlining per pass- --- -- On the other hand, I have seen cases where top-level fusion is- -- lost if we don't inline top level thing (e.g. string constants)- -- Hence the test for phase zero (which is the phase for all the final- -- simplifications). Until phase zero we take no special notice of- -- top level things, but then we become more leery about inlining- -- them.--{--************************************************************************-* *- postInlineUnconditionally-* *-************************************************************************--postInlineUnconditionally-~~~~~~~~~~~~~~~~~~~~~~~~~-@postInlineUnconditionally@ decides whether to unconditionally inline-a thing based on the form of its RHS; in particular if it has a-trivial RHS. If so, we can inline and discard the binding altogether.--NB: a loop breaker has must_keep_binding = True and non-loop-breakers-only have *forward* references. Hence, it's safe to discard the binding--NOTE: This isn't our last opportunity to inline. We're at the binding-site right now, and we'll get another opportunity when we get to the-occurrence(s)--Note that we do this unconditional inlining only for trivial RHSs.-Don't inline even WHNFs inside lambdas; doing so may simply increase-allocation when the function is called. This isn't the last chance; see-NOTE above.--NB: Even inline pragmas (e.g. IMustBeINLINEd) are ignored here Why?-Because we don't even want to inline them into the RHS of constructor-arguments. See NOTE above--NB: At one time even NOINLINE was ignored here: if the rhs is trivial-it's best to inline it anyway. We often get a=E; b=a from desugaring,-with both a and b marked NOINLINE. But that seems incompatible with-our new view that inlining is like a RULE, so I'm sticking to the 'active'-story for now.--NB: unconditional inlining of this sort can introduce ticks in places that-may seem surprising; for instance, the LHS of rules. See Note [Simplifying-rules] for details.--}--postInlineUnconditionally- :: SimplEnv -> BindContext- -> OutId -- The binder (*not* a CoVar), including its unfolding- -> OccInfo -- From the InId- -> OutExpr- -> Bool--- Precondition: rhs satisfies the let/app invariant--- See Note [Core let/app invariant] in GHC.Core--- Reason: we don't want to inline single uses, or discard dead bindings,--- for unlifted, side-effect-ful bindings-postInlineUnconditionally env bind_cxt bndr occ_info rhs- | not active = False- | isWeakLoopBreaker occ_info = False -- If it's a loop-breaker of any kind, don't inline- -- because it might be referred to "earlier"- | isStableUnfolding unfolding = False -- Note [Stable unfoldings and postInlineUnconditionally]- | isTopLevel (bindContextLevel bind_cxt)- = False -- Note [Top level and postInlineUnconditionally]- | exprIsTrivial rhs = True- | BC_Join {} <- bind_cxt -- See point (1) of Note [Duplicating join points]- , not (phase == FinalPhase) = False -- in Simplify.hs- | otherwise- = case occ_info of- OneOcc { occ_in_lam = in_lam, occ_int_cxt = int_cxt, occ_n_br = n_br }- -- See Note [Inline small things to avoid creating a thunk]-- -> n_br < 100 -- See Note [Suppress exponential blowup]-- && smallEnoughToInline uf_opts unfolding -- Small enough to dup- -- ToDo: consider discount on smallEnoughToInline if int_cxt is true- --- -- NB: Do NOT inline arbitrarily big things, even if occ_n_br=1- -- Reason: doing so risks exponential behaviour. We simplify a big- -- expression, inline it, and simplify it again. But if the- -- very same thing happens in the big expression, we get- -- exponential cost!- -- PRINCIPLE: when we've already simplified an expression once,- -- make sure that we only inline it if it's reasonably small.-- && (in_lam == NotInsideLam ||- -- Outside a lambda, we want to be reasonably aggressive- -- about inlining into multiple branches of case- -- e.g. let x = <non-value>- -- in case y of { C1 -> ..x..; C2 -> ..x..; C3 -> ... }- -- Inlining can be a big win if C3 is the hot-spot, even if- -- the uses in C1, C2 are not 'interesting'- -- An example that gets worse if you add int_cxt here is 'clausify'-- (isCheapUnfolding unfolding && int_cxt == IsInteresting))- -- isCheap => acceptable work duplication; in_lam may be true- -- int_cxt to prevent us inlining inside a lambda without some- -- good reason. See the notes on int_cxt in preInlineUnconditionally-- IAmDead -> True -- This happens; for example, the case_bndr during case of- -- known constructor: case (a,b) of x { (p,q) -> ... }- -- Here x isn't mentioned in the RHS, so we don't want to- -- create the (dead) let-binding let x = (a,b) in ...-- _ -> False---- Here's an example that we don't handle well:--- let f = if b then Left (\x.BIG) else Right (\y.BIG)--- in \y. ....case f of {...} ....--- Here f is used just once, and duplicating the case work is fine (exprIsCheap).--- But--- - We can't preInlineUnconditionally because that would invalidate--- the occ info for b.--- - We can't postInlineUnconditionally because the RHS is big, and--- that risks exponential behaviour--- - We can't call-site inline, because the rhs is big--- Alas!-- where- unfolding = idUnfolding bndr- uf_opts = seUnfoldingOpts env- phase = sm_phase (getMode env)- active = isActive phase (idInlineActivation bndr)- -- See Note [pre/postInlineUnconditionally in gentle mode]--{- Note [Inline small things to avoid creating a thunk]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-The point of examining occ_info here is that for *non-values* that-occur outside a lambda, the call-site inliner won't have a chance-(because it doesn't know that the thing only occurs once). The-pre-inliner won't have gotten it either, if the thing occurs in more-than one branch So the main target is things like-- let x = f y in- case v of- True -> case x of ...- False -> case x of ...--This is very important in practice; e.g. wheel-seive1 doubles-in allocation if you miss this out. And bits of GHC itself start-to allocate more. An egregious example is test perf/compiler/T14697,-where GHC.Driver.CmdLine.$wprocessArgs allocated hugely more.--Note [Suppress exponential blowup]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-In #13253, and several related tickets, we got an exponential blowup-in code size from postInlineUnconditionally. The trouble comes when-we have- let j1a = case f y of { True -> p; False -> q }- j1b = case f y of { True -> q; False -> p }- j2a = case f (y+1) of { True -> j1a; False -> j1b }- j2b = case f (y+1) of { True -> j1b; False -> j1a }- ...- in case f (y+10) of { True -> j10a; False -> j10b }--when there are many branches. In pass 1, postInlineUnconditionally-inlines j10a and j10b (they are both small). Now we have two calls-to j9a and two to j9b. In pass 2, postInlineUnconditionally inlines-all four of these calls, leaving four calls to j8a and j8b. Etc.-Yikes! This is exponential!--A possible plan: stop doing postInlineUnconditionally-for some fixed, smallish number of branches, say 4. But that turned-out to be bad: see Note [Inline small things to avoid creating a thunk].-And, as it happened, the problem with #13253 was solved in a-different way (Note [Duplicating StrictArg] in Simplify).--So I just set an arbitrary, high limit of 100, to stop any-totally exponential behaviour.--This still leaves the nasty possibility that /ordinary/ inlining (not-postInlineUnconditionally) might inline these join points, each of-which is individually quiet small. I'm still not sure what to do-about this (e.g. see #15488).--Note [Top level and postInlineUnconditionally]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We don't do postInlineUnconditionally for top-level things (even for-ones that are trivial):-- * Doing so will inline top-level error expressions that have been- carefully floated out by FloatOut. More generally, it might- replace static allocation with dynamic.-- * Even for trivial expressions there's a problem. Consider- {-# RULE "foo" forall (xs::[T]). reverse xs = ruggle xs #-}- blah xs = reverse xs- ruggle = sort- In one simplifier pass we might fire the rule, getting- blah xs = ruggle xs- but in *that* simplifier pass we must not do postInlineUnconditionally- on 'ruggle' because then we'll have an unbound occurrence of 'ruggle'-- If the rhs is trivial it'll be inlined by callSiteInline, and then- the binding will be dead and discarded by the next use of OccurAnal-- * There is less point, because the main goal is to get rid of local- bindings used in multiple case branches.-- * The inliner should inline trivial things at call sites anyway.-- * The Id might be exported. We could check for that separately,- but since we aren't going to postInlineUnconditionally /any/- top-level bindings, we don't need to test.--Note [Stable unfoldings and postInlineUnconditionally]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Do not do postInlineUnconditionally if the Id has a stable unfolding,-otherwise we lose the unfolding. Example-- -- f has stable unfolding with rhs (e |> co)- -- where 'e' is big- f = e |> co--Then there's a danger we'll optimise to-- f' = e- f = f' |> co--and now postInlineUnconditionally, losing the stable unfolding on f. Now f'-won't inline because 'e' is too big.-- c.f. Note [Stable unfoldings and preInlineUnconditionally]---************************************************************************-* *- Rebuilding a lambda-* *-************************************************************************--}--mkLam :: SimplEnv -> [OutBndr] -> OutExpr -> SimplCont -> SimplM OutExpr--- mkLam tries three things--- a) eta reduction, if that gives a trivial expression--- b) eta expansion [only if there are some value lambdas]------ NB: the SimplEnv already includes the [OutBndr] in its in-scope set-mkLam _env [] body _cont- = return body-mkLam env bndrs body cont- = {-#SCC "mkLam" #-}--- pprTrace "mkLam" (ppr bndrs $$ ppr body $$ ppr cont) $- do { dflags <- getDynFlags- ; mkLam' dflags bndrs body }- where- mode = getMode env- rec_ids = seRecIds env-- mkLam' :: DynFlags -> [OutBndr] -> OutExpr -> SimplM OutExpr- mkLam' dflags bndrs body@(Lam {})- = mkLam' dflags (bndrs ++ bndrs1) body1- where- (bndrs1, body1) = collectBinders body-- mkLam' dflags bndrs (Tick t expr)- | tickishFloatable t- = mkTick t <$> mkLam' dflags bndrs expr-- mkLam' dflags bndrs (Cast body co)- | -- Note [Casts and lambdas]- sm_cast_swizzle mode- , not (any bad bndrs)- = do { lam <- mkLam' dflags bndrs body- ; return (mkCast lam (mkPiCos Representational bndrs co)) }- where- co_vars = tyCoVarsOfCo co- bad bndr = isCoVar bndr && bndr `elemVarSet` co_vars-- mkLam' dflags bndrs body- | gopt Opt_DoEtaReduction dflags- , Just etad_lam <- {-# SCC "tryee" #-} tryEtaReduce rec_ids bndrs body- = do { tick (EtaReduction (head bndrs))- ; return etad_lam }-- | not (contIsRhs cont) -- See Note [Eta expanding lambdas]- , sm_eta_expand mode- , any isRuntimeVar bndrs- , let body_arity = {-# SCC "eta" #-} exprEtaExpandArity dflags body- , expandableArityType body_arity- = do { tick (EtaExpansion (head bndrs))- ; let res = {-# SCC "eta3" #-}- mkLams bndrs $- etaExpandAT in_scope body_arity body- ; traceSmpl "eta expand" (vcat [text "before" <+> ppr (mkLams bndrs body)- , text "after" <+> ppr res])- ; return res }-- | otherwise- = return (mkLams bndrs body)- where- in_scope = getInScope env -- Includes 'bndrs'--{--Note [Eta expanding lambdas]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~-In general we *do* want to eta-expand lambdas. Consider- f (\x -> case x of (a,b) -> \s -> blah)-where 's' is a state token, and hence can be eta expanded. This-showed up in the code for GHc.IO.Handle.Text.hPutChar, a rather-important function!--The eta-expansion will never happen unless we do it now. (Well, it's-possible that CorePrep will do it, but CorePrep only has a half-baked-eta-expander that can't deal with casts. So it's much better to do it-here.)--However, when the lambda is let-bound, as the RHS of a let, we have a-better eta-expander (in the form of tryEtaExpandRhs), so we don't-bother to try expansion in mkLam in that case; hence the contIsRhs-guard.--NB: We check the SimplEnv (sm_eta_expand), not DynFlags.- See Note [Eta-expansion in stable unfoldings]--Note [Casts and lambdas]-~~~~~~~~~~~~~~~~~~~~~~~~-Consider- (\x. (\y. e) `cast` g1) `cast` g2-There is a danger here that the two lambdas look separated, and the-full laziness pass might float an expression to between the two.--So this equation in mkLam' floats the g1 out, thus:- (\x. e `cast` g1) --> (\x.e) `cast` (tx -> g1)-where x:tx.--In general, this floats casts outside lambdas, where (I hope) they-might meet and cancel with some other cast:- \x. e `cast` co ===> (\x. e) `cast` (tx -> co)- /\a. e `cast` co ===> (/\a. e) `cast` (/\a. co)- /\g. e `cast` co ===> (/\g. e) `cast` (/\g. co)- (if not (g `in` co))--We call this "cast swizzling". It is controlled by sm_cast_swizzle.-See also Note [Cast swizzling on rule LHSs]--Wrinkles--* Notice that it works regardless of 'e'. Originally it worked only- if 'e' was itself a lambda, but in some cases that resulted in- fruitless iteration in the simplifier. A good example was when- compiling Text.ParserCombinators.ReadPrec, where we had a definition- like (\x. Get `cast` g)- where Get is a constructor with nonzero arity. Then mkLam eta-expanded- the Get, and the next iteration eta-reduced it, and then eta-expanded- it again.--* Note also the side condition for the case of coercion binders, namely- not (any bad bndrs). It does not make sense to transform- /\g. e `cast` g ==> (/\g.e) `cast` (/\g.g)- because the latter is not well-kinded.---************************************************************************-* *- Eta expansion-* *-************************************************************************--}--tryEtaExpandRhs :: SimplEnv -> OutId -> OutExpr- -> SimplM (ArityType, OutExpr)--- See Note [Eta-expanding at let bindings]--- If tryEtaExpandRhs rhs = (n, is_bot, rhs') then--- (a) rhs' has manifest arity n--- (b) if is_bot is True then rhs' applied to n args is guaranteed bottom-tryEtaExpandRhs env bndr rhs- | Just join_arity <- isJoinId_maybe bndr- = do { let (join_bndrs, join_body) = collectNBinders join_arity rhs- arity_type = mkManifestArityType join_bndrs join_body- ; return (arity_type, rhs) }- -- Note [Do not eta-expand join points]- -- But do return the correct arity and bottom-ness, because- -- these are used to set the bndr's IdInfo (#15517)- -- Note [Invariants on join points] invariant 2b, in GHC.Core-- | sm_eta_expand mode -- Provided eta-expansion is on- , new_arity > old_arity -- And the current manifest arity isn't enough- , want_eta rhs- = do { tick (EtaExpansion bndr)- ; return (arity_type, etaExpandAT in_scope arity_type rhs) }-- | otherwise- = return (arity_type, rhs)-- where- mode = getMode env- in_scope = getInScope env- dflags = sm_dflags mode- old_arity = exprArity rhs-- arity_type = findRhsArity dflags bndr rhs old_arity- `maxWithArity` idCallArity bndr- new_arity = arityTypeArity arity_type-- -- See Note [Which RHSs do we eta-expand?]- want_eta (Cast e _) = want_eta e- want_eta (Tick _ e) = want_eta e- want_eta (Lam b e) | isTyVar b = want_eta e- want_eta (App e a) | exprIsTrivial a = want_eta e- want_eta (Var {}) = False- want_eta (Lit {}) = False- want_eta _ = True-{-- want_eta _ = case arity_type of- ATop (os:_) -> isOneShotInfo os- ATop [] -> False- ABot {} -> True--}--{--Note [Eta-expanding at let bindings]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We now eta expand at let-bindings, which is where the payoff comes.-The most significant thing is that we can do a simple arity analysis-(in GHC.Core.Opt.Arity.findRhsArity), which we can't do for free-floating lambdas--One useful consequence of not eta-expanding lambdas is this example:- genMap :: C a => ...- {-# INLINE genMap #-}- genMap f xs = ...-- myMap :: D a => ...- {-# INLINE myMap #-}- myMap = genMap--Notice that 'genMap' should only inline if applied to two arguments.-In the stable unfolding for myMap we'll have the unfolding- (\d -> genMap Int (..d..))-We do not want to eta-expand to- (\d f xs -> genMap Int (..d..) f xs)-because then 'genMap' will inline, and it really shouldn't: at least-as far as the programmer is concerned, it's not applied to two-arguments!--Note [Which RHSs do we eta-expand?]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We don't eta-expand:--* Trivial RHSs, e.g. f = g- If we eta expand do- f = \x. g x- we'll just eta-reduce again, and so on; so the- simplifier never terminates.--* PAPs: see Note [Do not eta-expand PAPs]--What about things like this?- f = case y of p -> \x -> blah--Here we do eta-expand. This is a change (Jun 20), but if we have-really decided that f has arity 1, then putting that lambda at the top-seems like a Good idea.--Note [Do not eta-expand PAPs]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We used to have old_arity = manifestArity rhs, which meant that we-would eta-expand even PAPs. But this gives no particular advantage,-and can lead to a massive blow-up in code size, exhibited by #9020.-Suppose we have a PAP- foo :: IO ()- foo = returnIO ()-Then we can eta-expand to- foo = (\eta. (returnIO () |> sym g) eta) |> g-where- g :: IO () ~ State# RealWorld -> (# State# RealWorld, () #)--But there is really no point in doing this, and it generates masses of-coercions and whatnot that eventually disappear again. For T9020, GHC-allocated 6.6G before, and 0.8G afterwards; and residency dropped from-1.8G to 45M.--Moreover, if we eta expand- f = g d ==> f = \x. g d x-that might in turn make g inline (if it has an inline pragma), which-we might not want. After all, INLINE pragmas say "inline only when-saturated" so we don't want to be too gung-ho about saturating!--But note that this won't eta-expand, say- f = \g -> map g-Does it matter not eta-expanding such functions? I'm not sure. Perhaps-strictness analysis will have less to bite on?--Note [Do not eta-expand join points]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Similarly to CPR (see Note [Don't w/w join points for CPR] in-GHC.Core.Opt.WorkWrap), a join point stands well to gain from its outer binding's-eta-expansion, and eta-expanding a join point is fraught with issues like how to-deal with a cast:-- let join $j1 :: IO ()- $j1 = ...- $j2 :: Int -> IO ()- $j2 n = if n > 0 then $j1- else ...-- =>-- let join $j1 :: IO ()- $j1 = (\eta -> ...)- `cast` N:IO :: State# RealWorld -> (# State# RealWorld, ())- ~ IO ()- $j2 :: Int -> IO ()- $j2 n = (\eta -> if n > 0 then $j1- else ...)- `cast` N:IO :: State# RealWorld -> (# State# RealWorld, ())- ~ IO ()--The cast here can't be pushed inside the lambda (since it's not casting to a-function type), so the lambda has to stay, but it can't because it contains a-reference to a join point. In fact, $j2 can't be eta-expanded at all. Rather-than try and detect this situation (and whatever other situations crop up!), we-don't bother; again, any surrounding eta-expansion will improve these join-points anyway, since an outer cast can *always* be pushed inside. By the time-CorePrep comes around, the code is very likely to look more like this:-- let join $j1 :: State# RealWorld -> (# State# RealWorld, ())- $j1 = (...) eta- $j2 :: Int -> State# RealWorld -> (# State# RealWorld, ())- $j2 = if n > 0 then $j1- else (...) eta---************************************************************************-* *-\subsection{Floating lets out of big lambdas}-* *-************************************************************************--Note [Floating and type abstraction]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider this:- x = /\a. C e1 e2-We'd like to float this to- y1 = /\a. e1- y2 = /\a. e2- x = /\a. C (y1 a) (y2 a)-for the usual reasons: we want to inline x rather vigorously.--You may think that this kind of thing is rare. But in some programs it is-common. For example, if you do closure conversion you might get:-- data a :-> b = forall e. (e -> a -> b) :$ e-- f_cc :: forall a. a :-> a- f_cc = /\a. (\e. id a) :$ ()--Now we really want to inline that f_cc thing so that the-construction of the closure goes away.--So I have elaborated simplLazyBind to understand right-hand sides that look-like- /\ a1..an. body--and treat them specially. The real work is done in-GHC.Core.Opt.Simplify.Utils.abstractFloats, but there is quite a bit of plumbing-in simplLazyBind as well.--The same transformation is good when there are lets in the body:-- /\abc -> let(rec) x = e in b- ==>- let(rec) x' = /\abc -> let x = x' a b c in e- in- /\abc -> let x = x' a b c in b--This is good because it can turn things like:-- let f = /\a -> letrec g = ... g ... in g-into- letrec g' = /\a -> ... g' a ...- in- let f = /\ a -> g' a--which is better. In effect, it means that big lambdas don't impede-let-floating.--This optimisation is CRUCIAL in eliminating the junk introduced by-desugaring mutually recursive definitions. Don't eliminate it lightly!--[May 1999] If we do this transformation *regardless* then we can-end up with some pretty silly stuff. For example,-- let- st = /\ s -> let { x1=r1 ; x2=r2 } in ...- in ..-becomes- let y1 = /\s -> r1- y2 = /\s -> r2- st = /\s -> ...[y1 s/x1, y2 s/x2]- in ..--Unless the "..." is a WHNF there is really no point in doing this.-Indeed it can make things worse. Suppose x1 is used strictly,-and is of the form-- x1* = case f y of { (a,b) -> e }--If we abstract this wrt the tyvar we then can't do the case inline-as we would normally do.--That's why the whole transformation is part of the same process that-floats let-bindings and constructor arguments out of RHSs. In particular,-it is guarded by the doFloatFromRhs call in simplLazyBind.--Note [Which type variables to abstract over]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Abstract only over the type variables free in the rhs wrt which the-new binding is abstracted. Note that-- * The naive approach of abstracting wrt the- tyvars free in the Id's /type/ fails. Consider:- /\ a b -> let t :: (a,b) = (e1, e2)- x :: a = fst t- in ...- Here, b isn't free in x's type, but we must nevertheless- abstract wrt b as well, because t's type mentions b.- Since t is floated too, we'd end up with the bogus:- poly_t = /\ a b -> (e1, e2)- poly_x = /\ a -> fst (poly_t a *b*)-- * We must do closeOverKinds. Example (#10934):- f = /\k (f:k->*) (a:k). let t = AccFailure @ (f a) in ...- Here we want to float 't', but we must remember to abstract over- 'k' as well, even though it is not explicitly mentioned in the RHS,- otherwise we get- t = /\ (f:k->*) (a:k). AccFailure @ (f a)- which is obviously bogus.-- * We get the variables to abstract over by filtering down the- the main_tvs for the original function, picking only ones- mentioned in the abstracted body. This means:- - they are automatically in dependency order, because main_tvs is- - there is no issue about non-determinism- - we don't gratuitiously change order, which may help (in a tiny- way) with CSE and/or the compiler-debugging experience--}--abstractFloats :: UnfoldingOpts -> TopLevelFlag -> [OutTyVar] -> SimplFloats- -> OutExpr -> SimplM ([OutBind], OutExpr)-abstractFloats uf_opts top_lvl main_tvs floats body- = assert (notNull body_floats) $- assert (isNilOL (sfJoinFloats floats)) $- do { (subst, float_binds) <- mapAccumLM abstract empty_subst body_floats- ; return (float_binds, GHC.Core.Subst.substExpr subst body) }- where- is_top_lvl = isTopLevel top_lvl- body_floats = letFloatBinds (sfLetFloats floats)- empty_subst = GHC.Core.Subst.mkEmptySubst (sfInScope floats)-- abstract :: GHC.Core.Subst.Subst -> OutBind -> SimplM (GHC.Core.Subst.Subst, OutBind)- abstract subst (NonRec id rhs)- = do { (poly_id1, poly_app) <- mk_poly1 tvs_here id- ; let (poly_id2, poly_rhs) = mk_poly2 poly_id1 tvs_here rhs'- !subst' = GHC.Core.Subst.extendIdSubst subst id poly_app- ; return (subst', NonRec poly_id2 poly_rhs) }- where- rhs' = GHC.Core.Subst.substExpr subst rhs-- -- tvs_here: see Note [Which type variables to abstract over]- tvs_here = filter (`elemVarSet` free_tvs) main_tvs- free_tvs = closeOverKinds $- exprSomeFreeVars isTyVar rhs'-- abstract subst (Rec prs)- = do { (poly_ids, poly_apps) <- mapAndUnzipM (mk_poly1 tvs_here) ids- ; let subst' = GHC.Core.Subst.extendSubstList subst (ids `zip` poly_apps)- poly_pairs = [ mk_poly2 poly_id tvs_here rhs'- | (poly_id, rhs) <- poly_ids `zip` rhss- , let rhs' = GHC.Core.Subst.substExpr subst' rhs ]- ; return (subst', Rec poly_pairs) }- where- (ids,rhss) = unzip prs- -- For a recursive group, it's a bit of a pain to work out the minimal- -- set of tyvars over which to abstract:- -- /\ a b c. let x = ...a... in- -- letrec { p = ...x...q...- -- q = .....p...b... } in- -- ...- -- Since 'x' is abstracted over 'a', the {p,q} group must be abstracted- -- over 'a' (because x is replaced by (poly_x a)) as well as 'b'.- -- Since it's a pain, we just use the whole set, which is always safe- --- -- If you ever want to be more selective, remember this bizarre case too:- -- x::a = x- -- Here, we must abstract 'x' over 'a'.- tvs_here = scopedSort main_tvs-- mk_poly1 :: [TyVar] -> Id -> SimplM (Id, CoreExpr)- mk_poly1 tvs_here var- = do { uniq <- getUniqueM- ; let poly_name = setNameUnique (idName var) uniq -- Keep same name- poly_ty = mkInfForAllTys tvs_here (idType var) -- But new type of course- poly_id = transferPolyIdInfo var tvs_here $ -- Note [transferPolyIdInfo] in GHC.Types.Id- mkLocalId poly_name (idMult var) poly_ty- ; return (poly_id, mkTyApps (Var poly_id) (mkTyVarTys tvs_here)) }- -- In the olden days, it was crucial to copy the occInfo of the original var,- -- because we were looking at occurrence-analysed but as yet unsimplified code!- -- In particular, we mustn't lose the loop breakers. BUT NOW we are looking- -- at already simplified code, so it doesn't matter- --- -- It's even right to retain single-occurrence or dead-var info:- -- Suppose we started with /\a -> let x = E in B- -- where x occurs once in B. Then we transform to:- -- let x' = /\a -> E in /\a -> let x* = x' a in B- -- where x* has an INLINE prag on it. Now, once x* is inlined,- -- the occurrences of x' will be just the occurrences originally- -- pinned on x.-- mk_poly2 :: Id -> [TyVar] -> CoreExpr -> (Id, CoreExpr)- mk_poly2 poly_id tvs_here rhs- = (poly_id `setIdUnfolding` unf, poly_rhs)- where- poly_rhs = mkLams tvs_here rhs- unf = mkUnfolding uf_opts InlineRhs is_top_lvl False poly_rhs Nothing-- -- We want the unfolding. Consider- -- let- -- x = /\a. let y = ... in Just y- -- in body- -- Then we float the y-binding out (via abstractFloats and addPolyBind)- -- but 'x' may well then be inlined in 'body' in which case we'd like the- -- opportunity to inline 'y' too.--{--Note [Abstract over coercions]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-If a coercion variable (g :: a ~ Int) is free in the RHS, then so is the-type variable a. Rather than sort this mess out, we simply bale out and abstract-wrt all the type variables if any of them are coercion variables.---Historical note: if you use let-bindings instead of a substitution, beware of this:-- -- Suppose we start with:- --- -- x = /\ a -> let g = G in E- --- -- Then we'll float to get- --- -- x = let poly_g = /\ a -> G- -- in /\ a -> let g = poly_g a in E- --- -- But now the occurrence analyser will see just one occurrence- -- of poly_g, not inside a lambda, so the simplifier will- -- PreInlineUnconditionally poly_g back into g! Badk to square 1!- -- (I used to think that the "don't inline lone occurrences" stuff- -- would stop this happening, but since it's the *only* occurrence,- -- PreInlineUnconditionally kicks in first!)- --- -- Solution: put an INLINE note on g's RHS, so that poly_g seems- -- to appear many times. (NB: mkInlineMe eliminates- -- such notes on trivial RHSs, so do it manually.)--************************************************************************-* *- prepareAlts-* *-************************************************************************--prepareAlts tries these things:--1. filterAlts: eliminate alternatives that cannot match, including- the DEFAULT alternative. Here "cannot match" includes knowledge- from GADTs--2. refineDefaultAlt: if the DEFAULT alternative can match only one- possible constructor, then make that constructor explicit.- e.g.- case e of x { DEFAULT -> rhs }- ===>- case e of x { (a,b) -> rhs }- where the type is a single constructor type. This gives better code- when rhs also scrutinises x or e.- See CoreUtils Note [Refine DEFAULT case alternatives]--3. combineIdenticalAlts: combine identical alternatives into a DEFAULT.- See CoreUtils Note [Combine identical alternatives], which also- says why we do this on InAlts not on OutAlts--4. Returns a list of the constructors that cannot holds in the- DEFAULT alternative (if there is one)--It's a good idea to do this stuff before simplifying the alternatives, to-avoid simplifying alternatives we know can't happen, and to come up with-the list of constructors that are handled, to put into the IdInfo of the-case binder, for use when simplifying the alternatives.--Eliminating the default alternative in (1) isn't so obvious, but it can-happen:--data Colour = Red | Green | Blue--f x = case x of- Red -> ..- Green -> ..- DEFAULT -> h x--h y = case y of- Blue -> ..- DEFAULT -> [ case y of ... ]--If we inline h into f, the default case of the inlined h can't happen.-If we don't notice this, we may end up filtering out *all* the cases-of the inner case y, which give us nowhere to go!--Note [Shadowing in prepareAlts]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Note that we pass case_bndr::InId to prepareAlts; an /InId/, not an-/OutId/. This is vital, because `refineDefaultAlt` uses `tys` to build-a new /InAlt/. If you pass an OutId, we'll end up appling the-substitution twice: disaster (#23012).--However this does mean that filling in the default alt might be-delayed by a simplifier cycle, because an InId has less info than an-OutId. Test simplCore/should_compile/simpl013 apparently shows this-up, although I'm not sure exactly how..--}--prepareAlts :: OutExpr -> InId -> [InAlt] -> SimplM ([AltCon], [InAlt])--- The returned alternatives can be empty, none are possible------ Note that case_bndr is an InId; see Note [Shadowing in prepareAlts]-prepareAlts scrut case_bndr alts- | Just (tc, tys) <- splitTyConApp_maybe (idType case_bndr)- = do { us <- getUniquesM- ; let (idcs1, alts1) = filterAlts tc tys imposs_cons alts- (yes2, alts2) = refineDefaultAlt us (idMult case_bndr) tc tys idcs1 alts1- -- The multiplicity on case_bndr's is the multiplicity of the- -- case expression The newly introduced patterns in- -- refineDefaultAlt must be scaled by this multiplicity- (yes3, idcs3, alts3) = combineIdenticalAlts idcs1 alts2- -- "idcs" stands for "impossible default data constructors"- -- i.e. the constructors that can't match the default case- ; when yes2 $ tick (FillInCaseDefault case_bndr)- ; when yes3 $ tick (AltMerge case_bndr)- ; return (idcs3, alts3) }-- | otherwise -- Not a data type, so nothing interesting happens- = return ([], alts)- where- imposs_cons = case scrut of- Var v -> otherCons (idUnfolding v)- _ -> []---{--************************************************************************-* *- mkCase-* *-************************************************************************--mkCase tries these things--* Note [Merge Nested Cases]-* Note [Eliminate Identity Case]-* Note [Scrutinee Constant Folding]--Note [Merge Nested Cases]-~~~~~~~~~~~~~~~~~~~~~~~~~- case e of b { ==> case e of b {- p1 -> rhs1 p1 -> rhs1- ... ...- pm -> rhsm pm -> rhsm- _ -> case b of b' { pn -> let b'=b in rhsn- pn -> rhsn ...- ... po -> let b'=b in rhso- po -> rhso _ -> let b'=b in rhsd- _ -> rhsd- }--which merges two cases in one case when -- the default alternative of-the outer case scrutises the same variable as the outer case. This-transformation is called Case Merging. It avoids that the same-variable is scrutinised multiple times.--Note [Eliminate Identity Case]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~- case e of ===> e- True -> True;- False -> False--and similar friends.--Note [Scrutinee Constant Folding]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~- case x op# k# of _ { ===> case x of _ {- a1# -> e1 (a1# inv_op# k#) -> e1- a2# -> e2 (a2# inv_op# k#) -> e2- ... ...- DEFAULT -> ed DEFAULT -> ed-- where (x op# k#) inv_op# k# == x--And similarly for commuted arguments and for some unary operations.--The purpose of this transformation is not only to avoid an arithmetic-operation at runtime but to allow other transformations to apply in cascade.--Example with the "Merge Nested Cases" optimization (from #12877):-- main = case t of t0- 0## -> ...- DEFAULT -> case t0 `minusWord#` 1## of t1- 0## -> ...- DEFAULT -> case t1 `minusWord#` 1## of t2- 0## -> ...- DEFAULT -> case t2 `minusWord#` 1## of _- 0## -> ...- DEFAULT -> ...-- becomes:-- main = case t of _- 0## -> ...- 1## -> ...- 2## -> ...- 3## -> ...- DEFAULT -> ...--There are some wrinkles--* Do not apply caseRules if there is just a single DEFAULT alternative- case e +# 3# of b { DEFAULT -> rhs }- If we applied the transformation here we would (stupidly) get- case a of b' { DEFAULT -> let b = e +# 3# in rhs }- and now the process may repeat, because that let will really- be a case.--* The type of the scrutinee might change. E.g.- case tagToEnum (x :: Int#) of (b::Bool)- False -> e1- True -> e2- ==>- case x of (b'::Int#)- DEFAULT -> e1- 1# -> e2--* The case binder may be used in the right hand sides, so we need- to make a local binding for it, if it is alive. e.g.- case e +# 10# of b- DEFAULT -> blah...b...- 44# -> blah2...b...- ===>- case e of b'- DEFAULT -> let b = b' +# 10# in blah...b...- 34# -> let b = 44# in blah2...b...-- Note that in the non-DEFAULT cases we know what to bind 'b' to,- whereas in the DEFAULT case we must reconstruct the original value.- But NB: we use b'; we do not duplicate 'e'.--* In dataToTag we might need to make up some fake binders;- see Note [caseRules for dataToTag] in GHC.Core.Opt.ConstantFold--}--mkCase, mkCase1, mkCase2, mkCase3- :: DynFlags- -> OutExpr -> OutId- -> OutType -> [OutAlt] -- Alternatives in standard (increasing) order- -> SimplM OutExpr------------------------------------------------------- 1. Merge Nested Cases-----------------------------------------------------mkCase dflags scrut outer_bndr alts_ty (Alt DEFAULT _ deflt_rhs : outer_alts)- | gopt Opt_CaseMerge dflags- , (ticks, Case (Var inner_scrut_var) inner_bndr _ inner_alts)- <- stripTicksTop tickishFloatable deflt_rhs- , inner_scrut_var == outer_bndr- = do { tick (CaseMerge outer_bndr)-- ; let wrap_alt (Alt con args rhs) = assert (outer_bndr `notElem` args)- (Alt con args (wrap_rhs rhs))- -- Simplifier's no-shadowing invariant should ensure- -- that outer_bndr is not shadowed by the inner patterns- wrap_rhs rhs = Let (NonRec inner_bndr (Var outer_bndr)) rhs- -- The let is OK even for unboxed binders,-- wrapped_alts | isDeadBinder inner_bndr = inner_alts- | otherwise = map wrap_alt inner_alts-- merged_alts = mergeAlts outer_alts wrapped_alts- -- NB: mergeAlts gives priority to the left- -- case x of- -- A -> e1- -- DEFAULT -> case x of- -- A -> e2- -- B -> e3- -- When we merge, we must ensure that e1 takes- -- precedence over e2 as the value for A!-- ; fmap (mkTicks ticks) $- mkCase1 dflags scrut outer_bndr alts_ty merged_alts- }- -- Warning: don't call mkCase recursively!- -- Firstly, there's no point, because inner alts have already had- -- mkCase applied to them, so they won't have a case in their default- -- Secondly, if you do, you get an infinite loop, because the bindCaseBndr- -- in munge_rhs may put a case into the DEFAULT branch!--mkCase dflags scrut bndr alts_ty alts = mkCase1 dflags scrut bndr alts_ty alts------------------------------------------------------- 2. Eliminate Identity Case-----------------------------------------------------mkCase1 _dflags scrut case_bndr _ alts@(Alt _ _ rhs1 : _) -- Identity case- | all identity_alt alts- = do { tick (CaseIdentity case_bndr)- ; return (mkTicks ticks $ re_cast scrut rhs1) }- where- ticks = concatMap (\(Alt _ _ rhs) -> stripTicksT tickishFloatable rhs) (tail alts)- identity_alt (Alt con args rhs) = check_eq rhs con args-- check_eq (Cast rhs co) con args -- See Note [RHS casts]- = not (any (`elemVarSet` tyCoVarsOfCo co) args) && check_eq rhs con args- check_eq (Tick t e) alt args- = tickishFloatable t && check_eq e alt args-- check_eq (Lit lit) (LitAlt lit') _ = lit == lit'- check_eq (Var v) _ _ | v == case_bndr = True- check_eq (Var v) (DataAlt con) args- | null arg_tys, null args = v == dataConWorkId con- -- Optimisation only- check_eq rhs (DataAlt con) args = cheapEqExpr' tickishFloatable rhs $- mkConApp2 con arg_tys args- check_eq _ _ _ = False-- arg_tys = tyConAppArgs (idType case_bndr)-- -- Note [RHS casts]- -- ~~~~~~~~~~~~~~~~- -- We've seen this:- -- case e of x { _ -> x `cast` c }- -- And we definitely want to eliminate this case, to give- -- e `cast` c- -- So we throw away the cast from the RHS, and reconstruct- -- it at the other end. All the RHS casts must be the same- -- if (all identity_alt alts) holds.- --- -- Don't worry about nested casts, because the simplifier combines them-- re_cast scrut (Cast rhs co) = Cast (re_cast scrut rhs) co- re_cast scrut _ = scrut--mkCase1 dflags scrut bndr alts_ty alts = mkCase2 dflags scrut bndr alts_ty alts------------------------------------------------------- 2. Scrutinee Constant Folding-----------------------------------------------------mkCase2 dflags scrut bndr alts_ty alts- | -- See Note [Scrutinee Constant Folding]- case alts of -- Not if there is just a DEFAULT alternative- [Alt DEFAULT _ _] -> False- _ -> True- , gopt Opt_CaseFolding dflags- , Just (scrut', tx_con, mk_orig) <- caseRules (targetPlatform dflags) scrut- = do { bndr' <- newId (fsLit "lwild") Many (exprType scrut')-- ; alts' <- mapMaybeM (tx_alt tx_con mk_orig bndr') alts- -- mapMaybeM: discard unreachable alternatives- -- See Note [Unreachable caseRules alternatives]- -- in GHC.Core.Opt.ConstantFold-- ; mkCase3 dflags scrut' bndr' alts_ty $- add_default (re_sort alts')- }-- | otherwise- = mkCase3 dflags scrut bndr alts_ty alts- where- -- We need to keep the correct association between the scrutinee and its- -- binder if the latter isn't dead. Hence we wrap rhs of alternatives with- -- "let bndr = ... in":- --- -- case v + 10 of y =====> case v of y- -- 20 -> e1 10 -> let y = 20 in e1- -- DEFAULT -> e2 DEFAULT -> let y = v + 10 in e2- --- -- Other transformations give: =====> case v of y'- -- 10 -> let y = 20 in e1- -- DEFAULT -> let y = y' + 10 in e2- --- -- This wrapping is done in tx_alt; we use mk_orig, returned by caseRules,- -- to construct an expression equivalent to the original one, for use- -- in the DEFAULT case-- tx_alt :: (AltCon -> Maybe AltCon) -> (Id -> CoreExpr) -> Id- -> CoreAlt -> SimplM (Maybe CoreAlt)- tx_alt tx_con mk_orig new_bndr (Alt con bs rhs)- = case tx_con con of- Nothing -> return Nothing- Just con' -> do { bs' <- mk_new_bndrs new_bndr con'- ; return (Just (Alt con' bs' rhs')) }- where- rhs' | isDeadBinder bndr = rhs- | otherwise = bindNonRec bndr orig_val rhs-- orig_val = case con of- DEFAULT -> mk_orig new_bndr- LitAlt l -> Lit l- DataAlt dc -> mkConApp2 dc (tyConAppArgs (idType bndr)) bs-- mk_new_bndrs new_bndr (DataAlt dc)- | not (isNullaryRepDataCon dc)- = -- For non-nullary data cons we must invent some fake binders- -- See Note [caseRules for dataToTag] in GHC.Core.Opt.ConstantFold- do { us <- getUniquesM- ; let (ex_tvs, arg_ids) = dataConRepInstPat us (idMult new_bndr) dc- (tyConAppArgs (idType new_bndr))- ; return (ex_tvs ++ arg_ids) }- mk_new_bndrs _ _ = return []-- re_sort :: [CoreAlt] -> [CoreAlt]- -- Sort the alternatives to re-establish- -- GHC.Core Note [Case expression invariants]- re_sort alts = sortBy cmpAlt alts-- add_default :: [CoreAlt] -> [CoreAlt]- -- See Note [Literal cases]- add_default (Alt (LitAlt {}) bs rhs : alts) = Alt DEFAULT bs rhs : alts- add_default alts = alts--{- Note [Literal cases]-~~~~~~~~~~~~~~~~~~~~~~~-If we have- case tagToEnum (a ># b) of- False -> e1- True -> e2--then caseRules for TagToEnum will turn it into- case tagToEnum (a ># b) of- 0# -> e1- 1# -> e2--Since the case is exhaustive (all cases are) we can convert it to- case tagToEnum (a ># b) of- DEFAULT -> e1- 1# -> e2--This may generate sligthtly better code (although it should not, since-all cases are exhaustive) and/or optimise better. I'm not certain that-it's necessary, but currently we do make this change. We do it here,-NOT in the TagToEnum rules (see "Beware" in Note [caseRules for tagToEnum]-in GHC.Core.Opt.ConstantFold)--}------------------------------------------------------- Catch-all----------------------------------------------------mkCase3 _dflags scrut bndr alts_ty alts- = return (Case scrut bndr alts_ty alts)---- See Note [Exitification] and Note [Do not inline exit join points] in--- GHC.Core.Opt.Exitify--- This lives here (and not in Id) because occurrence info is only valid on--- InIds, so it's crucial that isExitJoinId is only called on freshly--- occ-analysed code. It's not a generic function you can call anywhere.-isExitJoinId :: Var -> Bool-isExitJoinId id- = isJoinId id- && isOneOcc (idOccInfo id)- && occ_in_lam (idOccInfo id) == IsInsideLam--{--Note [Dead binders]-~~~~~~~~~~~~~~~~~~~~-Note that dead-ness is maintained by the simplifier, so that it is-accurate after simplification as well as before.---Note [Cascading case merge]-~~~~~~~~~~~~~~~~~~~~~~~~~~~-Case merging should cascade in one sweep, because it-happens bottom-up-- case e of a {- DEFAULT -> case a of b- DEFAULT -> case b of c {- DEFAULT -> e- A -> ea- B -> eb- C -> ec-==>- case e of a {- DEFAULT -> case a of b- DEFAULT -> let c = b in e- A -> let c = b in ea- B -> eb- C -> ec-==>- case e of a {- DEFAULT -> let b = a in let c = b in e- A -> let b = a in let c = b in ea- B -> let b = a in eb- C -> ec---However here's a tricky case that we still don't catch, and I don't-see how to catch it in one pass:-- case x of c1 { I# a1 ->- case a1 of c2 ->- 0 -> ...- DEFAULT -> case x of c3 { I# a2 ->- case a2 of ...--After occurrence analysis (and its binder-swap) we get this-- case x of c1 { I# a1 ->- let x = c1 in -- Binder-swap addition- case a1 of c2 ->- 0 -> ...- DEFAULT -> case x of c3 { I# a2 ->- case a2 of ...--When we simplify the inner case x, we'll see that-x=c1=I# a1. So we'll bind a2 to a1, and get-- case x of c1 { I# a1 ->- case a1 of c2 ->- 0 -> ...- DEFAULT -> case a1 of ...--This is correct, but we can't do a case merge in this sweep-because c2 /= a1. Reason: the binding c1=I# a1 went inwards-without getting changed to c1=I# c2.--I don't think this is worth fixing, even if I knew how. It'll-all come out in the next pass anyway.--}
compiler/GHC/Core/Opt/SpecConstr.hs view
@@ -1,3 +1,7 @@+{-# LANGUAGE CPP #-}+#if __GLASGOW_HASKELL__ < 905+{-# LANGUAGE PatternSynonyms #-}+#endif {- ToDo [Oct 2013] ~~~~~~~~~~~~~~~@@ -28,14 +32,17 @@ import GHC.Core.Subst import GHC.Core.Utils import GHC.Core.Unfold-import GHC.Core.FVs ( exprsFreeVarsList )+import GHC.Core.FVs ( exprsFreeVarsList, exprFreeVars ) import GHC.Core.Opt.Monad import GHC.Core.Opt.WorkWrap.Utils+import GHC.Core.Opt.OccurAnal( scrutBinderSwap_maybe ) import GHC.Core.DataCon+import GHC.Core.Class( classTyVars ) import GHC.Core.Coercion hiding( substCo ) import GHC.Core.Rules+import GHC.Core.Predicate ( typeDeterminesValue ) import GHC.Core.Type hiding ( substTy )-import GHC.Core.TyCon (TyCon, tyConUnique, tyConName )+import GHC.Core.TyCon (TyCon, tyConName ) import GHC.Core.Multiplicity import GHC.Core.Ppr ( pprParendExpr ) import GHC.Core.Make ( mkImpossibleExpr )@@ -45,6 +52,7 @@ import GHC.Types.Literal ( litIsLifted ) import GHC.Types.Id+import GHC.Types.Id.Info ( IdDetails(..) ) import GHC.Types.Id.Make ( voidArgId, voidPrimId ) import GHC.Types.Var.Env import GHC.Types.Var.Set@@ -55,6 +63,7 @@ import GHC.Types.Cpr import GHC.Types.Unique.Supply import GHC.Types.Unique.FM+import GHC.Types.Unique( hasKey ) import GHC.Data.Maybe ( orElse, catMaybes, isJust, isNothing ) import GHC.Data.Pair@@ -66,7 +75,6 @@ import GHC.Utils.Panic import GHC.Utils.Constants (debugIsOn) import GHC.Utils.Monad-import GHC.Utils.Trace import GHC.Builtin.Names ( specTyConKey ) @@ -75,7 +83,9 @@ import Control.Monad ( zipWithM ) import Data.List (nubBy, sortBy, partition, dropWhileEnd, mapAccumL )+import Data.Maybe( mapMaybe ) import Data.Ord( comparing )+import Data.Tuple {- -----------------------------------------------------@@ -372,11 +382,14 @@ So we want to spot the constructor application inside the cast. That's why we have the Cast case in argToPat -Note [Local recursive groups]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-For a *local* recursive group, we can see all the calls to the-function, so we seed the specialisation loop from the calls in the-body, not from the calls in the RHS. Consider:+Note [Seeding recursive groups]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+For a recursive group that is either+ * nested, or+ * top-level, but with no exported Ids+we can see all the calls to the function, so we seed the specialisation+loop from the calls in the body, and /not/ from the calls in the RHS.+Consider: bar m n = foo n (n,n) (n,n) (n,n) (n,n) where@@ -399,53 +412,43 @@ In a case like the above we end up never calling the original un-specialised function. (Although we still leave its code around just in case.) -However, if we find any boring calls in the body, including *unsaturated*-ones, such as+Wrinkles++* Boring calls. If we find any boring calls in the body, including+ *unsaturated* ones, such as letrec foo x y = ....foo... in map foo xs-then we will end up calling the un-specialised function, so then we *should*-use the calls in the un-specialised RHS as seeds. We call these-"boring call patterns", and callsToPats reports if it finds any of these.+ then we will end up calling the un-specialised function, so then we+ *should* use the calls in the un-specialised RHS as seeds. We call+ these "boring call patterns", and callsToNewPats reports if it finds+ any of these. Then 'specialise' unleashes the usage info from the+ un-specialised RHS. -Note [Seeding top-level recursive groups]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-This seeding is done in the binding for seed_calls in specRec.+* Exported Ids. `specialise` /also/ unleashes `si_mb_unspec`+ for exported Ids. That way we are sure to generate usage info from+ the /un-specialised/ RHS of an exported function. -1. If all the bindings in a top-level recursive group are local (not- exported), then all the calls are in the rest of the top-level- bindings. This means we can specialise with those call patterns- ONLY, and NOT with the RHSs of the recursive group (exactly like- Note [Local recursive groups])+More precisely: -2. But if any of the bindings are exported, the function may be called- with any old arguments, so (for lack of anything better) we specialise- based on- (a) the call patterns in the RHS- (b) the call patterns in the rest of the top-level bindings- NB: before Apr 15 we used (a) only, but Dimitrios had an example- where (b) was crucial, so I added that.- Adding (b) also improved nofib allocation results:- multiplier: 4% better- minimax: 2.8% better+* Always start from the calls in the body of the let or (for top level)+ calls in the rest of the module. See the body_calls in the call to+ `specialise` in `specNonRec`, and to `go` in `specRec`. -Actually in case (2), instead of using the calls from the RHS, it-would be better to specialise in the importing module. We'd need to-add an INLINABLE pragma to the function, and then it can be-specialised in the importing scope, just as is done for type classes-in GHC.Core.Opt.Specialise.specImports. This remains to be done (#10346).+* si_mb_unspec holds the usage from the unspecialised RHS.+ See `initSpecInfo`. -Note [Top-level recursive groups]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-To get the call usage information from "the rest of the top level-bindings" (c.f. Note [Seeding top-level recursive groups]), we work-backwards through the top-level bindings so we see the usage before we-get to the binding of the function. Before we can collect the usage-though, we go through all the bindings and add them to the-environment. This is necessary because usage is only tracked for-functions in the environment. These two passes are called- 'go' and 'goEnv'-in specConstrProgram. (Looks a bit revolting to me.)+* `specialise` will unleash si_mb_unspec, if+ - `callsToNewPats` reports "boring calls found", or+ - this is a top-level exported Id. +Historical note. At an earlier point, if a top-level Id was exported,+we used only seeds from the RHS, and /not/from the body. But Dimitrios+had an example where using call patterns from the body (the other defns+in the module) was crucial. And doing so improved nofib allocation results:+ multiplier: 4% better+ minimax: 2.8% better+In any case, it is easier to do!+ Note [Do not specialise diverging functions] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Specialising a function that just diverges is a waste of code.@@ -634,6 +637,52 @@ representing strict fields. See Note [Call-by-value for worker args] for why we do this. +Note [Specialising on dictionaries]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+In #21386, SpecConstr saw this call:++ $wgo 100# @.. ($fMonadStateT @.. @.. $fMonadIdentity)++where $wgo :: Int# -> forall m. Monad m => blah++You might think that the type-class Specialiser would have specialised+this, but there are good reasons why not: the Specialiser ran too early.+But regardless, SpecConstr can and should! It's easy:++* isValue: treat ($fblah d1 .. dn)+ like a constructor application.++* scApp: treat (op_sel d), a class method selection,+ like a case expression++* Float that dictionary application to top level, thus+ lvl = $fMonadStateT @.. @.. $fMonadIdentity+ so the call looks like+ ($wgo 100# @.. lvl)++ Why? This way dictionaries will appear as top level binders which we+ can trivially match in rules. (CSE runs before SpecConstr, so we+ may hope to common-up duplicate top-level dictionaries.)+ For the floating part, see the "Arguments" case of Note+ [Floating to the top] in GHC.Core.Opt.SetLevels.++ We could be more clever, perhaps, and generate a RULE like+ $wgo _ @.. ($fMonadStateT @.. @.. $fMonadIdentity) = $s$wgo ...+ but that would mean making argToPat able to spot dfun applications as+ well as constructor applications.++Wrinkles:++* This should all work perfectly fine for newtype classes. Mind you,+ currently newtype classes are inlined fairly agressively, but we+ may change that. And it would take extra code to exclude them, as+ well as being unnecessary.++* In isValue, we (mis-) use LambdaVal for this ($fblah d1 .. dn)+ because ConVal requires us to list the data constructor and+ fields, and that is (a) inconvenient and (b) unnecessary for+ class methods.+ ----------------------------------------------------- Stuff not yet handled -----------------------------------------------------@@ -718,35 +767,18 @@ specConstrProgram :: ModGuts -> CoreM ModGuts specConstrProgram guts- = do- dflags <- getDynFlags- us <- getUniqueSupplyM- (_, annos) <- getFirstAnnotations deserializeWithData guts- this_mod <- getModule- -- pprTraceM "specConstrInput" (ppr $ mg_binds guts)- let binds' = reverse $ fst $ initUs us $ do- -- Note [Top-level recursive groups]- (env, binds) <- goEnv (initScEnv dflags this_mod annos)- (mg_binds guts)- -- binds is identical to (mg_binds guts), except that the- -- binders on the LHS have been replaced by extendBndr- -- (SPJ this seems like overkill; I don't think the binders- -- will change at all; and we don't substitute in the RHSs anyway!!)- go env nullUsage (reverse binds)+ = do { env0 <- initScEnv guts+ ; us <- getUniqueSupplyM+ ; let (_usg, binds') = initUs_ us $+ scTopBinds env0 (mg_binds guts) - return (guts { mg_binds = binds' })- where- -- See Note [Top-level recursive groups]- goEnv env [] = return (env, [])- goEnv env (bind:binds) = do (env', bind') <- scTopBindEnv env bind- (env'', binds') <- goEnv env' binds- return (env'', bind' : binds')+ ; return (guts { mg_binds = binds' }) } - -- Arg list of bindings is in reverse order- go _ _ [] = return []- go env usg (bind:binds) = do (usg', bind') <- scTopBind env usg bind- binds' <- go env usg' binds- return (bind' : binds')+scTopBinds :: ScEnv -> [InBind] -> UniqSM (ScUsage, [OutBind])+scTopBinds _env [] = return (nullUsage, [])+scTopBinds env (b:bs) = do { (usg, b', bs') <- scBind TopLevel env b $+ (\env -> scTopBinds env bs)+ ; return (usg, b' ++ bs') } {- ************************************************************************@@ -831,24 +863,38 @@ the function is applied to a data constructor. -} -data ScEnv = SCE { sc_dflags :: DynFlags,- sc_uf_opts :: !UnfoldingOpts, -- ^ Unfolding options- sc_module :: !Module,- sc_size :: Maybe Int, -- Size threshold- -- Nothing => no limit+-- | Options for Specializing over constructors in Core.+data SpecConstrOpts = SpecConstrOpts+ { sc_max_args :: !Int+ -- ^ The threshold at which a worker-wrapper transformation used as part of+ -- this pass will no longer happen, measured in the number of arguments. - sc_count :: Maybe Int, -- Max # of specialisations for any one fn- -- Nothing => no limit- -- See Note [Avoiding exponential blowup]+ , sc_debug :: !Bool+ -- ^ Whether to print debug information - sc_recursive :: Int, -- Max # of specialisations over recursive type.- -- Stops ForceSpecConstr from diverging.+ , sc_uf_opts :: !UnfoldingOpts+ -- ^ Unfolding options - sc_keen :: Bool, -- Specialise on arguments that are known- -- constructors, even if they are not- -- scrutinised in the body. See- -- Note [Making SpecConstr keener]+ , sc_module :: !Module+ -- ^ The name of the module being processed + , sc_size :: !(Maybe Int)+ -- ^ Size threshold: Nothing => no limit++ , sc_count :: !(Maybe Int)+ -- ^ Max # of specialisations for any one function. Nothing => no limit.+ -- See Note [Avoiding exponential blowup] and decreaseSpecCount++ , sc_recursive :: !Int+ -- ^ Max # of specialisations over recursive type. Stops+ -- ForceSpecConstr from diverging.++ , sc_keen :: !Bool+ -- ^ Specialise on arguments that are known constructors, even if they are+ -- not scrutinised in the body. See Note [Making SpecConstr keener].+ }++data ScEnv = SCE { sc_opts :: !SpecConstrOpts, sc_force :: Bool, -- Force specialisation? -- See Note [Forcing specialisation] @@ -884,21 +930,36 @@ ppr LambdaVal = text "<Lambda>" ----------------------initScEnv :: DynFlags -> Module -> UniqFM Name SpecConstrAnnotation -> ScEnv-initScEnv dflags this_mod anns- = SCE { sc_dflags = dflags,+initScOpts :: DynFlags -> Module -> SpecConstrOpts+initScOpts dflags this_mod = SpecConstrOpts+ { sc_max_args = maxWorkerArgs dflags,+ sc_debug = hasPprDebug dflags, sc_uf_opts = unfoldingOpts dflags, sc_module = this_mod, sc_size = specConstrThreshold dflags, sc_count = specConstrCount dflags, sc_recursive = specConstrRecursive dflags,- sc_keen = gopt Opt_SpecConstrKeen dflags,- sc_force = False,- sc_subst = emptySubst,- sc_how_bound = emptyVarEnv,- sc_vals = emptyVarEnv,- sc_annotations = anns }+ sc_keen = gopt Opt_SpecConstrKeen dflags+ } +initScEnv :: ModGuts -> CoreM ScEnv+initScEnv guts+ = do { dflags <- getDynFlags+ ; (_, anns) <- getFirstAnnotations deserializeWithData guts+ ; this_mod <- getModule+ ; return (SCE { sc_opts = initScOpts dflags this_mod,+ sc_force = False,+ sc_subst = init_subst,+ sc_how_bound = emptyVarEnv,+ sc_vals = emptyVarEnv,+ sc_annotations = anns }) }+ where+ init_subst = mkEmptySubst $ mkInScopeSetBndrs (mg_binds guts)+ -- Acccount for top-level bindings that are not in dependency order;+ -- see Note [Glomming] in GHC.Core.Opt.OccurAnal+ -- Easiest thing is to bring all the top level binders into scope at once,+ -- as if at once, as if all the top-level decls were mutually recursive.+ data HowBound = RecFun -- These are the recursive functions for which -- we seek interesting call patterns @@ -912,20 +973,42 @@ scForce :: ScEnv -> Bool -> ScEnv scForce env b = env { sc_force = b } -lookupHowBound :: ScEnv -> Id -> Maybe HowBound+lookupHowBound :: ScEnv -> OutId -> Maybe HowBound lookupHowBound env id = lookupVarEnv (sc_how_bound env) id -scSubstId :: ScEnv -> Id -> CoreExpr+scSubstId :: ScEnv -> InId -> OutExpr scSubstId env v = lookupIdSubst (sc_subst env) v -scSubstTy :: ScEnv -> Type -> Type-scSubstTy env ty = substTy (sc_subst env) ty +-- Solo is only defined in base starting from ghc-9.2+#if !(MIN_VERSION_base(4, 16, 0))+data Solo a = Solo a+#endif++-- The Solo constructor was renamed to MkSolo in ghc 9.5+#if __GLASGOW_HASKELL__ < 905+pattern MkSolo :: a -> Solo a+pattern MkSolo a = Solo a+#endif++-- The !subst ensures that we force the selection `(sc_subst env)`, which avoids+-- retaining all of `env` when we only need `subst`. The `Solo` means that the+-- substitution itself is lazy, because that type is often discarded.+-- The callers of `scSubstTy` always force the result (to unpack the `Solo`)+-- so we get the desired effect: we leave a thunk, but retain only the subst,+-- not the whole env.+--+-- Fully forcing the result of `scSubstTy` regresses performance (#22102)+scSubstTy :: ScEnv -> InType -> Solo OutType+scSubstTy env ty =+ let !subst = sc_subst env+ in MkSolo (substTyUnchecked subst ty)+ scSubstCo :: ScEnv -> Coercion -> Coercion scSubstCo env co = substCo (sc_subst env) co zapScSubst :: ScEnv -> ScEnv-zapScSubst env = env { sc_subst = zapSubstEnv (sc_subst env) }+zapScSubst env = env { sc_subst = zapSubst (sc_subst env) } extendScInScope :: ScEnv -> [Var] -> ScEnv -- Bring the quantified variables into scope@@ -991,8 +1074,8 @@ = (env2, alt_bndrs') where live_case_bndr = not (isDeadBinder case_bndr)- env1 | Var v <- stripTicksTopE (const True) scrut- = extendValEnv env v cval+ env1 | Just (v, mco) <- scrutBinderSwap_maybe scrut+ , isReflMCo mco = extendValEnv env v cval | otherwise = env -- See Note [Add scrutinee to ValueEnv too] env2 | live_case_bndr = extendValEnv env1 case_bndr cval | otherwise = env1@@ -1016,14 +1099,21 @@ decreaseSpecCount :: ScEnv -> Int -> ScEnv -- See Note [Avoiding exponential blowup]-decreaseSpecCount env n_specs+decreaseSpecCount env _n_specs = env { sc_force = False -- See Note [Forcing specialisation]- , sc_count = case sc_count env of- Nothing -> Nothing- Just n -> Just (n `div` (n_specs + 1)) }- -- The "+1" takes account of the original function;- -- See Note [Avoiding exponential blowup]+ , sc_opts = opts { sc_count = case sc_count opts of+ Nothing -> Nothing+ Just n -> Just $! dec n+ }+ }+ where+ opts = sc_opts env+ dec n = n `div` 2 -- See Note [Avoiding exponential blowup] + -- Or: n `div` (n_specs + 1)+ -- See the historical note part of Note [Avoiding exponential blowup]+ -- The "+1" takes account of the original function;+ --------------------------------------------------- -- See Note [Forcing specialisation] ignoreType :: ScEnv -> Type -> Bool@@ -1048,14 +1138,13 @@ forceSpecArgTy :: ScEnv -> Type -> Bool forceSpecArgTy env ty- | Just ty' <- coreView ty = forceSpecArgTy env ty'+ | isFunTy ty+ = False -forceSpecArgTy env ty | Just (tycon, tys) <- splitTyConApp_maybe ty- , tycon /= funTyCon- = tyConUnique tycon == specTyConKey- || lookupUFM (sc_annotations env) (tyConName tycon) == Just ForceSpecConstr- || any (forceSpecArgTy env) tys+ = tycon `hasKey` specTyConKey+ || lookupUFM (sc_annotations env) (tyConName tycon) == Just ForceSpecConstr+ || any (forceSpecArgTy env) tys forceSpecArgTy _ _ = False @@ -1079,6 +1168,10 @@ and 'b' with 'c' in the code. The use of 'b' in the ValueEnv came from outside the case. See #4908 for the live example. +It's very like the binder-swap story, so we use scrutBinderSwap_maybe+to identify suitable scrutinees -- but only if there is no cast+(isReflMCo) because that's all that the ValueEnv allows.+ Note [Avoiding exponential blowup] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The sc_count field of the ScEnv says how many times we are prepared to@@ -1098,9 +1191,20 @@ specialisation of each, because we also have the original we'll get 2^n copies of $j3, which is not good. -So when recursively specialising we divide the sc_count by the number of-copies we are making at this level, including the original.+So when recursively specialising we divide the sc_count (the maximum+number of specialisations, in the ScEnv) by two. You might think that+gives us n*(n/2)*(n/4)... copies of the innnermost thing, which is+still exponential the depth. But we use integer division, rounding+down, so if the starting sc_count is 3, we'll get 3 -> 1 -> 0, and+stop. In fact, simply subtracting 1 would be good enough, for the same+reason. +Historical note: in the past we divided by (n_specs+1), where n_specs+is the number of specialisations at this level; but that gets us down+to zero jolly quickly, which I found led to some regressions. (An+example is nofib/spectral/fibheaps, the getMin' function inside the+outer function $sfibToList, which has several interesting call+patterns.) ************************************************************************ * *@@ -1118,8 +1222,8 @@ scu_occs :: !(IdEnv ArgOcc) -- Information on argument occurrences } -- The domain is OutIds -type CallEnv = IdEnv [Call]-data Call = Call Id [CoreArg] ValueEnv+type CallEnv = IdEnv [Call] -- Domain is OutIds+data Call = Call OutId [CoreArg] ValueEnv -- The arguments of the call, together with the -- env giving the constructor bindings at the call site -- We keep the function mainly for debug output@@ -1141,6 +1245,9 @@ combineCalls :: CallEnv -> CallEnv -> CallEnv combineCalls = plusVarEnv_C (++) +delCallsFor :: ScUsage -> [Var] -> ScUsage+delCallsFor env bndrs = env { scu_calls = scu_calls env `delVarEnvList` bndrs }+ combineUsage :: ScUsage -> ScUsage -> ScUsage combineUsage u1 u2 = SCU { scu_calls = combineCalls (scu_calls u1) (scu_calls u2), scu_occs = plusVarEnv_C combineOcc (scu_occs u1) (scu_occs u2) }@@ -1158,8 +1265,21 @@ | UnkOcc -- Used in some unknown way | ScrutOcc -- See Note [ScrutOcc]- (DataConEnv [ArgOcc]) -- How the sub-components are used+ (DataConEnv [ArgOcc])+ -- [ArgOcc]: how the sub-components are used +deadArgOcc :: ArgOcc -> Bool+deadArgOcc (ScrutOcc {}) = False+deadArgOcc UnkOcc = False+deadArgOcc NoOcc = True++specialisableArgOcc :: ArgOcc -> Bool+-- | Does this occurence represent one worth specializing for.+specialisableArgOcc UnkOcc = False+specialisableArgOcc NoOcc = False+specialisableArgOcc (ScrutOcc {}) = True++ {- Note [ScrutOcc] ~~~~~~~~~~~~~~~~~~ An occurrence of ScrutOcc indicates that the thing, or a `cast` version of the thing,@@ -1184,6 +1304,9 @@ ppr NoOcc = text "no-occ" evalScrutOcc :: ArgOcc+-- We use evalScrutOcc for+-- - mkVarUsage: applied functions+-- - scApp: dicts that are the argument of a classop evalScrutOcc = ScrutOcc emptyUFM -- Experimentally, this version of combineOcc makes ScrutOcc "win", so@@ -1223,6 +1346,121 @@ creates specialised versions of functions. -} +scBind :: TopLevelFlag -> ScEnv -> InBind+ -> (ScEnv -> UniqSM (ScUsage, a)) -- Specialise the scope of the binding+ -> UniqSM (ScUsage, [OutBind], a)+scBind top_lvl env (NonRec bndr rhs) do_body+ | isTyVar bndr -- Type-lets may be created by doBeta+ = do { (final_usage, body') <- do_body (extendScSubst env bndr rhs)+ ; return (final_usage, [], body') }++ | not (isTopLevel top_lvl) -- Nested non-recursive value binding+ -- See Note [Specialising local let bindings]+ = do { let (body_env, bndr') = extendBndr env bndr+ -- Not necessary at top level; but here we are nested++ ; rhs_info <- scRecRhs env (bndr',rhs)++ ; let body_env2 = extendHowBound body_env [bndr'] RecFun+ rhs' = ri_new_rhs rhs_info+ body_env3 = extendValEnv body_env2 bndr' (isValue (sc_vals env) rhs')++ ; (body_usg, body') <- do_body body_env3++ -- Now make specialised copies of the binding,+ -- based on calls in body_usg+ ; (spec_usg, specs) <- specNonRec env (scu_calls body_usg) rhs_info+ -- NB: For non-recursive bindings we inherit sc_force flag from+ -- the parent function (see Note [Forcing specialisation])++ -- Specialized + original binding+ ; let spec_bnds = [NonRec b r | (b,r) <- ruleInfoBinds rhs_info specs]+ bind_usage = (body_usg `delCallsFor` [bndr'])+ `combineUsage` spec_usg -- Note [spec_usg includes rhs_usg]++ ; return (bind_usage, spec_bnds, body')+ }++ | otherwise -- Top-level, non-recursive value binding+ -- At top level we do not specialise non-recursive bindings; that+ -- is, we do not call specNonRec, passing the calls from the body.+ -- The original paper only specialised /recursive/ bindings, but+ -- we later started specialising nested non-recursive bindings:+ -- see Note [Specialising local let bindings]+ --+ -- I tried always specialising non-recursive top-level bindings too,+ -- but found some regressions (see !8135). So I backed off.+ = do { (rhs_usage, rhs') <- scExpr env rhs++ -- At top level, we've already put all binders into scope; see initScEnv+ -- Hence no need to call `extendBndr`. But we still want to+ -- extend the `ValueEnv` to record the value of this binder.+ ; let body_env = extendValEnv env bndr (isValue (sc_vals env) rhs')+ ; (body_usage, body') <- do_body body_env++ ; return (rhs_usage `combineUsage` body_usage, [NonRec bndr rhs'], body') }++scBind top_lvl env (Rec prs) do_body+ | isTopLevel top_lvl+ , Just threshold <- sc_size (sc_opts env)+ , not force_spec+ , not (all (couldBeSmallEnoughToInline (sc_uf_opts (sc_opts env)) threshold) rhss)+ = -- Do no specialisation if the RHSs are too big+ -- ToDo: I'm honestly not sure of the rationale of this size-testing, nor+ -- why it only applies at top level. But that's the way it has been+ -- for a while. See #21456.+ do { (body_usg, body') <- do_body rhs_env2+ ; (rhs_usgs, rhss') <- mapAndUnzipM (scExpr env) rhss+ ; let all_usg = (combineUsages rhs_usgs `combineUsage` body_usg)+ `delCallsFor` bndrs'+ bind' = Rec (bndrs' `zip` rhss')+ ; return (all_usg, [bind'], body') }++ | otherwise+ = do { rhs_infos <- mapM (scRecRhs rhs_env2) (bndrs' `zip` rhss)+ ; (body_usg, body') <- do_body rhs_env2++ ; (spec_usg, specs) <- specRec (scForce rhs_env2 force_spec)+ (scu_calls body_usg) rhs_infos+ -- Do not unconditionally generate specialisations from rhs_usgs+ -- Instead use them only if we find an unspecialised call+ -- See Note [Seeding recursive groups]++ ; let all_usg = (spec_usg `combineUsage` body_usg) -- Note [spec_usg includes rhs_usg]+ `delCallsFor` bndrs'+ bind' = Rec (concat (zipWithEqual "scExpr'" ruleInfoBinds rhs_infos specs))+ -- zipWithEqual: length of returned [SpecInfo]+ -- should be the same as incoming [RhsInfo]++ ; return (all_usg, [bind'], body') }+ where+ (bndrs,rhss) = unzip prs+ force_spec = any (forceSpecBndr env) bndrs -- Note [Forcing specialisation]++ (rhs_env1,bndrs') | isTopLevel top_lvl = (env, bndrs)+ | otherwise = extendRecBndrs env bndrs+ -- At top level, we've already put all binders into scope; see initScEnv++ rhs_env2 = extendHowBound rhs_env1 bndrs' RecFun++{- Note [Specialising local let bindings]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+It is not uncommon to find this++ let $j = \x. <blah> in ...$j True...$j True...++Here $j is an arbitrary let-bound function, but it often comes up for+join points. We might like to specialise $j for its call patterns.+Notice the difference from a letrec, where we look for call patterns+in the *RHS* of the function. Here we look for call patterns in the+*body* of the let.++At one point I predicated this on the RHS mentioning the outer+recursive function, but that's not essential and might even be+harmful. I'm not sure.+-}++------------------------ scExpr, scExpr' :: ScEnv -> CoreExpr -> UniqSM (ScUsage, CoreExpr) -- The unique supply is needed when we invent -- a new name for the specialised function and its args@@ -1233,7 +1471,9 @@ Var v' -> return (mkVarUsage env v' [], Var v') e' -> scExpr (zapScSubst env) e' -scExpr' env (Type t) = return (nullUsage, Type (scSubstTy env t))+scExpr' env (Type t) =+ let !(MkSolo ty') = scSubstTy env t+ in return (nullUsage, Type ty') scExpr' env (Coercion c) = return (nullUsage, Coercion (scSubstCo env c)) scExpr' _ e@(Lit {}) = return (nullUsage, e) scExpr' env (Tick t e) = do (usg, e') <- scExpr env e@@ -1247,6 +1487,11 @@ (usg, e') <- scExpr env' e return (usg, Lam b' e') +scExpr' env (Let bind body)+ = do { (final_usage, binds', body') <- scBind NotTopLevel env bind $+ (\env -> scExpr env body)+ ; return (final_usage, mkLets binds' body') }+ scExpr' env (Case scrut b ty alts) = do { (scrut_usg, scrut') <- scExpr env scrut ; case isValue (sc_vals env) scrut' of@@ -1256,7 +1501,7 @@ where sc_con_app con args scrut' -- Known constructor; simplify = do { let Alt _ bs rhs = findAlt con alts- `orElse` Alt DEFAULT [] (mkImpossibleExpr ty)+ `orElse` Alt DEFAULT [] (mkImpossibleExpr ty "SpecConstr") alt_env' = extendScSubstList env ((b,scrut') : bs `zip` trimConArgs con args) ; scExpr alt_env' rhs } @@ -1264,100 +1509,72 @@ = do { let (alt_env,b') = extendBndrWith RecArg env b -- Record RecArg for the components - ; (alt_usgs, alt_occs, alts')- <- mapAndUnzip3M (sc_alt alt_env scrut' b') alts+ ; (alt_usgs, alt_occs, alts') <- mapAndUnzip3M (sc_alt alt_env scrut' b') alts ; let scrut_occ = foldr combineOcc NoOcc alt_occs scrut_usg' = setScrutOcc env scrut_usg scrut' scrut_occ -- The combined usage of the scrutinee is given- -- by scrut_occ, which is passed to scScrut, which+ -- by scrut_occ, which is passed to setScrutOcc, which -- in turn treats a bare-variable scrutinee specially+ ; let !(MkSolo ty') = scSubstTy env ty ; return (foldr combineUsage scrut_usg' alt_usgs,- Case scrut' b' (scSubstTy env ty) alts') }+ Case scrut' b' ty' alts') } + single_alt = isSingleton alts+ sc_alt env scrut' b' (Alt con bs rhs) = do { let (env1, bs1) = extendBndrsWith RecArg env bs (env2, bs2) = extendCaseBndrs env1 scrut' b' con bs1 ; (usg, rhs') <- scExpr env2 rhs ; let (usg', b_occ:arg_occs) = lookupOccs usg (b':bs2) scrut_occ = case con of- DataAlt dc -> ScrutOcc (unitUFM dc arg_occs)- _ -> ScrutOcc emptyUFM+ DataAlt dc -- See Note [Do not specialise evals]+ | not (single_alt && all deadArgOcc arg_occs)+ -> ScrutOcc (unitUFM dc arg_occs)+ _ -> UnkOcc ; return (usg', b_occ `combineOcc` scrut_occ, Alt con bs2 rhs') } -scExpr' env (Let (NonRec bndr rhs) body)- | isTyVar bndr -- Type-lets may be created by doBeta- = scExpr' (extendScSubst env bndr rhs) body - | otherwise- = do { let (body_env, bndr') = extendBndr env bndr- ; rhs_info <- scRecRhs env (bndr',rhs) - ; let body_env2 = extendHowBound body_env [bndr'] RecFun- -- See Note [Local let bindings]- rhs' = ri_new_rhs rhs_info- body_env3 = extendValEnv body_env2 bndr' (isValue (sc_vals env) rhs')-- ; (body_usg, body') <- scExpr body_env3 body-- -- NB: For non-recursive bindings we inherit sc_force flag from- -- the parent function (see Note [Forcing specialisation])- ; (spec_usg, specs) <- specNonRec env body_usg rhs_info-- -- Specialized + original binding- ; let spec_bnds = mkLets [NonRec b r | (b,r) <- ruleInfoBinds rhs_info specs] body'- -- ; pprTraceM "spec_bnds" $ (ppr spec_bnds)-- ; return (body_usg { scu_calls = scu_calls body_usg `delVarEnv` bndr' }- `combineUsage` spec_usg, -- Note [spec_usg includes rhs_usg]- spec_bnds- )- }----- A *local* recursive group: see Note [Local recursive groups]-scExpr' env (Let (Rec prs) body)- = do { let (bndrs,rhss) = unzip prs- (rhs_env1,bndrs') = extendRecBndrs env bndrs- rhs_env2 = extendHowBound rhs_env1 bndrs' RecFun- force_spec = any (forceSpecBndr env) bndrs'- -- Note [Forcing specialisation]-- ; rhs_infos <- mapM (scRecRhs rhs_env2) (bndrs' `zip` rhss)- ; (body_usg, body') <- scExpr rhs_env2 body+{- Note [Do not specialise evals]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider+ f x y = case x of I# _ ->+ if y>1 then f x (y-1) else x - -- NB: start specLoop from body_usg- ; (spec_usg, specs) <- specRec NotTopLevel (scForce rhs_env2 force_spec)- body_usg rhs_infos- -- Do not unconditionally generate specialisations from rhs_usgs- -- Instead use them only if we find an unspecialised call- -- See Note [Local recursive groups]+Here `x` is scrutinised by a case, but only in an eval-like way; the+/component/ of the I# is unused. We don't want to specialise this+function, even if we find a call (f (I# z)), because nothing is gained+ * No case branches are discarded+ * No allocation in removed+The specialised version would take an unboxed Int#, pass it along,+and rebox it at the end. - ; let all_usg = spec_usg `combineUsage` body_usg -- Note [spec_usg includes rhs_usg]- bind' = Rec (concat (zipWithEqual "scExpr'" ruleInfoBinds rhs_infos specs))- -- zipWithEqual: length of returned [SpecInfo]- -- should be the same as incoming [RhsInfo]+In fact this can cause significant regression. In #21763 we had:+like+ f = ... case x of x' { I# n ->+ join j y = rhs+ in ...jump j x'... - ; return (all_usg { scu_calls = scu_calls all_usg `delVarEnvList` bndrs' },- Let bind' body') }+Now if we specialise `j` for the argument `I# n`, we'll end up reboxing+it in `j`, without even removing an allocation from the call site. -{--Note [Local let bindings]-~~~~~~~~~~~~~~~~~~~~~~~~~-It is not uncommon to find this+Reboxing is always a worry. But here we can ameliorate the problem as+follows. - let $j = \x. <blah> in ...$j True...$j True...+* In scExpr (Case ...), for a /single-alternative/ case expression, in+ which the pattern binders are all unused, we build a UnkOcc for+ the scrutinee, not one that maps the data constructor; we don't treat+ this occurrence as a reason for specialisation. -Here $j is an arbitrary let-bound function, but it often comes up for-join points. We might like to specialise $j for its call patterns.-Notice the difference from a letrec, where we look for call patterns-in the *RHS* of the function. Here we look for call patterns in the-*body* of the let.+* Conveniently, SpecConstr is doing its own occurrence analysis, so+ the "unused" bit is just looking for NoOcc -At one point I predicated this on the RHS mentioning the outer-recursive function, but that's not essential and might even be-harmful. I'm not sure.+* Note that if we have+ f x = case x of { True -> e1; False -> e2 }+ then even though the pattern binders are unused (there are none), it is+ still worth specialising on x. Hence the /single-alternative/ guard. -} scApp :: ScEnv -> (InExpr, [InExpr]) -> UniqSM (ScUsage, CoreExpr)@@ -1371,8 +1588,15 @@ fn'@(Lam {}) -> scExpr (zapScSubst env) (doBeta fn' args') -- Do beta-reduction and try again - Var fn' -> return (arg_usg `combineUsage` mkVarUsage env fn' args',+ Var fn' -> return (arg_usg' `combineUsage` mkVarUsage env fn' args', mkApps (Var fn') args')+ where+ -- arg_usg': see Note [Specialising on dictionaries]+ arg_usg' | Just cls <- isClassOpId_maybe fn'+ , dict_arg : _ <- dropList (classTyVars cls) args'+ = setScrutOcc env arg_usg dict_arg evalScrutOcc+ | otherwise+ = arg_usg other_fn' -> return (arg_usg, mkApps other_fn' args') } -- NB: doing this ignores any usage info from the substituted@@ -1380,7 +1604,6 @@ -- we can fix it. where doBeta :: OutExpr -> [OutExpr] -> OutExpr- -- ToDo: adjust for System IF doBeta (Lam bndr body) (arg : args) = Let (NonRec bndr arg) (doBeta body args) doBeta fn args = mkApps fn args @@ -1403,56 +1626,10 @@ , scu_occs = unitVarEnv fn arg_occ } Nothing -> nullUsage where- -- I rather think we could use UnkOcc all the time arg_occ | null args = UnkOcc | otherwise = evalScrutOcc -----------------------scTopBindEnv :: ScEnv -> CoreBind -> UniqSM (ScEnv, CoreBind)-scTopBindEnv env (Rec prs)- = do { let (rhs_env1,bndrs') = extendRecBndrs env bndrs- rhs_env2 = extendHowBound rhs_env1 bndrs RecFun-- prs' = zip bndrs' rhss- ; return (rhs_env2, Rec prs') }- where- (bndrs,rhss) = unzip prs--scTopBindEnv env (NonRec bndr rhs)- = do { let (env1, bndr') = extendBndr env bndr- env2 = extendValEnv env1 bndr' (isValue (sc_vals env) rhs)- ; return (env2, NonRec bndr' rhs) }-------------------------scTopBind :: ScEnv -> ScUsage -> CoreBind -> UniqSM (ScUsage, CoreBind)--scTopBind env body_usage (Rec prs)- | Just threshold <- sc_size env- , not force_spec- , not (all (couldBeSmallEnoughToInline (sc_uf_opts env) threshold) rhss)- -- No specialisation- = -- pprTrace "scTopBind: nospec" (ppr bndrs) $- do { (rhs_usgs, rhss') <- mapAndUnzipM (scExpr env) rhss- ; return (body_usage `combineUsage` combineUsages rhs_usgs, Rec (bndrs `zip` rhss')) }-- | otherwise -- Do specialisation- = do { rhs_infos <- mapM (scRecRhs env) prs-- ; (spec_usage, specs) <- specRec TopLevel (scForce env force_spec)- body_usage rhs_infos-- ; return (body_usage `combineUsage` spec_usage,- Rec (concat (zipWith ruleInfoBinds rhs_infos specs))) }- where- (bndrs,rhss) = unzip prs- force_spec = any (forceSpecBndr env) bndrs- -- Note [Forcing specialisation]--scTopBind env usage (NonRec bndr rhs) -- Oddly, we don't seem to specialise top-level non-rec functions- = do { (rhs_usg', rhs') <- scExpr env rhs- ; return (usage `combineUsage` rhs_usg', NonRec bndr rhs') }------------------------ scRecRhs :: ScEnv -> (OutId, InExpr) -> UniqSM RhsInfo scRecRhs env (bndr,rhs) = do { let (arg_bndrs,body) = collectBinders rhs@@ -1499,7 +1676,8 @@ } data SpecInfo -- Info about specialisations for a particular Id- = SI { si_specs :: [OneSpec] -- The specialisations we have generated+ = SI { si_specs :: [OneSpec] -- The specialisations we have+ -- generated for this function , si_n_specs :: Int -- Length of si_specs; used for numbering them @@ -1510,7 +1688,7 @@ -- RHS usage (which has not yet been -- unleashed) -- Nothing => we have- -- See Note [Local recursive groups]+ -- See Note [Seeding recursive groups] -- See Note [spec_usg includes rhs_usg] -- One specialisation: Rule plus definition@@ -1520,84 +1698,84 @@ , os_id :: OutId -- Spec id , os_rhs :: OutExpr } -- Spec rhs -noSpecInfo :: SpecInfo-noSpecInfo = SI { si_specs = [], si_n_specs = 0, si_mb_unspec = Nothing }+initSpecInfo :: RhsInfo -> SpecInfo+initSpecInfo (RI { ri_rhs_usg = rhs_usg })+ = SI { si_specs = [], si_n_specs = 0, si_mb_unspec = Just rhs_usg }+ -- si_mb_unspec: add in rhs_usg if there are any boring calls,+ -- or if the bndr is exported ---------------------- specNonRec :: ScEnv- -> ScUsage -- Body usage+ -> CallEnv -- Calls in body -> RhsInfo -- Structure info usage info for un-specialised RHS -> UniqSM (ScUsage, SpecInfo) -- Usage from RHSs (specialised and not) -- plus details of specialisations -specNonRec env body_usg rhs_info- = specialise env (scu_calls body_usg) rhs_info- (noSpecInfo { si_mb_unspec = Just (ri_rhs_usg rhs_info) })+specNonRec env body_calls rhs_info+ = specialise env body_calls rhs_info (initSpecInfo rhs_info) -----------------------specRec :: TopLevelFlag -> ScEnv- -> ScUsage -- Body usage+specRec :: ScEnv+ -> CallEnv -- Calls in body -> [RhsInfo] -- Structure info and usage info for un-specialised RHSs -> UniqSM (ScUsage, [SpecInfo]) -- Usage from all RHSs (specialised and not) -- plus details of specialisations -specRec top_lvl env body_usg rhs_infos- = go 1 seed_calls nullUsage init_spec_infos+specRec env body_calls rhs_infos+ = go 1 body_calls nullUsage (map initSpecInfo rhs_infos)+ -- body_calls: see Note [Seeding recursive groups]+ -- NB: 'go' always calls 'specialise' once, which in turn unleashes+ -- si_mb_unspec if there are any boring calls in body_calls,+ -- or if any of the Id(s) are exported where- (seed_calls, init_spec_infos) -- Note [Seeding top-level recursive groups]- | isTopLevel top_lvl- , any (isExportedId . ri_fn) rhs_infos -- Seed from body and RHSs- = (all_calls, [noSpecInfo | _ <- rhs_infos])- | otherwise -- Seed from body only- = (calls_in_body, [noSpecInfo { si_mb_unspec = Just (ri_rhs_usg ri) }- | ri <- rhs_infos])-- calls_in_body = scu_calls body_usg- calls_in_rhss = foldr (combineCalls . scu_calls . ri_rhs_usg) emptyVarEnv rhs_infos- all_calls = calls_in_rhss `combineCalls` calls_in_body+ opts = sc_opts env -- Loop, specialising, until you get no new specialisations- go :: Int -- Which iteration of the "until no new specialisations"- -- loop we are on; first iteration is 1- -> CallEnv -- Seed calls- -- Two accumulating parameters:- -> ScUsage -- Usage from earlier specialisations- -> [SpecInfo] -- Details of specialisations so far- -> UniqSM (ScUsage, [SpecInfo])+ go, go_again :: Int -- Which iteration of the "until no new specialisations"+ -- loop we are on; first iteration is 1+ -> CallEnv -- Seed calls+ -- Two accumulating parameters:+ -> ScUsage -- Usage from earlier specialisations+ -> [SpecInfo] -- Details of specialisations so far+ -> UniqSM (ScUsage, [SpecInfo]) go n_iter seed_calls usg_so_far spec_infos+ = -- pprTrace "specRec3" (vcat [ text "bndrs" <+> ppr (map ri_fn rhs_infos)+ -- , text "iteration" <+> int n_iter+ -- , text "spec_infos" <+> ppr (map (map os_pat . si_specs) spec_infos)+ -- ]) $+ do { specs_w_usg <- zipWithM (specialise env seed_calls) rhs_infos spec_infos+ ; let (extra_usg_s, all_spec_infos) = unzip specs_w_usg+ extra_usg = combineUsages extra_usg_s+ all_usg = usg_so_far `combineUsage` extra_usg+ new_calls = scu_calls extra_usg+ ; go_again n_iter new_calls all_usg all_spec_infos }++ -- go_again deals with termination+ go_again n_iter seed_calls usg_so_far spec_infos | isEmptyVarEnv seed_calls- = -- pprTrace "specRec1" (vcat [ ppr (map ri_fn rhs_infos)- -- , ppr seed_calls- -- , ppr body_usg ]) $- return (usg_so_far, spec_infos)+ = return (usg_so_far, spec_infos) -- Limit recursive specialisation -- See Note [Limit recursive specialisation]- | n_iter > sc_recursive env -- Too many iterations of the 'go' loop- , sc_force env || isNothing (sc_count env)+ | n_iter > sc_recursive opts -- Too many iterations of the 'go' loop+ , sc_force env || isNothing (sc_count opts) -- If both of these are false, the sc_count -- threshold will prevent non-termination , any ((> the_limit) . si_n_specs) spec_infos- = -- pprTrace "specRec2" (ppr (map (map os_pat . si_specs) spec_infos)) $- return (usg_so_far, spec_infos)+ = -- Give up on specialisation, but don't forget to include the rhs_usg+ -- for the unspecialised function, since it may now be called+ -- pprTrace "specRec2" (ppr (map (map os_pat . si_specs) spec_infos)) $+ let rhs_usgs = combineUsages (mapMaybe si_mb_unspec spec_infos)+ in return (usg_so_far `combineUsage` rhs_usgs, spec_infos) | otherwise- = -- pprTrace "specRec3" (vcat [ text "bndrs" <+> ppr (map ri_fn rhs_infos)- -- , text "iteration" <+> int n_iter- -- , text "spec_infos" <+> ppr (map (map os_pat . si_specs) spec_infos)- -- ]) $- do { specs_w_usg <- zipWithM (specialise env seed_calls) rhs_infos spec_infos- ; let (extra_usg_s, new_spec_infos) = unzip specs_w_usg- extra_usg = combineUsages extra_usg_s- all_usg = usg_so_far `combineUsage` extra_usg- ; go (n_iter + 1) (scu_calls extra_usg) all_usg new_spec_infos }+ = go (n_iter + 1) seed_calls usg_so_far spec_infos -- See Note [Limit recursive specialisation]- the_limit = case sc_count env of+ the_limit = case sc_count opts of Nothing -> 10 -- Ugh! Just max -> max - ---------------------- specialise :: ScEnv@@ -1620,14 +1798,12 @@ spec_info@(SI { si_specs = specs, si_n_specs = spec_count , si_mb_unspec = mb_unspec }) | isDeadEndId fn -- Note [Do not specialise diverging functions]- -- and do not generate specialisation seeds from its RHS+ -- /and/ do not generate specialisation seeds from its RHS = -- pprTrace "specialise bot" (ppr fn) $ return (nullUsage, spec_info) | not (isNeverActive (idInlineActivation fn)) -- See Note [Transfer activation]- --- -- -- Don't specialise OPAQUE things, see Note [OPAQUE pragma]. -- Since OPAQUE things are always never-active (see -- GHC.Parser.PostProcess.mkOpaquePragma) this guard never fires for@@ -1635,16 +1811,19 @@ , not (null arg_bndrs) -- Only specialise functions , Just all_calls <- lookupVarEnv bind_calls fn -- Some calls to it = -- pprTrace "specialise entry {" (ppr fn <+> ppr all_calls) $- do { (boring_call, new_pats) <- callsToNewPats env fn spec_info arg_occs all_calls+ do { (boring_call, pats_discarded, new_pats)+ <- callsToNewPats env fn spec_info arg_occs all_calls ; let n_pats = length new_pats--- ; if (not (null new_pats) || isJust mb_unspec) then--- pprTrace "specialise" (vcat [ ppr fn <+> text "with" <+> int n_pats <+> text "good patterns"--- , text "mb_unspec" <+> ppr (isJust mb_unspec)--- , text "arg_occs" <+> ppr arg_occs--- , text "good pats" <+> ppr new_pats]) $--- return ()--- else return ()+-- ; when (not (null new_pats) || isJust mb_unspec) $+-- pprTraceM "specialise" (vcat [ ppr fn <+> text "with" <+> int n_pats <+> text "good patterns"+-- , text "boring_call:" <+> ppr boring_call+-- , text "pats_discarded:" <+> ppr pats_discarded+-- , text "old spec_count" <+> ppr spec_count+-- , text "spec count limit" <+> ppr (sc_count (sc_opts env))+-- , text "mb_unspec" <+> ppr (isJust mb_unspec)+-- , text "arg_occs" <+> ppr arg_occs+-- , text "new_pats" <+> ppr new_pats]) ; let spec_env = decreaseSpecCount env n_pats ; (spec_usgs, new_specs) <- mapAndUnzipM (spec_one spec_env fn arg_bndrs body)@@ -1653,24 +1832,25 @@ ; let spec_usg = combineUsages spec_usgs + unspec_rhs_needed = pats_discarded || boring_call || isExportedId fn+ -- If there were any boring calls among the seeds (= all_calls), then those -- calls will call the un-specialised function. So we should use the seeds -- from the _unspecialised_ function's RHS, which are in mb_unspec, by returning -- then in new_usg.- (new_usg, mb_unspec')- = case mb_unspec of- Just rhs_usg | boring_call -> (spec_usg `combineUsage` rhs_usg, Nothing)- _ -> (spec_usg, mb_unspec)+ (new_usg, mb_unspec') = case mb_unspec of+ Just rhs_usg | unspec_rhs_needed+ -> (spec_usg `combineUsage` rhs_usg, Nothing)+ _ -> (spec_usg, mb_unspec) --- ; pprTrace "specialise return }"--- (vcat [ ppr fn--- , text "boring_call:" <+> ppr boring_call--- , text "new calls:" <+> ppr (scu_calls new_usg)]) $--- return ()+-- ; pprTraceM "specialise return }" $+-- vcat [ ppr fn+-- , text "unspec_rhs_needed:" <+> ppr unspec_rhs_needed+-- , text "new calls:" <+> ppr (scu_calls new_usg)] - ; return (new_usg, SI { si_specs = new_specs ++ specs- , si_n_specs = spec_count + n_pats- , si_mb_unspec = mb_unspec' }) }+ ; return (new_usg, SI { si_specs = new_specs ++ specs+ , si_n_specs = spec_count + n_pats+ , si_mb_unspec = mb_unspec' }) } | otherwise -- No calls, inactive, or not a function -- Behave as if there was a single, boring call@@ -1713,7 +1893,9 @@ spec_one env fn arg_bndrs body (call_pat, rule_number) | CP { cp_qvars = qvars, cp_args = pats, cp_strict_args = cbv_args } <- call_pat- = do { spec_uniq <- getUniqueM+ = do { -- pprTraceM "spec_one {" (ppr fn <+> ppr pats)++ ; spec_uniq <- getUniqueM ; let env1 = extendScSubstList (extendScInScope env qvars) (arg_bndrs `zip` pats) (body_env, extra_bndrs) = extendBndrs env1 (dropList pats arg_bndrs)@@ -1739,10 +1921,8 @@ -- ; pprTraceM "body_subst_for" $ ppr (spec_occ) $$ ppr (sc_subst body_env) ; (spec_usg, spec_body) <- scExpr body_env body --- ; pprTrace "done spec_one }" (ppr fn $$ ppr (scu_calls spec_usg)) $--- return ()- -- And build the results+ ; (qvars', pats') <- generaliseDictPats qvars pats ; let spec_body_ty = exprType spec_body (spec_lam_args, spec_call_args, spec_sig) = calcSpecInfo fn arg_bndrs call_pat extra_bndrs@@ -1751,7 +1931,7 @@ spec_join_arity | isJoinId fn = Just (length spec_call_args) | otherwise = Nothing spec_id = asWorkerLikeId $- mkLocalId spec_name Many+ mkLocalId spec_name ManyTy (mkLamTypes spec_lam_args spec_body_ty) -- See Note [Transfer strictness] `setIdDmdSig` spec_sig@@ -1763,30 +1943,53 @@ spec_rhs = mkLams spec_lam_args (mkSeqs cbv_args spec_body_ty spec_body) rule_rhs = mkVarApps (Var spec_id) spec_call_args inline_act = idInlineActivation fn- this_mod = sc_module env+ this_mod = sc_module $ sc_opts env rule = mkRule this_mod True {- Auto -} True {- Local -}- rule_name inline_act fn_name qvars pats rule_rhs+ rule_name inline_act+ fn_name qvars' pats' rule_rhs -- See Note [Transfer activation] - -- ; pprTrace "spec_one {" (vcat [ text "function:" <+> ppr fn <+> ppr (idUnique fn)- -- , text "sc_count:" <+> ppr (sc_count env)- -- , text "pats:" <+> ppr pats- -- , text "call_pat:" <+> ppr call_pat- -- , text "-->" <+> ppr spec_name- -- , text "bndrs" <+> ppr arg_bndrs- -- , text "extra_bndrs" <+> ppr extra_bndrs- -- , text "spec_lam_args" <+> ppr spec_lam_args- -- , text "spec_call_args" <+> ppr spec_call_args- -- , text "rule_rhs" <+> ppr rule_rhs- -- , text "adds_void_worker_arg" <+> ppr adds_void_worker_arg- -- , text "body" <+> ppr body- -- , text "spec_rhs" <+> ppr spec_rhs- -- , text "how_bound" <+> ppr (sc_how_bound env) ]) $- -- return ()+-- ; pprTraceM "spec_one end }" $+-- vcat [ text "function:" <+> ppr fn <+> braces (ppr (idUnique fn))+-- , text "pats:" <+> ppr pats+-- , text "call_pat:" <+> ppr call_pat+-- , text "-->" <+> ppr spec_name+-- , text "bndrs" <+> ppr arg_bndrs+-- , text "extra_bndrs" <+> ppr extra_bndrs+-- , text "cbv_args" <+> ppr cbv_args+-- , text "spec_lam_args" <+> ppr spec_lam_args+-- , text "spec_call_args" <+> ppr spec_call_args+-- , text "rule_rhs" <+> ppr rule_rhs+-- , text "adds_void_worker_arg" <+> ppr add_void_arg+---- , text "body" <+> ppr body+---- , text "spec_rhs" <+> ppr spec_rhs+---- , text "how_bound" <+> ppr (sc_how_bound env) ]+-- ] ; return (spec_usg, OS { os_pat = call_pat, os_rule = rule , os_id = spec_id , os_rhs = spec_rhs }) } +generaliseDictPats :: [Var] -> [CoreExpr] -- Quantified vars and pats+ -> UniqSM ([Var], [CoreExpr]) -- New quantified vars and pats+-- See Note [generaliseDictPats]+generaliseDictPats qvars pats+ = do { (extra_qvars, pats') <- mapAccumLM go [] pats+ ; case extra_qvars of+ [] -> return (qvars, pats)+ _ -> return (qvars ++ extra_qvars, pats') }+ where+ qvar_set = mkVarSet qvars+ go :: [Id] -> CoreExpr -> UniqSM ([Id], CoreExpr)+ go extra_qvs pat+ | not (isTyCoArg pat)+ , let pat_ty = exprType pat+ , typeDeterminesValue pat_ty+ , exprFreeVars pat `disjointVarSet` qvar_set+ = do { id <- mkSysLocalOrCoVarM (fsLit "dict") ManyTy pat_ty+ ; return (id:extra_qvs, Var id) }+ | otherwise+ = return (extra_qvs, pat)+ -- See Note [SpecConstr and strict fields] mkSeqs :: [Var] -> Type -> CoreExpr -> CoreExpr mkSeqs seqees res_ty rhs =@@ -1892,16 +2095,15 @@ calcSpecInfo fn arg_bndrs (CP { cp_qvars = qvars, cp_args = pats }) extra_bndrs = ( spec_lam_bndrs_w_dmds , spec_call_args- , zapDmdEnvSig (DmdSig (dt{dt_args = spec_fn_dmds})) )+ , mkClosedDmdSig [idDemandInfo b | b <- spec_lam_bndrs_w_dmds, isId b] div ) where- DmdSig dt@DmdType{dt_args=fn_dmds} = idDmdSig fn- spec_fn_dmds = [idDemandInfo b | b <- spec_lam_bndrs_w_dmds, isId b]+ DmdSig (DmdType _ fn_dmds div) = idDmdSig fn val_pats = filterOut isTypeArg pats -- Value args at call sites, used to determine how many demands to drop- -- from the original functions demand and for setting up arg_dmd_env.- arg_dmd_env = go emptyVarEnv fn_dmds val_pats- qvar_dmds = [ lookupVarEnv arg_dmd_env qv `orElse` topDmd | qv <- qvars, isId qv ]+ -- from the original functions demand and for setting up dmd_env.+ dmd_env = go emptyVarEnv fn_dmds val_pats+ qvar_dmds = [ lookupVarEnv dmd_env qv `orElse` topDmd | qv <- qvars, isId qv ] extra_dmds = dropList val_pats fn_dmds -- Annotate the variables with the strictness information from@@ -1925,12 +2127,12 @@ set_dmds (v:vs) ds@(d:ds') | isTyVar v = v : set_dmds vs ds | otherwise = setIdDemandInfo v d : set_dmds vs ds' - go :: VarEnv Demand -> [Demand] -> [CoreExpr] -> VarEnv Demand+ go :: DmdEnv -> [Demand] -> [CoreExpr] -> DmdEnv -- We've filtered out all the type patterns already go env (d:ds) (pat : pats) = go (go_one env d pat) ds pats go env _ _ = env - go_one :: VarEnv Demand -> Demand -> CoreExpr -> VarEnv Demand+ go_one :: DmdEnv -> Demand -> CoreExpr -> DmdEnv go_one env d (Var v) = extendVarEnv_C plusDmd env v d go_one env (_n :* cd) e -- NB: _n does not have to be strict | (Var _, args) <- collectArgs e@@ -1942,7 +2144,8 @@ Note [spec_usg includes rhs_usg] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In calls to 'specialise', the returned ScUsage must include the rhs_usg in-the passed-in SpecInfo, unless there are no calls at all to the function.+the passed-in SpecInfo in si_mb_unspec, unless there are no calls at all to+the function. The caller can, indeed must, assume this. They should not combine in rhs_usg themselves, or they'll get rhs_usg twice -- and that can lead to an exponential@@ -2159,10 +2362,14 @@ callsToNewPats :: ScEnv -> Id -> SpecInfo -> [ArgOcc] -> [Call]- -> UniqSM (Bool, [CallPat])- -- Result has no duplicate patterns,- -- nor ones mentioned in done_pats- -- Bool indicates that there was at least one boring pattern+ -> UniqSM ( Bool -- At least one boring call+ , Bool -- Patterns were discarded+ , [CallPat] ) -- Patterns to specialise+-- Result has no duplicate patterns,+-- nor ones mentioned in si_specs (hence "new" patterns)+-- Bool indicates that there was at least one boring pattern+-- The "New" in the name means "patterns that are not already covered+-- by an existing specialisation" callsToNewPats env fn spec_info@(SI { si_specs = done_specs }) bndr_occs calls = do { mb_pats <- mapM (callToPats env bndr_occs) calls @@ -2180,9 +2387,8 @@ -- Remove ones that have too many worker variables small_pats = filterOut too_big non_dups- max_args = maxWorkerArgs (sc_dflags env) too_big (CP { cp_qvars = vars, cp_args = args })- = not (isWorkerSmallEnough max_args (valArgCount args) vars)+ = not (isWorkerSmallEnough (sc_max_args $ sc_opts env) (valArgCount args) vars) -- We are about to construct w/w pair in 'spec_one'. -- Omit specialisation leading to high arity workers. -- See Note [Limit w/w arity] in GHC.Core.Opt.WorkWrap.Utils@@ -2190,12 +2396,11 @@ -- Discard specialisations if there are too many of them (pats_were_discarded, trimmed_pats) = trim_pats env fn spec_info small_pats --- ; pprTrace "callsToPats" (vcat [ text "calls to" <+> ppr fn <> colon <+> ppr calls--- , text "done_specs:" <+> ppr (map os_pat done_specs)--- , text "good_pats:" <+> ppr good_pats ]) $--- return ()+-- ; pprTraceM "callsToPats" (vcat [ text "calls to" <+> ppr fn <> colon <+> ppr calls+-- , text "done_specs:" <+> ppr (map os_pat done_specs)+-- , text "trimmed_pats:" <+> ppr trimmed_pats ]) - ; return (have_boring_call || pats_were_discarded, trimmed_pats) }+ ; return (have_boring_call, pats_were_discarded, trimmed_pats) } -- If any of the calls does not give rise to a specialisation, either -- because it is boring, or because there are too many specialisations, -- return a flag to say so, so that we know to keep the original function.@@ -2219,7 +2424,7 @@ n_pats = length pats spec_count' = n_pats + done_spec_count n_remaining = max_specs - done_spec_count- mb_scc = sc_count env+ mb_scc = sc_count $ sc_opts env Just max_specs = mb_scc sorted_pats = map fst $@@ -2244,7 +2449,7 @@ n_cons _ = 0 emit_trace result- | debugIsOn || hasPprDebug (sc_dflags env)+ | debugIsOn || sc_debug (sc_opts env) -- Suppress this scary message for ordinary users! #5125 = pprTrace "SpecConstr" msg result | otherwise@@ -2265,7 +2470,7 @@ -- over the following term variables -- The [CoreExpr] are the argument patterns for the rule callToPats env bndr_occs call@(Call fn args con_env)- = do { let in_scope = substInScope (sc_subst env)+ = do { let in_scope = getSubstInScope (sc_subst env) ; arg_tripples <- zipWith3M (argToPat env in_scope con_env) args bndr_occs (map (const NotMarkedStrict) args) -- This zip trims the args to be no longer than@@ -2304,29 +2509,29 @@ sanitise id = updateIdTypeAndMult expandTypeSynonyms id -- See Note [Free type variables of the qvar types] - -- Bad coercion variables: see Note [SpecConstr and casts]- bad_covars :: CoVarSet++ -- Check for bad coercion variables: see Note [SpecConstr and casts]+ ; let bad_covars :: CoVarSet bad_covars = mapUnionVarSet get_bad_covars pats get_bad_covars :: CoreArg -> CoVarSet get_bad_covars (Type ty) = filterVarSet bad_covar (tyCoVarsOfType ty) get_bad_covars _ = emptyVarSet bad_covar v = isId v && not (is_in_scope v) - ; -- pprTrace "callToPats" (ppr args $$ ppr bndr_occs) $- warnPprTrace (not (isEmptyVarSet bad_covars))+ ; warnPprTrace (not (isEmptyVarSet bad_covars)) "SpecConstr: bad covars" (ppr bad_covars $$ ppr call) $+ if interesting && isEmptyVarSet bad_covars- then do- -- pprTraceM "callToPatsOut" (- -- text "fn:" <+> ppr fn $$- -- text "args:" <+> ppr args $$- -- text "in_scope:" <+> ppr in_scope $$- -- -- text "in_scope:" <+> ppr in_scope $$- -- text "pat_fvs:" <+> ppr pat_fvs- -- )- -- ppr (CP { cp_qvars = qvars', cp_args = pats })) >>- return (Just (CP { cp_qvars = qvars', cp_args = pats, cp_strict_args = concat cbv_ids }))+ then do { let cp_res = CP { cp_qvars = qvars', cp_args = pats+ , cp_strict_args = concat cbv_ids }+-- ; pprTraceM "callToPatsOut" $+-- vcat [ text "fn:" <+> ppr fn+-- , text "args:" <+> ppr args+-- , text "bndr_occs:" <+> ppr bndr_occs+-- , text "pat_fvs:" <+> ppr pat_fvs+-- , text "cp_res:" <+> ppr cp_res ]+ ; return (Just cp_res) } else return Nothing } -- argToPat takes an actual argument, and returns an abstracted@@ -2430,7 +2635,7 @@ , Just arg_occs <- mb_scrut dc = do { let (ty_args, rest_args) = splitAtList (dataConUnivTyVars dc) args con_str, matched_str :: [StrictnessMark]- -- con_str corrresponds 1-1 with the /value/ arguments+ -- con_str corresponds 1-1 with the /value/ arguments -- matched_str corresponds 1-1 with /all/ arguments con_str = dataConRepStrictness dc matched_str = match_vals con_str rest_args@@ -2450,7 +2655,7 @@ mb_scrut dc = case arg_occ of ScrutOcc bs | Just occs <- lookupUFM bs dc -> Just (occs) -- See Note [Reboxing]- _other | sc_force env || sc_keen env+ _other | sc_force env || sc_keen (sc_opts env) -> Just (repeat UnkOcc) | otherwise -> Nothing@@ -2474,10 +2679,8 @@ -- (b) we know what its value is -- In that case it counts as "interesting" argToPat1 env in_scope val_env (Var v) arg_occ arg_str- | sc_force env || case arg_occ of { ScrutOcc {} -> True- ; UnkOcc -> False- ; NoOcc -> False } -- (a)- , is_value -- (b)+ | sc_force env || specialisableArgOcc arg_occ -- (a)+ , is_value -- (b) -- Ignoring sc_keen here to avoid gratuitously incurring Note [Reboxing] -- So sc_keen focused just on f (I# x), where we have freshly-allocated -- box that we can eliminate in the caller@@ -2518,7 +2721,7 @@ -- | wildCardPats are always boring wildCardPat :: Type -> StrictnessMark -> UniqSM (Bool, CoreArg, [Id]) wildCardPat ty str- = do { id <- mkSysLocalOrCoVarM (fsLit "sc") Many ty+ = do { id <- mkSysLocalOrCoVarM (fsLit "sc") ManyTy ty -- ; pprTraceM "wildCardPat" (ppr id' <+> ppr (idUnfolding id')) ; return (False, varToCoreExpr id, if isMarkedStrict str then [id] else []) } @@ -2552,12 +2755,14 @@ isValue _env expr -- Maybe it's a constructor application | (Var fun, args, _) <- collectArgsTicks (not . tickishIsCode) expr- = case isDataConWorkId_maybe fun of-- Just con | args `lengthAtLeast` dataConRepArity con+ = case idDetails fun of+ DataConWorkId con | args `lengthAtLeast` dataConRepArity con -- Check saturated; might be > because the -- arity excludes type args -> Just (ConVal (DataAlt con) args)++ DFunId {} -> Just LambdaVal+ -- DFunId: see Note [Specialising on dictionaries] _other | valArgCount args < idArity fun -- Under-applied function
compiler/GHC/Core/Opt/Specialise.hs view
@@ -1,3107 +1,3453 @@---{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}--{--(c) The GRASP/AQUA Project, Glasgow University, 1993-1998--\section[Specialise]{Stamping out overloading, and (optionally) polymorphism}--}--module GHC.Core.Opt.Specialise ( specProgram, specUnfolding ) where--import GHC.Prelude--import GHC.Driver.Session-import GHC.Driver.Ppr-import GHC.Driver.Config-import GHC.Driver.Config.Diagnostic-import GHC.Driver.Env--import GHC.Tc.Utils.TcType hiding( substTy )--import GHC.Core.Type hiding( substTy, extendTvSubstList )-import GHC.Core.Multiplicity-import GHC.Core.Predicate-import GHC.Core.Coercion( Coercion )-import GHC.Core.Opt.Monad-import qualified GHC.Core.Subst as Core-import GHC.Core.Unfold.Make-import GHC.Core-import GHC.Core.Rules-import GHC.Core.Utils ( exprIsTrivial, exprIsTopLevelBindable- , getIdFromTrivialExpr_maybe- , mkCast, exprType )-import GHC.Core.FVs-import GHC.Core.TyCo.Rep (TyCoBinder (..))-import GHC.Core.Opt.Arity ( collectBindersPushingCo- , etaExpandToJoinPointRule )--import GHC.Builtin.Types ( unboxedUnitTy )--import GHC.Data.Maybe ( mapMaybe, maybeToList, isJust )-import GHC.Data.Bag-import GHC.Data.FastString-import GHC.Data.List.SetOps--import GHC.Types.Basic-import GHC.Types.Unique.Supply-import GHC.Types.Unique.DFM-import GHC.Types.Name-import GHC.Types.Tickish-import GHC.Types.Id.Make ( voidArgId, voidPrimId )-import GHC.Types.Var ( isLocalVar, mkLocalVar )-import GHC.Types.Var.Set-import GHC.Types.Var.Env-import GHC.Types.Id-import GHC.Types.Id.Info-import GHC.Types.Error--import GHC.Utils.Error ( mkMCDiagnostic )-import GHC.Utils.Monad ( foldlM )-import GHC.Utils.Misc-import GHC.Utils.Outputable-import GHC.Utils.Panic-import GHC.Utils.Trace-import GHC.Utils.Panic.Plain( assert )--import GHC.Unit.Module( Module )-import GHC.Unit.Module.ModGuts-import GHC.Unit.External--{--************************************************************************-* *-\subsection[notes-Specialise]{Implementation notes [SLPJ, Aug 18 1993]}-* *-************************************************************************--These notes describe how we implement specialisation to eliminate-overloading.--The specialisation pass works on Core-syntax, complete with all the explicit dictionary application,-abstraction and construction as added by the type checker. The-existing type checker remains largely as it is.--One important thought: the {\em types} passed to an overloaded-function, and the {\em dictionaries} passed are mutually redundant.-If the same function is applied to the same type(s) then it is sure to-be applied to the same dictionary(s)---or rather to the same {\em-values}. (The arguments might look different but they will evaluate-to the same value.)--Second important thought: we know that we can make progress by-treating dictionary arguments as static and worth specialising on. So-we can do without binding-time analysis, and instead specialise on-dictionary arguments and no others.--The basic idea-~~~~~~~~~~~~~~-Suppose we have-- let f = <f_rhs>- in <body>--and suppose f is overloaded.--STEP 1: CALL-INSTANCE COLLECTION--We traverse <body>, accumulating all applications of f to types and-dictionaries.--(Might there be partial applications, to just some of its types and-dictionaries? In principle yes, but in practice the type checker only-builds applications of f to all its types and dictionaries, so partial-applications could only arise as a result of transformation, and even-then I think it's unlikely. In any case, we simply don't accumulate such-partial applications.)---STEP 2: EQUIVALENCES--So now we have a collection of calls to f:- f t1 t2 d1 d2- f t3 t4 d3 d4- ...-Notice that f may take several type arguments. To avoid ambiguity, we-say that f is called at type t1/t2 and t3/t4.--We take equivalence classes using equality of the *types* (ignoring-the dictionary args, which as mentioned previously are redundant).--STEP 3: SPECIALISATION--For each equivalence class, choose a representative (f t1 t2 d1 d2),-and create a local instance of f, defined thus:-- f@t1/t2 = <f_rhs> t1 t2 d1 d2--f_rhs presumably has some big lambdas and dictionary lambdas, so lots-of simplification will now result. However we don't actually *do* that-simplification. Rather, we leave it for the simplifier to do. If we-*did* do it, though, we'd get more call instances from the specialised-RHS. We can work out what they are by instantiating the call-instance-set from f's RHS with the types t1, t2.--Add this new id to f's IdInfo, to record that f has a specialised version.--Before doing any of this, check that f's IdInfo doesn't already-tell us about an existing instance of f at the required type/s.-(This might happen if specialisation was applied more than once, or-it might arise from user SPECIALIZE pragmas.)--Recursion-~~~~~~~~~-Wait a minute! What if f is recursive? Then we can't just plug in-its right-hand side, can we?--But it's ok. The type checker *always* creates non-recursive definitions-for overloaded recursive functions. For example:-- f x = f (x+x) -- Yes I know its silly--becomes-- f a (d::Num a) = let p = +.sel a d- in- letrec fl (y::a) = fl (p y y)- in- fl--We still have recursion for non-overloaded functions which we-specialise, but the recursive call should get specialised to the-same recursive version.---Polymorphism 1-~~~~~~~~~~~~~~--All this is crystal clear when the function is applied to *constant-types*; that is, types which have no type variables inside. But what if-it is applied to non-constant types? Suppose we find a call of f at type-t1/t2. There are two possibilities:--(a) The free type variables of t1, t2 are in scope at the definition point-of f. In this case there's no problem, we proceed just as before. A common-example is as follows. Here's the Haskell:-- g y = let f x = x+x- in f y + f y--After typechecking we have-- g a (d::Num a) (y::a) = let f b (d'::Num b) (x::b) = +.sel b d' x x- in +.sel a d (f a d y) (f a d y)--Notice that the call to f is at type type "a"; a non-constant type.-Both calls to f are at the same type, so we can specialise to give:-- g a (d::Num a) (y::a) = let f@a (x::a) = +.sel a d x x- in +.sel a d (f@a y) (f@a y)---(b) The other case is when the type variables in the instance types-are *not* in scope at the definition point of f. The example we are-working with above is a good case. There are two instances of (+.sel a d),-but "a" is not in scope at the definition of +.sel. Can we do anything?-Yes, we can "common them up", a sort of limited common sub-expression deal.-This would give:-- g a (d::Num a) (y::a) = let +.sel@a = +.sel a d- f@a (x::a) = +.sel@a x x- in +.sel@a (f@a y) (f@a y)--This can save work, and can't be spotted by the type checker, because-the two instances of +.sel weren't originally at the same type.--Further notes on (b)--* There are quite a few variations here. For example, the defn of- +.sel could be floated outside the \y, to attempt to gain laziness.- It certainly mustn't be floated outside the \d because the d has to- be in scope too.--* We don't want to inline f_rhs in this case, because-that will duplicate code. Just commoning up the call is the point.--* Nothing gets added to +.sel's IdInfo.--* Don't bother unless the equivalence class has more than one item!--Not clear whether this is all worth it. It is of course OK to-simply discard call-instances when passing a big lambda.--Polymorphism 2 -- Overloading-~~~~~~~~~~~~~~-Consider a function whose most general type is-- f :: forall a b. Ord a => [a] -> b -> b--There is really no point in making a version of g at Int/Int and another-at Int/Bool, because it's only instantiating the type variable "a" which-buys us any efficiency. Since g is completely polymorphic in b there-ain't much point in making separate versions of g for the different-b types.--That suggests that we should identify which of g's type variables-are constrained (like "a") and which are unconstrained (like "b").-Then when taking equivalence classes in STEP 2, we ignore the type args-corresponding to unconstrained type variable. In STEP 3 we make-polymorphic versions. Thus:-- f@t1/ = /\b -> <f_rhs> t1 b d1 d2--We do this.---Dictionary floating-~~~~~~~~~~~~~~~~~~~-Consider this-- f a (d::Num a) = let g = ...- in- ...(let d1::Ord a = Num.Ord.sel a d in g a d1)...--Here, g is only called at one type, but the dictionary isn't in scope at the-definition point for g. Usually the type checker would build a-definition for d1 which enclosed g, but the transformation system-might have moved d1's defn inward. Solution: float dictionary bindings-outwards along with call instances.--Consider-- f x = let g p q = p==q- h r s = (r+s, g r s)- in- h x x---Before specialisation, leaving out type abstractions we have-- f df x = let g :: Eq a => a -> a -> Bool- g dg p q = == dg p q- h :: Num a => a -> a -> (a, Bool)- h dh r s = let deq = eqFromNum dh- in (+ dh r s, g deq r s)- in- h df x x--After specialising h we get a specialised version of h, like this:-- h' r s = let deq = eqFromNum df- in (+ df r s, g deq r s)--But we can't naively make an instance for g from this, because deq is not in scope-at the defn of g. Instead, we have to float out the (new) defn of deq-to widen its scope. Notice that this floating can't be done in advance -- it only-shows up when specialisation is done.--User SPECIALIZE pragmas-~~~~~~~~~~~~~~~~~~~~~~~-Specialisation pragmas can be digested by the type checker, and implemented-by adding extra definitions along with that of f, in the same way as before-- f@t1/t2 = <f_rhs> t1 t2 d1 d2--Indeed the pragmas *have* to be dealt with by the type checker, because-only it knows how to build the dictionaries d1 and d2! For example-- g :: Ord a => [a] -> [a]- {-# SPECIALIZE f :: [Tree Int] -> [Tree Int] #-}--Here, the specialised version of g is an application of g's rhs to the-Ord dictionary for (Tree Int), which only the type checker can conjure-up. There might not even *be* one, if (Tree Int) is not an instance of-Ord! (All the other specialision has suitable dictionaries to hand-from actual calls.)--Problem. The type checker doesn't have to hand a convenient <f_rhs>, because-it is buried in a complex (as-yet-un-desugared) binding group.-Maybe we should say-- f@t1/t2 = f* t1 t2 d1 d2--where f* is the Id f with an IdInfo which says "inline me regardless!".-Indeed all the specialisation could be done in this way.-That in turn means that the simplifier has to be prepared to inline absolutely-any in-scope let-bound thing.---Again, the pragma should permit polymorphism in unconstrained variables:-- h :: Ord a => [a] -> b -> b- {-# SPECIALIZE h :: [Int] -> b -> b #-}--We *insist* that all overloaded type variables are specialised to ground types,-(and hence there can be no context inside a SPECIALIZE pragma).-We *permit* unconstrained type variables to be specialised to- - a ground type- - or left as a polymorphic type variable-but nothing in between. So-- {-# SPECIALIZE h :: [Int] -> [c] -> [c] #-}--is *illegal*. (It can be handled, but it adds complication, and gains the-programmer nothing.)---SPECIALISING INSTANCE DECLARATIONS-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider-- instance Foo a => Foo [a] where- ...- {-# SPECIALIZE instance Foo [Int] #-}--The original instance decl creates a dictionary-function-definition:-- dfun.Foo.List :: forall a. Foo a -> Foo [a]--The SPECIALIZE pragma just makes a specialised copy, just as for-ordinary function definitions:-- dfun.Foo.List@Int :: Foo [Int]- dfun.Foo.List@Int = dfun.Foo.List Int dFooInt--The information about what instance of the dfun exist gets added to-the dfun's IdInfo in the same way as a user-defined function too.---Automatic instance decl specialisation?-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Can instance decls be specialised automatically? It's tricky.-We could collect call-instance information for each dfun, but-then when we specialised their bodies we'd get new call-instances-for ordinary functions; and when we specialised their bodies, we might get-new call-instances of the dfuns, and so on. This all arises because of-the unrestricted mutual recursion between instance decls and value decls.--Still, there's no actual problem; it just means that we may not do all-the specialisation we could theoretically do.--Furthermore, instance decls are usually exported and used non-locally,-so we'll want to compile enough to get those specialisations done.--Lastly, there's no such thing as a local instance decl, so we can-survive solely by spitting out *usage* information, and then reading that-back in as a pragma when next compiling the file. So for now,-we only specialise instance decls in response to pragmas.---SPITTING OUT USAGE INFORMATION-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--To spit out usage information we need to traverse the code collecting-call-instance information for all imported (non-prelude?) functions-and data types. Then we equivalence-class it and spit it out.--This is done at the top-level when all the call instances which escape-must be for imported functions and data types.--*** Not currently done ***---Partial specialisation by pragmas-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-What about partial specialisation:-- k :: (Ord a, Eq b) => [a] -> b -> b -> [a]- {-# SPECIALIZE k :: Eq b => [Int] -> b -> b -> [a] #-}--or even-- {-# SPECIALIZE k :: Eq b => [Int] -> [b] -> [b] -> [a] #-}--Seems quite reasonable. Similar things could be done with instance decls:-- instance (Foo a, Foo b) => Foo (a,b) where- ...- {-# SPECIALIZE instance Foo a => Foo (a,Int) #-}- {-# SPECIALIZE instance Foo b => Foo (Int,b) #-}--Ho hum. Things are complex enough without this. I pass.---Requirements for the simplifier-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-The simplifier has to be able to take advantage of the specialisation.--* When the simplifier finds an application of a polymorphic f, it looks in-f's IdInfo in case there is a suitable instance to call instead. This converts-- f t1 t2 d1 d2 ===> f_t1_t2--Note that the dictionaries get eaten up too!--* Dictionary selection operations on constant dictionaries must be- short-circuited:-- +.sel Int d ===> +Int--The obvious way to do this is in the same way as other specialised-calls: +.sel has inside it some IdInfo which tells that if it's applied-to the type Int then it should eat a dictionary and transform to +Int.--In short, dictionary selectors need IdInfo inside them for constant-methods.--* Exactly the same applies if a superclass dictionary is being- extracted:-- Eq.sel Int d ===> dEqInt--* Something similar applies to dictionary construction too. Suppose-dfun.Eq.List is the function taking a dictionary for (Eq a) to-one for (Eq [a]). Then we want-- dfun.Eq.List Int d ===> dEq.List_Int--Where does the Eq [Int] dictionary come from? It is built in-response to a SPECIALIZE pragma on the Eq [a] instance decl.--In short, dfun Ids need IdInfo with a specialisation for each-constant instance of their instance declaration.--All this uses a single mechanism: the SpecEnv inside an Id---What does the specialisation IdInfo look like?-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--The SpecEnv of an Id maps a list of types (the template) to an expression-- [Type] |-> Expr--For example, if f has this RuleInfo:-- [Int, a] -> \d:Ord Int. f' a--it means that we can replace the call-- f Int t ===> (\d. f' t)--This chucks one dictionary away and proceeds with the-specialised version of f, namely f'.---What can't be done this way?-~~~~~~~~~~~~~~~~~~~~~~~~~~~~-There is no way, post-typechecker, to get a dictionary for (say)-Eq a from a dictionary for Eq [a]. So if we find-- ==.sel [t] d--we can't transform to-- eqList (==.sel t d')--where- eqList :: (a->a->Bool) -> [a] -> [a] -> Bool--Of course, we currently have no way to automatically derive-eqList, nor to connect it to the Eq [a] instance decl, but you-can imagine that it might somehow be possible. Taking advantage-of this is permanently ruled out.--Still, this is no great hardship, because we intend to eliminate-overloading altogether anyway!--A note about non-tyvar dictionaries-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Some Ids have types like-- forall a,b,c. Eq a -> Ord [a] -> tau--This seems curious at first, because we usually only have dictionary-args whose types are of the form (C a) where a is a type variable.-But this doesn't hold for the functions arising from instance decls,-which sometimes get arguments with types of form (C (T a)) for some-type constructor T.--Should we specialise wrt this compound-type dictionary? We used to say-"no", saying:- "This is a heuristic judgement, as indeed is the fact that we- specialise wrt only dictionaries. We choose *not* to specialise- wrt compound dictionaries because at the moment the only place- they show up is in instance decls, where they are simply plugged- into a returned dictionary. So nothing is gained by specialising- wrt them."--But it is simpler and more uniform to specialise wrt these dicts too;-and in future GHC is likely to support full fledged type signatures-like- f :: Eq [(a,b)] => ...---************************************************************************-* *-\subsubsection{The new specialiser}-* *-************************************************************************--Our basic game plan is this. For let(rec) bound function- f :: (C a, D c) => (a,b,c,d) -> Bool--* Find any specialised calls of f, (f ts ds), where- ts are the type arguments t1 .. t4, and- ds are the dictionary arguments d1 .. d2.--* Add a new definition for f1 (say):-- f1 = /\ b d -> (..body of f..) t1 b t3 d d1 d2-- Note that we abstract over the unconstrained type arguments.--* Add the mapping-- [t1,b,t3,d] |-> \d1 d2 -> f1 b d-- to the specialisations of f. This will be used by the- simplifier to replace calls- (f t1 t2 t3 t4) da db- by- (\d1 d1 -> f1 t2 t4) da db-- All the stuff about how many dictionaries to discard, and what types- to apply the specialised function to, are handled by the fact that the- SpecEnv contains a template for the result of the specialisation.--We don't build *partial* specialisations for f. For example:-- f :: Eq a => a -> a -> Bool- {-# SPECIALISE f :: (Eq b, Eq c) => (b,c) -> (b,c) -> Bool #-}--Here, little is gained by making a specialised copy of f.-There's a distinct danger that the specialised version would-first build a dictionary for (Eq b, Eq c), and then select the (==)-method from it! Even if it didn't, not a great deal is saved.--We do, however, generate polymorphic, but not overloaded, specialisations:-- f :: Eq a => [a] -> b -> b -> b- ... SPECIALISE f :: [Int] -> b -> b -> b ...--Hence, the invariant is this:-- *** no specialised version is overloaded ***---************************************************************************-* *-\subsubsection{The exported function}-* *-************************************************************************--}---- | Specialise calls to type-class overloaded functions occurring in a program.-specProgram :: ModGuts -> CoreM ModGuts-specProgram guts@(ModGuts { mg_module = this_mod- , mg_rules = local_rules- , mg_binds = binds })- = do { dflags <- getDynFlags-- -- We need to start with a Subst that knows all the things- -- that are in scope, so that the substitution engine doesn't- -- accidentally re-use a unique that's already in use- -- Easiest thing is to do it all at once, as if all the top-level- -- decls were mutually recursive- ; let top_env = SE { se_subst = Core.mkEmptySubst $ mkInScopeSet $ mkVarSet $- bindersOfBinds binds- , se_interesting = emptyVarSet- , se_module = this_mod- , se_dflags = dflags }-- go [] = return ([], emptyUDs)- go (bind:binds) = do (binds', uds) <- go binds- (bind', uds') <- specBind top_env bind uds- return (bind' ++ binds', uds')-- -- Specialise the bindings of this module- ; (binds', uds) <- runSpecM (go binds)-- ; (spec_rules, spec_binds) <- specImports top_env local_rules uds-- ; return (guts { mg_binds = spec_binds ++ binds'- , mg_rules = spec_rules ++ local_rules }) }--{--Note [Wrap bindings returned by specImports]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-'specImports' returns a set of specialized bindings. However, these are lacking-necessary floated dictionary bindings, which are returned by-UsageDetails(ud_binds). These dictionaries need to be brought into scope with-'wrapDictBinds' before the bindings returned by 'specImports' can be used. See,-for instance, the 'specImports' call in 'specProgram'.---Note [Disabling cross-module specialisation]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Since GHC 7.10 we have performed specialisation of INLINABLE bindings living-in modules outside of the current module. This can sometimes uncover user code-which explodes in size when aggressively optimized. The--fno-cross-module-specialise option was introduced to allow users to being-bitten by such instances to revert to the pre-7.10 behavior.--See #10491--}---{- *********************************************************************-* *- Specialising imported functions-* *-********************************************************************* -}--specImports :: SpecEnv- -> [CoreRule]- -> UsageDetails- -> CoreM ([CoreRule], [CoreBind])-specImports top_env local_rules- (MkUD { ud_binds = dict_binds, ud_calls = calls })- | not $ gopt Opt_CrossModuleSpecialise (se_dflags top_env)- -- See Note [Disabling cross-module specialisation]- = return ([], wrapDictBinds dict_binds [])-- | otherwise- = do { hpt_rules <- getRuleBase- ; let rule_base = extendRuleBaseList hpt_rules local_rules-- ; (spec_rules, spec_binds) <- spec_imports top_env [] rule_base- dict_binds calls-- -- Don't forget to wrap the specialized bindings with- -- bindings for the needed dictionaries.- -- See Note [Wrap bindings returned by specImports]- -- and Note [Glom the bindings if imported functions are specialised]- ; let final_binds- | null spec_binds = wrapDictBinds dict_binds []- | otherwise = [Rec $ flattenBinds $- wrapDictBinds dict_binds spec_binds]-- ; return (spec_rules, final_binds)- }---- | Specialise a set of calls to imported bindings-spec_imports :: SpecEnv -- Passed in so that all top-level Ids are in scope- -> [Id] -- Stack of imported functions being specialised- -- See Note [specImport call stack]- -> RuleBase -- Rules from this module and the home package- -- (but not external packages, which can change)- -> FloatedDictBinds -- Dict bindings, used /only/ for filterCalls- -- See Note [Avoiding loops in specImports]- -> CallDetails -- Calls for imported things- -> CoreM ( [CoreRule] -- New rules- , [CoreBind] ) -- Specialised bindings-spec_imports top_env callers rule_base dict_binds calls- = do { let import_calls = dVarEnvElts calls--- ; debugTraceMsg (text "specImports {" <+>--- vcat [ text "calls:" <+> ppr import_calls--- , text "dict_binds:" <+> ppr dict_binds ])- ; (rules, spec_binds) <- go rule_base import_calls--- ; debugTraceMsg (text "End specImports }" <+> ppr import_calls)-- ; return (rules, spec_binds) }- where- go :: RuleBase -> [CallInfoSet] -> CoreM ([CoreRule], [CoreBind])- go _ [] = return ([], [])- go rb (cis : other_calls)- = do { -- debugTraceMsg (text "specImport {" <+> ppr cis)- ; (rules1, spec_binds1) <- spec_import top_env callers rb dict_binds cis- ; -- debugTraceMsg (text "specImport }" <+> ppr cis)-- ; (rules2, spec_binds2) <- go (extendRuleBaseList rb rules1) other_calls- ; return (rules1 ++ rules2, spec_binds1 ++ spec_binds2) }--spec_import :: SpecEnv -- Passed in so that all top-level Ids are in scope- -> [Id] -- Stack of imported functions being specialised- -- See Note [specImport call stack]- -> RuleBase -- Rules from this module- -> FloatedDictBinds -- Dict bindings, used /only/ for filterCalls- -- See Note [Avoiding loops in specImports]- -> CallInfoSet -- Imported function and calls for it- -> CoreM ( [CoreRule] -- New rules- , [CoreBind] ) -- Specialised bindings-spec_import top_env callers rb dict_binds cis@(CIS fn _)- | isIn "specImport" fn callers- = return ([], []) -- No warning. This actually happens all the time- -- when specialising a recursive function, because- -- the RHS of the specialised function contains a recursive- -- call to the original function-- | null good_calls- = return ([], [])-- | Just rhs <- canSpecImport dflags fn- = do { -- Get rules from the external package state- -- We keep doing this in case we "page-fault in"- -- more rules as we go along- ; hsc_env <- getHscEnv- ; eps <- liftIO $ hscEPS hsc_env- ; vis_orphs <- getVisibleOrphanMods- ; let rules_for_fn = getRules (RuleEnv [rb, eps_rule_base eps] vis_orphs) fn-- ; -- debugTraceMsg (text "specImport1" <+> vcat [ppr fn, ppr good_calls, ppr rhs])- ; (rules1, spec_pairs, MkUD { ud_binds = dict_binds1, ud_calls = new_calls })- <- runSpecM $ specCalls True top_env dict_binds- rules_for_fn good_calls fn rhs-- ; let spec_binds1 = [NonRec b r | (b,r) <- spec_pairs]- -- After the rules kick in we may get recursion, but- -- we rely on a global GlomBinds to sort that out later- -- See Note [Glom the bindings if imported functions are specialised]-- -- Now specialise any cascaded calls- ; -- debugTraceMsg (text "specImport 2" <+> (ppr fn $$ ppr rules1 $$ ppr spec_binds1))- ; (rules2, spec_binds2) <- spec_imports top_env- (fn:callers)- (extendRuleBaseList rb rules1)- (dict_binds `thenFDBs` dict_binds1)- new_calls-- ; let final_binds = wrapDictBinds dict_binds1 $- spec_binds2 ++ spec_binds1-- ; return (rules2 ++ rules1, final_binds) }-- | otherwise- = do { tryWarnMissingSpecs dflags callers fn good_calls- ; return ([], [])}-- where- dflags = se_dflags top_env- good_calls = filterCalls cis dict_binds- -- SUPER IMPORTANT! Drop calls that (directly or indirectly) refer to fn- -- See Note [Avoiding loops in specImports]--canSpecImport :: DynFlags -> Id -> Maybe CoreExpr--- See Note [Specialise imported INLINABLE things]-canSpecImport dflags fn- | CoreUnfolding { uf_src = src, uf_tmpl = rhs } <- unf- , isStableSource src- = Just rhs -- By default, specialise only imported things that have a stable- -- unfolding; that is, have an INLINE or INLINABLE pragma- -- Specialise even INLINE things; it hasn't inlined yet,- -- so perhaps it never will. Moreover it may have calls- -- inside it that we want to specialise-- -- CoreUnfolding case does /not/ include DFunUnfoldings;- -- We only specialise DFunUnfoldings with -fspecialise-aggressively- -- See Note [Do not specialise imported DFuns]-- | gopt Opt_SpecialiseAggressively dflags- = maybeUnfoldingTemplate unf -- With -fspecialise-aggressively, specialise anything- -- with an unfolding, stable or not, DFun or not-- | otherwise = Nothing- where- unf = realIdUnfolding fn -- We want to see the unfolding even for loop breakers---- | Returns whether or not to show a missed-spec warning.--- If -Wall-missed-specializations is on, show the warning.--- Otherwise, if -Wmissed-specializations is on, only show a warning--- if there is at least one imported function being specialized,--- and if all imported functions are marked with an inline pragma--- Use the most specific warning as the reason.-tryWarnMissingSpecs :: DynFlags -> [Id] -> Id -> [CallInfo] -> CoreM ()--- See Note [Warning about missed specialisations]-tryWarnMissingSpecs dflags callers fn calls_for_fn- | isClassOpId fn = return () -- See Note [Missed specialisation for ClassOps]- | wopt Opt_WarnMissedSpecs dflags- && not (null callers)- && allCallersInlined = doWarn $ WarningWithFlag Opt_WarnMissedSpecs- | wopt Opt_WarnAllMissedSpecs dflags = doWarn $ WarningWithFlag Opt_WarnAllMissedSpecs- | otherwise = return ()- where- allCallersInlined = all (isAnyInlinePragma . idInlinePragma) callers- diag_opts = initDiagOpts dflags- doWarn reason =- msg (mkMCDiagnostic diag_opts reason)- (vcat [ hang (text ("Could not specialise imported function") <+> quotes (ppr fn))- 2 (vcat [ text "when specialising" <+> quotes (ppr caller)- | caller <- callers])- , whenPprDebug (text "calls:" <+> vcat (map (pprCallInfo fn) calls_for_fn))- , text "Probable fix: add INLINABLE pragma on" <+> quotes (ppr fn) ])--{- Note [Missed specialisation for ClassOps]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-In #19592 I saw a number of missed specialisation warnings-which were the result of things like:-- case isJumpishInstr @X86.Instr $dInstruction_s7f8 eta3_a78C of { ...--where isJumpishInstr is part of the Instruction class and defined like-this:-- class Instruction instr where- ...- isJumpishInstr :: instr -> Bool- ...--isJumpishInstr is a ClassOp which will select the right method-from within the dictionary via our built in rules. See also-Note [ClassOp/DFun selection] in GHC.Tc.TyCl.Instance.--We don't give these unfoldings, and as a result the specialiser-complains. But usually this doesn't matter. The simplifier will-apply the rule and we end up with-- case isJumpishInstrImplX86 eta3_a78C of { ...--Since isJumpishInstrImplX86 is defined for a concrete instance (given-by the dictionary) it is usually already well specialised!-Theoretically the implementation of a method could still be overloaded-over a different type class than what it's a method of. But I wasn't able-to make this go wrong, and SPJ thinks this should be fine as well.--So I decided to remove the warnings for failed specialisations on ClassOps-alltogether as they do more harm than good.--}--{- Note [Do not specialise imported DFuns]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Ticket #18223 shows that specialising calls of DFuns is can cause a huge-and entirely unnecessary blowup in program size. Consider a call to- f @[[[[[[[[T]]]]]]]] d1 x-where df :: C a => C [a]- d1 :: C [[[[[[[[T]]]]]]]] = dfC[] @[[[[[[[T]]]]]]] d1- d2 :: C [[[[[[[T]]]]]]] = dfC[] @[[[[[[T]]]]]] d3- ...-Now we'll specialise f's RHS, which may give rise to calls to 'g',-also overloaded, which we will specialise, and so on. However, if-we specialise the calls to dfC[], we'll generate specialised copies of-all methods of C, at all types; and the same for C's superclasses.--And many of these specialised functions will never be called. We are-going to call the specialised 'f', and the specialised 'g', but DFuns-group functions into a tuple, many of whose elements may never be used.--With deeply-nested types this can lead to a simply overwhelming number-of specialisations: see #18223 for a simple example (from the wild).-I measured the number of specialisations for various numbers of calls-of `flip evalStateT ()`, and got this-- Size after one simplification- #calls #SPEC rules Terms Types- 5 56 3100 10600- 9 108 13660 77206--The real tests case has 60+ calls, which blew GHC out of the water.--Solution: don't specialise DFuns. The downside is that if we end-up with (h (dfun d)), /and/ we don't specialise 'h', then we won't-pass to 'h' a tuple of specialised functions.--However, the flag -fspecialise-aggressively (experimental, off by default)-allows DFuns to specialise as well.--Note [Avoiding loops in specImports]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We must take great care when specialising instance declarations-(DFuns like $fOrdList) lest we accidentally build a recursive-dictionary. See Note [Avoiding loops (DFuns)].--The basic strategy of Note [Avoiding loops (DFuns)] is to use filterCalls-to discard loopy specialisations. But to do that we must ensure-that the in-scope dict-binds (passed to filterCalls) contains-all the needed dictionary bindings. In particular, in the recursive-call to spec_imports in spec_import, we must include the dict-binds-from the parent. Lacking this caused #17151, a really nasty bug.--Here is what happened.-* Class structure:- Source is a superclass of Mut- Index is a superclass of Source--* We started with these dict binds- dSource = $fSourcePix @Int $fIndexInt- dIndex = sc_sel dSource- dMut = $fMutPix @Int dIndex- and these calls to specialise- $fMutPix @Int dIndex- $fSourcePix @Int $fIndexInt--* We specialised the call ($fMutPix @Int dIndex)- ==> new call ($fSourcePix @Int dIndex)- (because Source is a superclass of Mut)--* We specialised ($fSourcePix @Int dIndex)- ==> produces specialised dict $s$fSourcePix,- a record with dIndex as a field- plus RULE forall d. ($fSourcePix @Int d) = $s$fSourcePix- *** This is the bogus step ***--* Now we decide not to specialise the call- $fSourcePix @Int $fIndexInt- because we alredy have a RULE that matches it--* Finally the simplifer rewrites- dSource = $fSourcePix @Int $fIndexInt- ==> dSource = $s$fSourcePix--Disaster. Now we have--Rewrite dSource's RHS to $s$fSourcePix Disaster- dSource = $s$fSourcePix- dIndex = sc_sel dSource- $s$fSourcePix = MkSource dIndex ...--Solution: filterCalls should have stopped the bogus step,-by seeing that dIndex transitively uses $fSourcePix. But-it can only do that if it sees all the dict_binds. Wow.-----------------Here's another example (#13429). Suppose we have- class Monoid v => C v a where ...--We start with a call- f @ [Integer] @ Integer $fC[]Integer--Specialising call to 'f' gives dict bindings- $dMonoid_1 :: Monoid [Integer]- $dMonoid_1 = M.$p1C @ [Integer] $fC[]Integer-- $dC_1 :: C [Integer] (Node [Integer] Integer)- $dC_1 = M.$fCvNode @ [Integer] $dMonoid_1--...plus a recursive call to- f @ [Integer] @ (Node [Integer] Integer) $dC_1--Specialising that call gives- $dMonoid_2 :: Monoid [Integer]- $dMonoid_2 = M.$p1C @ [Integer] $dC_1-- $dC_2 :: C [Integer] (Node [Integer] Integer)- $dC_2 = M.$fCvNode @ [Integer] $dMonoid_2--Now we have two calls to the imported function- M.$fCvNode :: Monoid v => C v a- M.$fCvNode @v @a m = C m some_fun--But we must /not/ use the call (M.$fCvNode @ [Integer] $dMonoid_2)-for specialisation, else we get:-- $dC_1 = M.$fCvNode @ [Integer] $dMonoid_1- $dMonoid_2 = M.$p1C @ [Integer] $dC_1- $s$fCvNode = C $dMonoid_2 ...- RULE M.$fCvNode [Integer] _ _ = $s$fCvNode--Now use the rule to rewrite the call in the RHS of $dC_1-and we get a loop!---Note [specImport call stack]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-When specialising an imports function 'f', we may get new calls-of an imported function 'g', which we want to specialise in turn,-and similarly specialising 'g' might expose a new call to 'h'.--We track the stack of enclosing functions. So when specialising 'h' we-have a specImport call stack of [g,f]. We do this for two reasons:-* Note [Warning about missed specialisations]-* Note [Avoiding recursive specialisation]--Note [Warning about missed specialisations]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Suppose- * In module Lib, you carefully mark a function 'foo' INLINABLE- * Import Lib(foo) into another module M- * Call 'foo' at some specialised type in M-Then you jolly well expect it to be specialised in M. But what if-'foo' calls another function 'Lib.bar'. Then you'd like 'bar' to be-specialised too. But if 'bar' is not marked INLINABLE it may well-not be specialised. The warning Opt_WarnMissedSpecs warns about this.--It's more noisy to warning about a missed specialisation opportunity-for /every/ overloaded imported function, but sometimes useful. That-is what Opt_WarnAllMissedSpecs does.--ToDo: warn about missed opportunities for local functions.--Note [Avoiding recursive specialisation]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-When we specialise 'f' we may find new overloaded calls to 'g', 'h' in-'f's RHS. So we want to specialise g,h. But we don't want to-specialise f any more! It's possible that f's RHS might have a-recursive yet-more-specialised call, so we'd diverge in that case.-And if the call is to the same type, one specialisation is enough.-Avoiding this recursive specialisation loop is one reason for the-'callers' stack passed to specImports and specImport.--Note [Specialise imported INLINABLE things]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-What imported functions do we specialise? The basic set is- * DFuns and things with INLINABLE pragmas.-but with -fspecialise-aggressively we add- * Anything with an unfolding template--#8874 has a good example of why we want to auto-specialise DFuns.--We have the -fspecialise-aggressively flag (usually off), because we-risk lots of orphan modules from over-vigorous specialisation.-However it's not a big deal: anything non-recursive with an-unfolding-template will probably have been inlined already.--Note [Glom the bindings if imported functions are specialised]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Suppose we have an imported, *recursive*, INLINABLE function- f :: Eq a => a -> a- f = /\a \d x. ...(f a d)...-In the module being compiled we have- g x = f (x::Int)-Now we'll make a specialised function- f_spec :: Int -> Int- f_spec = \x -> ...(f Int dInt)...- {-# RULE f Int _ = f_spec #-}- g = \x. f Int dInt x-Note that f_spec doesn't look recursive-After rewriting with the RULE, we get- f_spec = \x -> ...(f_spec)...-BUT since f_spec was non-recursive before it'll *stay* non-recursive.-The occurrence analyser never turns a NonRec into a Rec. So we must-make sure that f_spec is recursive. Easiest thing is to make all-the specialisations for imported bindings recursive.----************************************************************************-* *-\subsubsection{@specExpr@: the main function}-* *-************************************************************************--}--data SpecEnv- = SE { se_subst :: Core.Subst- -- We carry a substitution down:- -- a) we must clone any binding that might float outwards,- -- to avoid name clashes- -- b) we carry a type substitution to use when analysing- -- the RHS of specialised bindings (no type-let!)--- , se_interesting :: VarSet- -- Dict Ids that we know something about- -- and hence may be worth specialising against- -- See Note [Interesting dictionary arguments]-- , se_module :: Module- , se_dflags :: DynFlags- }--instance Outputable SpecEnv where- ppr (SE { se_subst = subst, se_interesting = interesting })- = text "SE" <+> braces (sep $ punctuate comma- [ text "subst =" <+> ppr subst- , text "interesting =" <+> ppr interesting ])--specVar :: SpecEnv -> Id -> CoreExpr-specVar env v = Core.lookupIdSubst (se_subst env) v--specExpr :: SpecEnv -> CoreExpr -> SpecM (CoreExpr, UsageDetails)------------------ First the easy cases ---------------------specExpr env (Type ty) = return (Type (substTy env ty), emptyUDs)-specExpr env (Coercion co) = return (Coercion (substCo env co), emptyUDs)-specExpr env (Var v) = return (specVar env v, emptyUDs)-specExpr _ (Lit lit) = return (Lit lit, emptyUDs)-specExpr env (Cast e co)- = do { (e', uds) <- specExpr env e- ; return ((mkCast e' (substCo env co)), uds) }-specExpr env (Tick tickish body)- = do { (body', uds) <- specExpr env body- ; return (Tick (specTickish env tickish) body', uds) }------------------ Applications might generate a call instance ---------------------specExpr top_env expr@(App {})- = go top_env expr []- where- go env (App fun arg) args = do (arg', uds_arg) <- specExpr env arg- let env_args = env `bringFloatedDictsIntoScope` ud_binds uds_arg- -- Some dicts may have floated out of arg;- -- they should be in scope (#21689)- (fun', uds_app) <- go env_args fun (arg':args)- return (App fun' arg', uds_arg `thenUDs` uds_app)-- go env (Var f) args = case specVar env f of- Var f' -> return (Var f', mkCallUDs env f' args)- e' -> return (e', emptyUDs) -- I don't expect this!- go env other _ = specExpr env other------------------ Lambda/case require dumping of usage details ---------------------specExpr env e@(Lam {})- = specLam env' bndrs' body- where- (bndrs, body) = collectBinders e- (env', bndrs') = substBndrs env bndrs- -- More efficient to collect a group of binders together all at once- -- and we don't want to split a lambda group with dumped bindings--specExpr env (Case scrut case_bndr ty alts)- = do { (scrut', scrut_uds) <- specExpr env scrut- ; (scrut'', case_bndr', alts', alts_uds)- <- specCase env scrut' case_bndr alts- ; return (Case scrut'' case_bndr' (substTy env ty) alts'- , scrut_uds `thenUDs` alts_uds) }------------------ Finally, let is the interesting case ---------------------specExpr env (Let bind body)- = do { -- Clone binders- (rhs_env, body_env, bind') <- cloneBindSM env bind-- -- Deal with the body- ; (body', body_uds) <- specExpr body_env body-- -- Deal with the bindings- ; (binds', uds) <- specBind rhs_env bind' body_uds-- -- All done- ; return (foldr Let body' binds', uds) }-----------------specLam :: SpecEnv -> [OutBndr] -> InExpr -> SpecM (OutExpr, UsageDetails)--- The binders have been substituted, but the body has not-specLam env bndrs body- | null bndrs- = specExpr env body- | otherwise- = do { (body', uds) <- specExpr env body- ; let (free_uds, dumped_dbs) = dumpUDs bndrs uds- ; return (mkLams bndrs (wrapDictBindsE dumped_dbs body'), free_uds) }-----------------specTickish :: SpecEnv -> CoreTickish -> CoreTickish-specTickish env (Breakpoint ext ix ids)- = Breakpoint ext ix [ id' | id <- ids, Var id' <- [specVar env id]]- -- drop vars from the list if they have a non-variable substitution.- -- should never happen, but it's harmless to drop them anyway.-specTickish _ other_tickish = other_tickish-----------------specCase :: SpecEnv- -> CoreExpr -- Scrutinee, already done- -> Id -> [CoreAlt]- -> SpecM ( CoreExpr -- New scrutinee- , Id- , [CoreAlt]- , UsageDetails)-specCase env scrut' case_bndr [Alt con args rhs]- | isDictId case_bndr -- See Note [Floating dictionaries out of cases]- , interestingDict env scrut'- , not (isDeadBinder case_bndr && null sc_args')- = do { (case_bndr_flt : sc_args_flt) <- mapM clone_me (case_bndr' : sc_args')-- ; let sc_rhss = [ Case (Var case_bndr_flt) case_bndr' (idType sc_arg')- [Alt con args' (Var sc_arg')]- | sc_arg' <- sc_args' ]-- -- Extend the substitution for RHS to map the *original* binders- -- to their floated versions.- mb_sc_flts :: [Maybe DictId]- mb_sc_flts = map (lookupVarEnv clone_env) args'- clone_env = zipVarEnv sc_args' sc_args_flt- subst_prs = (case_bndr, Var case_bndr_flt)- : [ (arg, Var sc_flt)- | (arg, Just sc_flt) <- args `zip` mb_sc_flts ]- env_rhs' = env_rhs { se_subst = Core.extendIdSubstList (se_subst env_rhs) subst_prs- , se_interesting = se_interesting env_rhs `extendVarSetList`- (case_bndr_flt : sc_args_flt) }-- ; (rhs', rhs_uds) <- specExpr env_rhs' rhs- ; let scrut_bind = mkDB (NonRec case_bndr_flt scrut')- case_bndr_set = unitVarSet case_bndr_flt- sc_binds = [ DB { db_bind = NonRec sc_arg_flt sc_rhs- , db_fvs = case_bndr_set }- | (sc_arg_flt, sc_rhs) <- sc_args_flt `zip` sc_rhss ]- flt_binds = scrut_bind : sc_binds- (free_uds, dumped_dbs) = dumpUDs (case_bndr':args') rhs_uds- all_uds = flt_binds `consDictBinds` free_uds- alt' = Alt con args' (wrapDictBindsE dumped_dbs rhs')- ; return (Var case_bndr_flt, case_bndr', [alt'], all_uds) }- where- (env_rhs, (case_bndr':args')) = substBndrs env (case_bndr:args)- sc_args' = filter is_flt_sc_arg args'-- clone_me bndr = do { uniq <- getUniqueM- ; return (mkUserLocalOrCoVar occ uniq wght ty loc) }- where- name = idName bndr- wght = idMult bndr- ty = idType bndr- occ = nameOccName name- loc = getSrcSpan name-- arg_set = mkVarSet args'- is_flt_sc_arg var = isId var- && not (isDeadBinder var)- && isDictTy var_ty- && tyCoVarsOfType var_ty `disjointVarSet` arg_set- where- var_ty = idType var---specCase env scrut case_bndr alts- = do { (alts', uds_alts) <- mapAndCombineSM spec_alt alts- ; return (scrut, case_bndr', alts', uds_alts) }- where- (env_alt, case_bndr') = substBndr env case_bndr- spec_alt (Alt con args rhs) = do- (rhs', uds) <- specExpr env_rhs rhs- let (free_uds, dumped_dbs) = dumpUDs (case_bndr' : args') uds- return (Alt con args' (wrapDictBindsE dumped_dbs rhs'), free_uds)- where- (env_rhs, args') = substBndrs env_alt args--{--Note [Floating dictionaries out of cases]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider- g = \d. case d of { MkD sc ... -> ...(f sc)... }-Naively we can't float d2's binding out of the case expression,-because 'sc' is bound by the case, and that in turn means we can't-specialise f, which seems a pity.--So we invert the case, by floating out a binding-for 'sc_flt' thus:- sc_flt = case d of { MkD sc ... -> sc }-Now we can float the call instance for 'f'. Indeed this is just-what'll happen if 'sc' was originally bound with a let binding,-but case is more efficient, and necessary with equalities. So it's-good to work with both.--You might think that this won't make any difference, because the-call instance will only get nuked by the \d. BUT if 'g' itself is-specialised, then transitively we should be able to specialise f.--In general, given- case e of cb { MkD sc ... -> ...(f sc)... }-we transform to- let cb_flt = e- sc_flt = case cb_flt of { MkD sc ... -> sc }- in- case cb_flt of bg { MkD sc ... -> ....(f sc_flt)... }--The "_flt" things are the floated binds; we use the current substitution-to substitute sc -> sc_flt in the RHS--************************************************************************-* *- Dealing with a binding-* *-************************************************************************--}--bringFloatedDictsIntoScope :: SpecEnv -> FloatedDictBinds -> SpecEnv-bringFloatedDictsIntoScope env (FDB { fdb_bndrs = dx_bndrs })- = -- pprTrace "brought into scope" (ppr dx_bndrs) $- env {se_subst=subst'}- where- subst' = se_subst env `Core.extendSubstInScopeSet` dx_bndrs--specBind :: SpecEnv -- Use this for RHSs- -> CoreBind -- Binders are already cloned by cloneBindSM,- -- but RHSs are un-processed- -> UsageDetails -- Info on how the scope of the binding- -> SpecM ([CoreBind], -- New bindings- UsageDetails) -- And info to pass upstream---- Returned UsageDetails:--- No calls for binders of this bind-specBind rhs_env (NonRec fn rhs) body_uds- = do { (rhs', rhs_uds) <- specExpr rhs_env rhs-- ; let zapped_fn = zapIdDemandInfo fn- -- We zap the demand info because the binding may float,- -- which would invaidate the demand info (see #17810 for example).- -- Destroying demand info is not terrible; specialisation is- -- always followed soon by demand analysis.- ; (fn', spec_defns, body_uds1) <- specDefn rhs_env body_uds zapped_fn rhs-- ; let pairs = spec_defns ++ [(fn', rhs')]- -- fn' mentions the spec_defns in its rules,- -- so put the latter first-- combined_uds = body_uds1 `thenUDs` rhs_uds-- (free_uds, dump_dbs, float_all) = dumpBindUDs [fn] combined_uds-- final_binds :: [DictBind]- -- See Note [From non-recursive to recursive]- final_binds- | not (isEmptyBag dump_dbs)- , not (null spec_defns)- = [recWithDumpedDicts pairs dump_dbs]- | otherwise- = [mkDB $ NonRec b r | (b,r) <- pairs]- ++ bagToList dump_dbs-- can_float_this_one = exprIsTopLevelBindable rhs (idType fn)- -- exprIsTopLevelBindable: see Note [Care with unlifted bindings]-- ; if float_all && can_float_this_one then- -- Rather than discard the calls mentioning the bound variables- -- we float this (dictionary) binding along with the others- return ([], free_uds `snocDictBinds` final_binds)- else- -- No call in final_uds mentions bound variables,- -- so we can just leave the binding here- return (map db_bind final_binds, free_uds) }---specBind rhs_env (Rec pairs) body_uds- -- Note [Specialising a recursive group]- = do { let (bndrs,rhss) = unzip pairs- ; (rhss', rhs_uds) <- mapAndCombineSM (specExpr rhs_env) rhss- ; let scope_uds = body_uds `thenUDs` rhs_uds- -- Includes binds and calls arising from rhss-- ; (bndrs1, spec_defns1, uds1) <- specDefns rhs_env scope_uds pairs-- ; (bndrs3, spec_defns3, uds3)- <- if null spec_defns1 -- Common case: no specialisation- then return (bndrs1, [], uds1)- else do { -- Specialisation occurred; do it again- (bndrs2, spec_defns2, uds2)- <- specDefns rhs_env uds1 (bndrs1 `zip` rhss)- ; return (bndrs2, spec_defns2 ++ spec_defns1, uds2) }-- ; let (final_uds, dumped_dbs, float_all) = dumpBindUDs bndrs uds3- final_bind = recWithDumpedDicts (spec_defns3 ++ zip bndrs3 rhss')- dumped_dbs-- ; if float_all then- return ([], final_uds `snocDictBind` final_bind)- else- return ([db_bind final_bind], final_uds) }-------------------------------specDefns :: SpecEnv- -> UsageDetails -- Info on how it is used in its scope- -> [(OutId,InExpr)] -- The things being bound and their un-processed RHS- -> SpecM ([OutId], -- Original Ids with RULES added- [(OutId,OutExpr)], -- Extra, specialised bindings- UsageDetails) -- Stuff to fling upwards from the specialised versions---- Specialise a list of bindings (the contents of a Rec), but flowing usages--- upwards binding by binding. Example: { f = ...g ...; g = ...f .... }--- Then if the input CallDetails has a specialised call for 'g', whose specialisation--- in turn generates a specialised call for 'f', we catch that in this one sweep.--- But not vice versa (it's a fixpoint problem).--specDefns _env uds []- = return ([], [], uds)-specDefns env uds ((bndr,rhs):pairs)- = do { (bndrs1, spec_defns1, uds1) <- specDefns env uds pairs- ; (bndr1, spec_defns2, uds2) <- specDefn env uds1 bndr rhs- ; return (bndr1 : bndrs1, spec_defns1 ++ spec_defns2, uds2) }------------------------------specDefn :: SpecEnv- -> UsageDetails -- Info on how it is used in its scope- -> OutId -> InExpr -- The thing being bound and its un-processed RHS- -> SpecM (Id, -- Original Id with added RULES- [(Id,CoreExpr)], -- Extra, specialised bindings- UsageDetails) -- Stuff to fling upwards from the specialised versions--specDefn env body_uds fn rhs- = do { let (body_uds_without_me, calls_for_me) = callsForMe fn body_uds- rules_for_me = idCoreRules fn- dict_binds = ud_binds body_uds-- ; (rules, spec_defns, spec_uds) <- specCalls False env dict_binds- rules_for_me calls_for_me fn rhs-- ; return ( fn `addIdSpecialisations` rules- , spec_defns- , body_uds_without_me `thenUDs` spec_uds) }- -- It's important that the `thenUDs` is this way- -- round, because body_uds_without_me may bind- -- dictionaries that are used in calls_for_me passed- -- to specDefn. So the dictionary bindings in- -- spec_uds may mention dictionaries bound in- -- body_uds_without_me------------------------------specCalls :: Bool -- True => specialising imported fn- -- False => specialising local fn- -> SpecEnv- -> FloatedDictBinds -- Just so that we can extend the in-scope set- -> [CoreRule] -- Existing RULES for the fn- -> [CallInfo]- -> OutId -> InExpr- -> SpecM SpecInfo -- New rules, specialised bindings, and usage details---- This function checks existing rules, and does not create--- duplicate ones. So the caller does not need to do this filtering.--- See 'already_covered'--type SpecInfo = ( [CoreRule] -- Specialisation rules- , [(Id,CoreExpr)] -- Specialised definition- , UsageDetails ) -- Usage details from specialised RHSs--specCalls spec_imp env dict_binds existing_rules calls_for_me fn rhs- -- The first case is the interesting one- | notNull calls_for_me -- And there are some calls to specialise- && not (isNeverActive (idInlineActivation fn))- -- Don't specialise NOINLINE things- -- See Note [Auto-specialisation and RULES]- --- -- Don't specialise OPAQUE things, see Note [OPAQUE pragma].- -- Since OPAQUE things are always never-active (see- -- GHC.Parser.PostProcess.mkOpaquePragma) this guard never fires for- -- OPAQUE things.---- && not (certainlyWillInline (idUnfolding fn)) -- And it's not small--- See Note [Inline specialisations] for why we do not--- switch off specialisation for inline functions-- = -- pprTrace "specCalls: some" (ppr fn $$ ppr calls_for_me $$ ppr existing_rules) $- foldlM spec_call ([], [], emptyUDs) calls_for_me-- | otherwise -- No calls or RHS doesn't fit our preconceptions- = warnPprTrace (not (exprIsTrivial rhs) && notNull calls_for_me)- "Missed specialisation opportunity" (ppr fn $$ _trace_doc) $- -- Note [Specialisation shape]- -- pprTrace "specCalls: none" (ppr fn <+> ppr calls_for_me) $- return ([], [], emptyUDs)- where- _trace_doc = sep [ ppr rhs_bndrs, ppr (idInlineActivation fn) ]-- fn_type = idType fn- fn_arity = idArity fn- fn_unf = realIdUnfolding fn -- Ignore loop-breaker-ness here- inl_prag = idInlinePragma fn- inl_act = inlinePragmaActivation inl_prag- is_local = isLocalId fn- is_dfun = isDFunId fn- dflags = se_dflags env- this_mod = se_module env- -- Figure out whether the function has an INLINE pragma- -- See Note [Inline specialisations]-- (rhs_bndrs, rhs_body) = collectBindersPushingCo rhs- -- See Note [Account for casts in binding]-- -- Bring into scope the binders from the floated dicts- env_with_dict_bndrs = bringFloatedDictsIntoScope env dict_binds-- already_covered :: SpecEnv -> [CoreRule] -> [CoreExpr] -> Bool- already_covered env new_rules args -- Note [Specialisations already covered]- = isJust (specLookupRule env fn args (new_rules ++ existing_rules))- -- NB: we look both in the new_rules (generated by this invocation- -- of specCalls), and in existing_rules (passed in to specCalls)-- ----------------------------------------------------------- -- Specialise to one particular call pattern- spec_call :: SpecInfo -- Accumulating parameter- -> CallInfo -- Call instance- -> SpecM SpecInfo- spec_call spec_acc@(rules_acc, pairs_acc, uds_acc) _ci@(CI { ci_key = call_args })- = -- See Note [Specialising Calls]- do { let all_call_args | is_dfun = call_args ++ repeat UnspecArg- | otherwise = call_args- -- See Note [Specialising DFuns]- ; ( useful, rhs_env2, leftover_bndrs- , rule_bndrs, rule_lhs_args- , spec_bndrs1, dx_binds, spec_args) <- specHeader env_with_dict_bndrs- rhs_bndrs all_call_args---- ; pprTrace "spec_call" (vcat [ text "fun: " <+> ppr fn--- , text "call info: " <+> ppr _ci--- , text "useful: " <+> ppr useful--- , text "rule_bndrs:" <+> ppr rule_bndrs--- , text "lhs_args: " <+> ppr rule_lhs_args--- , text "spec_bndrs1:" <+> ppr spec_bndrs1--- , text "leftover_bndrs:" <+> pprIds leftover_bndrs--- , text "spec_args: " <+> ppr spec_args--- , text "dx_binds: " <+> ppr dx_binds--- , text "rhs_body" <+> ppr rhs_body--- , text "rhs_env2: " <+> ppr (se_subst rhs_env2)--- , ppr dx_binds ]) $--- return ()-- ; if not useful -- No useful specialisation- || already_covered rhs_env2 rules_acc rule_lhs_args- then return spec_acc- else- do { -- Run the specialiser on the specialised RHS- -- The "1" suffix is before we maybe add the void arg- ; (spec_rhs1, rhs_uds) <- specLam rhs_env2 (spec_bndrs1 ++ leftover_bndrs) rhs_body- ; let spec_fn_ty1 = exprType spec_rhs1-- -- Maybe add a void arg to the specialised function,- -- to avoid unlifted bindings- -- See Note [Specialisations Must Be Lifted]- -- C.f. GHC.Core.Opt.WorkWrap.Utils.needsVoidWorkerArg- add_void_arg = isUnliftedType spec_fn_ty1 && not (isJoinId fn)- (spec_bndrs, spec_rhs, spec_fn_ty)- | add_void_arg = ( voidPrimId : spec_bndrs1- , Lam voidArgId spec_rhs1- , mkVisFunTyMany unboxedUnitTy spec_fn_ty1)- | otherwise = (spec_bndrs1, spec_rhs1, spec_fn_ty1)-- join_arity_decr = length rule_lhs_args - length spec_bndrs-- simpl_opts = initSimpleOpts dflags-- --------------------------------------- -- Add a suitable unfolding if the spec_inl_prag says so- -- See Note [Inline specialisations]- (spec_inl_prag, spec_unf)- | not is_local && isStrongLoopBreaker (idOccInfo fn)- = (neverInlinePragma, noUnfolding)- -- See Note [Specialising imported functions] in "GHC.Core.Opt.OccurAnal"-- | isInlinablePragma inl_prag- = (inl_prag { inl_inline = NoUserInlinePrag }, noUnfolding)-- | otherwise- = (inl_prag, specUnfolding simpl_opts spec_bndrs (`mkApps` spec_args)- rule_lhs_args fn_unf)-- --------------------------------------- -- Adding arity information just propagates it a bit faster- -- See Note [Arity decrease] in GHC.Core.Opt.Simplify- -- Copy InlinePragma information from the parent Id.- -- So if f has INLINE[1] so does spec_fn- arity_decr = count isValArg rule_lhs_args - count isId spec_bndrs-- --------------------------------------- -- Add a suitable unfolding; see Note [Inline specialisations]- -- The wrap_unf_body applies the original unfolding to the specialised- -- arguments, not forgetting to wrap the dx_binds around the outside (#22358)- spec_fn_info- = vanillaIdInfo `setArityInfo` max 0 (fn_arity - arity_decr)- `setInlinePragInfo` spec_inl_prag- `setUnfoldingInfo` spec_unf-- -- Compute the IdDetails of the specialise Id- -- See Note [Transfer IdDetails during specialisation]- spec_fn_details- = case idDetails fn of- JoinId join_arity _ -> JoinId (join_arity - join_arity_decr) Nothing- DFunId is_nt -> DFunId is_nt- _ -> VanillaId-- ; spec_fn <- newSpecIdSM (idName fn) spec_fn_ty spec_fn_details spec_fn_info- ; let- -- The rule to put in the function's specialisation is:- -- forall x @b d1' d2'.- -- f x @T1 @b @T2 d1' d2' = f1 x @b- -- See Note [Specialising Calls]- herald | spec_imp = -- Specialising imported fn- text "SPEC/" <> ppr this_mod- | otherwise = -- Specialising local fn- text "SPEC"-- rule_name = mkFastString $ showSDoc dflags $- herald <+> ftext (occNameFS (getOccName fn))- <+> hsep (mapMaybe ppr_call_key_ty call_args)- -- This name ends up in interface files, so use occNameString.- -- Otherwise uniques end up there, making builds- -- less deterministic (See #4012 comment:61 ff)-- rule_wout_eta = mkRule- this_mod- True {- Auto generated -}- is_local- rule_name- inl_act -- Note [Auto-specialisation and RULES]- (idName fn)- rule_bndrs- rule_lhs_args- (mkVarApps (Var spec_fn) spec_bndrs)-- spec_rule- = case isJoinId_maybe fn of- Just join_arity -> etaExpandToJoinPointRule join_arity rule_wout_eta- Nothing -> rule_wout_eta-- -- Add the { d1' = dx1; d2' = dx2 } usage stuff- -- See Note [Specialising Calls]- spec_uds = foldr consDictBind rhs_uds dx_binds-- spec_f_w_arity = spec_fn-- _rule_trace_doc = vcat [ ppr fn <+> dcolon <+> ppr fn_type- , ppr spec_fn <+> dcolon <+> ppr spec_fn_ty- , ppr rhs_bndrs, ppr call_args- , ppr spec_rule- ]-- ; -- pprTrace "spec_call: rule" _rule_trace_doc- return ( spec_rule : rules_acc- , (spec_f_w_arity, spec_rhs) : pairs_acc- , spec_uds `thenUDs` uds_acc- ) } }---- Convenience function for invoking lookupRule from Specialise--- The SpecEnv's InScopeSet should include all the Vars in the [CoreExpr]-specLookupRule :: SpecEnv -> Id -> [CoreExpr] -> [CoreRule] -> Maybe (CoreRule, CoreExpr)-specLookupRule env fn args rules- = lookupRule ropts (in_scope, realIdUnfolding) (const True) fn args rules- where- dflags = se_dflags env- in_scope = Core.substInScope (se_subst env)- ropts = initRuleOpts dflags---{- Note [Specialising DFuns]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~-DFuns have a special sort of unfolding (DFunUnfolding), and it is-hard to specialise a DFunUnfolding to give another DFunUnfolding-unless the DFun is fully applied (#18120). So, in the case of DFunIds-we simply extend the CallKey with trailing UnspecArgs, so we'll-generate a rule that completely saturates the DFun.--There is an ASSERT that checks this, in the DFunUnfolding case of-GHC.Core.Unfold.specUnfolding.--Note [Transfer IdDetails during specialisation]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-When specialising a function, `newSpecIdSM` comes up with a fresh Id the-specialised RHS will be bound to. It is critical that we get the `IdDetails` of-the specialised Id correct:--* JoinId: We want the specialised Id to be a join point, too. But- we have to carefully adjust the arity--* DFunId: It is crucial that we also make the new Id a DFunId.- - First, because it obviously /is/ a DFun, having a DFunUnfolding and- all that; see Note [Specialising DFuns]-- - Second, DFuns get very delicate special treatment in the demand analyser;- see GHC.Core.Opt.DmdAnal.enterDFun. If the specialised function isn't- also a DFunId, this special treatment doesn't happen, so the demand- analyser makes a too-strict DFun, and we get an infinite loop. See Note- [Do not strictify a DFun's parameter dictionaries] in GHC.Core.Opt.DmdAnal.- #22549 describes the loop, and (lower down) a case where a /specialised/- DFun caused a loop.--* WorkerLikeId: Introduced by WW, so after Specialise. Nevertheless, they come- up when specialising imports. We must keep them as VanillaIds because WW- will detect them as WorkerLikeIds again. That is, unless specialisation- allows unboxing of all previous CBV args, in which case sticking to- VanillaIds was the only correct choice to begin with.--* RecSelId, DataCon*Id, ClassOpId, PrimOpId, FCallId, CoVarId, TickBoxId:- Never specialised.--Note [Specialisation Must Preserve Sharing]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider a function:-- f :: forall a. Eq a => a -> blah- f =- if expensive- then f1- else f2--As written, all calls to 'f' will share 'expensive'. But if we specialise 'f'-at 'Int', eg:-- $sfInt = SUBST[a->Int,dict->dEqInt] (if expensive then f1 else f2)-- RULE "SPEC f"- forall (d :: Eq Int).- f Int _ = $sfIntf--We've now lost sharing between 'f' and '$sfInt' for 'expensive'. Yikes!--To avoid this, we only generate specialisations for functions whose arity is-enough to bind all of the arguments we need to specialise. This ensures our-specialised functions don't do any work before receiving all of their dicts,-and thus avoids the 'f' case above.--Note [Specialisations Must Be Lifted]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider a function 'f':-- f = forall a. Eq a => Array# a--used like-- case x of- True -> ...f @Int dEqInt...- False -> 0--Naively, we might generate an (expensive) specialisation-- $sfInt :: Array# Int--even in the case that @x = False@! Instead, we add a dummy 'Void#' argument to-the specialisation '$sfInt' ($sfInt :: Void# -> Array# Int) in order to-preserve laziness.--Note [Care with unlifted bindings]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider (#22998)- f x = let x::ByteArray# = <some literal>- n::Natural = NB x- in wombat @192827 (n |> co)-where- co :: Natural ~ KnownNat 192827- wombat :: forall (n:Nat). KnownNat n => blah--Left to itself, the specialiser would float the bindings for `x` and `n` to top-level, so we can specialise `wombat`. But we can't have a top-level ByteArray#-(see Note [Core letrec invariant] in GHC.Core). Boo.--This is pretty exotic, so we take a simple way out: in specBind (the NonRec-case) do not float the binding itself unless it satisfies exprIsTopLevelBindable.-This is conservative: maybe the RHS of `x` has a free var that would stop it-floating to top level anyway; but that is hard to spot (since we don't know what-the non-top-level in-scope binders are) and rare (since the binding must satisfy-Note [Core let-can-float invariant] in GHC.Core).---Note [Specialising Calls]-~~~~~~~~~~~~~~~~~~~~~~~~~-Suppose we have a function with a complicated type:-- f :: forall a b c. Int -> Eq a => Show b => c -> Blah- f @a @b @c i dEqA dShowA x = blah--and suppose it is called at:-- f 7 @T1 @T2 @T3 dEqT1 ($dfShow dShowT2) t3--This call is described as a 'CallInfo' whose 'ci_key' is:-- [ SpecType T1, SpecType T2, UnspecType, UnspecArg, SpecDict dEqT1- , SpecDict ($dfShow dShowT2), UnspecArg ]--Why are 'a' and 'b' identified as 'SpecType', while 'c' is 'UnspecType'?-Because we must specialise the function on type variables that appear-free in its *dictionary* arguments; but not on type variables that do not-appear in any dictionaries, i.e. are fully polymorphic.--Because this call has dictionaries applied, we'd like to specialise-the call on any type argument that appears free in those dictionaries.-In this case, those are [a :-> T1, b :-> T2].--We also need to substitute the dictionary binders with their-specialised dictionaries. The simplest substitution would be-[dEqA :-> dEqT1, dShowA :-> $dfShow dShowT2], but this duplicates-work, since `$dfShow dShowT2` is a function application. Therefore, we-also want to *float the dictionary out* (via bindAuxiliaryDict),-creating a new dict binding-- dShow1 = $dfShow dShowT2--and the substitution [dEqA :-> dEqT1, dShowA :-> dShow1].--With the substitutions in hand, we can generate a specialised function:-- $sf :: forall c. Int -> c -> Blah- $sf = SUBST[a :-> T1, b :-> T2, dEqA :-> dEqT1, dShowA :-> dShow1] (\@c i x -> blah)--Note that the substitution is applied to the whole thing. This is-convenient, but just slightly fragile. Notably:- * There had better be no name clashes in a/b/c--We must construct a rewrite rule:-- RULE "SPEC f @T1 @T2 _"- forall (@c :: Type) (i :: Int) (d1 :: Eq T1) (d2 :: Show T2).- f @T1 @T2 @c i d1 d2 = $sf @c i--In the rule, d1 and d2 are just wildcards, not used in the RHS. Note-additionally that 'x' isn't captured by this rule --- we bind only-enough etas in order to capture all of the *specialised* arguments.--Note [Drop dead args from specialisations]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-When specialising a function, it’s possible some of the arguments may-actually be dead. For example, consider:-- f :: forall a. () -> Show a => a -> String- f x y = show y ++ "!"--We might generate the following CallInfo for `f @Int`:-- [SpecType Int, UnspecArg, SpecDict $dShowInt, UnspecArg]--Normally we’d include both the x and y arguments in the-specialisation, since we’re not specialising on either of them. But-that’s silly, since x is actually unused! So we might as well drop it-in the specialisation:-- $sf :: Int -> String- $sf y = show y ++ "!"-- {-# RULE "SPEC f @Int" forall x. f @Int x $dShow = $sf #-}--This doesn’t save us much, since the arg would be removed later by-worker/wrapper, anyway, but it’s easy to do. Note, however, that we-only drop dead arguments if:-- 1. We don’t specialise on them.- 2. They come before an argument we do specialise on.-- The right thing to do is to produce a LitRubbish; it should rapidly- disappear. Rather like GHC.Core.Opt.WorkWrap.Utils.mk_absent_let.--Note [Zap occ info in rule binders]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-When we generate a specialisation RULE, we need to drop occurrence-info on the binders. If we don’t, things go wrong when we specialise a-function like-- f :: forall a. () -> Show a => a -> String- f x y = show y ++ "!"--since we’ll generate a RULE like-- RULE "SPEC f @Int" forall x [Occ=Dead].- f @Int x $dShow = $sf--and Core Lint complains, even though x only appears on the LHS (due to-Note [Drop dead args from specialisations]).--Why is that a Lint error? Because the arguments on the LHS of a rule-are syntactically expressions, not patterns, so Lint treats the-appearance of x as a use rather than a binding. Fortunately, the-solution is simple: we just make sure to zap the occ info before-using ids as wildcard binders in a rule.--Note [Account for casts in binding]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider- f :: Eq a => a -> IO ()- {-# INLINABLE f- StableUnf = (/\a \(d:Eq a) (x:a). blah) |> g- #-}- f = ...--In f's stable unfolding we have done some modest simplification which-has pushed the cast to the outside. (I wonder if this is the Right-Thing, but it's what happens now; see GHC.Core.Opt.Simplify.Utils Note [Casts and-lambdas].) Now that stable unfolding must be specialised, so we want-to push the cast back inside. It would be terrible if the cast-defeated specialisation! Hence the use of collectBindersPushingCo.--Note [Evidence foralls]-~~~~~~~~~~~~~~~~~~~~~~~~~~-Suppose (#12212) that we are specialising- f :: forall a b. (Num a, F a ~ F b) => blah-with a=b=Int. Then the RULE will be something like- RULE forall (d:Num Int) (g :: F Int ~ F Int).- f Int Int d g = f_spec-But both varToCoreExpr (when constructing the LHS args), and the-simplifier (when simplifying the LHS args), will transform to- RULE forall (d:Num Int) (g :: F Int ~ F Int).- f Int Int d <F Int> = f_spec-by replacing g with Refl. So now 'g' is unbound, which results in a later-crash. So we use Refl right off the bat, and do not forall-quantify 'g':- * varToCoreExpr generates a Refl- * exprsFreeIdsList returns the Ids bound by the args,- which won't include g--You might wonder if this will match as often, but the simplifier replaces-complicated Refl coercions with Refl pretty aggressively.--Note [Orphans and auto-generated rules]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-When we specialise an INLINABLE function, or when we have--fspecialise-aggressively, we auto-generate RULES that are orphans.-We don't want to warn about these, or we'd generate a lot of warnings.-Thus, we only warn about user-specified orphan rules.--Indeed, we don't even treat the module as an orphan module if it has-auto-generated *rule* orphans. Orphan modules are read every time we-compile, so they are pretty obtrusive and slow down every compilation,-even non-optimised ones. (Reason: for type class instances it's a-type correctness issue.) But specialisation rules are strictly for-*optimisation* only so it's fine not to read the interface.--What this means is that a SPEC rules from auto-specialisation in-module M will be used in other modules only if M.hi has been read for-some other reason, which is actually pretty likely.--Note [From non-recursive to recursive]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Even in the non-recursive case, if any dict-binds depend on 'fn' we might-have built a recursive knot-- f a d x = <blah>- MkUD { ud_binds = NonRec d7 (MkD ..f..)- , ud_calls = ...(f T d7)... }--The we generate-- Rec { fs x = <blah>[T/a, d7/d]- f a d x = <blah>- RULE f T _ = fs- d7 = ...f... }--Here the recursion is only through the RULE.--However we definitely should /not/ make the Rec in this wildly common-case:- d = ...- MkUD { ud_binds = NonRec d7 (...d...)- , ud_calls = ...(f T d7)... }--Here we want simply to add d to the floats, giving- MkUD { ud_binds = NonRec d (...)- NonRec d7 (...d...)- , ud_calls = ...(f T d7)... }--In general, we need only make this Rec if- - there are some specialisations (spec_binds non-empty)- - there are some dict_binds that depend on f (dump_dbs non-empty)--Note [Avoiding loops (DFuns)]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-When specialising /dictionary functions/ we must be very careful to-avoid building loops. Here is an example that bit us badly, on-several distinct occasions.--Here is one: #3591- class Eq a => C a- instance Eq [a] => C [a]--This translates to- dfun :: Eq [a] -> C [a]- dfun a d = MkD a d (meth d)-- d4 :: Eq [T] = <blah>- d2 :: C [T] = dfun T d4- d1 :: Eq [T] = $p1 d2- d3 :: C [T] = dfun T d1--None of these definitions is recursive. What happened was that we-generated a specialisation:- RULE forall d. dfun T d = dT :: C [T]- dT = (MkD a d (meth d)) [T/a, d1/d]- = MkD T d1 (meth d1)--But now we use the RULE on the RHS of d2, to get- d2 = dT = MkD d1 (meth d1)- d1 = $p1 d2--and now d1 is bottom! The problem is that when specialising 'dfun' we-should first dump "below" the binding all floated dictionary bindings-that mention 'dfun' itself. So d2 and d3 (and hence d1) must be-placed below 'dfun', and thus unavailable to it when specialising-'dfun'. That in turn means that the call (dfun T d1) must be-discarded. On the other hand, the call (dfun T d4) is fine, assuming-d4 doesn't mention dfun.--Solution:- Discard all calls that mention dictionaries that depend- (directly or indirectly) on the dfun we are specialising.- This is done by 'filterCalls'--Note [Avoiding loops (non-DFuns)]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-The whole Note [Avoiding loops (DFuns)] things applies only to DFuns.-It's important /not/ to apply filterCalls to non-DFuns. For example:-- class C a where { foo,bar :: [a] -> [a] }-- instance C Int where- foo x = r_bar x- bar xs = reverse xs-- r_bar :: C a => [a] -> [a]- r_bar xs = bar (xs ++ xs)--That translates to:-- r_bar a (c::C a) (xs::[a]) = bar a d (xs ++ xs)-- Rec { $fCInt :: C Int = MkC foo_help reverse- foo_help (xs::[Int]) = r_bar Int $fCInt xs }--The call (r_bar $fCInt) mentions $fCInt,- which mentions foo_help,- which mentions r_bar--But we DO want to specialise r_bar at Int:- Rec { $fCInt :: C Int = MkC foo_help reverse- foo_help (xs::[Int]) = r_bar Int $fCInt xs-- r_bar a (c::C a) (xs::[a]) = bar a d (xs ++ xs)- RULE r_bar Int _ = r_bar_Int-- r_bar_Int xs = bar Int $fCInt (xs ++ xs)- }--Note that, because of its RULE, r_bar joins the recursive-group. (In this case it'll unravel a short moment later.)-See test simplCore/should_compile/T19599a.--Another example is #19599, which looked like this:-- class (Show a, Enum a) => MyShow a where- myShow :: a -> String-- myShow_impl :: MyShow a => a -> String-- foo :: Int -> String- foo = myShow_impl @Int $fMyShowInt-- Rec { $fMyShowInt = MkMyShowD $fEnumInt $fShowInt $cmyShow- ; $cmyShow = myShow_impl @Int $fMyShowInt }--Here, we really do want to specialise `myShow_impl @Int $fMyShowInt`.---Note [Specialising a recursive group]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider- let rec { f x = ...g x'...- ; g y = ...f y'.... }- in f 'a'-Here we specialise 'f' at Char; but that is very likely to lead to-a specialisation of 'g' at Char. We must do the latter, else the-whole point of specialisation is lost.--But we do not want to keep iterating to a fixpoint, because in the-presence of polymorphic recursion we might generate an infinite number-of specialisations.--So we use the following heuristic:- * Arrange the rec block in dependency order, so far as possible- (the occurrence analyser already does this)-- * Specialise it much like a sequence of lets-- * Then go through the block a second time, feeding call-info from- the RHSs back in the bottom, as it were--In effect, the ordering maxmimises the effectiveness of each sweep,-and we do just two sweeps. This should catch almost every case of-monomorphic recursion -- the exception could be a very knotted-up-recursion with multiple cycles tied up together.--This plan is implemented in the Rec case of specBindItself.--Note [Specialisations already covered]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We obviously don't want to generate two specialisations for the same-argument pattern. There are two wrinkles--1. We do the already-covered test in specDefn, not when we generate-the CallInfo in mkCallUDs. We used to test in the latter place, but-we now iterate the specialiser somewhat, and the Id at the call site-might therefore not have all the RULES that we can see in specDefn--2. What about two specialisations where the second is an *instance*-of the first? If the more specific one shows up first, we'll generate-specialisations for both. If the *less* specific one shows up first,-we *don't* currently generate a specialisation for the more specific-one. (See the call to lookupRule in already_covered.) Reasons:- (a) lookupRule doesn't say which matches are exact (bad reason)- (b) if the earlier specialisation is user-provided, it's- far from clear that we should auto-specialise further--Note [Auto-specialisation and RULES]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider:- g :: Num a => a -> a- g = ...-- f :: (Int -> Int) -> Int- f w = ...- {-# RULE f g = 0 #-}--Suppose that auto-specialisation makes a specialised version of-g::Int->Int That version won't appear in the LHS of the RULE for f.-So if the specialisation rule fires too early, the rule for f may-never fire.--It might be possible to add new rules, to "complete" the rewrite system.-Thus when adding- RULE forall d. g Int d = g_spec-also add- RULE f g_spec = 0--But that's a bit complicated. For now we ask the programmer's help,-by *copying the INLINE activation pragma* to the auto-specialised-rule. So if g says {-# NOINLINE[2] g #-}, then the auto-spec rule-will also not be active until phase 2. And that's what programmers-should jolly well do anyway, even aside from specialisation, to ensure-that g doesn't inline too early.--This in turn means that the RULE would never fire for a NOINLINE-thing so not much point in generating a specialisation at all.--Note [Specialisation shape]-~~~~~~~~~~~~~~~~~~~~~~~~~~~-We only specialise a function if it has visible top-level lambdas-corresponding to its overloading. E.g. if- f :: forall a. Eq a => ....-then its body must look like- f = /\a. \d. ...--Reason: when specialising the body for a call (f ty dexp), we want to-substitute dexp for d, and pick up specialised calls in the body of f.--We do allow casts, however; see Note [Account for casts in binding].--This doesn't always work. One example I came across was this:- newtype Gen a = MkGen{ unGen :: Int -> a }-- choose :: Eq a => a -> Gen a- choose n = MkGen (\r -> n)-- oneof = choose (1::Int)--It's a silly example, but we get- choose = /\a. g `cast` co-where choose doesn't have any dict arguments. Thus far I have not-tried to fix this (wait till there's a real example).--Mind you, then 'choose' will be inlined (since RHS is trivial) so-it doesn't matter. This comes up with single-method classes-- class C a where { op :: a -> a }- instance C a => C [a] where ....-==>- $fCList :: C a => C [a]- $fCList = $copList |> (...coercion>...)- ....(uses of $fCList at particular types)...--So we suppress the WARN if the rhs is trivial.--Note [Inline specialisations]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Here is what we do with the InlinePragma of the original function- * Activation/RuleMatchInfo: both transferred to the- specialised function- * InlineSpec:- (a) An INLINE pragma is transferred- (b) An INLINABLE pragma is *not* transferred--Why (a): transfer INLINE pragmas? The point of INLINE was precisely to-specialise the function at its call site, and arguably that's not so-important for the specialised copies. BUT *pragma-directed*-specialisation now takes place in the typechecker/desugarer, with-manually specified INLINEs. The specialisation here is automatic.-It'd be very odd if a function marked INLINE was specialised (because-of some local use), and then forever after (including importing-modules) the specialised version wasn't INLINEd. After all, the-programmer said INLINE!--You might wonder why we specialise INLINE functions at all. After-all they should be inlined, right? Two reasons:-- * Even INLINE functions are sometimes not inlined, when they aren't- applied to interesting arguments. But perhaps the type arguments- alone are enough to specialise (even though the args are too boring- to trigger inlining), and it's certainly better to call the- specialised version.-- * The RHS of an INLINE function might call another overloaded function,- and we'd like to generate a specialised version of that function too.- This actually happens a lot. Consider- replicateM_ :: (Monad m) => Int -> m a -> m ()- {-# INLINABLE replicateM_ #-}- replicateM_ d x ma = ...- The strictness analyser may transform to- replicateM_ :: (Monad m) => Int -> m a -> m ()- {-# INLINE replicateM_ #-}- replicateM_ d x ma = case x of I# x' -> $wreplicateM_ d x' ma-- $wreplicateM_ :: (Monad m) => Int# -> m a -> m ()- {-# INLINABLE $wreplicateM_ #-}- $wreplicateM_ = ...- Now an importing module has a specialised call to replicateM_, say- (replicateM_ dMonadIO). We certainly want to specialise $wreplicateM_!- This particular example had a huge effect on the call to replicateM_- in nofib/shootout/n-body.--Why (b): discard INLINABLE pragmas? See #4874 for persuasive examples.-Suppose we have- {-# INLINABLE f #-}- f :: Ord a => [a] -> Int- f xs = letrec f' = ...f'... in f'-Then, when f is specialised and optimised we might get- wgo :: [Int] -> Int#- wgo = ...wgo...- f_spec :: [Int] -> Int- f_spec xs = case wgo xs of { r -> I# r }-and we clearly want to inline f_spec at call sites. But if we still-have the big, un-optimised of f (albeit specialised) captured in an-INLINABLE pragma for f_spec, we won't get that optimisation.--So we simply drop INLINABLE pragmas when specialising. It's not really-a complete solution; ignoring specialisation for now, INLINABLE functions-don't get properly strictness analysed, for example. But it works well-for examples involving specialisation, which is the dominant use of-INLINABLE. See #4874.--}--{- *********************************************************************-* *- SpecArg, and specHeader-* *-********************************************************************* -}---- | An argument that we might want to specialise.--- See Note [Specialising Calls] for the nitty gritty details.-data SpecArg- =- -- | Type arguments that should be specialised, due to appearing- -- free in the type of a 'SpecDict'.- SpecType Type-- -- | Type arguments that should remain polymorphic.- | UnspecType-- -- | Dictionaries that should be specialised. mkCallUDs ensures- -- that only "interesting" dictionary arguments get a SpecDict;- -- see Note [Interesting dictionary arguments]- | SpecDict DictExpr-- -- | Value arguments that should not be specialised.- | UnspecArg--instance Outputable SpecArg where- ppr (SpecType t) = text "SpecType" <+> ppr t- ppr UnspecType = text "UnspecType"- ppr (SpecDict d) = text "SpecDict" <+> ppr d- ppr UnspecArg = text "UnspecArg"--specArgFreeVars :: SpecArg -> VarSet-specArgFreeVars (SpecType ty) = tyCoVarsOfType ty-specArgFreeVars (SpecDict dx) = exprFreeVars dx-specArgFreeVars UnspecType = emptyVarSet-specArgFreeVars UnspecArg = emptyVarSet--isSpecDict :: SpecArg -> Bool-isSpecDict (SpecDict {}) = True-isSpecDict _ = False---- | Given binders from an original function 'f', and the 'SpecArg's--- corresponding to its usage, compute everything necessary to build--- a specialisation.------ We will use the running example from Note [Specialising Calls]:------ f :: forall a b c. Int -> Eq a => Show b => c -> Blah--- f @a @b @c i dEqA dShowA x = blah------ Suppose we decide to specialise it at the following pattern:------ [ SpecType T1, SpecType T2, UnspecType, UnspecArg--- , SpecDict dEqT1, SpecDict ($dfShow dShowT2), UnspecArg ]------ We'd eventually like to build the RULE------ RULE "SPEC f @T1 @T2 _"--- forall (@c :: Type) (i :: Int) (d1 :: Eq T1) (d2 :: Show T2).--- f @T1 @T2 @c i d1 d2 = $sf @c i------ and the specialisation '$sf'------ $sf :: forall c. Int -> c -> Blah--- $sf = SUBST[a :-> T1, b :-> T2, dEqA :-> dEqT1, dShowA :-> dShow1] (\@c i x -> blah)------ where dShow1 is a floated binding created by bindAuxiliaryDict.------ The cases for 'specHeader' below are presented in the same order as this--- running example. The result of 'specHeader' for this example is as follows:------ ( -- Returned arguments--- env + [a :-> T1, b :-> T2, dEqA :-> dEqT1, dShowA :-> dShow1]--- , [x]------ -- RULE helpers--- , [c, i, d1, d2]--- , [T1, T2, c, i, d1, d2]------ -- Specialised function helpers--- , [c, i, x]--- , [dShow1 = $dfShow dShowT2]--- , [T1, T2, c, i, dEqT1, dShow1]--- )-specHeader- :: SpecEnv- -> [InBndr] -- The binders from the original function 'f'- -> [SpecArg] -- From the CallInfo- -> SpecM ( Bool -- True <=> some useful specialisation happened- -- Not the same as any (isSpecDict args) because- -- the args might be longer than bndrs-- -- Returned arguments- , SpecEnv -- Substitution to apply to the body of 'f'- , [OutBndr] -- Leftover binders from the original function 'f'- -- that don’t have a corresponding SpecArg-- -- RULE helpers- , [OutBndr] -- Binders for the RULE- , [OutExpr] -- Args for the LHS of the rule-- -- Specialised function helpers- , [OutBndr] -- Binders for $sf- , [DictBind] -- Auxiliary dictionary bindings- , [OutExpr] -- Specialised arguments for unfolding- -- Same length as "args for LHS of rule"- )---- We want to specialise on type 'T1', and so we must construct a substitution--- 'a->T1', as well as a LHS argument for the resulting RULE and unfolding--- details.-specHeader env (bndr : bndrs) (SpecType t : args)- = do { let env' = extendTvSubstList env [(bndr, t)]- ; (useful, env'', leftover_bndrs, rule_bs, rule_es, bs', dx, spec_args)- <- specHeader env' bndrs args- ; pure ( useful- , env''- , leftover_bndrs- , rule_bs- , Type t : rule_es- , bs'- , dx- , Type t : spec_args- )- }---- Next we have a type that we don't want to specialise. We need to perform--- a substitution on it (in case the type refers to 'a'). Additionally, we need--- to produce a binder, LHS argument and RHS argument for the resulting rule,--- /and/ a binder for the specialised body.-specHeader env (bndr : bndrs) (UnspecType : args)- = do { let (env', bndr') = substBndr env bndr- ; (useful, env'', leftover_bndrs, rule_bs, rule_es, bs', dx, spec_args)- <- specHeader env' bndrs args- ; pure ( useful- , env''- , leftover_bndrs- , bndr' : rule_bs- , varToCoreExpr bndr' : rule_es- , bndr' : bs'- , dx- , varToCoreExpr bndr' : spec_args- )- }---- Next we want to specialise the 'Eq a' dict away. We need to construct--- a wildcard binder to match the dictionary (See Note [Specialising Calls] for--- the nitty-gritty), as a LHS rule and unfolding details.-specHeader env (bndr : bndrs) (SpecDict d : args)- = do { (env1, bndr') <- newDictBndr env bndr -- See Note [Zap occ info in rule binders]- ; let (env2, dx_bind, spec_dict) = bindAuxiliaryDict env1 bndr bndr' d- ; (_, env3, leftover_bndrs, rule_bs, rule_es, bs', dx, spec_args)- <- specHeader env2 bndrs args- ; pure ( True -- Ha! A useful specialisation!- , env3- , leftover_bndrs- -- See Note [Evidence foralls]- , exprFreeIdsList (varToCoreExpr bndr') ++ rule_bs- , varToCoreExpr bndr' : rule_es- , bs'- , maybeToList dx_bind ++ dx- , spec_dict : spec_args- )- }---- Finally, we have the unspecialised argument 'i'. We need to produce--- a binder, LHS and RHS argument for the RULE, and a binder for the--- specialised body.------ NB: Calls to 'specHeader' will trim off any trailing 'UnspecArg's, which is--- why 'i' doesn't appear in our RULE above. But we have no guarantee that--- there aren't 'UnspecArg's which come /before/ all of the dictionaries, so--- this case must be here.-specHeader env (bndr : bndrs) (UnspecArg : args)- = do { -- see Note [Zap occ info in rule binders]- let (env', bndr') = substBndr env (zapIdOccInfo bndr)- ; (useful, env'', leftover_bndrs, rule_bs, rule_es, bs', dx, spec_args)- <- specHeader env' bndrs args- ; pure ( useful- , env''- , leftover_bndrs- , bndr' : rule_bs- , varToCoreExpr bndr' : rule_es- , if isDeadBinder bndr- then bs' -- see Note [Drop dead args from specialisations]- else bndr' : bs'- , dx- , varToCoreExpr bndr' : spec_args- )- }---- If we run out of binders, stop immediately--- See Note [Specialisation Must Preserve Sharing]-specHeader env [] _ = pure (False, env, [], [], [], [], [], [])---- Return all remaining binders from the original function. These have the--- invariant that they should all correspond to unspecialised arguments, so--- it's safe to stop processing at this point.-specHeader env bndrs []- = pure (False, env', bndrs', [], [], [], [], [])- where- (env', bndrs') = substBndrs env bndrs----- | Binds a dictionary argument to a fresh name, to preserve sharing-bindAuxiliaryDict- :: SpecEnv- -> InId -> OutId -> OutExpr -- Original dict binder, and the witnessing expression- -> ( SpecEnv -- Substitutes for orig_dict_id- , Maybe DictBind -- Auxiliary dict binding, if any- , OutExpr) -- Witnessing expression (always trivial)-bindAuxiliaryDict env@(SE { se_subst = subst, se_interesting = interesting })- orig_dict_id fresh_dict_id dict_expr-- -- If the dictionary argument is trivial,- -- don’t bother creating a new dict binding; just substitute- | Just dict_id <- getIdFromTrivialExpr_maybe dict_expr- = let env' = env { se_subst = Core.extendSubst subst orig_dict_id dict_expr- -- See Note [Keep the old dictionaries interesting]- , se_interesting = interesting `extendVarSet` dict_id }- in (env', Nothing, dict_expr)-- | otherwise -- Non-trivial dictionary arg; make an auxiliary binding- = let dict_bind = mkDB (NonRec fresh_dict_id dict_expr)- env' = env { se_subst = Core.extendSubst subst orig_dict_id (Var fresh_dict_id)- `Core.extendSubstInScope` fresh_dict_id- -- See Note [Make the new dictionaries interesting]- , se_interesting = interesting `extendVarSet` fresh_dict_id }- in (env', Just dict_bind, Var fresh_dict_id)--{--Note [Make the new dictionaries interesting]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Important! We're going to substitute dx_id1 for d-and we want it to look "interesting", else we won't gather *any*-consequential calls. E.g.- f d = ...g d....-If we specialise f for a call (f (dfun dNumInt)), we'll get-a consequent call (g d') with an auxiliary definition- d' = df dNumInt-We want that consequent call to look interesting--Note [Keep the old dictionaries interesting]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-In bindAuxiliaryDict, we don’t bother creating a new dict binding if-the dict expression is trivial. For example, if we have-- f = \ @m1 (d1 :: Monad m1) -> ...--and we specialize it at the pattern-- [SpecType IO, SpecArg $dMonadIO]--it would be silly to create a new binding for $dMonadIO; it’s already-a binding! So we just extend the substitution directly:-- m1 :-> IO- d1 :-> $dMonadIO--But this creates a new subtlety: the dict expression might be a dict-binding we floated out while specializing another function. For-example, we might have-- d2 = $p1Monad $dMonadIO -- floated out by bindAuxiliaryDict- $sg = h @IO d2- h = \ @m2 (d2 :: Applicative m2) -> ...--and end up specializing h at the following pattern:-- [SpecType IO, SpecArg d2]--When we created the d2 binding in the first place, we locally marked-it as interesting while specializing g as described above by-Note [Make the new dictionaries interesting]. But when we go to-specialize h, it isn’t in the SpecEnv anymore, so we’ve lost the-knowledge that we should specialize on it.--To fix this, we have to explicitly add d2 *back* to the interesting-set. That way, it will still be considered interesting while-specializing the body of h. See !2913.--}---{- *********************************************************************-* *- UsageDetails and suchlike-* *-********************************************************************* -}--data UsageDetails- = MkUD { ud_binds :: !FloatedDictBinds- , ud_calls :: !CallDetails }- -- INVARIANT: suppose bs = fdb_bndrs ud_binds- -- Then 'calls' may *mention* 'bs',- -- but there should be no calls *for* bs--data FloatedDictBinds -- See Note [Floated dictionary bindings]- = FDB { fdb_binds :: !(Bag DictBind)- -- The order is important;- -- in ds1 `unionBags` ds2, bindings in ds2 can depend on those in ds1- -- (Remember, Bags preserve order in GHC.)-- , fdb_bndrs :: !IdSet- } -- ^ The binders of 'fdb_binds'.- -- Caches a superset of the expression- -- `mkVarSet (bindersOfDictBinds fdb_binds))`- -- for later addition to an InScopeSet---- | A 'DictBind' is a binding along with a cached set containing its free--- variables (both type variables and dictionaries)-data DictBind = DB { db_bind :: CoreBind, db_fvs :: VarSet }--bindersOfDictBind :: DictBind -> [Id]-bindersOfDictBind = bindersOf . db_bind--bindersOfDictBinds :: Foldable f => f DictBind -> [Id]-bindersOfDictBinds = bindersOfBinds . foldr ((:) . db_bind) []--{- Note [Floated dictionary bindings]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We float out dictionary bindings for the reasons described under-"Dictionary floating" above. But not /just/ dictionary bindings.-Consider-- f :: Eq a => blah- f a d = rhs-- $c== :: T -> T -> Bool- $c== x y = ...-- $df :: Eq T- $df = Eq $c== ...-- gurgle = ...(f @T $df)...--We gather the call info for (f @T $df), and we don't want to drop it-when we come across the binding for $df. So we add $df to the floats-and continue. But then we have to add $c== to the floats, and so on.-These all float above the binding for 'f', and now we can-successfully specialise 'f'.--So the DictBinds in (ud_binds :: Bag DictBind) may contain-non-dictionary bindings too.--It's important to add the dictionary binders that are currently in-float to the-InScopeSet of the SpecEnv before calling 'specBind'. That's what we do when we-call 'bringFloatedDictsIntoScope'.--}--instance Outputable DictBind where- ppr (DB { db_bind = bind, db_fvs = fvs })- = text "DB" <+> braces (sep [ text "fvs: " <+> ppr fvs- , text "bind:" <+> ppr bind ])--instance Outputable UsageDetails where- ppr (MkUD { ud_binds = dbs, ud_calls = calls })- = text "MkUD" <+> braces (sep (punctuate comma- [text "binds" <+> equals <+> ppr dbs,- text "calls" <+> equals <+> ppr calls]))--instance Outputable FloatedDictBinds where- ppr (FDB { fdb_binds = binds }) = ppr binds--emptyUDs :: UsageDetails-emptyUDs = MkUD { ud_binds = emptyFDBs, ud_calls = emptyDVarEnv }---emptyFDBs :: FloatedDictBinds-emptyFDBs = FDB { fdb_binds = emptyBag, fdb_bndrs = emptyVarSet }---------------------------------------------------------------type CallDetails = DIdEnv CallInfoSet- -- The order of specialized binds and rules depends on how we linearize- -- CallDetails, so to get determinism we must use a deterministic set here.- -- See Note [Deterministic UniqFM] in GHC.Types.Unique.DFM--data CallInfoSet = CIS Id (Bag CallInfo)- -- The list of types and dictionaries is guaranteed to- -- match the type of f- -- The Bag may contain duplicate calls (i.e. f @T and another f @T)- -- These dups are eliminated by already_covered in specCalls--data CallInfo- = CI { ci_key :: [SpecArg] -- All arguments- , ci_fvs :: IdSet -- Free Ids of the ci_key call- -- _not_ including the main id itself, of course- -- NB: excluding tyvars:- }--type DictExpr = CoreExpr--ciSetFilter :: (CallInfo -> Bool) -> CallInfoSet -> CallInfoSet-ciSetFilter p (CIS id a) = CIS id (filterBag p a)--instance Outputable CallInfoSet where- ppr (CIS fn map) = hang (text "CIS" <+> ppr fn)- 2 (ppr map)--pprCallInfo :: Id -> CallInfo -> SDoc-pprCallInfo fn (CI { ci_key = key })- = ppr fn <+> ppr key--ppr_call_key_ty :: SpecArg -> Maybe SDoc-ppr_call_key_ty (SpecType ty) = Just $ char '@' <> pprParendType ty-ppr_call_key_ty UnspecType = Just $ char '_'-ppr_call_key_ty (SpecDict _) = Nothing-ppr_call_key_ty UnspecArg = Nothing--instance Outputable CallInfo where- ppr (CI { ci_key = key, ci_fvs = _fvs })- = text "CI" <> braces (sep (map ppr key))--unionCalls :: CallDetails -> CallDetails -> CallDetails-unionCalls c1 c2 = plusDVarEnv_C unionCallInfoSet c1 c2--unionCallInfoSet :: CallInfoSet -> CallInfoSet -> CallInfoSet-unionCallInfoSet (CIS f calls1) (CIS _ calls2) =- CIS f (calls1 `unionBags` calls2)--callDetailsFVs :: CallDetails -> VarSet-callDetailsFVs calls =- nonDetStrictFoldUDFM (unionVarSet . callInfoFVs) emptyVarSet calls- -- It's OK to use nonDetStrictFoldUDFM here because we forget the ordering- -- immediately by converting to a nondeterministic set.--callInfoFVs :: CallInfoSet -> VarSet-callInfoFVs (CIS _ call_info) =- foldr (\(CI { ci_fvs = fv }) vs -> unionVarSet fv vs) emptyVarSet call_info--getTheta :: [TyCoBinder] -> [PredType]-getTheta = fmap tyBinderType . filter isInvisibleBinder . filter (not . isNamedBinder)----------------------------------------------------------------singleCall :: Id -> [SpecArg] -> UsageDetails-singleCall id args- = MkUD {ud_binds = emptyFDBs,- ud_calls = unitDVarEnv id $ CIS id $- unitBag (CI { ci_key = args -- used to be tys- , ci_fvs = call_fvs }) }- where- call_fvs = foldr (unionVarSet . specArgFreeVars) emptyVarSet args- -- The type args (tys) are guaranteed to be part of the dictionary- -- types, because they are just the constrained types,- -- and the dictionary is therefore sure to be bound- -- inside the binding for any type variables free in the type;- -- hence it's safe to neglect tyvars free in tys when making- -- the free-var set for this call- -- BUT I don't trust this reasoning; play safe and include tys_fvs- --- -- We don't include the 'id' itself.--mkCallUDs, mkCallUDs' :: SpecEnv -> Id -> [CoreExpr] -> UsageDetails-mkCallUDs env f args- = -- pprTrace "mkCallUDs" (vcat [ ppr f, ppr args, ppr res ])- res- where- res = mkCallUDs' env f args--mkCallUDs' env f args- | wantCallsFor env f -- We want it, and...- , not (null ci_key) -- this call site has a useful specialisation- = -- pprTrace "mkCallUDs: keeping" _trace_doc- singleCall f ci_key-- | otherwise -- See also Note [Specialisations already covered]- = -- pprTrace "mkCallUDs: discarding" _trace_doc- emptyUDs-- where- _trace_doc = vcat [ppr f, ppr args, ppr ci_key]- pis = fst $ splitPiTys $ idType f- constrained_tyvars = tyCoVarsOfTypes $ getTheta pis-- ci_key :: [SpecArg]- ci_key = dropWhileEndLE (not . isSpecDict) $- zipWith mk_spec_arg args pis- -- Drop trailing args until we get to a SpecDict- -- In this way the RULE has as few args as possible,- -- which broadens its applicability, since rules only- -- fire when saturated-- mk_spec_arg :: CoreExpr -> TyCoBinder -> SpecArg- mk_spec_arg arg (Named bndr)- | binderVar bndr `elemVarSet` constrained_tyvars- = case arg of- Type ty -> SpecType ty- _ -> pprPanic "ci_key" $ ppr arg- | otherwise = UnspecType-- -- For "InvisArg", which are the type-class dictionaries,- -- we decide on a case by case basis if we want to specialise- -- on this argument; if so, SpecDict, if not UnspecArg- mk_spec_arg arg (Anon InvisArg pred)- | not (isIPLikePred (scaledThing pred))- -- See Note [Type determines value]- , interestingDict env arg- -- See Note [Interesting dictionary arguments]- = SpecDict arg-- | otherwise = UnspecArg-- mk_spec_arg _ (Anon VisArg _)- = UnspecArg--wantCallsFor :: SpecEnv -> Id -> Bool-wantCallsFor _env _f = True- -- We could reduce the size of the UsageDetails by being less eager- -- about collecting calls for LocalIds: there is no point for- -- ones that are lambda-bound. We can't decide this by looking at- -- the (absence of an) unfolding, because unfoldings for local- -- functions are discarded by cloneBindSM, so no local binder will- -- have an unfolding at this stage. We'd have to keep a candidate- -- set of let-binders.- --- -- Not many lambda-bound variables have dictionary arguments, so- -- this would make little difference anyway.- --- -- For imported Ids we could check for an unfolding, but we have to- -- do so anyway in canSpecImport, and it seems better to have it- -- all in one place. So we simply collect usage info for imported- -- overloaded functions.--{- Note [Type determines value]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Only specialise on non-impicit-parameter predicates, because these-are the ones whose *type* determines their *value*. In particular,-with implicit params, the type args *don't* say what the value of the-implicit param is! See #7101.--So we treat implicit params just like ordinary arguments for the-purposes of specialisation. Note that we still want to specialise-functions with implicit params if they have *other* dicts which are-class params; see #17930.--Note [Interesting dictionary arguments]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider this- \a.\d:Eq a. let f = ... in ...(f d)...-There really is not much point in specialising f wrt the dictionary d,-because the code for the specialised f is not improved at all, because-d is lambda-bound. We simply get junk specialisations.--What is "interesting"? Just that it has *some* structure. But what about-variables?-- * A variable might be imported, in which case its unfolding- will tell us whether it has useful structure-- * Local variables are cloned on the way down (to avoid clashes when- we float dictionaries), and cloning drops the unfolding- (cloneIdBndr). Moreover, we make up some new bindings, and it's a- nuisance to give them unfoldings. So we keep track of the- "interesting" dictionaries as a VarSet in SpecEnv.- We have to take care to put any new interesting dictionary- bindings in the set.--We accidentally lost accurate tracking of local variables for a long-time, because cloned variables don't have unfoldings. But makes a-massive difference in a few cases, eg #5113. For nofib as a-whole it's only a small win: 2.2% improvement in allocation for ansi,-1.2% for bspt, but mostly 0.0! Average 0.1% increase in binary size.--}--interestingDict :: SpecEnv -> CoreExpr -> Bool--- A dictionary argument is interesting if it has *some* structure--- NB: "dictionary" arguments include constraints of all sorts,--- including equality constraints; hence the Coercion case-interestingDict env (Var v) = hasSomeUnfolding (idUnfolding v)- || isDataConWorkId v- || v `elemVarSet` se_interesting env-interestingDict _ (Type _) = False-interestingDict _ (Coercion _) = False-interestingDict env (App fn (Type _)) = interestingDict env fn-interestingDict env (App fn (Coercion _)) = interestingDict env fn-interestingDict env (Tick _ a) = interestingDict env a-interestingDict env (Cast e _) = interestingDict env e-interestingDict _ _ = True--thenUDs :: UsageDetails -> UsageDetails -> UsageDetails-thenUDs (MkUD {ud_binds = db1, ud_calls = calls1})- (MkUD {ud_binds = db2, ud_calls = calls2})- = MkUD { ud_binds = db1 `thenFDBs` db2- , ud_calls = calls1 `unionCalls` calls2 }--thenFDBs :: FloatedDictBinds -> FloatedDictBinds -> FloatedDictBinds--- Combine FloatedDictBinds--- In (dbs1 `thenFDBs` dbs2), dbs2 may mention dbs1 but not vice versa-thenFDBs (FDB { fdb_binds = dbs1, fdb_bndrs = bs1 })- (FDB { fdb_binds = dbs2, fdb_bndrs = bs2 })- = FDB { fdb_binds = dbs1 `unionBags` dbs2- , fdb_bndrs = bs1 `unionVarSet` bs2 }--------------------------------_dictBindBndrs :: Bag DictBind -> [Id]-_dictBindBndrs dbs = foldr ((++) . bindersOf . db_bind) [] dbs---- | Construct a 'DictBind' from a 'CoreBind'-mkDB :: CoreBind -> DictBind-mkDB bind = DB { db_bind = bind, db_fvs = bind_fvs bind }---- | Identify the free variables of a 'CoreBind'-bind_fvs :: CoreBind -> VarSet-bind_fvs (NonRec bndr rhs) = pair_fvs (bndr,rhs)-bind_fvs (Rec prs) = rhs_fvs `delVarSetList` (map fst prs)- where- rhs_fvs = unionVarSets (map pair_fvs prs)--pair_fvs :: (Id, CoreExpr) -> VarSet-pair_fvs (bndr, rhs) = exprSomeFreeVars interesting rhs- `unionVarSet` idFreeVars bndr- -- idFreeVars: don't forget variables mentioned in- -- the rules of the bndr. C.f. OccAnal.addRuleUsage- -- Also tyvars mentioned in its type; they may not appear- -- in the RHS- -- type T a = Int- -- x :: T a = 3- where- interesting :: InterestingVarFun- interesting v = isLocalVar v || (isId v && isDFunId v)- -- Very important: include DFunIds /even/ if it is imported- -- Reason: See Note [Avoiding loops in specImports], the #13429- -- example involving an imported dfun. We must know- -- whether a dictionary binding depends on an imported- -- DFun in case we try to specialise that imported DFun---- | Flatten a set of "dumped" 'DictBind's, and some other binding--- pairs, into a single recursive binding.-recWithDumpedDicts :: [(Id,CoreExpr)] -> Bag DictBind -> DictBind-recWithDumpedDicts pairs dbs- = DB { db_bind = Rec bindings- , db_fvs = fvs `delVarSetList` map fst bindings }- where- (bindings, fvs) = foldr add ([], emptyVarSet)- (dbs `snocBag` mkDB (Rec pairs))- add (DB { db_bind = bind, db_fvs = fvs }) (prs_acc, fvs_acc)- = case bind of- NonRec b r -> ((b,r) : prs_acc, fvs')- Rec prs1 -> (prs1 ++ prs_acc, fvs')- where- fvs' = fvs_acc `unionVarSet` fvs--snocDictBind :: UsageDetails -> DictBind -> UsageDetails-snocDictBind uds@MkUD{ud_binds= FDB { fdb_binds = dbs, fdb_bndrs = bs }} db- = uds { ud_binds = FDB { fdb_binds = dbs `snocBag` db- , fdb_bndrs = bs `extendVarSetList` bindersOfDictBind db } }--snocDictBinds :: UsageDetails -> [DictBind] -> UsageDetails--- Add ud_binds to the tail end of the bindings in uds-snocDictBinds uds@MkUD{ud_binds=FDB{ fdb_binds = binds, fdb_bndrs = bs }} dbs- = uds { ud_binds = FDB { fdb_binds = binds `unionBags` listToBag dbs- , fdb_bndrs = bs `extendVarSetList` bindersOfDictBinds dbs } }--consDictBind :: DictBind -> UsageDetails -> UsageDetails-consDictBind db uds@MkUD{ud_binds=FDB{fdb_binds = binds, fdb_bndrs=bs}}- = uds { ud_binds = FDB { fdb_binds = db `consBag` binds- , fdb_bndrs = bs `extendVarSetList` bindersOfDictBind db } }--consDictBinds :: [DictBind] -> UsageDetails -> UsageDetails-consDictBinds dbs uds@MkUD{ud_binds=FDB{fdb_binds = binds, fdb_bndrs = bs}}- = uds { ud_binds = FDB{ fdb_binds = listToBag dbs `unionBags` binds- , fdb_bndrs = bs `extendVarSetList` bindersOfDictBinds dbs } }--wrapDictBinds :: FloatedDictBinds -> [CoreBind] -> [CoreBind]-wrapDictBinds (FDB { fdb_binds = dbs }) binds- = foldr add binds dbs- where- add (DB { db_bind = bind }) binds = bind : binds--wrapDictBindsE :: Bag DictBind -> CoreExpr -> CoreExpr-wrapDictBindsE dbs expr- = foldr add expr dbs- where- add (DB { db_bind = bind }) expr = Let bind expr-------------------------dumpUDs :: [CoreBndr] -> UsageDetails -> (UsageDetails, Bag DictBind)--- Used at a lambda or case binder; just dump anything mentioning the binder-dumpUDs bndrs uds@(MkUD { ud_binds = orig_dbs, ud_calls = orig_calls })- | null bndrs = (uds, emptyBag) -- Common in case alternatives- | otherwise = -- pprTrace "dumpUDs" (ppr bndrs $$ ppr free_uds $$ ppr dump_dbs) $- (free_uds, dump_dbs)- where- free_uds = uds { ud_binds = free_dbs, ud_calls = free_calls }- bndr_set = mkVarSet bndrs- (free_dbs, dump_dbs, dump_set) = splitDictBinds orig_dbs bndr_set- free_calls = deleteCallsMentioning dump_set $ -- Drop calls mentioning bndr_set on the floor- deleteCallsFor bndrs orig_calls -- Discard calls for bndr_set; there should be- -- no calls for any of the dicts in dump_dbs--dumpBindUDs :: [CoreBndr] -> UsageDetails -> (UsageDetails, Bag DictBind, Bool)--- Used at a let(rec) binding.--- We return a boolean indicating whether the binding itself is mentioned,--- directly or indirectly, by any of the ud_calls; in that case we want to--- float the binding itself;--- See Note [Floated dictionary bindings]-dumpBindUDs bndrs (MkUD { ud_binds = orig_dbs, ud_calls = orig_calls })- = -- pprTrace "dumpBindUDs" (ppr bndrs $$ ppr free_uds $$ ppr dump_dbs) $- (free_uds, dump_dbs, float_all)- where- free_uds = MkUD { ud_binds = free_dbs, ud_calls = free_calls }- bndr_set = mkVarSet bndrs- (free_dbs, dump_dbs, dump_set) = splitDictBinds orig_dbs bndr_set- free_calls = deleteCallsFor bndrs orig_calls- float_all = dump_set `intersectsVarSet` callDetailsFVs free_calls--callsForMe :: Id -> UsageDetails -> (UsageDetails, [CallInfo])-callsForMe fn uds@MkUD { ud_binds = orig_dbs, ud_calls = orig_calls }- = -- pprTrace ("callsForMe")- -- (vcat [ppr fn,- -- text "Orig dbs =" <+> ppr (_dictBindBndrs orig_dbs),- -- text "Orig calls =" <+> ppr orig_calls,- -- text "Calls for me =" <+> ppr calls_for_me]) $- (uds_without_me, calls_for_me)- where- uds_without_me = uds { ud_calls = delDVarEnv orig_calls fn }- calls_for_me = case lookupDVarEnv orig_calls fn of- Nothing -> []- Just cis -> filterCalls cis orig_dbs- -- filterCalls: drop calls that (directly or indirectly)- -- refer to fn. See Note [Avoiding loops (DFuns)]-------------------------filterCalls :: CallInfoSet -> FloatedDictBinds -> [CallInfo]--- Remove dominated calls (Note [Specialising polymorphic dictionaries])--- and loopy DFuns (Note [Avoiding loops (DFuns)])-filterCalls (CIS fn call_bag) (FDB { fdb_binds = dbs })- | isDFunId fn -- Note [Avoiding loops (DFuns)] applies only to DFuns- = filter ok_call unfiltered_calls- | otherwise -- Do not apply it to non-DFuns- = unfiltered_calls -- See Note [Avoiding loops (non-DFuns)]- where- unfiltered_calls = bagToList call_bag-- dump_set = foldl' go (unitVarSet fn) dbs- -- This dump-set could also be computed by splitDictBinds- -- (_,_,dump_set) = splitDictBinds dbs {fn}- -- But this variant is shorter-- go so_far (DB { db_bind = bind, db_fvs = fvs })- | fvs `intersectsVarSet` so_far- = extendVarSetList so_far (bindersOf bind)- | otherwise = so_far-- ok_call (CI { ci_fvs = fvs }) = fvs `disjointVarSet` dump_set-------------------------splitDictBinds :: FloatedDictBinds -> IdSet -> (FloatedDictBinds, Bag DictBind, IdSet)--- splitDictBinds dbs bndrs returns--- (free_dbs, dump_dbs, dump_set)--- where--- * dump_dbs depends, transitively on bndrs--- * free_dbs does not depend on bndrs--- * dump_set = bndrs `union` bndrs(dump_dbs)-splitDictBinds (FDB { fdb_binds = dbs, fdb_bndrs = bs }) bndr_set- = (FDB { fdb_binds = free_dbs- , fdb_bndrs = bs `minusVarSet` dump_set }- , dump_dbs, dump_set)- where- (free_dbs, dump_dbs, dump_set)- = foldl' split_db (emptyBag, emptyBag, bndr_set) dbs- -- Important that it's foldl' not foldr;- -- we're accumulating the set of dumped ids in dump_set-- split_db (free_dbs, dump_dbs, dump_idset) db- | DB { db_bind = bind, db_fvs = fvs } <- db- , dump_idset `intersectsVarSet` fvs -- Dump it- = (free_dbs, dump_dbs `snocBag` db,- extendVarSetList dump_idset (bindersOf bind))-- | otherwise -- Don't dump it- = (free_dbs `snocBag` db, dump_dbs, dump_idset)--------------------------deleteCallsMentioning :: VarSet -> CallDetails -> CallDetails--- Remove calls *mentioning* bs in any way-deleteCallsMentioning bs calls- = mapDVarEnv (ciSetFilter keep_call) calls- where- keep_call (CI { ci_fvs = fvs }) = fvs `disjointVarSet` bs--deleteCallsFor :: [Id] -> CallDetails -> CallDetails--- Remove calls *for* bs-deleteCallsFor bs calls = delDVarEnvList calls bs--{--************************************************************************-* *-\subsubsection{Boring helper functions}-* *-************************************************************************--}--type SpecM a = UniqSM a--runSpecM :: SpecM a -> CoreM a-runSpecM thing_inside- = do { us <- getUniqueSupplyM- ; return (initUs_ us thing_inside) }--mapAndCombineSM :: (a -> SpecM (b, UsageDetails)) -> [a] -> SpecM ([b], UsageDetails)-mapAndCombineSM _ [] = return ([], emptyUDs)-mapAndCombineSM f (x:xs) = do (y, uds1) <- f x- (ys, uds2) <- mapAndCombineSM f xs- return (y:ys, uds1 `thenUDs` uds2)--extendTvSubstList :: SpecEnv -> [(TyVar,Type)] -> SpecEnv-extendTvSubstList env tv_binds- = env { se_subst = Core.extendTvSubstList (se_subst env) tv_binds }--substTy :: SpecEnv -> Type -> Type-substTy env ty = Core.substTy (se_subst env) ty--substCo :: SpecEnv -> Coercion -> Coercion-substCo env co = Core.substCo (se_subst env) co--substBndr :: SpecEnv -> CoreBndr -> (SpecEnv, CoreBndr)-substBndr env bs = case Core.substBndr (se_subst env) bs of- (subst', bs') -> (env { se_subst = subst' }, bs')--substBndrs :: SpecEnv -> [CoreBndr] -> (SpecEnv, [CoreBndr])-substBndrs env bs = case Core.substBndrs (se_subst env) bs of- (subst', bs') -> (env { se_subst = subst' }, bs')--cloneBindSM :: SpecEnv -> CoreBind -> SpecM (SpecEnv, SpecEnv, CoreBind)--- Clone the binders of the bind; return new bind with the cloned binders--- Return the substitution to use for RHSs, and the one to use for the body-cloneBindSM env@(SE { se_subst = subst, se_interesting = interesting }) (NonRec bndr rhs)- = do { us <- getUniqueSupplyM- ; let (subst', bndr') = Core.cloneIdBndr subst us bndr- interesting' | interestingDict env rhs- = interesting `extendVarSet` bndr'- | otherwise = interesting--- ; pprTrace "cloneBindSM" (ppr bndr <+> text ":->" <+> ppr bndr') return ()- ; return (env, env { se_subst = subst', se_interesting = interesting' }- , NonRec bndr' rhs) }--cloneBindSM env@(SE { se_subst = subst, se_interesting = interesting }) (Rec pairs)- = do { us <- getUniqueSupplyM- ; let (subst', bndrs') = Core.cloneRecIdBndrs subst us (map fst pairs)- env' = env { se_subst = subst'- , se_interesting = interesting `extendVarSetList`- [ v | (v,r) <- pairs, interestingDict env r ] }- ; return (env', env', Rec (bndrs' `zip` map snd pairs)) }--newDictBndr :: SpecEnv -> CoreBndr -> SpecM (SpecEnv, CoreBndr)--- Make up completely fresh binders for the dictionaries--- Their bindings are going to float outwards-newDictBndr env@(SE { se_subst = subst }) b- = do { uniq <- getUniqueM- ; let n = idName b- ty' = Core.substTy subst (idType b)- b' = mkUserLocal (nameOccName n) uniq Many ty' (getSrcSpan n)- env' = env { se_subst = subst `Core.extendSubstInScope` b' }- ; pure (env', b') }--newSpecIdSM :: Name -> Type -> IdDetails -> IdInfo -> SpecM Id- -- Give the new Id a similar occurrence name to the old one-newSpecIdSM old_name new_ty details info- = do { uniq <- getUniqueM- ; let new_occ = mkSpecOcc (nameOccName old_name)- new_name = mkInternalName uniq new_occ (getSrcSpan old_name)- ; return (assert (not (isCoVarType new_ty)) $- mkLocalVar details new_name Many new_ty info) }+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}++{-+(c) The GRASP/AQUA Project, Glasgow University, 1993-1998++\section[Specialise]{Stamping out overloading, and (optionally) polymorphism}+-}++module GHC.Core.Opt.Specialise ( specProgram, specUnfolding ) where++import GHC.Prelude++import GHC.Driver.Session+import GHC.Driver.Config+import GHC.Driver.Config.Diagnostic+import GHC.Driver.Config.Core.Rules ( initRuleOpts )++import GHC.Core.Type hiding( substTy, substCo, extendTvSubst, zapSubst )+import GHC.Core.Multiplicity+import GHC.Core.SimpleOpt( defaultSimpleOpts, simpleOptExprWith )+import GHC.Core.Predicate+import GHC.Core.Coercion( Coercion )+import GHC.Core.Opt.Monad+import qualified GHC.Core.Subst as Core+import GHC.Core.Unfold.Make+import GHC.Core+import GHC.Core.Make ( mkLitRubbish )+import GHC.Core.Unify ( tcMatchTy )+import GHC.Core.Rules+import GHC.Core.Utils ( exprIsTrivial, exprIsTopLevelBindable+ , mkCast, exprType+ , stripTicksTop, mkInScopeSetBndrs )+import GHC.Core.FVs+import GHC.Core.TyCo.FVs ( tyCoVarsOfTypeList )+import GHC.Core.Opt.Arity( collectBindersPushingCo )+-- import GHC.Core.Ppr( pprIds )++import GHC.Builtin.Types ( unboxedUnitTy )++import GHC.Data.Maybe ( maybeToList, isJust )+import GHC.Data.Bag+import GHC.Data.OrdList+import GHC.Data.List.SetOps++import GHC.Types.Basic+import GHC.Types.Unique.Supply+import GHC.Types.Unique.DFM+import GHC.Types.Name+import GHC.Types.Tickish+import GHC.Types.Id.Make ( voidArgId, voidPrimId )+import GHC.Types.Var ( PiTyBinder(..), isLocalVar, isInvisibleFunArg )+import GHC.Types.Var.Set+import GHC.Types.Var.Env+import GHC.Types.Id+import GHC.Types.Error++import GHC.Utils.Error ( mkMCDiagnostic )+import GHC.Utils.Monad ( foldlM )+import GHC.Utils.Misc+import GHC.Utils.Outputable+import GHC.Utils.Panic++import GHC.Unit.Module( Module )+import GHC.Unit.Module.ModGuts+import GHC.Core.Unfold++import Data.List( partition )+import Data.List.NonEmpty ( NonEmpty (..) )++{-+************************************************************************+* *+\subsection[notes-Specialise]{Implementation notes [SLPJ, Aug 18 1993]}+* *+************************************************************************++These notes describe how we implement specialisation to eliminate+overloading.++The specialisation pass works on Core+syntax, complete with all the explicit dictionary application,+abstraction and construction as added by the type checker. The+existing type checker remains largely as it is.++One important thought: the {\em types} passed to an overloaded+function, and the {\em dictionaries} passed are mutually redundant.+If the same function is applied to the same type(s) then it is sure to+be applied to the same dictionary(s)---or rather to the same {\em+values}. (The arguments might look different but they will evaluate+to the same value.)++Second important thought: we know that we can make progress by+treating dictionary arguments as static and worth specialising on. So+we can do without binding-time analysis, and instead specialise on+dictionary arguments and no others.++The basic idea+~~~~~~~~~~~~~~+Suppose we have++ let f = <f_rhs>+ in <body>++and suppose f is overloaded.++STEP 1: CALL-INSTANCE COLLECTION++We traverse <body>, accumulating all applications of f to types and+dictionaries.++(Might there be partial applications, to just some of its types and+dictionaries? In principle yes, but in practice the type checker only+builds applications of f to all its types and dictionaries, so partial+applications could only arise as a result of transformation, and even+then I think it's unlikely. In any case, we simply don't accumulate such+partial applications.)+++STEP 2: EQUIVALENCES++So now we have a collection of calls to f:+ f t1 t2 d1 d2+ f t3 t4 d3 d4+ ...+Notice that f may take several type arguments. To avoid ambiguity, we+say that f is called at type t1/t2 and t3/t4.++We take equivalence classes using equality of the *types* (ignoring+the dictionary args, which as mentioned previously are redundant).++STEP 3: SPECIALISATION++For each equivalence class, choose a representative (f t1 t2 d1 d2),+and create a local instance of f, defined thus:++ f@t1/t2 = <f_rhs> t1 t2 d1 d2++f_rhs presumably has some big lambdas and dictionary lambdas, so lots+of simplification will now result. However we don't actually *do* that+simplification. Rather, we leave it for the simplifier to do. If we+*did* do it, though, we'd get more call instances from the specialised+RHS. We can work out what they are by instantiating the call-instance+set from f's RHS with the types t1, t2.++Add this new id to f's IdInfo, to record that f has a specialised version.++Before doing any of this, check that f's IdInfo doesn't already+tell us about an existing instance of f at the required type/s.+(This might happen if specialisation was applied more than once, or+it might arise from user SPECIALIZE pragmas.)++Recursion+~~~~~~~~~+Wait a minute! What if f is recursive? Then we can't just plug in+its right-hand side, can we?++But it's ok. The type checker *always* creates non-recursive definitions+for overloaded recursive functions. For example:++ f x = f (x+x) -- Yes I know its silly++becomes++ f a (d::Num a) = let p = +.sel a d+ in+ letrec fl (y::a) = fl (p y y)+ in+ fl++We still have recursion for non-overloaded functions which we+specialise, but the recursive call should get specialised to the+same recursive version.+++Polymorphism 1+~~~~~~~~~~~~~~++All this is crystal clear when the function is applied to *constant+types*; that is, types which have no type variables inside. But what if+it is applied to non-constant types? Suppose we find a call of f at type+t1/t2. There are two possibilities:++(a) The free type variables of t1, t2 are in scope at the definition point+of f. In this case there's no problem, we proceed just as before. A common+example is as follows. Here's the Haskell:++ g y = let f x = x+x+ in f y + f y++After typechecking we have++ g a (d::Num a) (y::a) = let f b (d'::Num b) (x::b) = +.sel b d' x x+ in +.sel a d (f a d y) (f a d y)++Notice that the call to f is at type type "a"; a non-constant type.+Both calls to f are at the same type, so we can specialise to give:++ g a (d::Num a) (y::a) = let f@a (x::a) = +.sel a d x x+ in +.sel a d (f@a y) (f@a y)+++(b) The other case is when the type variables in the instance types+are *not* in scope at the definition point of f. The example we are+working with above is a good case. There are two instances of (+.sel a d),+but "a" is not in scope at the definition of +.sel. Can we do anything?+Yes, we can "common them up", a sort of limited common sub-expression deal.+This would give:++ g a (d::Num a) (y::a) = let +.sel@a = +.sel a d+ f@a (x::a) = +.sel@a x x+ in +.sel@a (f@a y) (f@a y)++This can save work, and can't be spotted by the type checker, because+the two instances of +.sel weren't originally at the same type.++Further notes on (b)++* There are quite a few variations here. For example, the defn of+ +.sel could be floated outside the \y, to attempt to gain laziness.+ It certainly mustn't be floated outside the \d because the d has to+ be in scope too.++* We don't want to inline f_rhs in this case, because+that will duplicate code. Just commoning up the call is the point.++* Nothing gets added to +.sel's IdInfo.++* Don't bother unless the equivalence class has more than one item!++Not clear whether this is all worth it. It is of course OK to+simply discard call-instances when passing a big lambda.++Polymorphism 2 -- Overloading+~~~~~~~~~~~~~~+Consider a function whose most general type is++ f :: forall a b. Ord a => [a] -> b -> b++There is really no point in making a version of g at Int/Int and another+at Int/Bool, because it's only instantiating the type variable "a" which+buys us any efficiency. Since g is completely polymorphic in b there+ain't much point in making separate versions of g for the different+b types.++That suggests that we should identify which of g's type variables+are constrained (like "a") and which are unconstrained (like "b").+Then when taking equivalence classes in STEP 2, we ignore the type args+corresponding to unconstrained type variable. In STEP 3 we make+polymorphic versions. Thus:++ f@t1/ = /\b -> <f_rhs> t1 b d1 d2++We do this.+++Dictionary floating+~~~~~~~~~~~~~~~~~~~+Consider this++ f a (d::Num a) = let g = ...+ in+ ...(let d1::Ord a = Num.Ord.sel a d in g a d1)...++Here, g is only called at one type, but the dictionary isn't in scope at the+definition point for g. Usually the type checker would build a+definition for d1 which enclosed g, but the transformation system+might have moved d1's defn inward. Solution: float dictionary bindings+outwards along with call instances.++Consider++ f x = let g p q = p==q+ h r s = (r+s, g r s)+ in+ h x x+++Before specialisation, leaving out type abstractions we have++ f df x = let g :: Eq a => a -> a -> Bool+ g dg p q = == dg p q+ h :: Num a => a -> a -> (a, Bool)+ h dh r s = let deq = eqFromNum dh+ in (+ dh r s, g deq r s)+ in+ h df x x++After specialising h we get a specialised version of h, like this:++ h' r s = let deq = eqFromNum df+ in (+ df r s, g deq r s)++But we can't naively make an instance for g from this, because deq is not in scope+at the defn of g. Instead, we have to float out the (new) defn of deq+to widen its scope. Notice that this floating can't be done in advance -- it only+shows up when specialisation is done.++User SPECIALIZE pragmas+~~~~~~~~~~~~~~~~~~~~~~~+Specialisation pragmas can be digested by the type checker, and implemented+by adding extra definitions along with that of f, in the same way as before++ f@t1/t2 = <f_rhs> t1 t2 d1 d2++Indeed the pragmas *have* to be dealt with by the type checker, because+only it knows how to build the dictionaries d1 and d2! For example++ g :: Ord a => [a] -> [a]+ {-# SPECIALIZE f :: [Tree Int] -> [Tree Int] #-}++Here, the specialised version of g is an application of g's rhs to the+Ord dictionary for (Tree Int), which only the type checker can conjure+up. There might not even *be* one, if (Tree Int) is not an instance of+Ord! (All the other specialisation has suitable dictionaries to hand+from actual calls.)++Problem. The type checker doesn't have to hand a convenient <f_rhs>, because+it is buried in a complex (as-yet-un-desugared) binding group.+Maybe we should say++ f@t1/t2 = f* t1 t2 d1 d2++where f* is the Id f with an IdInfo which says "inline me regardless!".+Indeed all the specialisation could be done in this way.+That in turn means that the simplifier has to be prepared to inline absolutely+any in-scope let-bound thing.+++Again, the pragma should permit polymorphism in unconstrained variables:++ h :: Ord a => [a] -> b -> b+ {-# SPECIALIZE h :: [Int] -> b -> b #-}++We *insist* that all overloaded type variables are specialised to ground types,+(and hence there can be no context inside a SPECIALIZE pragma).+We *permit* unconstrained type variables to be specialised to+ - a ground type+ - or left as a polymorphic type variable+but nothing in between. So++ {-# SPECIALIZE h :: [Int] -> [c] -> [c] #-}++is *illegal*. (It can be handled, but it adds complication, and gains the+programmer nothing.)+++SPECIALISING INSTANCE DECLARATIONS+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider++ instance Foo a => Foo [a] where+ ...+ {-# SPECIALIZE instance Foo [Int] #-}++The original instance decl creates a dictionary-function+definition:++ dfun.Foo.List :: forall a. Foo a -> Foo [a]++The SPECIALIZE pragma just makes a specialised copy, just as for+ordinary function definitions:++ dfun.Foo.List@Int :: Foo [Int]+ dfun.Foo.List@Int = dfun.Foo.List Int dFooInt++The information about what instance of the dfun exist gets added to+the dfun's IdInfo in the same way as a user-defined function too.+++Automatic instance decl specialisation?+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Can instance decls be specialised automatically? It's tricky.+We could collect call-instance information for each dfun, but+then when we specialised their bodies we'd get new call-instances+for ordinary functions; and when we specialised their bodies, we might get+new call-instances of the dfuns, and so on. This all arises because of+the unrestricted mutual recursion between instance decls and value decls.++Still, there's no actual problem; it just means that we may not do all+the specialisation we could theoretically do.++Furthermore, instance decls are usually exported and used non-locally,+so we'll want to compile enough to get those specialisations done.++Lastly, there's no such thing as a local instance decl, so we can+survive solely by spitting out *usage* information, and then reading that+back in as a pragma when next compiling the file. So for now,+we only specialise instance decls in response to pragmas.+++SPITTING OUT USAGE INFORMATION+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++To spit out usage information we need to traverse the code collecting+call-instance information for all imported (non-prelude?) functions+and data types. Then we equivalence-class it and spit it out.++This is done at the top-level when all the call instances which escape+must be for imported functions and data types.++*** Not currently done ***+++Partial specialisation by pragmas+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+What about partial specialisation:++ k :: (Ord a, Eq b) => [a] -> b -> b -> [a]+ {-# SPECIALIZE k :: Eq b => [Int] -> b -> b -> [a] #-}++or even++ {-# SPECIALIZE k :: Eq b => [Int] -> [b] -> [b] -> [a] #-}++Seems quite reasonable. Similar things could be done with instance decls:++ instance (Foo a, Foo b) => Foo (a,b) where+ ...+ {-# SPECIALIZE instance Foo a => Foo (a,Int) #-}+ {-# SPECIALIZE instance Foo b => Foo (Int,b) #-}++Ho hum. Things are complex enough without this. I pass.+++Requirements for the simplifier+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+The simplifier has to be able to take advantage of the specialisation.++* When the simplifier finds an application of a polymorphic f, it looks in+f's IdInfo in case there is a suitable instance to call instead. This converts++ f t1 t2 d1 d2 ===> f_t1_t2++Note that the dictionaries get eaten up too!++* Dictionary selection operations on constant dictionaries must be+ short-circuited:++ +.sel Int d ===> +Int++The obvious way to do this is in the same way as other specialised+calls: +.sel has inside it some IdInfo which tells that if it's applied+to the type Int then it should eat a dictionary and transform to +Int.++In short, dictionary selectors need IdInfo inside them for constant+methods.++* Exactly the same applies if a superclass dictionary is being+ extracted:++ Eq.sel Int d ===> dEqInt++* Something similar applies to dictionary construction too. Suppose+dfun.Eq.List is the function taking a dictionary for (Eq a) to+one for (Eq [a]). Then we want++ dfun.Eq.List Int d ===> dEq.List_Int++Where does the Eq [Int] dictionary come from? It is built in+response to a SPECIALIZE pragma on the Eq [a] instance decl.++In short, dfun Ids need IdInfo with a specialisation for each+constant instance of their instance declaration.++All this uses a single mechanism: the SpecEnv inside an Id+++What does the specialisation IdInfo look like?+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++The SpecEnv of an Id maps a list of types (the template) to an expression++ [Type] |-> Expr++For example, if f has this RuleInfo:++ [Int, a] -> \d:Ord Int. f' a++it means that we can replace the call++ f Int t ===> (\d. f' t)++This chucks one dictionary away and proceeds with the+specialised version of f, namely f'.+++What can't be done this way?+~~~~~~~~~~~~~~~~~~~~~~~~~~~~+There is no way, post-typechecker, to get a dictionary for (say)+Eq a from a dictionary for Eq [a]. So if we find++ ==.sel [t] d++we can't transform to++ eqList (==.sel t d')++where+ eqList :: (a->a->Bool) -> [a] -> [a] -> Bool++Of course, we currently have no way to automatically derive+eqList, nor to connect it to the Eq [a] instance decl, but you+can imagine that it might somehow be possible. Taking advantage+of this is permanently ruled out.++Still, this is no great hardship, because we intend to eliminate+overloading altogether anyway!++A note about non-tyvar dictionaries+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Some Ids have types like++ forall a,b,c. Eq a -> Ord [a] -> tau++This seems curious at first, because we usually only have dictionary+args whose types are of the form (C a) where a is a type variable.+But this doesn't hold for the functions arising from instance decls,+which sometimes get arguments with types of form (C (T a)) for some+type constructor T.++Should we specialise wrt this compound-type dictionary? We used to say+"no", saying:+ "This is a heuristic judgement, as indeed is the fact that we+ specialise wrt only dictionaries. We choose *not* to specialise+ wrt compound dictionaries because at the moment the only place+ they show up is in instance decls, where they are simply plugged+ into a returned dictionary. So nothing is gained by specialising+ wrt them."++But it is simpler and more uniform to specialise wrt these dicts too;+and in future GHC is likely to support full fledged type signatures+like+ f :: Eq [(a,b)] => ...+++Note [Specialisation and overlapping instances]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Here is at tricky case (see a comment in MR !8916):++ module A where+ class C a where+ meth :: a -> String+ instance {-# OVERLAPPABLE #-} C (Maybe a) where+ meth _ = "Maybe"++ {-# SPECIALISE f :: Maybe a -> Bool -> String #-}+ f :: C a => a -> Bool -> String+ f a True = f a False+ f a _ = meth a++ module B where+ import A++ instance C (Maybe Int) where+ meth _ = "Int"++ main = putStrLn $ f (Just 42 :: Maybe Int) True++Running main without optimisations yields "Int", the correct answer.+Activating optimisations yields "Maybe" due to a rewrite rule in module+A generated by the SPECIALISE pragma:++ RULE "USPEC f" forall a (d :: C a). f @a d = $sf++In B we get the call (f @(Maybe Int) (d :: C (Maybe Int))), and+that rewrites to $sf, but that isn't really right.++Overlapping instances mean that `C (Maybe Int)` is not a singleton+type: there two distinct dictionaries that have this type. And that+spells trouble for specialistion, which really asssumes singleton+types.++For now, we just accept this problem, but it may bite us one day.+One solution would be to decline to expose any specialisation rules+to an importing module -- but that seems a bit drastic.+++************************************************************************+* *+\subsubsection{The new specialiser}+* *+************************************************************************++Our basic game plan is this. For let(rec) bound function+ f :: (C a, D c) => (a,b,c,d) -> Bool++* Find any specialised calls of f, (f ts ds), where+ ts are the type arguments t1 .. t4, and+ ds are the dictionary arguments d1 .. d2.++* Add a new definition for f1 (say):++ f1 = /\ b d -> (..body of f..) t1 b t3 d d1 d2++ Note that we abstract over the unconstrained type arguments.++* Add the mapping++ [t1,b,t3,d] |-> \d1 d2 -> f1 b d++ to the specialisations of f. This will be used by the+ simplifier to replace calls+ (f t1 t2 t3 t4) da db+ by+ (\d1 d1 -> f1 t2 t4) da db++ All the stuff about how many dictionaries to discard, and what types+ to apply the specialised function to, are handled by the fact that the+ SpecEnv contains a template for the result of the specialisation.++We don't build *partial* specialisations for f. For example:++ f :: Eq a => a -> a -> Bool+ {-# SPECIALISE f :: (Eq b, Eq c) => (b,c) -> (b,c) -> Bool #-}++Here, little is gained by making a specialised copy of f.+There's a distinct danger that the specialised version would+first build a dictionary for (Eq b, Eq c), and then select the (==)+method from it! Even if it didn't, not a great deal is saved.++We do, however, generate polymorphic, but not overloaded, specialisations:++ f :: Eq a => [a] -> b -> b -> b+ ... SPECIALISE f :: [Int] -> b -> b -> b ...++Hence, the invariant is this:++ *** no specialised version is overloaded ***+++************************************************************************+* *+\subsubsection{The exported function}+* *+************************************************************************+-}++-- | Specialise calls to type-class overloaded functions occurring in a program.+specProgram :: ModGuts -> CoreM ModGuts+specProgram guts@(ModGuts { mg_module = this_mod+ , mg_rules = local_rules+ , mg_binds = binds })+ = do { dflags <- getDynFlags+ ; rule_env <- initRuleEnv guts+ -- See Note [Fire rules in the specialiser]++ -- We need to start with a Subst that knows all the things+ -- that are in scope, so that the substitution engine doesn't+ -- accidentally re-use a unique that's already in use+ -- Easiest thing is to do it all at once, as if all the top-level+ -- decls were mutually recursive+ ; let top_env = SE { se_subst = Core.mkEmptySubst $+ mkInScopeSetBndrs binds+ -- mkInScopeSetList $+ -- bindersOfBinds binds+ , se_module = this_mod+ , se_rules = rule_env+ , se_dflags = dflags }++ go [] = return ([], emptyUDs)+ go (bind:binds) = do (bind', binds', uds') <- specBind TopLevel top_env bind $ \_ ->+ go binds+ return (bind' ++ binds', uds')++ -- Specialise the bindings of this module+ ; (binds', uds) <- runSpecM (go binds)++ ; (spec_rules, spec_binds) <- specImports top_env uds++ ; return (guts { mg_binds = spec_binds ++ binds'+ , mg_rules = spec_rules ++ local_rules }) }++{-+Note [Wrap bindings returned by specImports]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+'specImports' returns a set of specialized bindings. However, these are lacking+necessary floated dictionary bindings, which are returned by+UsageDetails(ud_binds). These dictionaries need to be brought into scope with+'wrapDictBinds' before the bindings returned by 'specImports' can be used. See,+for instance, the 'specImports' call in 'specProgram'.+++Note [Disabling cross-module specialisation]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Since GHC 7.10 we have performed specialisation of INLINABLE bindings living+in modules outside of the current module. This can sometimes uncover user code+which explodes in size when aggressively optimized. The+-fno-cross-module-specialise option was introduced to allow users to being+bitten by such instances to revert to the pre-7.10 behavior.++See #10491+-}+++{- *********************************************************************+* *+ Specialising imported functions+* *+********************************************************************* -}++{- Note [Specialising imported functions]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+specImports specialises imported functions, based on calls in this module.++When -fspecialise-aggressively is on, we specialise any imported+function for which we have an unfolding. The+-fspecialise-aggressively flag is usually off, because we risk lots of+orphan modules from over-vigorous specialisation. (See Note [Orphans]+in GHC.Core.) However it's not a big deal: anything non-recursive with+an unfolding-template will probably have been inlined already.++When -fspecialise-aggressively is off, we are more selective about+specialisation (see canSpecImport):++(1) Without -fspecialise-aggressively, do not specialise+ DFunUnfoldings. Note [Do not specialise imported DFuns].++(2) Without -fspecialise-aggressively, specialise only imported things+ that have a /user-supplied/ INLINE or INLINABLE pragma (hence+ isAnyInlinePragma rather than isStableSource).++ In particular, we don't want to specialise workers created by+ worker/wrapper (for functions with no pragma) because they won't+ specialise usefully, and they generate quite a bit of useless code+ bloat.++ Specialise even INLINE things; it hasn't inlined yet, so perhaps+ it never will. Moreover it may have calls inside it that we want+ to specialise++Wrinkle (W1): If we specialise an imported Id M.foo, we make a /local/+binding $sfoo. But specImports may further specialise $sfoo. So we end up+with RULES for both M.foo (imported) and $sfoo (local). Rules for local+Ids should be attached to the Ids themselves (see GHC.HsToCore+Note [Attach rules to local ids]); so we must partition the rules and+attach the local rules. That is done in specImports, via addRulesToId.++Note [Glom the bindings if imported functions are specialised]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Suppose we have an imported, *recursive*, INLINABLE function+ f :: Eq a => a -> a+ f = /\a \d x. ...(f a d)...+In the module being compiled we have+ g x = f (x::Int)+Now we'll make a specialised function+ f_spec :: Int -> Int+ f_spec = \x -> ...(f Int dInt)...+ {-# RULE f Int _ = f_spec #-}+ g = \x. f Int dInt x+Note that f_spec doesn't look recursive+After rewriting with the RULE, we get+ f_spec = \x -> ...(f_spec)...+BUT since f_spec was non-recursive before it'll *stay* non-recursive.+The occurrence analyser never turns a NonRec into a Rec. So we must+make sure that f_spec is recursive. Easiest thing is to make all+the specialisations for imported bindings recursive.+-}++specImports :: SpecEnv+ -> UsageDetails+ -> CoreM ([CoreRule], [CoreBind])+specImports top_env (MkUD { ud_binds = dict_binds, ud_calls = calls })+ | not $ gopt Opt_CrossModuleSpecialise (se_dflags top_env)+ -- See Note [Disabling cross-module specialisation]+ = return ([], wrapDictBinds dict_binds [])++ | otherwise+ = do { let env_w_dict_bndrs = top_env `bringFloatedDictsIntoScope` dict_binds+ ; (_env, spec_rules, spec_binds) <- spec_imports env_w_dict_bndrs [] dict_binds calls++ -- Make a Rec: see Note [Glom the bindings if imported functions are specialised]+ --+ -- wrapDictBinds: don't forget to wrap the specialized bindings with+ -- bindings for the needed dictionaries.+ -- See Note [Wrap bindings returned by specImports]+ --+ -- addRulesToId: see Wrinkle (W1) in Note [Specialising imported functions]+ -- c.f. GHC.HsToCore.addExportFlagsAndRules+ ; let (rules_for_locals, rules_for_imps) = partition isLocalRule spec_rules+ local_rule_base = extendRuleBaseList emptyRuleBase rules_for_locals+ final_binds+ | null spec_binds = wrapDictBinds dict_binds []+ | otherwise = [Rec $ mapFst (addRulesToId local_rule_base) $+ flattenBinds $+ wrapDictBinds dict_binds $+ spec_binds]++ ; return (rules_for_imps, final_binds)+ }++-- | Specialise a set of calls to imported bindings+spec_imports :: SpecEnv -- Passed in so that all top-level Ids are in scope+ ---In-scope set includes the FloatedDictBinds+ -> [Id] -- Stack of imported functions being specialised+ -- See Note [specImport call stack]+ -> FloatedDictBinds -- Dict bindings, used /only/ for filterCalls+ -- See Note [Avoiding loops in specImports]+ -> CallDetails -- Calls for imported things+ -> CoreM ( SpecEnv -- Env contains the new rules+ , [CoreRule] -- New rules+ , [CoreBind] ) -- Specialised bindings+spec_imports env callers dict_binds calls+ = do { let import_calls = dVarEnvElts calls+-- ; debugTraceMsg (text "specImports {" <+>+-- vcat [ text "calls:" <+> ppr import_calls+-- , text "dict_binds:" <+> ppr dict_binds ])+ ; (env, rules, spec_binds) <- go env import_calls+-- ; debugTraceMsg (text "End specImports }" <+> ppr import_calls)++ ; return (env, rules, spec_binds) }+ where+ go :: SpecEnv -> [CallInfoSet] -> CoreM (SpecEnv, [CoreRule], [CoreBind])+ go env [] = return (env, [], [])+ go env (cis : other_calls)+ = do { -- debugTraceMsg (text "specImport {" <+> ppr cis)+ ; (env, rules1, spec_binds1) <- spec_import env callers dict_binds cis+ ; -- debugTraceMsg (text "specImport }" <+> ppr cis)++ ; (env, rules2, spec_binds2) <- go env other_calls+ ; return (env, rules1 ++ rules2, spec_binds1 ++ spec_binds2) }++spec_import :: SpecEnv -- Passed in so that all top-level Ids are in scope+ ---In-scope set includes the FloatedDictBinds+ -> [Id] -- Stack of imported functions being specialised+ -- See Note [specImport call stack]+ -> FloatedDictBinds -- Dict bindings, used /only/ for filterCalls+ -- See Note [Avoiding loops in specImports]+ -> CallInfoSet -- Imported function and calls for it+ -> CoreM ( SpecEnv+ , [CoreRule] -- New rules+ , [CoreBind] ) -- Specialised bindings+spec_import env callers dict_binds cis@(CIS fn _)+ | isIn "specImport" fn callers+ = return (env, [], []) -- No warning. This actually happens all the time+ -- when specialising a recursive function, because+ -- the RHS of the specialised function contains a recursive+ -- call to the original function++ | null good_calls+ = return (env, [], [])++ | Just rhs <- canSpecImport dflags fn+ = do { -- Get rules from the external package state+ -- We keep doing this in case we "page-fault in"+ -- more rules as we go along+ ; eps_rules <- getExternalRuleBase+ ; let rule_env = se_rules env `updExternalPackageRules` eps_rules++-- ; debugTraceMsg (text "specImport1" <+> vcat+-- [ text "function:" <+> ppr fn+-- , text "good calls:" <+> ppr good_calls+-- , text "existing rules:" <+> ppr (getRules rule_env fn)+-- , text "rhs:" <+> ppr rhs+-- , text "dict_binds:" <+> ppr dict_binds ])++ ; (rules1, spec_pairs, MkUD { ud_binds = dict_binds1, ud_calls = new_calls })+ <- runSpecM $ specCalls True env (getRules rule_env fn) good_calls fn rhs++ ; let spec_binds1 = [NonRec b r | (b,r) <- spec_pairs]+ -- After the rules kick in, via fireRewriteRules, we may get recursion,+ -- but we rely on a global GlomBinds to sort that out later+ -- See Note [Glom the bindings if imported functions are specialised]+ -- Meanwhile, though, bring the binders into scope++ new_subst = se_subst env `Core.extendSubstInScopeList` map fst spec_pairs+ new_env = env { se_rules = rule_env `addLocalRules` rules1+ , se_subst = new_subst }+ `bringFloatedDictsIntoScope` dict_binds1++ -- Now specialise any cascaded calls+-- ; debugTraceMsg (text "specImport 2" <+> vcat+-- [ text "function:" <+> ppr fn+-- , text "rules1:" <+> ppr rules1+-- , text "spec_binds1" <+> ppr spec_binds1+-- , text "dict_binds1" <+> ppr dict_binds1+-- , text "new_calls" <+> ppr new_calls ])++ ; (env, rules2, spec_binds2)+ <- spec_imports new_env (fn:callers)+ (dict_binds `thenFDBs` dict_binds1)+ new_calls++ ; let final_binds = wrapDictBinds dict_binds1 $+ spec_binds2 ++ spec_binds1++ ; return (env, rules2 ++ rules1, final_binds) }++ | otherwise+ = do { tryWarnMissingSpecs dflags callers fn good_calls+ ; return (env, [], [])}++ where+ dflags = se_dflags env+ good_calls = filterCalls cis dict_binds+ -- SUPER IMPORTANT! Drop calls that (directly or indirectly) refer to fn+ -- See Note [Avoiding loops in specImports]++canSpecImport :: DynFlags -> Id -> Maybe CoreExpr+canSpecImport dflags fn+ | isDataConWrapId fn+ = Nothing -- Don't specialise data-con wrappers, even if they+ -- have dict args; there is no benefit.++ | CoreUnfolding { uf_tmpl = rhs } <- unf+ -- CoreUnfolding: see Note [Specialising imported functions] point (1).+ , isAnyInlinePragma (idInlinePragma fn)+ -- See Note [Specialising imported functions] point (2).+ = Just rhs++ | gopt Opt_SpecialiseAggressively dflags+ = maybeUnfoldingTemplate unf+ -- With -fspecialise-aggressively, specialise anything+ -- with an unfolding, stable or not, DFun or not++ | otherwise = Nothing+ where+ unf = realIdUnfolding fn -- We want to see the unfolding even for loop breakers++-- | Returns whether or not to show a missed-spec warning.+-- If -Wall-missed-specializations is on, show the warning.+-- Otherwise, if -Wmissed-specializations is on, only show a warning+-- if there is at least one imported function being specialized,+-- and if all imported functions are marked with an inline pragma+-- Use the most specific warning as the reason.+tryWarnMissingSpecs :: DynFlags -> [Id] -> Id -> [CallInfo] -> CoreM ()+-- See Note [Warning about missed specialisations]+tryWarnMissingSpecs dflags callers fn calls_for_fn+ | isClassOpId fn = return () -- See Note [Missed specialisation for ClassOps]+ | wopt Opt_WarnMissedSpecs dflags+ && not (null callers)+ && allCallersInlined = doWarn $ WarningWithFlag Opt_WarnMissedSpecs+ | wopt Opt_WarnAllMissedSpecs dflags = doWarn $ WarningWithFlag Opt_WarnAllMissedSpecs+ | otherwise = return ()+ where+ allCallersInlined = all (isAnyInlinePragma . idInlinePragma) callers+ diag_opts = initDiagOpts dflags+ doWarn reason =+ msg (mkMCDiagnostic diag_opts reason Nothing)+ (vcat [ hang (text ("Could not specialise imported function") <+> quotes (ppr fn))+ 2 (vcat [ text "when specialising" <+> quotes (ppr caller)+ | caller <- callers])+ , whenPprDebug (text "calls:" <+> vcat (map (pprCallInfo fn) calls_for_fn))+ , text "Probable fix: add INLINABLE pragma on" <+> quotes (ppr fn) ])++{- Note [Missed specialisation for ClassOps]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+In #19592 I saw a number of missed specialisation warnings+which were the result of things like:++ case isJumpishInstr @X86.Instr $dInstruction_s7f8 eta3_a78C of { ...++where isJumpishInstr is part of the Instruction class and defined like+this:++ class Instruction instr where+ ...+ isJumpishInstr :: instr -> Bool+ ...++isJumpishInstr is a ClassOp which will select the right method+from within the dictionary via our built in rules. See also+Note [ClassOp/DFun selection] in GHC.Tc.TyCl.Instance.++We don't give these unfoldings, and as a result the specialiser+complains. But usually this doesn't matter. The simplifier will+apply the rule and we end up with++ case isJumpishInstrImplX86 eta3_a78C of { ...++Since isJumpishInstrImplX86 is defined for a concrete instance (given+by the dictionary) it is usually already well specialised!+Theoretically the implementation of a method could still be overloaded+over a different type class than what it's a method of. But I wasn't able+to make this go wrong, and SPJ thinks this should be fine as well.++So I decided to remove the warnings for failed specialisations on ClassOps+alltogether as they do more harm than good.+-}++{- Note [Do not specialise imported DFuns]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Ticket #18223 shows that specialising calls of DFuns is can cause a huge+and entirely unnecessary blowup in program size. Consider a call to+ f @[[[[[[[[T]]]]]]]] d1 x+where df :: C a => C [a]+ d1 :: C [[[[[[[[T]]]]]]]] = dfC[] @[[[[[[[T]]]]]]] d1+ d2 :: C [[[[[[[T]]]]]]] = dfC[] @[[[[[[T]]]]]] d3+ ...+Now we'll specialise f's RHS, which may give rise to calls to 'g',+also overloaded, which we will specialise, and so on. However, if+we specialise the calls to dfC[], we'll generate specialised copies of+all methods of C, at all types; and the same for C's superclasses.++And many of these specialised functions will never be called. We are+going to call the specialised 'f', and the specialised 'g', but DFuns+group functions into a tuple, many of whose elements may never be used.++With deeply-nested types this can lead to a simply overwhelming number+of specialisations: see #18223 for a simple example (from the wild).+I measured the number of specialisations for various numbers of calls+of `flip evalStateT ()`, and got this++ Size after one simplification+ #calls #SPEC rules Terms Types+ 5 56 3100 10600+ 9 108 13660 77206++The real tests case has 60+ calls, which blew GHC out of the water.++Solution: don't specialise DFuns. The downside is that if we end+up with (h (dfun d)), /and/ we don't specialise 'h', then we won't+pass to 'h' a tuple of specialised functions.++However, the flag -fspecialise-aggressively (experimental, off by default)+allows DFuns to specialise as well.++Note [Avoiding loops in specImports]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+We must take great care when specialising instance declarations+(DFuns like $fOrdList) lest we accidentally build a recursive+dictionary. See Note [Avoiding loops (DFuns)].++The basic strategy of Note [Avoiding loops (DFuns)] is to use filterCalls+to discard loopy specialisations. But to do that we must ensure+that the in-scope dict-binds (passed to filterCalls) contains+all the needed dictionary bindings. In particular, in the recursive+call to spec_imports in spec_import, we must include the dict-binds+from the parent. Lacking this caused #17151, a really nasty bug.++Here is what happened.+* Class structure:+ Source is a superclass of Mut+ Index is a superclass of Source++* We started with these dict binds+ dSource = $fSourcePix @Int $fIndexInt+ dIndex = sc_sel dSource+ dMut = $fMutPix @Int dIndex+ and these calls to specialise+ $fMutPix @Int dIndex+ $fSourcePix @Int $fIndexInt++* We specialised the call ($fMutPix @Int dIndex)+ ==> new call ($fSourcePix @Int dIndex)+ (because Source is a superclass of Mut)++* We specialised ($fSourcePix @Int dIndex)+ ==> produces specialised dict $s$fSourcePix,+ a record with dIndex as a field+ plus RULE forall d. ($fSourcePix @Int d) = $s$fSourcePix+ *** This is the bogus step ***++* Now we decide not to specialise the call+ $fSourcePix @Int $fIndexInt+ because we alredy have a RULE that matches it++* Finally the simplifer rewrites+ dSource = $fSourcePix @Int $fIndexInt+ ==> dSource = $s$fSourcePix++Disaster. Now we have++Rewrite dSource's RHS to $s$fSourcePix Disaster+ dSource = $s$fSourcePix+ dIndex = sc_sel dSource+ $s$fSourcePix = MkSource dIndex ...++Solution: filterCalls should have stopped the bogus step,+by seeing that dIndex transitively uses $fSourcePix. But+it can only do that if it sees all the dict_binds. Wow.++--------------+Here's another example (#13429). Suppose we have+ class Monoid v => C v a where ...++We start with a call+ f @ [Integer] @ Integer $fC[]Integer++Specialising call to 'f' gives dict bindings+ $dMonoid_1 :: Monoid [Integer]+ $dMonoid_1 = M.$p1C @ [Integer] $fC[]Integer++ $dC_1 :: C [Integer] (Node [Integer] Integer)+ $dC_1 = M.$fCvNode @ [Integer] $dMonoid_1++...plus a recursive call to+ f @ [Integer] @ (Node [Integer] Integer) $dC_1++Specialising that call gives+ $dMonoid_2 :: Monoid [Integer]+ $dMonoid_2 = M.$p1C @ [Integer] $dC_1++ $dC_2 :: C [Integer] (Node [Integer] Integer)+ $dC_2 = M.$fCvNode @ [Integer] $dMonoid_2++Now we have two calls to the imported function+ M.$fCvNode :: Monoid v => C v a+ M.$fCvNode @v @a m = C m some_fun++But we must /not/ use the call (M.$fCvNode @ [Integer] $dMonoid_2)+for specialisation, else we get:++ $dC_1 = M.$fCvNode @ [Integer] $dMonoid_1+ $dMonoid_2 = M.$p1C @ [Integer] $dC_1+ $s$fCvNode = C $dMonoid_2 ...+ RULE M.$fCvNode [Integer] _ _ = $s$fCvNode++Now use the rule to rewrite the call in the RHS of $dC_1+and we get a loop!+++Note [specImport call stack]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+When specialising an imports function 'f', we may get new calls+of an imported function 'g', which we want to specialise in turn,+and similarly specialising 'g' might expose a new call to 'h'.++We track the stack of enclosing functions. So when specialising 'h' we+have a specImport call stack of [g,f]. We do this for two reasons:+* Note [Warning about missed specialisations]+* Note [Avoiding recursive specialisation]++Note [Warning about missed specialisations]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Suppose+ * In module Lib, you carefully mark a function 'foo' INLINABLE+ * Import Lib(foo) into another module M+ * Call 'foo' at some specialised type in M+Then you jolly well expect it to be specialised in M. But what if+'foo' calls another function 'Lib.bar'. Then you'd like 'bar' to be+specialised too. But if 'bar' is not marked INLINABLE it may well+not be specialised. The warning Opt_WarnMissedSpecs warns about this.++It's more noisy to warning about a missed specialisation opportunity+for /every/ overloaded imported function, but sometimes useful. That+is what Opt_WarnAllMissedSpecs does.++ToDo: warn about missed opportunities for local functions.++Note [Avoiding recursive specialisation]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+When we specialise 'f' we may find new overloaded calls to 'g', 'h' in+'f's RHS. So we want to specialise g,h. But we don't want to+specialise f any more! It's possible that f's RHS might have a+recursive yet-more-specialised call, so we'd diverge in that case.+And if the call is to the same type, one specialisation is enough.+Avoiding this recursive specialisation loop is one reason for the+'callers' stack passed to specImports and specImport.+++************************************************************************+* *+\subsubsection{@specExpr@: the main function}+* *+************************************************************************+-}++data SpecEnv+ = SE { se_subst :: Core.Subst+ -- We carry a substitution down:+ -- a) we must clone any binding that might float outwards,+ -- to avoid name clashes+ -- b) we carry a type substitution to use when analysing+ -- the RHS of specialised bindings (no type-let!)++ , se_module :: Module+ , se_rules :: RuleEnv -- From the home package and this module+ , se_dflags :: DynFlags+ }++instance Outputable SpecEnv where+ ppr (SE { se_subst = subst })+ = text "SE" <+> braces (text "subst =" <+> ppr subst)++specVar :: SpecEnv -> InId -> SpecM (OutExpr, UsageDetails)+specVar env@(SE { se_subst = Core.Subst in_scope ids _ _ }) v+ | not (isLocalId v) = return (Var v, emptyUDs)+ | Just e <- lookupVarEnv ids v = specExpr (zapSubst env) e -- Note (1)+ | Just v' <- lookupInScope in_scope v = return (Var v', emptyUDs)+ | otherwise = pprPanic "specVar" (ppr v $$ ppr in_scope)+ -- c.f. GHC.Core.Subst.lookupIdSubst+ -- Note (1): we recurse so we do the lookupInScope thing on any Vars in e+ -- probably has little effect, but it's the right thing.+ -- We need zapSubst because `e` is an OutExpr++specExpr :: SpecEnv -> CoreExpr -> SpecM (CoreExpr, UsageDetails)++---------------- First the easy cases --------------------+specExpr env (Var v) = specVar env v+specExpr env (Type ty) = return (Type (substTy env ty), emptyUDs)+specExpr env (Coercion co) = return (Coercion (substCo env co), emptyUDs)+specExpr _ (Lit lit) = return (Lit lit, emptyUDs)+specExpr env (Cast e co)+ = do { (e', uds) <- specExpr env e+ ; return ((mkCast e' (substCo env co)), uds) }+specExpr env (Tick tickish body)+ = do { (body', uds) <- specExpr env body+ ; return (Tick (specTickish env tickish) body', uds) }++---------------- Applications might generate a call instance --------------------+specExpr env expr@(App {})+ = do { let (fun_in, args_in) = collectArgs expr+ ; (args_out, uds_args) <- mapAndCombineSM (specExpr env) args_in+ ; let env_args = env `bringFloatedDictsIntoScope` ud_binds uds_args+ -- Some dicts may have floated out of args_in;+ -- they should be in scope for fireRewriteRules (#21689)+ (fun_in', args_out') = fireRewriteRules env_args fun_in args_out+ ; (fun_out', uds_fun) <- specExpr env fun_in'+ ; let uds_call = mkCallUDs env fun_out' args_out'+ ; return (fun_out' `mkApps` args_out', uds_fun `thenUDs` uds_call `thenUDs` uds_args) }++---------------- Lambda/case require dumping of usage details --------------------+specExpr env e@(Lam {})+ = specLam env' bndrs' body+ where+ (bndrs, body) = collectBinders e+ (env', bndrs') = substBndrs env bndrs+ -- More efficient to collect a group of binders together all at once+ -- and we don't want to split a lambda group with dumped bindings++specExpr env (Case scrut case_bndr ty alts)+ = do { (scrut', scrut_uds) <- specExpr env scrut+ ; (scrut'', case_bndr', alts', alts_uds)+ <- specCase env scrut' case_bndr alts+-- ; pprTrace "specExpr:case" (vcat+-- [ text "scrut" <+> ppr scrut, text "scrut'" <+> ppr scrut'+-- , text "case_bndr'" <+> ppr case_bndr'+-- , text "alts_uds" <+> ppr alts_uds+-- ])+ ; return (Case scrut'' case_bndr' (substTy env ty) alts'+ , scrut_uds `thenUDs` alts_uds) }++---------------- Finally, let is the interesting case --------------------+specExpr env (Let bind body)+ = do { (binds', body', uds) <- specBind NotTopLevel env bind $ \body_env ->+ -- pprTrace "specExpr:let" (ppr (se_subst body_env) $$ ppr body) $+ specExpr body_env body+ -- All done+ ; return (foldr Let body' binds', uds) }++-- See Note [Specialisation modulo dictionary selectors]+-- Note [ClassOp/DFun selection]+-- Note [Fire rules in the specialiser]+fireRewriteRules :: SpecEnv -> InExpr -> [OutExpr] -> (InExpr, [OutExpr])+fireRewriteRules env (Var f) args+ | Just (rule, expr) <- specLookupRule env f args InitialPhase (getRules (se_rules env) f)+ , let rest_args = drop (ruleArity rule) args -- See Note [Extra args in the target]+ zapped_subst = Core.zapSubst (se_subst env)+ expr' = simpleOptExprWith defaultSimpleOpts zapped_subst expr+ -- simplOptExpr needed because lookupRule returns+ -- (\x y. rhs) arg1 arg2+ , (fun, args) <- collectArgs expr'+ = fireRewriteRules env fun (args++rest_args)+fireRewriteRules _ fun args = (fun, args)++--------------+specLam :: SpecEnv -> [OutBndr] -> InExpr -> SpecM (OutExpr, UsageDetails)+-- The binders have been substituted, but the body has not+specLam env bndrs body+ | null bndrs+ = specExpr env body+ | otherwise+ = do { (body', uds) <- specExpr env body+ ; let (free_uds, dumped_dbs) = dumpUDs bndrs uds+ ; return (mkLams bndrs (wrapDictBindsE dumped_dbs body'), free_uds) }++--------------+specTickish :: SpecEnv -> CoreTickish -> CoreTickish+specTickish (SE { se_subst = subst }) (Breakpoint ext ix ids)+ = Breakpoint ext ix [ id' | id <- ids, Var id' <- [Core.lookupIdSubst subst id]]+ -- drop vars from the list if they have a non-variable substitution.+ -- should never happen, but it's harmless to drop them anyway.+specTickish _ other_tickish = other_tickish++--------------+specCase :: SpecEnv+ -> OutExpr -- Scrutinee, already done+ -> InId -> [InAlt]+ -> SpecM ( OutExpr -- New scrutinee+ , OutId+ , [OutAlt]+ , UsageDetails)+specCase env scrut' case_bndr [Alt con args rhs]+ | -- See Note [Floating dictionaries out of cases]+ interestingDict scrut' (idType case_bndr)+ , not (isDeadBinder case_bndr && null sc_args')+ = do { case_bndr_flt :| sc_args_flt <- mapM clone_me (case_bndr' :| sc_args')++ ; let case_bndr_flt' = case_bndr_flt `addDictUnfolding` scrut'+ scrut_bind = mkDB (NonRec case_bndr_flt scrut')++ sc_args_flt' = zipWith addDictUnfolding sc_args_flt sc_rhss+ sc_rhss = [ Case (Var case_bndr_flt') case_bndr' (idType sc_arg')+ [Alt con args' (Var sc_arg')]+ | sc_arg' <- sc_args' ]+ cb_set = unitVarSet case_bndr_flt'+ sc_binds = [ DB { db_bind = NonRec sc_arg_flt sc_rhs, db_fvs = cb_set }+ | (sc_arg_flt, sc_rhs) <- sc_args_flt' `zip` sc_rhss ]++ flt_binds = scrut_bind : sc_binds++ -- Extend the substitution for RHS to map the *original* binders+ -- to their floated versions.+ mb_sc_flts :: [Maybe DictId]+ mb_sc_flts = map (lookupVarEnv clone_env) args'+ clone_env = zipVarEnv sc_args' sc_args_flt'++ subst_prs = (case_bndr, Var case_bndr_flt)+ : [ (arg, Var sc_flt)+ | (arg, Just sc_flt) <- args `zip` mb_sc_flts ]+ subst' = se_subst env_rhs+ `Core.extendSubstInScopeList` (case_bndr_flt' : sc_args_flt')+ `Core.extendIdSubstList` subst_prs+ env_rhs' = env_rhs { se_subst = subst' }++ ; (rhs', rhs_uds) <- specExpr env_rhs' rhs+ ; let (free_uds, dumped_dbs) = dumpUDs (case_bndr':args') rhs_uds+ all_uds = flt_binds `consDictBinds` free_uds+ alt' = Alt con args' (wrapDictBindsE dumped_dbs rhs')+-- ; pprTrace "specCase" (ppr case_bndr $$ ppr scrut_bind) $+ ; return (Var case_bndr_flt, case_bndr', [alt'], all_uds) }+ where+ (env_rhs, (case_bndr':args')) = substBndrs env (case_bndr:args)+ sc_args' = filter is_flt_sc_arg args'++ clone_me bndr = do { uniq <- getUniqueM+ ; return (mkUserLocalOrCoVar occ uniq wght ty loc) }+ where+ name = idName bndr+ wght = idMult bndr+ ty = idType bndr+ occ = nameOccName name+ loc = getSrcSpan name++ arg_set = mkVarSet args'+ is_flt_sc_arg var = isId var+ && not (isDeadBinder var)+ && isDictTy var_ty+ && tyCoVarsOfType var_ty `disjointVarSet` arg_set+ where+ var_ty = idType var+++specCase env scrut case_bndr alts+ = do { (alts', uds_alts) <- mapAndCombineSM spec_alt alts+ ; return (scrut, case_bndr', alts', uds_alts) }+ where+ (env_alt, case_bndr') = substBndr env case_bndr+ spec_alt (Alt con args rhs)+ = do { (rhs', uds) <- specExpr env_rhs rhs+ ; let (free_uds, dumped_dbs) = dumpUDs (case_bndr' : args') uds+-- ; unless (isNilOL dumped_dbs) $+-- pprTrace "specAlt" (vcat+-- [text "case_bndr', args" <+> (ppr case_bndr' $$ ppr args)+-- ,text "dumped" <+> ppr dumped_dbs ]) return ()+ ; return (Alt con args' (wrapDictBindsE dumped_dbs rhs'), free_uds) }+ where+ (env_rhs, args') = substBndrs env_alt args++{- Note [Fire rules in the specialiser]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider this (#21851)++ module A where+ f :: Num b => b -> (b, b)+ f x = (x + 1, snd (f x))+ {-# SPECIALIZE f :: Int -> (Int, Int) #-}++ module B (g') where+ import A++ g :: Num a => a -> a+ g x = fst (f x)+ {-# NOINLINE[99] g #-}++ h :: Int -> Int+ h = g++Note that `f` has the CPR property, and so will worker/wrapper.++The call to `g` in `h` will make us specialise `g @Int`. And the specialised+version of `g` will contain the call `f @Int`; but in the subsequent run of+the Simplifier, there will be a competition between:+* The user-supplied SPECIALISE rule for `f`+* The inlining of the wrapper for `f`+In fact, the latter wins -- see Note [Rewrite rules and inlining] in+GHC.Core.Opt.Simplify.Iteration. However, it a bit fragile.++Moreover consider (test T21851_2):++ module A+ f :: (Ord a, Show b) => a -> b -> blah+ {-# RULE forall b. f @Int @b = wombat #-}++ wombat :: Show b => Int -> b -> blah+ wombat = blah++ module B+ import A+ g :: forall a. Ord a => blah+ g @a = ...g...f @a @Char....++ h = ....g @Int....++Now, in module B, GHC will specialise `g @Int`, which will lead to a+call `f @Int @Char`. If we immediately (in the specialiser) rewrite+that to `womabat @Char`, we have a chance to specialise `wombat`.++Conclusion: it's treat if the Specialiser fires RULEs itself.+It's not hard to achieve: see `fireRewriteRules`. The only tricky bit is+making sure that we have a reasonably up to date EPS rule base. Currently+we load it up just once, in `initRuleEnv`, called at the beginning of+`specProgram`.++NB: you might wonder if running rules in the specialiser (this Note)+renders Note [Rewrite rules and inlining] in the Simplifier redundant.+That is, if we run rules in the specialiser, does it matter if we make+rules "win" over inlining in the Simplifier? Yes, it does! See the+discussion in #21851.++Note [Floating dictionaries out of cases]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider+ g = \d. case d of { MkD sc ... -> ...(f sc)... }+Naively we can't float d2's binding out of the case expression,+because 'sc' is bound by the case, and that in turn means we can't+specialise f, which seems a pity.++So we invert the case, by floating out a binding+for 'sc_flt' thus:+ sc_flt = case d of { MkD sc ... -> sc }+Now we can float the call instance for 'f'. Indeed this is just+what'll happen if 'sc' was originally bound with a let binding,+but case is more efficient, and necessary with equalities. So it's+good to work with both.++You might think that this won't make any difference, because the+call instance will only get nuked by the \d. BUT if 'g' itself is+specialised, then transitively we should be able to specialise f.++In general, given+ case e of cb { MkD sc ... -> ...(f sc)... }+we transform to+ let cb_flt = e+ sc_flt = case cb_flt of { MkD sc ... -> sc }+ in+ case cb_flt of bg { MkD sc ... -> ....(f sc_flt)... }++The "_flt" things are the floated binds; we use the current substitution+to substitute sc -> sc_flt in the RHS++************************************************************************+* *+ Dealing with a binding+* *+************************************************************************+-}++bringFloatedDictsIntoScope :: SpecEnv -> FloatedDictBinds -> SpecEnv+bringFloatedDictsIntoScope env (FDB { fdb_bndrs = dx_bndrs })+ = -- pprTrace "brought into scope" (ppr dx_bndrs) $+ env {se_subst=subst'}+ where+ subst' = se_subst env `Core.extendSubstInScopeSet` dx_bndrs++specBind :: TopLevelFlag+ -> SpecEnv -- At top-level only, this env already has the+ -- top level binders in scope+ -> InBind+ -> (SpecEnv -> SpecM (body, UsageDetails)) -- Process the body+ -> SpecM ( [OutBind] -- New bindings+ , body -- Body+ , UsageDetails) -- And info to pass upstream++-- Returned UsageDetails:+-- No calls for binders of this bind+specBind top_lvl env (NonRec fn rhs) do_body+ = do { (rhs', rhs_uds) <- specExpr env rhs++ ; (body_env1, fn1) <- case top_lvl of+ TopLevel -> return (env, fn)+ NotTopLevel -> cloneBndrSM env fn++ ; let fn2 | isStableUnfolding (idUnfolding fn1) = fn1+ | otherwise = fn1 `setIdUnfolding` mkSimpleUnfolding defaultUnfoldingOpts rhs'+ -- Update the unfolding with the perhaps-simpler or more specialised rhs'+ -- This is important: see Note [Update unfolding after specialisation]+ -- And in any case cloneBndrSM discards non-Stable unfoldings++ fn3 = zapIdDemandInfo fn2+ -- We zap the demand info because the binding may float,+ -- which would invalidate the demand info (see #17810 for example).+ -- Destroying demand info is not terrible; specialisation is+ -- always followed soon by demand analysis.++ body_env2 = body_env1 `extendInScope` fn3++ ; (body', body_uds) <- do_body body_env2++ ; (fn4, spec_defns, body_uds1) <- specDefn env body_uds fn3 rhs++ ; let (free_uds, dump_dbs, float_all) = dumpBindUDs [fn4] body_uds1+ all_free_uds = free_uds `thenUDs` rhs_uds++ pairs = spec_defns ++ [(fn4, rhs')]+ -- fn4 mentions the spec_defns in its rules,+ -- so put the latter first++ final_binds :: [DictBind]+ -- See Note [From non-recursive to recursive]+ final_binds | not (isNilOL dump_dbs)+ , not (null spec_defns)+ = [recWithDumpedDicts pairs dump_dbs]+ | otherwise+ = [mkDB $ NonRec b r | (b,r) <- pairs]+ ++ fromOL dump_dbs++ can_float_this_one = exprIsTopLevelBindable rhs (idType fn)+ -- exprIsTopLevelBindable: see Note [Care with unlifted bindings]++ ; if float_all && can_float_this_one then+ -- Rather than discard the calls mentioning the bound variables+ -- we float this (dictionary) binding along with the others+ return ([], body', all_free_uds `snocDictBinds` final_binds)+ else+ -- No call in final_uds mentions bound variables,+ -- so we can just leave the binding here+ return (map db_bind final_binds, body', all_free_uds) }+++specBind top_lvl env (Rec pairs) do_body+ -- Note [Specialising a recursive group]+ = do { let (bndrs,rhss) = unzip pairs++ ; (rec_env, bndrs1) <- case top_lvl of+ TopLevel -> return (env, bndrs)+ NotTopLevel -> cloneRecBndrsSM env bndrs++ ; (rhss', rhs_uds) <- mapAndCombineSM (specExpr rec_env) rhss+ ; (body', body_uds) <- do_body rec_env++ ; let scope_uds = body_uds `thenUDs` rhs_uds+ -- Includes binds and calls arising from rhss++ ; (bndrs2, spec_defns2, uds2) <- specDefns rec_env scope_uds (bndrs1 `zip` rhss)+ -- bndrs2 is like bndrs1, but with RULES added++ ; (bndrs3, spec_defns3, uds3)+ <- if null spec_defns2 -- Common case: no specialisation+ then return (bndrs2, [], uds2)+ else do { -- Specialisation occurred; do it again+ (bndrs3, spec_defns3, uds3)+ <- specDefns rec_env uds2 (bndrs2 `zip` rhss)+ ; return (bndrs3, spec_defns3 ++ spec_defns2, uds3) }++ ; let (final_uds, dumped_dbs, float_all) = dumpBindUDs bndrs1 uds3+ final_bind = recWithDumpedDicts (spec_defns3 ++ zip bndrs3 rhss')+ dumped_dbs++ ; if float_all then+ return ([], body', final_uds `snocDictBind` final_bind)+ else+ return ([db_bind final_bind], body', final_uds) }+++---------------------------+specDefns :: SpecEnv+ -> UsageDetails -- Info on how it is used in its scope+ -> [(OutId,InExpr)] -- The things being bound and their un-processed RHS+ -> SpecM ([OutId], -- Original Ids with RULES added+ [(OutId,OutExpr)], -- Extra, specialised bindings+ UsageDetails) -- Stuff to fling upwards from the specialised versions++-- Specialise a list of bindings (the contents of a Rec), but flowing usages+-- upwards binding by binding. Example: { f = ...g ...; g = ...f .... }+-- Then if the input CallDetails has a specialised call for 'g', whose specialisation+-- in turn generates a specialised call for 'f', we catch that in this one sweep.+-- But not vice versa (it's a fixpoint problem).++specDefns _env uds []+ = return ([], [], uds)+specDefns env uds ((bndr,rhs):pairs)+ = do { (bndrs1, spec_defns1, uds1) <- specDefns env uds pairs+ ; (bndr1, spec_defns2, uds2) <- specDefn env uds1 bndr rhs+ ; return (bndr1 : bndrs1, spec_defns1 ++ spec_defns2, uds2) }++---------------------------+specDefn :: SpecEnv+ -> UsageDetails -- Info on how it is used in its scope+ -> OutId -> InExpr -- The thing being bound and its un-processed RHS+ -> SpecM (Id, -- Original Id with added RULES+ [(Id,CoreExpr)], -- Extra, specialised bindings+ UsageDetails) -- Stuff to fling upwards from the specialised versions++specDefn env body_uds fn rhs+ = do { let (body_uds_without_me, calls_for_me) = callsForMe fn body_uds+ rules_for_me = idCoreRules fn+ -- Bring into scope the binders from the floated dicts+ env_w_dict_bndrs = bringFloatedDictsIntoScope env (ud_binds body_uds)++ ; (rules, spec_defns, spec_uds) <- specCalls False env_w_dict_bndrs+ rules_for_me calls_for_me fn rhs++ ; return ( fn `addIdSpecialisations` rules+ , spec_defns+ , body_uds_without_me `thenUDs` spec_uds) }+ -- It's important that the `thenUDs` is this way+ -- round, because body_uds_without_me may bind+ -- dictionaries that are used in calls_for_me passed+ -- to specDefn. So the dictionary bindings in+ -- spec_uds may mention dictionaries bound in+ -- body_uds_without_me++---------------------------+specCalls :: Bool -- True => specialising imported fn+ -- False => specialising local fn+ -> SpecEnv+ -> [CoreRule] -- Existing RULES for the fn+ -> [CallInfo]+ -> OutId -> InExpr+ -> SpecM SpecInfo -- New rules, specialised bindings, and usage details++-- This function checks existing rules, and does not create+-- duplicate ones. So the caller does not need to do this filtering.+-- See 'already_covered'++type SpecInfo = ( [CoreRule] -- Specialisation rules+ , [(Id,CoreExpr)] -- Specialised definition+ , UsageDetails ) -- Usage details from specialised RHSs++specCalls spec_imp env existing_rules calls_for_me fn rhs+ -- The first case is the interesting one+ | notNull calls_for_me -- And there are some calls to specialise+ && not (isNeverActive (idInlineActivation fn))+ -- Don't specialise NOINLINE things+ -- See Note [Auto-specialisation and RULES]+ --+ -- Don't specialise OPAQUE things, see Note [OPAQUE pragma].+ -- Since OPAQUE things are always never-active (see+ -- GHC.Parser.PostProcess.mkOpaquePragma) this guard never fires for+ -- OPAQUE things.++-- && not (certainlyWillInline (idUnfolding fn)) -- And it's not small+-- See Note [Inline specialisations] for why we do not+-- switch off specialisation for inline functions++ = -- pprTrace "specCalls: some" (vcat+ -- [ text "function" <+> ppr fn+ -- , text "calls:" <+> ppr calls_for_me+ -- , text "subst" <+> ppr (se_subst env) ]) $+ foldlM spec_call ([], [], emptyUDs) calls_for_me++ | otherwise -- No calls or RHS doesn't fit our preconceptions+ = warnPprTrace (not (exprIsTrivial rhs) && notNull calls_for_me && not (isClassOpId fn))+ "Missed specialisation opportunity for" (ppr fn $$ trace_doc) $+ -- isClassOpId: class-op Ids never inline; we specialise them+ -- through fireRewriteRules. So don't complain about missed opportunities+ -- Note [Specialisation shape]+ -- pprTrace "specCalls: none" (ppr fn <+> ppr calls_for_me) $+ return ([], [], emptyUDs)+ where+ trace_doc = sep [ ppr rhs_bndrs, ppr (idInlineActivation fn) ]++ fn_type = idType fn+ fn_arity = idArity fn+ fn_unf = realIdUnfolding fn -- Ignore loop-breaker-ness here+ inl_prag = idInlinePragma fn+ inl_act = inlinePragmaActivation inl_prag+ is_local = isLocalId fn+ is_dfun = isDFunId fn+ dflags = se_dflags env+ this_mod = se_module env+ -- Figure out whether the function has an INLINE pragma+ -- See Note [Inline specialisations]++ (rhs_bndrs, rhs_body) = collectBindersPushingCo rhs+ -- See Note [Account for casts in binding]++ already_covered :: SpecEnv -> [CoreRule] -> [CoreExpr] -> Bool+ already_covered env new_rules args -- Note [Specialisations already covered]+ = isJust (specLookupRule env fn args (beginPhase inl_act)+ (new_rules ++ existing_rules))+ -- Rules: we look both in the new_rules (generated by this invocation+ -- of specCalls), and in existing_rules (passed in to specCalls)+ -- inl_act: is the activation we are going to put in the new SPEC+ -- rule; so we want to see if it is covered by another rule with+ -- that same activation.++ ----------------------------------------------------------+ -- Specialise to one particular call pattern+ spec_call :: SpecInfo -- Accumulating parameter+ -> CallInfo -- Call instance+ -> SpecM SpecInfo+ spec_call spec_acc@(rules_acc, pairs_acc, uds_acc) _ci@(CI { ci_key = call_args })+ = -- See Note [Specialising Calls]+ do { let all_call_args | is_dfun = saturating_call_args -- See Note [Specialising DFuns]+ | otherwise = call_args+ saturating_call_args = call_args ++ map mk_extra_dfun_arg (dropList call_args rhs_bndrs)+ mk_extra_dfun_arg bndr | isTyVar bndr = UnspecType+ | otherwise = UnspecArg++ ; ( useful, rhs_env2, leftover_bndrs+ , rule_bndrs, rule_lhs_args+ , spec_bndrs1, dx_binds, spec_args) <- specHeader env rhs_bndrs all_call_args++-- ; pprTrace "spec_call" (vcat+-- [ text "fun: " <+> ppr fn+-- , text "call info: " <+> ppr _ci+-- , text "useful: " <+> ppr useful+-- , text "rule_bndrs:" <+> ppr rule_bndrs+-- , text "lhs_args: " <+> ppr rule_lhs_args+-- , text "spec_bndrs1:" <+> ppr spec_bndrs1+-- , text "leftover_bndrs:" <+> pprIds leftover_bndrs+-- , text "spec_args: " <+> ppr spec_args+-- , text "dx_binds: " <+> ppr dx_binds+-- , text "rhs_bndrs" <+> ppr rhs_bndrs+-- , text "rhs_body" <+> ppr rhs_body+-- , text "rhs_env2: " <+> ppr (se_subst rhs_env2)+-- , ppr dx_binds ]) $+-- return ()++ ; if not useful -- No useful specialisation+ || already_covered rhs_env2 rules_acc rule_lhs_args+ then return spec_acc+ else+ do { -- Run the specialiser on the specialised RHS+ -- The "1" suffix is before we maybe add the void arg+ ; (rhs_body', rhs_uds) <- specExpr rhs_env2 rhs_body+ -- Add the { d1' = dx1; d2' = dx2 } usage stuff+ -- to the rhs_uds; see Note [Specialising Calls]+ ; let rhs_uds_w_dx = dx_binds `consDictBinds` rhs_uds+ spec_rhs_bndrs = spec_bndrs1 ++ leftover_bndrs+ (spec_uds, dumped_dbs) = dumpUDs spec_rhs_bndrs rhs_uds_w_dx+ spec_rhs1 = mkLams spec_rhs_bndrs $+ wrapDictBindsE dumped_dbs rhs_body'++ spec_fn_ty1 = exprType spec_rhs1++ -- Maybe add a void arg to the specialised function,+ -- to avoid unlifted bindings+ -- See Note [Specialisations Must Be Lifted]+ -- C.f. GHC.Core.Opt.WorkWrap.Utils.needsVoidWorkerArg+ add_void_arg = isUnliftedType spec_fn_ty1 && not (isJoinId fn)+ (spec_bndrs, spec_rhs, spec_fn_ty)+ | add_void_arg = ( voidPrimId : spec_bndrs1+ , Lam voidArgId spec_rhs1+ , mkVisFunTyMany unboxedUnitTy spec_fn_ty1)+ | otherwise = (spec_bndrs1, spec_rhs1, spec_fn_ty1)++ join_arity_decr = length rule_lhs_args - length spec_bndrs+ spec_join_arity | Just orig_join_arity <- isJoinId_maybe fn+ = Just (orig_join_arity - join_arity_decr)+ | otherwise+ = Nothing++ ; spec_fn <- newSpecIdSM fn spec_fn_ty spec_join_arity+ ; let+ -- The rule to put in the function's specialisation is:+ -- forall x @b d1' d2'.+ -- f x @T1 @b @T2 d1' d2' = f1 x @b+ -- See Note [Specialising Calls]+ herald | spec_imp = -- Specialising imported fn+ text "SPEC/" <> ppr this_mod+ | otherwise = -- Specialising local fn+ text "SPEC"++ spec_rule = mkSpecRule dflags this_mod True inl_act+ herald fn rule_bndrs rule_lhs_args+ (mkVarApps (Var spec_fn) spec_bndrs)++ simpl_opts = initSimpleOpts dflags++ --------------------------------------+ -- Add a suitable unfolding; see Note [Inline specialisations]+ -- The wrap_unf_body applies the original unfolding to the specialised+ -- arguments, not forgetting to wrap the dx_binds around the outside (#22358)+ wrap_unf_body body = foldr (Let . db_bind) (body `mkApps` spec_args) dx_binds+ spec_unf = specUnfolding simpl_opts spec_bndrs wrap_unf_body+ rule_lhs_args fn_unf++ spec_inl_prag+ | not is_local -- See Note [Specialising imported functions]+ , isStrongLoopBreaker (idOccInfo fn) -- in GHC.Core.Opt.OccurAnal+ = neverInlinePragma+ | otherwise+ = inl_prag++ --------------------------------------+ -- Adding arity information just propagates it a bit faster+ -- See Note [Arity decrease] in GHC.Core.Opt.Simplify+ -- Copy InlinePragma information from the parent Id.+ -- So if f has INLINE[1] so does spec_fn+ arity_decr = count isValArg rule_lhs_args - count isId spec_bndrs+ spec_f_w_arity = spec_fn `setIdArity` max 0 (fn_arity - arity_decr)+ `setInlinePragma` spec_inl_prag+ `setIdUnfolding` spec_unf+ `asJoinId_maybe` spec_join_arity++ _rule_trace_doc = vcat [ ppr fn <+> dcolon <+> ppr fn_type+ , ppr spec_fn <+> dcolon <+> ppr spec_fn_ty+ , ppr rhs_bndrs, ppr call_args+ , ppr spec_rule+ ]++ ; -- pprTrace "spec_call: rule" _rule_trace_doc+ return ( spec_rule : rules_acc+ , (spec_f_w_arity, spec_rhs) : pairs_acc+ , spec_uds `thenUDs` uds_acc+ ) } }++-- Convenience function for invoking lookupRule from Specialise+-- The SpecEnv's InScopeSet should include all the Vars in the [CoreExpr]+specLookupRule :: SpecEnv -> Id -> [CoreExpr]+ -> CompilerPhase -- Look up rules as if we were in this phase+ -> [CoreRule] -> Maybe (CoreRule, CoreExpr)+specLookupRule env fn args phase rules+ = lookupRule ropts in_scope_env is_active fn args rules+ where+ dflags = se_dflags env+ in_scope = getSubstInScope (se_subst env)+ in_scope_env = ISE in_scope (whenActiveUnfoldingFun is_active)+ ropts = initRuleOpts dflags+ is_active = isActive phase++{- Note [Specialising DFuns]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~+DFuns have a special sort of unfolding (DFunUnfolding), and these are+hard to specialise a DFunUnfolding to give another DFunUnfolding+unless the DFun is fully applied (#18120). So, in the case of DFunIds+we simply extend the CallKey with trailing UnspecTypes/UnspecArgs,+so that we'll generate a rule that completely saturates the DFun.++There is an ASSERT that checks this, in the DFunUnfolding case of+GHC.Core.Unfold.Make.specUnfolding.++Note [Specialisation Must Preserve Sharing]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider a function:++ f :: forall a. Eq a => a -> blah+ f =+ if expensive+ then f1+ else f2++As written, all calls to 'f' will share 'expensive'. But if we specialise 'f'+at 'Int', eg:++ $sfInt = SUBST[a->Int,dict->dEqInt] (if expensive then f1 else f2)++ RULE "SPEC f"+ forall (d :: Eq Int).+ f Int _ = $sfIntf++We've now lost sharing between 'f' and '$sfInt' for 'expensive'. Yikes!++To avoid this, we only generate specialisations for functions whose arity is+enough to bind all of the arguments we need to specialise. This ensures our+specialised functions don't do any work before receiving all of their dicts,+and thus avoids the 'f' case above.++Note [Specialisations Must Be Lifted]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider a function 'f':++ f = forall a. Eq a => Array# a++used like++ case x of+ True -> ...f @Int dEqInt...+ False -> 0++Naively, we might generate an (expensive) specialisation++ $sfInt :: Array# Int++even in the case that @x = False@! Instead, we add a dummy 'Void#' argument to+the specialisation '$sfInt' ($sfInt :: Void# -> Array# Int) in order to+preserve laziness.++Note [Care with unlifted bindings]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider (#22998)+ f x = let x::ByteArray# = <some literal>+ n::Natural = NB x+ in wombat @192827 (n |> co)+where+ co :: Natural ~ KnownNat 192827+ wombat :: forall (n:Nat). KnownNat n => blah++Left to itself, the specialiser would float the bindings for `x` and `n` to top+level, so we can specialise `wombat`. But we can't have a top-level ByteArray#+(see Note [Core letrec invariant] in GHC.Core). Boo.++This is pretty exotic, so we take a simple way out: in specBind (the NonRec+case) do not float the binding itself unless it satisfies exprIsTopLevelBindable.+This is conservative: maybe the RHS of `x` has a free var that would stop it+floating to top level anyway; but that is hard to spot (since we don't know what+the non-top-level in-scope binders are) and rare (since the binding must satisfy+Note [Core let-can-float invariant] in GHC.Core).+++Note [Specialising Calls]+~~~~~~~~~~~~~~~~~~~~~~~~~+Suppose we have a function with a complicated type:++ f :: forall a b c. Int -> Eq a => Show b => c -> Blah+ f @a @b @c i dEqA dShowA x = blah++and suppose it is called at:++ f 7 @T1 @T2 @T3 dEqT1 ($dfShow dShowT2) t3++This call is described as a 'CallInfo' whose 'ci_key' is:++ [ SpecType T1, SpecType T2, UnspecType, UnspecArg, SpecDict dEqT1+ , SpecDict ($dfShow dShowT2), UnspecArg ]++Why are 'a' and 'b' identified as 'SpecType', while 'c' is 'UnspecType'?+Because we must specialise the function on type variables that appear+free in its *dictionary* arguments; but not on type variables that do not+appear in any dictionaries, i.e. are fully polymorphic.++Because this call has dictionaries applied, we'd like to specialise+the call on any type argument that appears free in those dictionaries.+In this case, those are [a :-> T1, b :-> T2].++We also need to substitute the dictionary binders with their+specialised dictionaries. The simplest substitution would be+[dEqA :-> dEqT1, dShowA :-> $dfShow dShowT2], but this duplicates+work, since `$dfShow dShowT2` is a function application. Therefore, we+also want to *float the dictionary out* (via bindAuxiliaryDict),+creating a new dict binding++ dShow1 = $dfShow dShowT2++and the substitution [dEqA :-> dEqT1, dShowA :-> dShow1].++With the substitutions in hand, we can generate a specialised function:++ $sf :: forall c. Int -> c -> Blah+ $sf = SUBST[a :-> T1, b :-> T2, dEqA :-> dEqT1, dShowA :-> dShow1] (\@c i x -> blah)++Note that the substitution is applied to the whole thing. This is+convenient, but just slightly fragile. Notably:+ * There had better be no name clashes in a/b/c++We must construct a rewrite rule:++ RULE "SPEC f @T1 @T2 _"+ forall (@c :: Type) (i :: Int) (d1 :: Eq T1) (d2 :: Show T2).+ f @T1 @T2 @c i d1 d2 = $sf @c i++In the rule, d1 and d2 are just wildcards, not used in the RHS. Note+additionally that 'x' isn't captured by this rule --- we bind only+enough etas in order to capture all of the *specialised* arguments.++Note [Drop dead args from specialisations]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+When specialising a function, it’s possible some of the arguments may+actually be dead. For example, consider:++ f :: forall a. () -> Show a => a -> String+ f x y = show y ++ "!"++We might generate the following CallInfo for `f @Int`:++ [SpecType Int, UnspecArg, SpecDict $dShowInt, UnspecArg]++Normally we’d include both the x and y arguments in the+specialisation, since we’re not specialising on either of them. But+that’s silly, since x is actually unused! So we might as well drop it+in the specialisation:++ $sf :: Int -> String+ $sf y = show y ++ "!"++ {-# RULE "SPEC f @Int" forall x. f @Int x $dShow = $sf #-}++This doesn’t save us much, since the arg would be removed later by+worker/wrapper, anyway, but it’s easy to do.++Wrinkles++* Note that we only drop dead arguments if:+ 1. We don’t specialise on them.+ 2. They come before an argument we do specialise on.+ Doing the latter would require eta-expanding the RULE, which could+ make it match less often, so it’s not worth it. Doing the former could+ be more useful --- it would stop us from generating pointless+ specialisations --- but it’s more involved to implement and unclear if+ it actually provides much benefit in practice.++* If the function has a stable unfolding, specHeader has to come up with+ arguments to pass to that stable unfolding, when building the stable+ unfolding of the specialised function: this is the last field in specHeader's+ big result tuple.++ The right thing to do is to produce a LitRubbish; it should rapidly+ disappear. Rather like GHC.Core.Opt.WorkWrap.Utils.mk_absent_let.++Note [Specialisation modulo dictionary selectors]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+In #19644, we discovered that the ClassOp/DFun rules from+Note [ClassOp/DFun selection] inhibit transitive specialisation.+Example, inspired by T17966:++ class C a where+ m :: Show b => a -> b -> String+ dummy :: a -> () -- Force a datatype dictionary representation++ instance C Int where+ m a b = show a ++ show b+ dummy _ = ()++ f :: (C a, Show b) => a -> b -> String+ f a b = m a b ++ "!"+ {-# INLINABLE[0] f #-}++ main = putStrLn (f (42::Int) (True::Bool))++Here, we specialise `f` at `Int` and `Bool`, giving++ $dC = $fCInt+ $dShow = GHC.Show.$fShowBool+ $sf (a::Int) (b::Bool) =+ ... (m @Int $dC @Bool $dShow a b) ...++Here `m` is just a DictSel, so there is (apparently) nothing to specialise!+However, the next Simplifier run will expose the rewritten instance method:++ ... $fCInt_$cm @Bool $fShowBool a b ...++where $fCInt_$cm is the instance method for `m` in `instance C Int`:++ $fCInt_$cm :: forall b. Show b => Int -> b -> String+ $fCInt_$cm b d x y = show @Int $dShowInt x ++ show @b d y++We want to specialise this! How? By doing the method-selection rewrite in+the Specialiser. Hence++1. In the App case of 'specExpr', try to apply the ClassOp/DFun rule on the+ head of the application, repeatedly, via 'fireRewriteRules'.+2. Attach an unfolding to freshly-bound dictionary ids such as `$dC` and+ `$dShow` in `bindAuxiliaryDict`, so that we can exploit the unfolding+ in 'fireRewriteRules' to do the ClassOp/DFun rewrite.++NB: Without (2), (1) would be pointless, because 'lookupRule' wouldn't be able+to look into the RHS of `$dC` to see the DFun.++Note [Zap occ info in rule binders]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+When we generate a specialisation RULE, we need to drop occurrence+info on the binders. If we don’t, things go wrong when we specialise a+function like++ f :: forall a. () -> Show a => a -> String+ f x y = show y ++ "!"++since we’ll generate a RULE like++ RULE "SPEC f @Int" forall x [Occ=Dead].+ f @Int x $dShow = $sf++and Core Lint complains, even though x only appears on the LHS (due to+Note [Drop dead args from specialisations]).++Why is that a Lint error? Because the arguments on the LHS of a rule+are syntactically expressions, not patterns, so Lint treats the+appearance of x as a use rather than a binding. Fortunately, the+solution is simple: we just make sure to zap the occ info before+using ids as wildcard binders in a rule.++Note [Account for casts in binding]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider+ f :: Eq a => a -> IO ()+ {-# INLINABLE f+ StableUnf = (/\a \(d:Eq a) (x:a). blah) |> g+ #-}+ f = ...++In f's stable unfolding we have done some modest simplification which+has pushed the cast to the outside. (I wonder if this is the Right+Thing, but it's what happens now; see GHC.Core.Opt.Simplify.Utils Note [Casts and+lambdas].) Now that stable unfolding must be specialised, so we want+to push the cast back inside. It would be terrible if the cast+defeated specialisation! Hence the use of collectBindersPushingCo.++Note [Evidence foralls]+~~~~~~~~~~~~~~~~~~~~~~~~~~+Suppose (#12212) that we are specialising+ f :: forall a b. (Num a, F a ~ F b) => blah+with a=b=Int. Then the RULE will be something like+ RULE forall (d:Num Int) (g :: F Int ~ F Int).+ f Int Int d g = f_spec+But both varToCoreExpr (when constructing the LHS args), and the+simplifier (when simplifying the LHS args), will transform to+ RULE forall (d:Num Int) (g :: F Int ~ F Int).+ f Int Int d <F Int> = f_spec+by replacing g with Refl. So now 'g' is unbound, which results in a later+crash. So we use Refl right off the bat, and do not forall-quantify 'g':+ * varToCoreExpr generates a Refl+ * exprsFreeIdsList returns the Ids bound by the args,+ which won't include g++You might wonder if this will match as often, but the simplifier replaces+complicated Refl coercions with Refl pretty aggressively.++Note [Orphans and auto-generated rules]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+When we specialise an INLINABLE function, or when we have+-fspecialise-aggressively, we auto-generate RULES that are orphans.+We don't want to warn about these, or we'd generate a lot of warnings.+Thus, we only warn about user-specified orphan rules.++Indeed, we don't even treat the module as an orphan module if it has+auto-generated *rule* orphans. Orphan modules are read every time we+compile, so they are pretty obtrusive and slow down every compilation,+even non-optimised ones. (Reason: for type class instances it's a+type correctness issue.) But specialisation rules are strictly for+*optimisation* only so it's fine not to read the interface.++What this means is that a SPEC rules from auto-specialisation in+module M will be used in other modules only if M.hi has been read for+some other reason, which is actually pretty likely.++Note [From non-recursive to recursive]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Even in the non-recursive case, if any dict-binds depend on 'fn' we might+have built a recursive knot++ f a d x = <blah>+ MkUD { ud_binds = NonRec d7 (MkD ..f..)+ , ud_calls = ...(f T d7)... }++The we generate++ Rec { fs x = <blah>[T/a, d7/d]+ f a d x = <blah>+ RULE f T _ = fs+ d7 = ...f... }++Here the recursion is only through the RULE.++However we definitely should /not/ make the Rec in this wildly common+case:+ d = ...+ MkUD { ud_binds = NonRec d7 (...d...)+ , ud_calls = ...(f T d7)... }++Here we want simply to add d to the floats, giving+ MkUD { ud_binds = NonRec d (...)+ NonRec d7 (...d...)+ , ud_calls = ...(f T d7)... }++In general, we need only make this Rec if+ - there are some specialisations (spec_binds non-empty)+ - there are some dict_binds that depend on f (dump_dbs non-empty)++Note [Avoiding loops (DFuns)]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+When specialising /dictionary functions/ we must be very careful to+avoid building loops. Here is an example that bit us badly, on+several distinct occasions.++Here is one: #3591+ class Eq a => C a+ instance Eq [a] => C [a]++This translates to+ dfun :: Eq [a] -> C [a]+ dfun a d = MkD a d (meth d)++ d4 :: Eq [T] = <blah>+ d2 :: C [T] = dfun T d4+ d1 :: Eq [T] = $p1 d2+ d3 :: C [T] = dfun T d1++None of these definitions is recursive. What happened was that we+generated a specialisation:+ RULE forall d. dfun T d = dT :: C [T]+ dT = (MkD a d (meth d)) [T/a, d1/d]+ = MkD T d1 (meth d1)++But now we use the RULE on the RHS of d2, to get+ d2 = dT = MkD d1 (meth d1)+ d1 = $p1 d2++and now d1 is bottom! The problem is that when specialising 'dfun' we+should first dump "below" the binding all floated dictionary bindings+that mention 'dfun' itself. So d2 and d3 (and hence d1) must be+placed below 'dfun', and thus unavailable to it when specialising+'dfun'. That in turn means that the call (dfun T d1) must be+discarded. On the other hand, the call (dfun T d4) is fine, assuming+d4 doesn't mention dfun.++Solution:+ Discard all calls that mention dictionaries that depend+ (directly or indirectly) on the dfun we are specialising.+ This is done by 'filterCalls'++Note [Avoiding loops (non-DFuns)]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+The whole Note [Avoiding loops (DFuns)] things applies only to DFuns.+It's important /not/ to apply filterCalls to non-DFuns. For example:++ class C a where { foo,bar :: [a] -> [a] }++ instance C Int where+ foo x = r_bar x+ bar xs = reverse xs++ r_bar :: C a => [a] -> [a]+ r_bar xs = bar (xs ++ xs)++That translates to:++ r_bar a (c::C a) (xs::[a]) = bar a d (xs ++ xs)++ Rec { $fCInt :: C Int = MkC foo_help reverse+ foo_help (xs::[Int]) = r_bar Int $fCInt xs }++The call (r_bar $fCInt) mentions $fCInt,+ which mentions foo_help,+ which mentions r_bar++But we DO want to specialise r_bar at Int:+ Rec { $fCInt :: C Int = MkC foo_help reverse+ foo_help (xs::[Int]) = r_bar Int $fCInt xs++ r_bar a (c::C a) (xs::[a]) = bar a d (xs ++ xs)+ RULE r_bar Int _ = r_bar_Int++ r_bar_Int xs = bar Int $fCInt (xs ++ xs)+ }++Note that, because of its RULE, r_bar joins the recursive+group. (In this case it'll unravel a short moment later.)+See test simplCore/should_compile/T19599a.++Another example is #19599, which looked like this:++ class (Show a, Enum a) => MyShow a where+ myShow :: a -> String++ myShow_impl :: MyShow a => a -> String++ foo :: Int -> String+ foo = myShow_impl @Int $fMyShowInt++ Rec { $fMyShowInt = MkMyShowD $fEnumInt $fShowInt $cmyShow+ ; $cmyShow = myShow_impl @Int $fMyShowInt }++Here, we really do want to specialise `myShow_impl @Int $fMyShowInt`.+++Note [Specialising a recursive group]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider+ let rec { f x = ...g x'...+ ; g y = ...f y'.... }+ in f 'a'+Here we specialise 'f' at Char; but that is very likely to lead to+a specialisation of 'g' at Char. We must do the latter, else the+whole point of specialisation is lost.++But we do not want to keep iterating to a fixpoint, because in the+presence of polymorphic recursion we might generate an infinite number+of specialisations.++So we use the following heuristic:+ * Arrange the rec block in dependency order, so far as possible+ (the occurrence analyser already does this)++ * Specialise it much like a sequence of lets++ * Then go through the block a second time, feeding call-info from+ the RHSs back in the bottom, as it were++In effect, the ordering maxmimises the effectiveness of each sweep,+and we do just two sweeps. This should catch almost every case of+monomorphic recursion -- the exception could be a very knotted-up+recursion with multiple cycles tied up together.++This plan is implemented in the Rec case of specBindItself.++Note [Specialisations already covered]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+We obviously don't want to generate two specialisations for the same+argument pattern. There are two wrinkles++1. We do the already-covered test in specDefn, not when we generate+the CallInfo in mkCallUDs. We used to test in the latter place, but+we now iterate the specialiser somewhat, and the Id at the call site+might therefore not have all the RULES that we can see in specDefn++2. What about two specialisations where the second is an *instance*+of the first? If the more specific one shows up first, we'll generate+specialisations for both. If the *less* specific one shows up first,+we *don't* currently generate a specialisation for the more specific+one. (See the call to lookupRule in already_covered.) Reasons:+ (a) lookupRule doesn't say which matches are exact (bad reason)+ (b) if the earlier specialisation is user-provided, it's+ far from clear that we should auto-specialise further++Note [Auto-specialisation and RULES]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider:+ g :: Num a => a -> a+ g = ...++ f :: (Int -> Int) -> Int+ f w = ...+ {-# RULE f g = 0 #-}++Suppose that auto-specialisation makes a specialised version of+g::Int->Int. That version won't appear in the LHS of the RULE for f.+So if the specialisation rule fires too early, the rule for f may+never fire.++It might be possible to add new rules, to "complete" the rewrite system.+Thus when adding+ RULE forall d. g Int d = g_spec+also add+ RULE f g_spec = 0++But that's a bit complicated. For now we ask the programmer's help,+by *copying the INLINE activation pragma* to the auto-specialised+rule. So if g says {-# NOINLINE[2] g #-}, then the auto-spec rule+will also not be active until phase 2. And that's what programmers+should jolly well do anyway, even aside from specialisation, to ensure+that g doesn't inline too early.++This in turn means that the RULE would never fire for a NOINLINE+thing so not much point in generating a specialisation at all.++Note [Specialisation shape]+~~~~~~~~~~~~~~~~~~~~~~~~~~~+We only specialise a function if it has visible top-level lambdas+corresponding to its overloading. E.g. if+ f :: forall a. Eq a => ....+then its body must look like+ f = /\a. \d. ...++Reason: when specialising the body for a call (f ty dexp), we want to+substitute dexp for d, and pick up specialised calls in the body of f.++We do allow casts, however; see Note [Account for casts in binding].++This doesn't always work. One example I came across was this:+ newtype Gen a = MkGen{ unGen :: Int -> a }++ choose :: Eq a => a -> Gen a+ choose n = MkGen (\r -> n)++ oneof = choose (1::Int)++It's a silly example, but we get+ choose = /\a. g `cast` co+where choose doesn't have any dict arguments. Thus far I have not+tried to fix this (wait till there's a real example).++Mind you, then 'choose' will be inlined (since RHS is trivial) so+it doesn't matter. This comes up with single-method classes++ class C a where { op :: a -> a }+ instance C a => C [a] where ....+==>+ $fCList :: C a => C [a]+ $fCList = $copList |> (...coercion>...)+ ....(uses of $fCList at particular types)...++So we suppress the WARN if the rhs is trivial.++Note [Inline specialisations]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Here is what we do with the InlinePragma of the original function++ * Activation/RuleMatchInfo: both inherited from the original function++ * InlineSpec: inherit from original function++ * Unfolding: transfer a StableUnfolding iff it is UnfWhen+ See GHC.Core.Unfold.Make.specUnfolding+ and its Note [Specialising unfoldings]++InlineSpec: you might wonder why we specialise INLINE functions at all.+After all they should be inlined, right? Two reasons:++ * Even INLINE functions are sometimes not inlined, when they aren't+ applied to interesting arguments. But perhaps the type arguments+ alone are enough to specialise (even though the args are too boring+ to trigger inlining), and it's certainly better to call the+ specialised version.++ * The RHS of an INLINE function might call another overloaded function,+ and we'd like to generate a specialised version of that function too.+ This actually happens a lot. Consider+ replicateM_ :: (Monad m) => Int -> m a -> m ()+ {-# INLINABLE replicateM_ #-}+ replicateM_ d x ma = ...+ The strictness analyser may transform to+ replicateM_ :: (Monad m) => Int -> m a -> m ()+ {-# INLINE replicateM_ #-}+ replicateM_ d x ma = case x of I# x' -> $wreplicateM_ d x' ma++ $wreplicateM_ :: (Monad m) => Int# -> m a -> m ()+ {-# INLINABLE $wreplicateM_ #-}+ $wreplicateM_ = ...+ Now an importing module has a specialised call to replicateM_, say+ (replicateM_ dMonadIO). We certainly want to specialise $wreplicateM_!+ This particular example had a huge effect on the call to replicateM_+ in nofib/shootout/n-body.+-}++{- *********************************************************************+* *+ SpecArg, and specHeader+* *+********************************************************************* -}++-- | An argument that we might want to specialise.+-- See Note [Specialising Calls] for the nitty gritty details.+data SpecArg+ =+ -- | Type arguments that should be specialised, due to appearing+ -- free in the type of a 'SpecDict'.+ SpecType Type++ -- | Type arguments that should remain polymorphic.+ | UnspecType++ -- | Dictionaries that should be specialised. mkCallUDs ensures+ -- that only "interesting" dictionary arguments get a SpecDict;+ -- see Note [Interesting dictionary arguments]+ | SpecDict DictExpr++ -- | Value arguments that should not be specialised.+ | UnspecArg++instance Outputable SpecArg where+ ppr (SpecType t) = text "SpecType" <+> ppr t+ ppr UnspecType = text "UnspecType"+ ppr (SpecDict d) = text "SpecDict" <+> ppr d+ ppr UnspecArg = text "UnspecArg"++specArgFreeIds :: SpecArg -> IdSet+specArgFreeIds (SpecType {}) = emptyVarSet+specArgFreeIds (SpecDict dx) = exprFreeIds dx+specArgFreeIds UnspecType = emptyVarSet+specArgFreeIds UnspecArg = emptyVarSet++isSpecDict :: SpecArg -> Bool+isSpecDict (SpecDict {}) = True+isSpecDict _ = False++-- | Given binders from an original function 'f', and the 'SpecArg's+-- corresponding to its usage, compute everything necessary to build+-- a specialisation.+--+-- We will use the running example from Note [Specialising Calls]:+--+-- f :: forall a b c. Int -> Eq a => Show b => c -> Blah+-- f @a @b @c i dEqA dShowB x = blah+--+-- Suppose we decide to specialise it at the following pattern:+--+-- [ SpecType T1, SpecType T2, UnspecType, UnspecArg+-- , SpecDict dEqT1, SpecDict ($dfShow dShowT2), UnspecArg ]+--+-- We'd eventually like to build the RULE+--+-- RULE "SPEC f @T1 @T2 _"+-- forall (@c :: Type) (i :: Int) (d1 :: Eq T1) (d2 :: Show T2).+-- f @T1 @T2 @c i d1 d2 = $sf @c i+--+-- and the specialisation '$sf'+--+-- $sf :: forall c. Int -> c -> Blah+-- $sf = SUBST[a :-> T1, b :-> T2, dEqA :-> dEqT1, dShowB :-> dShow1] (\@c i x -> blah)+--+-- where dShow1 is a floated binding created by bindAuxiliaryDict.+--+-- The cases for 'specHeader' below are presented in the same order as this+-- running example. The result of 'specHeader' for this example is as follows:+--+-- ( -- Returned arguments+-- env + [a :-> T1, b :-> T2, dEqA :-> dEqT1, dShowB :-> dShow1]+-- , [x]+--+-- -- RULE helpers+-- , [c, i, d1, d2]+-- , [T1, T2, c, i, d1, d2]+--+-- -- Specialised function helpers+-- , [c, i, x]+-- , [dShow1 = $dfShow dShowT2]+-- , [T1, T2, c, i, dEqT1, dShow1]+-- )+specHeader+ :: SpecEnv+ -> [InBndr] -- The binders from the original function 'f'+ -> [SpecArg] -- From the CallInfo+ -> SpecM ( Bool -- True <=> some useful specialisation happened+ -- Not the same as any (isSpecDict args) because+ -- the args might be longer than bndrs++ -- Returned arguments+ , SpecEnv -- Substitution to apply to the body of 'f'+ , [OutBndr] -- Leftover binders from the original function 'f'+ -- that don’t have a corresponding SpecArg++ -- RULE helpers+ , [OutBndr] -- Binders for the RULE+ , [OutExpr] -- Args for the LHS of the rule++ -- Specialised function helpers+ , [OutBndr] -- Binders for $sf+ , [DictBind] -- Auxiliary dictionary bindings+ , [OutExpr] -- Specialised arguments for unfolding+ -- Same length as "Args for LHS of rule"+ )++-- We want to specialise on type 'T1', and so we must construct a substitution+-- 'a->T1', as well as a LHS argument for the resulting RULE and unfolding+-- details.+specHeader env (bndr : bndrs) (SpecType ty : args)+ = do { -- Find qvars, the type variables to add to the binders for the rule+ -- Namely those free in `ty` that aren't in scope+ -- See (MP2) in Note [Specialising polymorphic dictionaries]+ let in_scope = Core.getSubstInScope (se_subst env)+ qvars = scopedSort $+ filterOut (`elemInScopeSet` in_scope) $+ tyCoVarsOfTypeList ty+ (env1, qvars') = substBndrs env qvars+ ty' = substTy env1 ty+ env2 = extendTvSubst env1 bndr ty'+ ; (useful, env3, leftover_bndrs, rule_bs, rule_es, bs', dx, spec_args)+ <- specHeader env2 bndrs args+ ; pure ( useful+ , env3+ , leftover_bndrs+ , qvars' ++ rule_bs+ , Type ty' : rule_es+ , qvars' ++ bs'+ , dx+ , Type ty' : spec_args+ )+ }++-- Next we have a type that we don't want to specialise. We need to perform+-- a substitution on it (in case the type refers to 'a'). Additionally, we need+-- to produce a binder, LHS argument and RHS argument for the resulting rule,+-- /and/ a binder for the specialised body.+specHeader env (bndr : bndrs) (UnspecType : args)+ = do { let (env', bndr') = substBndr env bndr+ ; (useful, env'', leftover_bndrs, rule_bs, rule_es, bs', dx, spec_args)+ <- specHeader env' bndrs args+ ; pure ( useful+ , env''+ , leftover_bndrs+ , bndr' : rule_bs+ , varToCoreExpr bndr' : rule_es+ , bndr' : bs'+ , dx+ , varToCoreExpr bndr' : spec_args+ )+ }++-- Next we want to specialise the 'Eq a' dict away. We need to construct+-- a wildcard binder to match the dictionary (See Note [Specialising Calls] for+-- the nitty-gritty), as a LHS rule and unfolding details.+specHeader env (bndr : bndrs) (SpecDict d : args)+ | not (isDeadBinder bndr)+ , allVarSet (`elemInScopeSet` in_scope) (exprFreeVars d)+ -- See Note [Weird special case for SpecDict]+ = do { (env1, bndr') <- newDictBndr env bndr -- See Note [Zap occ info in rule binders]+ ; let (env2, dx_bind, spec_dict) = bindAuxiliaryDict env1 bndr bndr' d+ ; (_, env3, leftover_bndrs, rule_bs, rule_es, bs', dx, spec_args)+ <- specHeader env2 bndrs args+ ; pure ( True -- Ha! A useful specialisation!+ , env3+ , leftover_bndrs+ -- See Note [Evidence foralls]+ , exprFreeIdsList (varToCoreExpr bndr') ++ rule_bs+ , varToCoreExpr bndr' : rule_es+ , bs'+ , maybeToList dx_bind ++ dx+ , spec_dict : spec_args+ )+ }+ where+ in_scope = Core.getSubstInScope (se_subst env)++-- Finally, we don't want to specialise on this argument 'i':+-- - It's an UnSpecArg, or+-- - It's a dead dictionary+-- We need to produce a binder, LHS and RHS argument for the RULE, and+-- a binder for the specialised body.+--+-- NB: Calls to 'specHeader' will trim off any trailing 'UnspecArg's, which is+-- why 'i' doesn't appear in our RULE above. But we have no guarantee that+-- there aren't 'UnspecArg's which come /before/ all of the dictionaries, so+-- this case must be here.+specHeader env (bndr : bndrs) (_ : args)+ -- The "_" can be UnSpecArg, or SpecDict where the bndr is dead+ = do { -- see Note [Zap occ info in rule binders]+ let (env', bndr') = substBndr env (zapIdOccInfo bndr)+ ; (useful, env'', leftover_bndrs, rule_bs, rule_es, bs', dx, spec_args)+ <- specHeader env' bndrs args++ ; let bndr_ty = idType bndr'++ -- See Note [Drop dead args from specialisations]+ -- C.f. GHC.Core.Opt.WorkWrap.Utils.mk_absent_let+ (mb_spec_bndr, spec_arg)+ | isDeadBinder bndr+ , Just lit_expr <- mkLitRubbish bndr_ty+ = (Nothing, lit_expr)+ | otherwise+ = (Just bndr', varToCoreExpr bndr')++ ; pure ( useful+ , env''+ , leftover_bndrs+ , bndr' : rule_bs+ , varToCoreExpr bndr' : rule_es+ , case mb_spec_bndr of+ Just b' -> b' : bs'+ Nothing -> bs'+ , dx+ , spec_arg : spec_args+ )+ }++-- If we run out of binders, stop immediately+-- See Note [Specialisation Must Preserve Sharing]+specHeader env [] _ = pure (False, env, [], [], [], [], [], [])++-- Return all remaining binders from the original function. These have the+-- invariant that they should all correspond to unspecialised arguments, so+-- it's safe to stop processing at this point.+specHeader env bndrs []+ = pure (False, env', bndrs', [], [], [], [], [])+ where+ (env', bndrs') = substBndrs env bndrs+++-- | Binds a dictionary argument to a fresh name, to preserve sharing+bindAuxiliaryDict+ :: SpecEnv+ -> InId -> OutId -> OutExpr -- Original dict binder, and the witnessing expression+ -> ( SpecEnv -- Substitutes for orig_dict_id+ , Maybe DictBind -- Auxiliary dict binding, if any+ , OutExpr) -- Witnessing expression (always trivial)+bindAuxiliaryDict env@(SE { se_subst = subst })+ orig_dict_id fresh_dict_id dict_expr++ -- If the dictionary argument is trivial,+ -- don’t bother creating a new dict binding; just substitute+ | exprIsTrivial dict_expr+ = let env' = env { se_subst = Core.extendSubst subst orig_dict_id dict_expr }+ in -- pprTrace "bindAuxiliaryDict:trivial" (ppr orig_dict_id <+> ppr dict_id) $+ (env', Nothing, dict_expr)++ | otherwise -- Non-trivial dictionary arg; make an auxiliary binding+ = let fresh_dict_id' = fresh_dict_id `addDictUnfolding` dict_expr++ dict_bind = mkDB (NonRec fresh_dict_id' dict_expr)+ env' = env { se_subst = Core.extendSubst subst orig_dict_id (Var fresh_dict_id')+ `Core.extendSubstInScope` fresh_dict_id' }+ -- Ensure the new unfolding is in the in-scope set+ in -- pprTrace "bindAuxiliaryDict:non-trivial" (ppr orig_dict_id <+> ppr fresh_dict_id') $+ (env', Just dict_bind, Var fresh_dict_id')++addDictUnfolding :: Id -> CoreExpr -> Id+-- Add unfolding for freshly-bound Ids: see Note [Make the new dictionaries interesting]+-- and Note [Specialisation modulo dictionary selectors]+addDictUnfolding id rhs+ = id `setIdUnfolding` mkSimpleUnfolding defaultUnfoldingOpts rhs++{-+Note [Make the new dictionaries interesting]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Important! We're going to substitute dx_id1 for d+and we want it to look "interesting", else we won't gather *any*+consequential calls. E.g.+ f d = ...g d....+If we specialise f for a call (f (dfun dNumInt)), we'll get+a consequent call (g d') with an auxiliary definition+ d' = df dNumInt+We want that consequent call to look interesting; so we add an unfolding+in the dictionary Id.+-}+++{- *********************************************************************+* *+ UsageDetails and suchlike+* *+********************************************************************* -}++data UsageDetails+ = MkUD { ud_binds :: !FloatedDictBinds+ , ud_calls :: !CallDetails }+ -- INVARIANT: suppose bs = fdb_bndrs ud_binds+ -- Then 'calls' may *mention* 'bs',+ -- but there should be no calls *for* bs++data FloatedDictBinds -- See Note [Floated dictionary bindings]+ = FDB { fdb_binds :: !(OrdList DictBind)+ -- The order is important;+ -- in ds1 `appOL` ds2, bindings in ds2 can depend on those in ds1++ , fdb_bndrs :: !IdSet+ } -- ^ The binders of 'fdb_binds'.+ -- Caches a superset of the expression+ -- `mkVarSet (bindersOfDictBinds fdb_binds))`+ -- for later addition to an InScopeSet++-- | A 'DictBind' is a binding along with a cached set containing its free+-- variables (both type variables and dictionaries). We need this set+-- in splitDictBinds, when filtering bindings to decide which are+-- captured by a binder+data DictBind = DB { db_bind :: CoreBind, db_fvs :: VarSet }++bindersOfDictBind :: DictBind -> [Id]+bindersOfDictBind = bindersOf . db_bind++bindersOfDictBinds :: Foldable f => f DictBind -> [Id]+bindersOfDictBinds = bindersOfBinds . foldr ((:) . db_bind) []++{- Note [Floated dictionary bindings]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+We float out dictionary bindings for the reasons described under+"Dictionary floating" above. But not /just/ dictionary bindings.+Consider++ f :: Eq a => blah+ f a d = rhs++ $c== :: T -> T -> Bool+ $c== x y = ...++ $df :: Eq T+ $df = Eq $c== ...++ gurgle = ...(f @T $df)...++We gather the call info for (f @T $df), and we don't want to drop it+when we come across the binding for $df. So we add $df to the floats+and continue. But then we have to add $c== to the floats, and so on.+These all float above the binding for 'f', and now we can+successfully specialise 'f'.++So the DictBinds in (ud_binds :: OrdList DictBind) may contain+non-dictionary bindings too.++Note [Specialising polymorphic dictionaries]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider+ class M a where { foo :: a -> Int }++ instance M (ST s) where ...+ -- dMST :: forall s. M (ST s)++ wimwam :: forall a. M a => a -> Int+ wimwam = /\a \(d::M a). body++ f :: ST s -> Int+ f = /\s \(x::ST s). wimwam @(ST s) (dMST @s) dx + 1++We'd like to specialise wimwam at (ST s), thus+ $swimwam :: forall s. ST s -> Int+ $swimwam = /\s. body[ST s/a, (dMST @s)/d]++ RULE forall s (d :: M (ST s)).+ wimwam @(ST s) d = $swimwam @s++Here are the moving parts:++(MP1) We must /not/ dump the CallInfo+ CIS wimwam (CI { ci_key = [@(ST s), dMST @s]+ , ci_fvs = {dMST} })+ when we come to the /\s. Instead, we simply let it continue to float+ upwards. Hence ci_fvs is an IdSet, listing the /Ids/ that+ are free in the call, but not the /TyVars/. Hence using specArgFreeIds+ in singleCall.++ NB to be fully kosher we should explicitly quantifying the CallInfo+ over 's', but we don't bother. This would matter if there was an+ enclosing binding of the same 's', which I don't expect to happen.++(MP2) When we come to specialise the call, we must remember to quantify+ over 's'. That is done in the SpecType case of specHeader, where+ we add 's' (called qvars) to the binders of the RULE and the specialised+ function.++(MP3) If we have f :: forall m. Monoid m => blah, and two calls+ (f @(Endo b) (d :: Monoid (Endo b))+ (f @(Endo (c->c)) (d :: Monoid (Endo (c->c)))+ we want to generate a specialisation only for the first. The second+ is just a substitution instance of the first, with no greater specialisation.+ Hence the call to `remove_dups` in `filterCalls`.++All this arose in #13873, in the unexpected form that a SPECIALISE+pragma made the program slower! The reason was that the specialised+function $sinsertWith arising from the pragma looked rather like `f`+above, and failed to specialise a call in its body like wimwam.+Without the pragma, the original call to `insertWith` was completely+monomorpic, and specialised in one go.++Wrinkles.++* See Note [Weird special case for SpecDict]++* With -XOverlappingInstances you might worry about this:+ class C a where ...+ instance C (Maybe Int) where ... -- $df1 :: C (Maybe Int)+ instance C (Maybe a) where ... -- $df2 :: forall a. C (Maybe a)++ f :: C a => blah+ f = rhs++ g = /\a. ...(f @(Maybe a) ($df2 a))...+ h = ...f @(Maybe Int) $df1++ There are two calls to f, but with different evidence. This patch will+ combine them into one. But it's OK: this code will never arise unless you+ use -XIncoherentInstances. Even with -XOverlappingInstances, GHC tries hard+ to keep dictionaries as singleton types. But that goes out of the window+ with -XIncoherentInstances -- and that is true even with ordianry type-class+ specialisation (at least if any inlining has taken place).++ GHC makes very few guarantees when you use -XIncoherentInstances, and its+ not worth crippling the normal case for the incoherent corner. (The best+ thing might be to switch off specialisation altogether if incoherence is+ involved... but incoherence is a property of an instance, not a class, so+ it's a hard test to make.)++ But see Note [Specialisation and overlapping instances].++Note [Weird special case for SpecDict]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Suppose we are trying to specialise for this this call:+ $wsplit @T (mkD @k @(a::k) :: C T)+where+ mkD :: forall k (a::k). C T+is a top-level dictionary-former. This actually happened in #22459,+because of (MP1) of Note [Specialising polymorphic dictionaries].++How can we speicalise $wsplit? We might try++ RULE "SPEC" forall (d :: C T). $wsplit @T d = $s$wsplit++but then in the body of $s$wsplit what will we use for the dictionary+evidence? We can't use (mkD @k @(a::k)) because k and a aren't in scope.+We could zap `k` to (Any @Type) and `a` to (Any @(Any @Type)), but that+is a lot of hard work for a very strange case.++So we simply refrain from specialising in this case; hence the guard+ allVarSet (`elemInScopeSet` in_scope) (exprFreeVars d)+in the SpecDict cased of specHeader.++How did this strange polymorphic mkD arise in the first place?+From GHC.Core.Opt.Utils.abstractFloats, which was abstracting+over too many type variables. But that too is now fixed;+see Note [Which type variables to abstract over] in that module.+-}++instance Outputable DictBind where+ ppr (DB { db_bind = bind, db_fvs = fvs })+ = text "DB" <+> braces (sep [ text "fvs: " <+> ppr fvs+ , text "bind:" <+> ppr bind ])++instance Outputable UsageDetails where+ ppr (MkUD { ud_binds = dbs, ud_calls = calls })+ = text "MkUD" <+> braces (sep (punctuate comma+ [text "binds" <+> equals <+> ppr dbs,+ text "calls" <+> equals <+> ppr calls]))++instance Outputable FloatedDictBinds where+ ppr (FDB { fdb_binds = binds }) = ppr binds++emptyUDs :: UsageDetails+emptyUDs = MkUD { ud_binds = emptyFDBs, ud_calls = emptyDVarEnv }+++emptyFDBs :: FloatedDictBinds+emptyFDBs = FDB { fdb_binds = nilOL, fdb_bndrs = emptyVarSet }++------------------------------------------------------------+type CallDetails = DIdEnv CallInfoSet+ -- The order of specialized binds and rules depends on how we linearize+ -- CallDetails, so to get determinism we must use a deterministic set here.+ -- See Note [Deterministic UniqFM] in GHC.Types.Unique.DFM++data CallInfoSet = CIS Id (Bag CallInfo)+ -- The list of types and dictionaries is guaranteed to+ -- match the type of f+ -- The Bag may contain duplicate calls (i.e. f @T and another f @T)+ -- These dups are eliminated by already_covered in specCalls++data CallInfo+ = CI { ci_key :: [SpecArg] -- All arguments+ , ci_fvs :: IdSet -- Free Ids of the ci_key call+ -- /not/ including the main id itself, of course+ -- NB: excluding tyvars:+ -- See Note [Specialising polymorphic dictionaries]+ }++type DictExpr = CoreExpr++ciSetFilter :: (CallInfo -> Bool) -> CallInfoSet -> CallInfoSet+ciSetFilter p (CIS id a) = CIS id (filterBag p a)++instance Outputable CallInfoSet where+ ppr (CIS fn map) = hang (text "CIS" <+> ppr fn)+ 2 (ppr map)++pprCallInfo :: Id -> CallInfo -> SDoc+pprCallInfo fn (CI { ci_key = key })+ = ppr fn <+> ppr key++instance Outputable CallInfo where+ ppr (CI { ci_key = key, ci_fvs = _fvs })+ = text "CI" <> braces (sep (map ppr key))++unionCalls :: CallDetails -> CallDetails -> CallDetails+unionCalls c1 c2 = plusDVarEnv_C unionCallInfoSet c1 c2++unionCallInfoSet :: CallInfoSet -> CallInfoSet -> CallInfoSet+unionCallInfoSet (CIS f calls1) (CIS _ calls2) =+ CIS f (calls1 `unionBags` calls2)++callDetailsFVs :: CallDetails -> VarSet+callDetailsFVs calls =+ nonDetStrictFoldUDFM (unionVarSet . callInfoFVs) emptyVarSet calls+ -- It's OK to use nonDetStrictFoldUDFM here because we forget the ordering+ -- immediately by converting to a nondeterministic set.++callInfoFVs :: CallInfoSet -> VarSet+callInfoFVs (CIS _ call_info) =+ foldr (\(CI { ci_fvs = fv }) vs -> unionVarSet fv vs) emptyVarSet call_info++getTheta :: [PiTyBinder] -> [PredType]+getTheta = fmap piTyBinderType . filter isInvisiblePiTyBinder . filter isAnonPiTyBinder+++------------------------------------------------------------+singleCall :: Id -> [SpecArg] -> UsageDetails+singleCall id args+ = MkUD {ud_binds = emptyFDBs,+ ud_calls = unitDVarEnv id $ CIS id $+ unitBag (CI { ci_key = args+ , ci_fvs = call_fvs }) }+ where+ call_fvs = foldr (unionVarSet . specArgFreeIds) emptyVarSet args+ -- specArgFreeIds: we specifically look for free Ids, not TyVars+ -- see (MP1) in Note [Specialising polymorphic dictionaries]+ --+ -- We don't include the 'id' itself.++mkCallUDs :: SpecEnv -> OutExpr -> [OutExpr] -> UsageDetails+mkCallUDs env fun args+ | (_, Var f) <- stripTicksTop tickishFloatable fun -- See Note [Ticks on applications]+ = -- pprTraceWith "mkCallUDs" (\res -> vcat [ ppr f, ppr args, ppr res ]) $+ mkCallUDs' env f args+ | otherwise+ = emptyUDs++mkCallUDs' :: SpecEnv -> Id -> [OutExpr] -> UsageDetails+mkCallUDs' env f args+ | wantCallsFor env f -- We want it, and...+ , not (null ci_key) -- this call site has a useful specialisation+ = -- pprTrace "mkCallUDs: keeping" _trace_doc+ singleCall f ci_key++ | otherwise -- See also Note [Specialisations already covered]+ = -- pprTrace "mkCallUDs: discarding" _trace_doc+ emptyUDs++ where+ _trace_doc = vcat [ppr f, ppr args, ppr ci_key]+ pis = fst $ splitPiTys $ idType f+ constrained_tyvars = tyCoVarsOfTypes $ getTheta pis++ ci_key :: [SpecArg]+ ci_key = dropWhileEndLE (not . isSpecDict) $+ zipWith mk_spec_arg args pis+ -- Drop trailing args until we get to a SpecDict+ -- In this way the RULE has as few args as possible,+ -- which broadens its applicability, since rules only+ -- fire when saturated++ mk_spec_arg :: OutExpr -> PiTyBinder -> SpecArg+ mk_spec_arg arg (Named bndr)+ | binderVar bndr `elemVarSet` constrained_tyvars+ = case arg of+ Type ty -> SpecType ty+ _ -> pprPanic "ci_key" $ ppr arg+ | otherwise = UnspecType++ -- For "invisibleFunArg", which are the type-class dictionaries,+ -- we decide on a case by case basis if we want to specialise+ -- on this argument; if so, SpecDict, if not UnspecArg+ mk_spec_arg arg (Anon pred af)+ | isInvisibleFunArg af+ , interestingDict arg (scaledThing pred)+ -- See Note [Interesting dictionary arguments]+ = SpecDict arg++ | otherwise = UnspecArg++{-+Note [Ticks on applications]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Ticks such as source location annotations can sometimes make their way+onto applications (see e.g. #21697). So if we see something like++ App (Tick _ f) e++we need to descend below the tick to find what the real function being+applied is.++The resulting RULE also has to be able to match this annotated use+site, so we only look through ticks that RULE matching looks through+(see Note [Tick annotations in RULE matching] in GHC.Core.Rules).+-}++wantCallsFor :: SpecEnv -> Id -> Bool+wantCallsFor _env _f = True+ -- We could reduce the size of the UsageDetails by being less eager+ -- about collecting calls for LocalIds: there is no point for+ -- ones that are lambda-bound. We can't decide this by looking at+ -- the (absence of an) unfolding, because unfoldings for local+ -- functions are discarded by cloneBindSM, so no local binder will+ -- have an unfolding at this stage. We'd have to keep a candidate+ -- set of let-binders.+ --+ -- Not many lambda-bound variables have dictionary arguments, so+ -- this would make little difference anyway.+ --+ -- For imported Ids we could check for an unfolding, but we have to+ -- do so anyway in canSpecImport, and it seems better to have it+ -- all in one place. So we simply collect usage info for imported+ -- overloaded functions.++{- Note [Interesting dictionary arguments]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider this+ \a.\d:Eq a. let f = ... in ...(f d)...+There really is not much point in specialising f wrt the dictionary d,+because the code for the specialised f is not improved at all, because+d is lambda-bound. We simply get junk specialisations.++What is "interesting"? Just that it has *some* structure. But what about+variables? We look in the variable's /unfolding/. And that means+that we must be careful to ensure that dictionaries have unfoldings,++* cloneBndrSM discards non-Stable unfoldings+* specBind updates the unfolding after specialisation+ See Note [Update unfolding after specialisation]+* bindAuxiliaryDict adds an unfolding for an aux dict+ see Note [Specialisation modulo dictionary selectors]+* specCase adds unfoldings for the new bindings it creates++We accidentally lost accurate tracking of local variables for a long+time, because cloned variables didn't have unfoldings. But makes a+massive difference in a few cases, eg #5113. For nofib as a+whole it's only a small win: 2.2% improvement in allocation for ansi,+1.2% for bspt, but mostly 0.0! Average 0.1% increase in binary size.++Note [Update unfolding after specialisation]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider (#21848)++ wombat :: Show b => Int -> b -> String+ wombat a b | a>0 = wombat (a-1) b+ | otherwise = show a ++ wombat a b++ class C a where+ meth :: Show b => a -> b -> String+ dummy :: a -> () -- Force a datatype dictionary representation++ instance C Int where+ meth = wombat+ dummy _ = ()++ class C a => D a -- D has C as a superclass+ instance D Int++ f :: (D a, Show b) => a -> b -> String+ {-# INLINABLE[0] f #-}+ f a b = meth a b ++ "!" ++ meth a b++Now `f` turns into:++ f @a @b (dd :: D a) (ds :: Show b) a b+ = let dc :: D a = %p1 dd -- Superclass selection+ in meth @a dc ....+ meth @a dc ....++When we specialise `f`, at a=Int say, that superclass selection can+nfire (via rewiteClassOps), but that info (that 'dc' is now a+particular dictionary `C`, of type `C Int`) must be available to+the call `meth @a dc`, so that we can fire the `meth` class-op, and+thence specialise `wombat`.++We deliver on this idea by updating the unfolding for the binder+in the NonRec case of specBind. (This is too exotic to trouble with+the Rec case.)+-}++interestingDict :: CoreExpr -> Type -> Bool+-- A dictionary argument is interesting if it has *some* structure,+-- see Note [Interesting dictionary arguments]+-- NB: "dictionary" arguments include constraints of all sorts,+-- including equality constraints; hence the Coercion case+-- To make this work, we need to ensure that dictionaries have+-- unfoldings in them.+interestingDict arg arg_ty+ | not (typeDeterminesValue arg_ty) = False -- See Note [Type determines value]+ | otherwise = go arg+ where+ go (Var v) = hasSomeUnfolding (idUnfolding v)+ || isDataConWorkId v+ go (Type _) = False+ go (Coercion _) = False+ go (App fn (Type _)) = go fn+ go (App fn (Coercion _)) = go fn+ go (Tick _ a) = go a+ go (Cast e _) = go e+ go _ = True++thenUDs :: UsageDetails -> UsageDetails -> UsageDetails+thenUDs (MkUD {ud_binds = db1, ud_calls = calls1})+ (MkUD {ud_binds = db2, ud_calls = calls2})+ = MkUD { ud_binds = db1 `thenFDBs` db2+ , ud_calls = calls1 `unionCalls` calls2 }++thenFDBs :: FloatedDictBinds -> FloatedDictBinds -> FloatedDictBinds+-- Combine FloatedDictBinds+-- In (dbs1 `thenFDBs` dbs2), dbs2 may mention dbs1 but not vice versa+thenFDBs (FDB { fdb_binds = dbs1, fdb_bndrs = bs1 })+ (FDB { fdb_binds = dbs2, fdb_bndrs = bs2 })+ = FDB { fdb_binds = dbs1 `appOL` dbs2+ , fdb_bndrs = bs1 `unionVarSet` bs2 }++-----------------------------+_dictBindBndrs :: OrdList DictBind -> [Id]+_dictBindBndrs dbs = foldr ((++) . bindersOf . db_bind) [] dbs++-- | Construct a 'DictBind' from a 'CoreBind'+mkDB :: CoreBind -> DictBind+mkDB bind = DB { db_bind = bind, db_fvs = bind_fvs bind }++-- | Identify the free variables of a 'CoreBind'+bind_fvs :: CoreBind -> VarSet+bind_fvs (NonRec bndr rhs) = pair_fvs (bndr,rhs)+bind_fvs (Rec prs) = rhs_fvs `delVarSetList` (map fst prs)+ where+ rhs_fvs = unionVarSets (map pair_fvs prs)++pair_fvs :: (Id, CoreExpr) -> VarSet+pair_fvs (bndr, rhs) = exprSomeFreeVars interesting rhs+ `unionVarSet` idFreeVars bndr+ -- idFreeVars: don't forget variables mentioned in+ -- the rules of the bndr. C.f. OccAnal.addRuleUsage+ -- Also tyvars mentioned in its type; they may not appear+ -- in the RHS+ -- type T a = Int+ -- x :: T a = 3+ where+ interesting :: InterestingVarFun+ interesting v = isLocalVar v || (isId v && isDFunId v)+ -- Very important: include DFunIds /even/ if it is imported+ -- Reason: See Note [Avoiding loops in specImports], the #13429+ -- example involving an imported dfun. We must know+ -- whether a dictionary binding depends on an imported+ -- DFun in case we try to specialise that imported DFun++-- | Flatten a set of "dumped" 'DictBind's, and some other binding+-- pairs, into a single recursive binding.+recWithDumpedDicts :: [(Id,CoreExpr)] -> OrdList DictBind -> DictBind+recWithDumpedDicts pairs dbs+ = DB { db_bind = Rec bindings+ , db_fvs = fvs `delVarSetList` map fst bindings }+ where+ (bindings, fvs) = foldr add ([], emptyVarSet)+ (dbs `snocOL` mkDB (Rec pairs))+ add (DB { db_bind = bind, db_fvs = fvs }) (prs_acc, fvs_acc)+ = case bind of+ NonRec b r -> ((b,r) : prs_acc, fvs')+ Rec prs1 -> (prs1 ++ prs_acc, fvs')+ where+ fvs' = fvs_acc `unionVarSet` fvs++snocDictBind :: UsageDetails -> DictBind -> UsageDetails+snocDictBind uds@MkUD{ud_binds= FDB { fdb_binds = dbs, fdb_bndrs = bs }} db+ = uds { ud_binds = FDB { fdb_binds = dbs `snocOL` db+ , fdb_bndrs = bs `extendVarSetList` bindersOfDictBind db } }++snocDictBinds :: UsageDetails -> [DictBind] -> UsageDetails+-- Add ud_binds to the tail end of the bindings in uds+snocDictBinds uds@MkUD{ud_binds=FDB{ fdb_binds = binds, fdb_bndrs = bs }} dbs+ = uds { ud_binds = FDB { fdb_binds = binds `appOL` (toOL dbs)+ , fdb_bndrs = bs `extendVarSetList` bindersOfDictBinds dbs } }++consDictBinds :: [DictBind] -> UsageDetails -> UsageDetails+consDictBinds dbs uds@MkUD{ud_binds=FDB{fdb_binds = binds, fdb_bndrs = bs}}+ = uds { ud_binds = FDB{ fdb_binds = toOL dbs `appOL` binds+ , fdb_bndrs = bs `extendVarSetList` bindersOfDictBinds dbs } }++wrapDictBinds :: FloatedDictBinds -> [CoreBind] -> [CoreBind]+wrapDictBinds (FDB { fdb_binds = dbs }) binds+ = foldr add binds dbs+ where+ add (DB { db_bind = bind }) binds = bind : binds++wrapDictBindsE :: OrdList DictBind -> CoreExpr -> CoreExpr+wrapDictBindsE dbs expr+ = foldr add expr dbs+ where+ add (DB { db_bind = bind }) expr = Let bind expr++----------------------+dumpUDs :: [CoreBndr] -> UsageDetails -> (UsageDetails, OrdList DictBind)+-- Used at a lambda or case binder; just dump anything mentioning the binder+dumpUDs bndrs uds@(MkUD { ud_binds = orig_dbs, ud_calls = orig_calls })+ | null bndrs = (uds, nilOL) -- Common in case alternatives+ | otherwise = -- pprTrace "dumpUDs" (ppr bndrs $$ ppr free_uds $$ ppr dump_dbs) $+ (free_uds, dump_dbs)+ where+ free_uds = uds { ud_binds = free_dbs, ud_calls = free_calls }+ bndr_set = mkVarSet bndrs+ (free_dbs, dump_dbs, dump_set) = splitDictBinds orig_dbs bndr_set+ free_calls = deleteCallsMentioning dump_set $ -- Drop calls mentioning bndr_set on the floor+ deleteCallsFor bndrs orig_calls -- Discard calls for bndr_set; there should be+ -- no calls for any of the dicts in dump_dbs++dumpBindUDs :: [CoreBndr] -> UsageDetails -> (UsageDetails, OrdList DictBind, Bool)+-- Used at a let(rec) binding.+-- We return a boolean indicating whether the binding itself is mentioned,+-- directly or indirectly, by any of the ud_calls; in that case we want to+-- float the binding itself;+-- See Note [Floated dictionary bindings]+dumpBindUDs bndrs (MkUD { ud_binds = orig_dbs, ud_calls = orig_calls })+ = -- pprTrace "dumpBindUDs" (ppr bndrs $$ ppr free_uds $$ ppr dump_dbs $$ ppr float_all) $+ (free_uds, dump_dbs, float_all)+ where+ free_uds = MkUD { ud_binds = free_dbs, ud_calls = free_calls }+ bndr_set = mkVarSet bndrs+ (free_dbs, dump_dbs, dump_set) = splitDictBinds orig_dbs bndr_set+ free_calls = deleteCallsFor bndrs orig_calls+ float_all = dump_set `intersectsVarSet` callDetailsFVs free_calls++callsForMe :: Id -> UsageDetails -> (UsageDetails, [CallInfo])+callsForMe fn uds@MkUD { ud_binds = orig_dbs, ud_calls = orig_calls }+ = -- pprTrace ("callsForMe")+ -- (vcat [ppr fn,+ -- text "Orig dbs =" <+> ppr (_dictBindBndrs orig_dbs),+ -- text "Orig calls =" <+> ppr orig_calls,+ -- text "Calls for me =" <+> ppr calls_for_me]) $+ (uds_without_me, calls_for_me)+ where+ uds_without_me = uds { ud_calls = delDVarEnv orig_calls fn }+ calls_for_me = case lookupDVarEnv orig_calls fn of+ Nothing -> []+ Just cis -> filterCalls cis orig_dbs+ -- filterCalls: drop calls that (directly or indirectly)+ -- refer to fn. See Note [Avoiding loops (DFuns)]++----------------------+filterCalls :: CallInfoSet -> FloatedDictBinds -> [CallInfo]+-- Remove dominated calls (Note [Specialising polymorphic dictionaries])+-- and loopy DFuns (Note [Avoiding loops (DFuns)])+filterCalls (CIS fn call_bag) (FDB { fdb_binds = dbs })+ | isDFunId fn -- Note [Avoiding loops (DFuns)] applies only to DFuns+ = filter ok_call de_dupd_calls+ | otherwise -- Do not apply it to non-DFuns+ = de_dupd_calls -- See Note [Avoiding loops (non-DFuns)]+ where+ de_dupd_calls = remove_dups call_bag++ dump_set = foldl' go (unitVarSet fn) dbs+ -- This dump-set could also be computed by splitDictBinds+ -- (_,_,dump_set) = splitDictBinds dbs {fn}+ -- But this variant is shorter++ go so_far (DB { db_bind = bind, db_fvs = fvs })+ | fvs `intersectsVarSet` so_far+ = extendVarSetList so_far (bindersOf bind)+ | otherwise = so_far++ ok_call (CI { ci_fvs = fvs }) = fvs `disjointVarSet` dump_set++remove_dups :: Bag CallInfo -> [CallInfo]+-- Calls involving more generic instances beat more specific ones.+-- See (MP3) in Note [Specialising polymorphic dictionaries]+remove_dups calls = foldr add [] calls+ where+ add :: CallInfo -> [CallInfo] -> [CallInfo]+ add ci [] = [ci]+ add ci1 (ci2:cis) | ci2 `beats_or_same` ci1 = ci2:cis+ | ci1 `beats_or_same` ci2 = ci1:cis+ | otherwise = ci2 : add ci1 cis++beats_or_same :: CallInfo -> CallInfo -> Bool+beats_or_same (CI { ci_key = args1 }) (CI { ci_key = args2 })+ = go args1 args2+ where+ go [] _ = True+ go (arg1:args1) (arg2:args2) = go_arg arg1 arg2 && go args1 args2+ go (_:_) [] = False++ go_arg (SpecType ty1) (SpecType ty2) = isJust (tcMatchTy ty1 ty2)+ go_arg UnspecType UnspecType = True+ go_arg (SpecDict {}) (SpecDict {}) = True+ go_arg UnspecArg UnspecArg = True+ go_arg _ _ = False++----------------------+splitDictBinds :: FloatedDictBinds -> IdSet -> (FloatedDictBinds, OrdList DictBind, IdSet)+-- splitDictBinds dbs bndrs returns+-- (free_dbs, dump_dbs, dump_set)+-- where+-- * dump_dbs depends, transitively on bndrs+-- * free_dbs does not depend on bndrs+-- * dump_set = bndrs `union` bndrs(dump_dbs)+splitDictBinds (FDB { fdb_binds = dbs, fdb_bndrs = bs }) bndr_set+ = (FDB { fdb_binds = free_dbs+ , fdb_bndrs = bs `minusVarSet` dump_set }+ , dump_dbs, dump_set)+ where+ (free_dbs, dump_dbs, dump_set)+ = foldl' split_db (nilOL, nilOL, bndr_set) dbs+ -- Important that it's foldl' not foldr;+ -- we're accumulating the set of dumped ids in dump_set++ split_db (free_dbs, dump_dbs, dump_idset) db+ | DB { db_bind = bind, db_fvs = fvs } <- db+ , dump_idset `intersectsVarSet` fvs -- Dump it+ = (free_dbs, dump_dbs `snocOL` db,+ extendVarSetList dump_idset (bindersOf bind))++ | otherwise -- Don't dump it+ = (free_dbs `snocOL` db, dump_dbs, dump_idset)+++----------------------+deleteCallsMentioning :: VarSet -> CallDetails -> CallDetails+-- Remove calls mentioning any Id in bndrs+-- NB: The call is allowed to mention TyVars in bndrs+-- Note [Specialising polymorphic dictionaries]+-- ci_fvs are just the free /Ids/+deleteCallsMentioning bndrs calls+ = mapDVarEnv (ciSetFilter keep_call) calls+ where+ keep_call (CI { ci_fvs = fvs }) = fvs `disjointVarSet` bndrs++deleteCallsFor :: [Id] -> CallDetails -> CallDetails+-- Remove calls *for* bndrs+deleteCallsFor bndrs calls = delDVarEnvList calls bndrs++{-+************************************************************************+* *+\subsubsection{Boring helper functions}+* *+************************************************************************+-}++type SpecM a = UniqSM a++runSpecM :: SpecM a -> CoreM a+runSpecM thing_inside+ = do { us <- getUniqueSupplyM+ ; return (initUs_ us thing_inside) }++mapAndCombineSM :: (a -> SpecM (b, UsageDetails)) -> [a] -> SpecM ([b], UsageDetails)+mapAndCombineSM _ [] = return ([], emptyUDs)+mapAndCombineSM f (x:xs) = do (y, uds1) <- f x+ (ys, uds2) <- mapAndCombineSM f xs+ return (y:ys, uds1 `thenUDs` uds2)++extendTvSubst :: SpecEnv -> TyVar -> Type -> SpecEnv+extendTvSubst env tv ty+ = env { se_subst = Core.extendTvSubst (se_subst env) tv ty }++extendInScope :: SpecEnv -> OutId -> SpecEnv+extendInScope env@(SE { se_subst = subst }) bndr+ = env { se_subst = subst `Core.extendSubstInScope` bndr }++zapSubst :: SpecEnv -> SpecEnv+zapSubst env@(SE { se_subst = subst })+ = env { se_subst = Core.zapSubst subst }++substTy :: SpecEnv -> Type -> Type+substTy env ty = substTyUnchecked (se_subst env) ty++substCo :: SpecEnv -> Coercion -> Coercion+substCo env co = Core.substCo (se_subst env) co++substBndr :: SpecEnv -> CoreBndr -> (SpecEnv, CoreBndr)+substBndr env bs = case Core.substBndr (se_subst env) bs of+ (subst', bs') -> (env { se_subst = subst' }, bs')++substBndrs :: SpecEnv -> [CoreBndr] -> (SpecEnv, [CoreBndr])+substBndrs env bs = case Core.substBndrs (se_subst env) bs of+ (subst', bs') -> (env { se_subst = subst' }, bs')++cloneBndrSM :: SpecEnv -> Id -> SpecM (SpecEnv, Id)+-- Clone the binders of the bind; return new bind with the cloned binders+-- Return the substitution to use for RHSs, and the one to use for the body+-- Discards non-Stable unfoldings+cloneBndrSM env@(SE { se_subst = subst }) bndr+ = do { us <- getUniqueSupplyM+ ; let (subst', bndr') = Core.cloneIdBndr subst us bndr+ ; return (env { se_subst = subst' }, bndr') }++cloneRecBndrsSM :: SpecEnv -> [Id] -> SpecM (SpecEnv, [Id])+cloneRecBndrsSM env@(SE { se_subst = subst }) bndrs+ = do { us <- getUniqueSupplyM+ ; let (subst', bndrs') = Core.cloneRecIdBndrs subst us bndrs+ env' = env { se_subst = subst' }+ ; return (env', bndrs') }++newDictBndr :: SpecEnv -> CoreBndr -> SpecM (SpecEnv, CoreBndr)+-- Make up completely fresh binders for the dictionaries+-- Their bindings are going to float outwards+newDictBndr env@(SE { se_subst = subst }) b+ = do { uniq <- getUniqueM+ ; let n = idName b+ ty' = substTyUnchecked subst (idType b)+ b' = mkUserLocal (nameOccName n) uniq ManyTy ty' (getSrcSpan n)+ env' = env { se_subst = subst `Core.extendSubstInScope` b' }+ ; pure (env', b') }++newSpecIdSM :: Id -> Type -> Maybe JoinArity -> SpecM Id+ -- Give the new Id a similar occurrence name to the old one+newSpecIdSM old_id new_ty join_arity_maybe+ = do { uniq <- getUniqueM+ ; let name = idName old_id+ new_occ = mkSpecOcc (nameOccName name)+ new_id = mkUserLocal new_occ uniq ManyTy new_ty (getSrcSpan name)+ `asJoinId_maybe` join_arity_maybe+ ; return new_id } {- Old (but interesting) stuff about unboxed bindings
compiler/GHC/Core/Opt/StaticArgs.hs view
@@ -54,20 +54,23 @@ import GHC.Prelude -import GHC.Types.Var import GHC.Core import GHC.Core.Utils import GHC.Core.Type import GHC.Core.Coercion+import GHC.Core.TyCo.Compare( eqType )++import GHC.Types.Var import GHC.Types.Id import GHC.Types.Name import GHC.Types.Var.Env import GHC.Types.Unique.Supply-import GHC.Utils.Misc import GHC.Types.Unique.FM import GHC.Types.Var.Set import GHC.Types.Unique import GHC.Types.Unique.Set++import GHC.Utils.Misc import GHC.Utils.Outputable import GHC.Utils.Panic @@ -420,13 +423,13 @@ shadow_rhs = mkLams shadow_lam_bndrs local_body -- nonrec_rhs = \alpha' beta' c n xs -> sat_worker xs - rec_body_bndr = mkSysLocal (fsLit "sat_worker") uniq Many (exprType rec_body)+ rec_body_bndr = mkSysLocal (fsLit "sat_worker") uniq ManyTy (exprType rec_body) -- rec_body_bndr = sat_worker -- See Note [Shadow binding]; make a SysLocal shadow_bndr = mkSysLocal (occNameFS (getOccName binder)) (idUnique binder)- Many+ ManyTy (exprType shadow_rhs) isStaticValue :: Staticness App -> Bool
compiler/GHC/Core/Opt/WorkWrap.hs view
@@ -5,18 +5,19 @@ -} -module GHC.Core.Opt.WorkWrap ( wwTopBinds ) where+module GHC.Core.Opt.WorkWrap+ ( WwOpts (..)+ , wwTopBinds+ )+where import GHC.Prelude -import GHC.Driver.Session- import GHC.Core import GHC.Core.Unfold.Make import GHC.Core.Utils ( exprType, exprIsHNF ) import GHC.Core.Type import GHC.Core.Opt.WorkWrap.Utils-import GHC.Core.FamInstEnv import GHC.Core.SimpleOpt import GHC.Types.Var@@ -34,8 +35,6 @@ import GHC.Utils.Panic import GHC.Utils.Panic.Plain import GHC.Utils.Monad-import GHC.Utils.Trace-import GHC.Unit.Types import GHC.Core.DataCon {-@@ -66,14 +65,12 @@ \end{enumerate} -} -wwTopBinds :: Module -> DynFlags -> FamInstEnvs -> UniqSupply -> CoreProgram -> CoreProgram+wwTopBinds :: WwOpts -> UniqSupply -> CoreProgram -> CoreProgram -wwTopBinds this_mod dflags fam_envs us top_binds+wwTopBinds ww_opts us top_binds = initUs_ us $ do top_binds' <- mapM (wwBind ww_opts) top_binds return (concat top_binds')- where- ww_opts = initWwOpts this_mod dflags fam_envs {- ************************************************************************@@ -197,20 +194,48 @@ This comes up in practice (#6056). -Solution: do the w/w for strictness analysis, but transfer the Stable-unfolding to the *worker*. So we will get something like this:+Solution: - {-# INLINE[2] f #-}+* Do the w/w for strictness analysis, even for INLINABLE functions++* Transfer the Stable unfolding to the *worker*. How do we "transfer+ the unfolding"? Easy: by using the old one, wrapped in work_fn! See+ GHC.Core.Unfold.Make.mkWorkerUnfolding.++* We use the /original, user-specified/ function's InlineSpec pragma+ for both the wrapper and the worker (see `mkStrWrapperInlinePrag`).+ So if f is INLINEABLE, both worker and wrapper will get an InlineSpec+ of (Inlinable "blah").++ It's important that both get this, because the specialiser uses+ the existence of a /user-specified/ INLINE/INLINABLE pragma to+ drive specialisation of imported functions. See GHC.Core.Opt.Specialise+ Note [Specialising imported functions]++* Remember, the subsequent inlining behaviour of the wrapper is expressed by+ (a) the stable unfolding+ (b) the unfolding guidance of UnfWhen+ (c) the inl_act activation (see Note [Wrapper activation]++For our {-# INLINEABLE f #-} example above, we will get something a+bit like like this:++ {-# Has stable unfolding, active in phase 2;+ plus InlineSpec = INLINEABLE #-} f :: Ord a => [a] -> Int -> a f d x y = case y of I# y' -> fw d x y' - {-# INLINABLE[2] fw #-}+ {-# Has stable unfolding, plus InlineSpec = INLINEABLE #-} fw :: Ord a => [a] -> Int# -> a fw d x y' = let y = I# y' in ...f... -How do we "transfer the unfolding"? Easy: by using the old one, wrapped-in work_fn! See GHC.Core.Unfold.Make.mkWorkerUnfolding. +(Historical note: we used to always give the wrapper an INLINE pragma,+but CSE will not happen if there is a user-specified pragma, but+should happen for w/w’ed things (#14186). But now we simply propagate+any user-defined pragma info, so we'll defeat CSE (rightly) only when+there is a user-supplied INLINE/INLINEABLE pragma.)+ Note [No worker/wrapper for record selectors] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We sometimes generate a lot of record selectors, and generally the@@ -488,7 +513,7 @@ about INLINE things here. -What if `foo` has no specialiations, is worker/wrappered (with the+What if `foo` has no specialisations, is worker/wrappered (with the wrapper inlining very early), and exported; and then in an importing module we have {-# SPECIALISE foo : ... #-}? @@ -498,19 +523,6 @@ in which case we don't unpack dictionaries for the worker; see see Note [Do not unbox class dictionaries].) -Note [Wrapper NoUserInlinePrag]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We use NoUserInlinePrag on the wrapper, to say that there is no-user-specified inline pragma. (The worker inherits that; see Note-[Worker/wrapper for INLINABLE functions].) The wrapper has no pragma-given by the user.--(Historical note: we used to give the wrapper an INLINE pragma, but-CSE will not happen if there is a user-specified pragma, but should-happen for w/w’ed things (#14186). We don't need a pragma, because-everything we needs is expressed by (a) the stable unfolding and (b)-the inl_act activation.)- Note [Drop absent bindings] ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Consider (#19824):@@ -647,7 +659,7 @@ in p `seq` (v,v) I think we'll give `f` the strictness signature `<SP(M,A)>`, where the-`M` sayd that we'll evaluate the first component of the pair at most+`M` says that we'll evaluate the first component of the pair at most once. Why? Because the RHS of the thunk `v` is evaluated at most once. @@ -682,10 +694,11 @@ Note [Don't eta expand in w/w] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-A binding where the manifestArity of the RHS is less than idArity of the binder-means GHC.Core.Opt.Arity didn't eta expand that binding. When this happens, it does so-for a reason (see Note [exprArity invariant] in GHC.Core.Opt.Arity) and we probably have-a PAP, cast or trivial expression as RHS.+A binding where the manifestArity of the RHS is less than idArity of+the binder means GHC.Core.Opt.Arity didn't eta expand that binding+When this happens, it does so for a reason (see Note [Arity invariants for bindings]+in GHC.Core.Opt.Arity) and we probably have a PAP, cast or trivial expression+as RHS. Below is a historical account of what happened when w/w still did eta expansion. Nowadays, it doesn't do that, but will simply w/w for the wrong arity, unleashing@@ -746,11 +759,7 @@ --------------------- splitFun :: WwOpts -> Id -> CoreExpr -> UniqSM [(Id, CoreExpr)] splitFun ww_opts fn_id rhs- | not (wrap_dmds `lengthIs` count isId arg_vars)- -- See Note [Don't eta expand in w/w]- = return [(fn_id, rhs)]-- | otherwise+ | Just (arg_vars, body) <- collectNValBinders_maybe (length wrap_dmds) rhs = warnPprTrace (not (wrap_dmds `lengthIs` (arityInfo fn_info))) "splitFun" (ppr fn_id <+> (ppr wrap_dmds $$ ppr cpr)) $@@ -773,10 +782,13 @@ -> do { work_uniq <- getUniqueM ; return (mkWWBindPair ww_opts fn_id fn_info arg_vars body work_uniq div stuff) } }++ | otherwise -- See Note [Don't eta expand in w/w]+ = return [(fn_id, rhs)]+ where uf_opts = so_uf_opts (wo_simple_opts ww_opts) fn_info = idInfo fn_id- (arg_vars, body) = collectBinders rhs (wrap_dmds, div) = splitDmdSig (dmdSigInfo fn_info) @@ -874,17 +886,23 @@ fn_rules = ruleInfoRules (ruleInfo fn_info) mkStrWrapperInlinePrag :: InlinePragma -> [CoreRule] -> InlinePragma--- See Note [Wrapper activation]-mkStrWrapperInlinePrag (InlinePragma { inl_act = act, inl_rule = rule_info }) rules+mkStrWrapperInlinePrag (InlinePragma { inl_inline = fn_inl+ , inl_act = fn_act+ , inl_rule = rule_info }) rules = InlinePragma { inl_src = SourceText "{-# INLINE"- , inl_inline = NoUserInlinePrag -- See Note [Wrapper NoUserInlinePrag] , inl_sat = Nothing++ , inl_inline = fn_inl+ -- See Note [Worker/wrapper for INLINABLE functions]+ , inl_act = activeAfter wrapper_phase+ -- See Note [Wrapper activation]+ , inl_rule = rule_info } -- RuleMatchInfo is (and must be) unaffected where -- See Note [Wrapper activation] wrapper_phase = foldr (laterPhase . get_rule_phase) earliest_inline_phase rules- earliest_inline_phase = beginPhase act `laterPhase` nextPhase InitialPhase+ earliest_inline_phase = beginPhase fn_act `laterPhase` nextPhase InitialPhase -- laterPhase (nextPhase InitialPhase) is a temporary hack -- to inline no earlier than phase 2. I got regressions in -- 'mate', due to changes in full laziness due to Note [Case@@ -906,15 +924,15 @@ Example: -- Original function- f [Demand=<L,1*C1(U)>] :: (a,a) -> a+ f [Demand=<L,1*C(1,U)>] :: (a,a) -> a f = \p -> ... -- Wrapper- f [Demand=<L,1*C1(U)>] :: a -> a -> a+ f [Demand=<L,1*C(1,U)>] :: a -> a -> a f = \p -> case p of (a,b) -> $wf a b -- Worker- $wf [Demand=<L,1*C1(C1(U))>] :: Int -> Int+ $wf [Demand=<L,1*C(1,C(1,U))>] :: Int -> Int $wf = \a b -> ... We need to check whether the original function is called once, with@@ -923,7 +941,7 @@ the original function. The demand on the worker is then calculated using mkWorkerDemand, and always of-the form [Demand=<L,1*(C1(...(C1(U))))>]+the form [Demand=<L,1*(C(1,...(C(1,U))))>] Note [Thunk splitting] ~~~~~~~~~~~~~~~~~~~~~~
compiler/GHC/Core/Opt/WorkWrap/Utils.hs view
@@ -8,22 +8,19 @@ {-# LANGUAGE ViewPatterns #-} module GHC.Core.Opt.WorkWrap.Utils- ( WwOpts(..), initWwOpts, mkWwBodies, mkWWstr, mkWWstr_one+ ( WwOpts(..), mkWwBodies, mkWWstr, mkWWstr_one , needsVoidWorkerArg , DataConPatContext(..)- , UnboxingDecision(..), wantToUnboxArg+ , UnboxingDecision(..), canUnboxArg , findTypeShape, IsRecDataConResult(..), isRecDataCon , mkAbsentFiller , isWorkerSmallEnough, dubiousDataConInstArgTys- , isGoodWorker, badWorker , goodWorker+ , boringSplit , usefulSplit ) where import GHC.Prelude -import GHC.Driver.Session-import GHC.Driver.Config (initSimpleOpts)- import GHC.Core import GHC.Core.Utils import GHC.Core.DataCon@@ -59,7 +56,6 @@ import GHC.Utils.Outputable import GHC.Utils.Panic import GHC.Utils.Panic.Plain-import GHC.Utils.Trace import Control.Applicative ( (<|>) ) import Control.Monad ( zipWithM )@@ -139,25 +135,18 @@ data WwOpts = MkWwOpts- { wo_fam_envs :: !FamInstEnvs- , wo_simple_opts :: !SimpleOpts- , wo_cpr_anal :: !Bool-- -- Used for absent argument error message- , wo_module :: !Module- , wo_unlift_strict :: !Bool -- Generate workers even if the only effect is some args- -- get passed unlifted.- -- See Note [WW for calling convention]- }--initWwOpts :: Module -> DynFlags -> FamInstEnvs -> WwOpts-initWwOpts this_mod dflags fam_envs = MkWwOpts- { wo_fam_envs = fam_envs- , wo_simple_opts = initSimpleOpts dflags- , wo_cpr_anal = gopt Opt_CprAnal dflags- , wo_module = this_mod- , wo_unlift_strict = gopt Opt_WorkerWrapperUnlift dflags- }+ { -- | Environment of type/data family instances+ wo_fam_envs :: !FamInstEnvs+ , -- | Options for the "Simple optimiser"+ wo_simple_opts :: !SimpleOpts+ , -- | Whether to enable "Constructed Product Result" analysis.+ -- (Originally from DOI: 10.1017/S0956796803004751)+ wo_cpr_anal :: !Bool+ , -- | Used for absent argument error message+ wo_module :: !Module+ , -- | Generate workers even if the only effect is some args get passed+ -- unlifted. See Note [WW for calling convention]+ wo_unlift_strict :: !Bool } type WwResult = ([Demand], -- Demands for worker (value) args@@ -229,19 +218,20 @@ empty_subst = mkEmptySubst (mkInScopeSet args_free_tcvs) zapped_arg_vars = map zap_var arg_vars (subst, cloned_arg_vars) = cloneBndrs empty_subst uniq_supply zapped_arg_vars- res_ty' = GHC.Core.Subst.substTy subst res_ty- init_cbv_marks = map (const NotMarkedStrict) cloned_arg_vars+ res_ty' = substTyUnchecked subst res_ty+ init_str_marks = map (const NotMarkedStrict) cloned_arg_vars - ; (useful1, work_args_cbv, wrap_fn_str, fn_args)- <- mkWWstr opts cloned_arg_vars init_cbv_marks+ ; (useful1, work_args_str, wrap_fn_str, fn_args)+ <- -- pprTrace "mkWWbodies" (ppr fun_id $$ ppr (arg_vars `zip` cloned_arg_vars) $$ ppr demands) $+ mkWWstr opts cloned_arg_vars init_str_marks - ; let (work_args, work_marks) = unzip work_args_cbv+ ; let (work_args, work_marks) = unzip work_args_str -- Do CPR w/w. See Note [Always do CPR w/w] ; (useful2, wrap_fn_cpr, work_fn_cpr) <- mkWWcpr_entry opts res_ty' res_cpr - ; let (work_lam_args, work_call_args, work_call_cbv)+ ; let (work_lam_args, work_call_args, work_call_str) | needsVoidWorkerArg fun_id arg_vars work_args = addVoidWorkerArg work_args work_marks | otherwise@@ -252,9 +242,9 @@ -- See Note [Join points and beta-redexes] wrapper_body = mkLams cloned_arg_vars . wrap_fn_cpr . wrap_fn_str . call_work -- See Note [Call-by-value for worker args]- work_seq_str_flds = mkStrictFieldSeqs (zip work_lam_args work_call_cbv)+ work_seq_str_flds = mkStrictFieldSeqs (zip work_lam_args work_call_str) worker_body = mkLams work_lam_args . work_seq_str_flds . work_fn_cpr . call_rhs- worker_args_dmds= [(idDemandInfo v) | v <- work_call_args, isId v]+ worker_args_dmds= [ idDemandInfo v | v <- work_call_args, isId v] ; if ((useful1 && not only_one_void_argument) || useful2) then return (Just (worker_args_dmds, length work_call_args,@@ -575,7 +565,7 @@ saw a type variable in the worker shadowing an outer term-variable binding. We avoid the issue by freshening the argument variables from the original fun-RHS through 'cloneBndrs', which will also take care of subsitution in binder+RHS through 'cloneBndrs', which will also take care of substitution in binder types. Fortunately, it's sufficient to pick the FVs of the arg vars as in-scope set, so that we don't need to do a FV traversal over the whole body of the original function.@@ -605,86 +595,79 @@ -- -- * @dc @exs flds :: T tys@ -- * @co :: T tys ~ ty@-data DataConPatContext+--+-- 's' will be 'Demand' or 'Cpr'.+data DataConPatContext s = DataConPatContext { dcpc_dc :: !DataCon , dcpc_tc_args :: ![Type] , dcpc_co :: !Coercion+ , dcpc_args :: ![s] } -- | Describes the outer shape of an argument to be unboxed or left as-is -- Depending on how @s@ is instantiated (e.g., 'Demand' or 'Cpr').-data UnboxingDecision s- = StopUnboxing- -- ^ We ran out of strictness info. Leave untouched.- | DropAbsent- -- ^ The argument/field was absent. Drop it.- | Unbox !DataConPatContext [s]- -- ^ The argument is used strictly or the returned product was constructed, so- -- unbox it.- -- The 'DataConPatContext' carries the bits necessary for- -- instantiation with 'dataConRepInstPat'.- -- The @[s]@ carries the bits of information with which we can continue- -- unboxing, e.g. @s@ will be 'Demand' or 'Cpr'.- | Unlift- -- ^ The argument can't be unboxed, but we want it to be passed evaluated to the worker.+data UnboxingDecision unboxing_info+ = DontUnbox -- ^ We ran out of strictness info. Leave untouched.+ | DoUnbox !unboxing_info -- ^ The argument is used strictly or the+ -- returned product was constructed, so unbox it.+ | DropAbsent -- ^ The argument/field was absent. Drop it. --- Do we want to create workers just for unlifting?-wwForUnlifting :: WwOpts -> Bool-wwForUnlifting !opts+instance Outputable i => Outputable (UnboxingDecision i) where+ ppr DontUnbox = text "DontUnbox"+ ppr DropAbsent = text "DropAbsent"+ ppr (DoUnbox i) = text "DoUnbox" <> braces (ppr i)++-- | Do we want to create workers just for unlifting?+wwUseForUnlifting :: WwOpts -> WwUse+wwUseForUnlifting !opts -- Always unlift if possible- | wo_unlift_strict opts = goodWorker+ | wo_unlift_strict opts = usefulSplit -- Don't unlift it would cause additional W/W splits.- | otherwise = badWorker--badWorker :: Bool-badWorker = False+ | otherwise = boringSplit -goodWorker :: Bool-goodWorker = True+-- | Is the worker/wrapper split profitable?+type WwUse = Bool -isGoodWorker :: Bool -> Bool-isGoodWorker = id+-- | WW split not profitable+boringSplit :: WwUse+boringSplit = False +-- | WW split profitable+usefulSplit :: WwUse+usefulSplit = True -- | Unwraps the 'Boxity' decision encoded in the given 'SubDemand' and returns -- a 'DataConPatContext' as well the nested demands on fields of the 'DataCon' -- to unbox.-wantToUnboxArg- :: Bool -- ^ Consider unlifting- -> FamInstEnvs- -> Type -- ^ Type of the argument- -> Demand -- ^ How the arg was used- -> UnboxingDecision Demand+canUnboxArg+ :: FamInstEnvs+ -> Type -- ^ Type of the argument+ -> Demand -- ^ How the arg was used+ -> UnboxingDecision (DataConPatContext Demand) -- See Note [Which types are unboxed?]-wantToUnboxArg do_unlifting fam_envs ty dmd@(n :* sd)+canUnboxArg fam_envs ty (n :* sd) | isAbs n = DropAbsent + -- From here we are strict and not absent | Just (tc, tc_args, co) <- normSplitTyConApp_maybe fam_envs ty , Just dc <- tyConSingleAlgDataCon_maybe tc , let arity = dataConRepArity dc- , Just (Unboxed, ds) <- viewProd arity sd -- See Note [Boxity analysis]- -- NB: No strictness or evaluatedness checks for unboxing here.- -- That is done by 'finaliseArgBoxities'!- = Unbox (DataConPatContext dc tc_args co) ds-- -- See Note [CBV Function Ids]- | do_unlifting- , isStrUsedDmd dmd- , not (isFunTy ty)- , not (isUnliftedType ty) -- Already unlifted!- -- NB: function arguments have a fixed RuntimeRep, so it's OK to call isUnliftedType here- = Unlift+ , Just (Unboxed, dmds) <- viewProd arity sd -- See Note [Boxity analysis]+ , dmds `lengthIs` dataConRepArity dc+ = DoUnbox (DataConPatContext { dcpc_dc = dc, dcpc_tc_args = tc_args+ , dcpc_co = co, dcpc_args = dmds }) | otherwise- = StopUnboxing+ = DontUnbox -- | Unboxing strategy for constructed results.-wantToUnboxResult :: FamInstEnvs -> Type -> Cpr -> UnboxingDecision Cpr+canUnboxResult :: FamInstEnvs -> Type -> Cpr+ -> UnboxingDecision (DataConPatContext Cpr) -- See Note [Which types are unboxed?]-wantToUnboxResult fam_envs ty cpr+canUnboxResult fam_envs ty cpr | Just (con_tag, arg_cprs) <- asConCpr cpr , Just (tc, tc_args, co) <- normSplitTyConApp_maybe fam_envs ty , Just dcs <- tyConAlgDataCons_maybe tc <|> open_body_ty_warning@@ -699,20 +682,21 @@ -- Deactivates CPR worker/wrapper splits on constructors with non-linear -- arguments, for the moment, because they require unboxed tuple with variable -- multiplicity fields.- = Unbox (DataConPatContext dc tc_args co) arg_cprs+ = DoUnbox (DataConPatContext { dcpc_dc = dc, dcpc_tc_args = tc_args+ , dcpc_co = co, dcpc_args = arg_cprs }) | otherwise- = StopUnboxing+ = DontUnbox where -- See Note [non-algebraic or open body type warning]- open_body_ty_warning = warnPprTrace True "wantToUnboxResult: non-algebraic or open body type" (ppr ty) Nothing+ open_body_ty_warning = warnPprTrace True "canUnboxResult: non-algebraic or open body type" (ppr ty) Nothing isLinear :: Scaled a -> Bool isLinear (Scaled w _ ) = case w of- One -> True- _ -> False+ OneTy -> True+ _ -> False {- Note [Which types are unboxed?]@@ -723,7 +707,7 @@ * is an algebraic data type (not a newtype) * is not recursive (as per 'isRecDataCon') * has a single constructor (thus is a "product")- * that may bind existentials+ * that may bind existentials (#18982) We can transform > data D a = forall b. D a b > f (D @ex a b) = e@@ -748,8 +732,8 @@ to > $wf x y = let ... in (# @ex, (a :: ..ex..), (b :: ..ex..) #) -The respective tests are in 'wantToUnboxArg' and-'wantToUnboxResult', respectively.+The respective tests are in 'canUnboxArg' and+'canUnboxResult', respectively. Note that the data constructor /can/ have evidence arguments: equality constraints, type classes etc. So it can be GADT. These evidence@@ -780,7 +764,7 @@ other cases where something went avoidably wrong. This warning also triggers for the stream fusion library within `text`.-We can'easily W/W constructed results like `Stream` because we have no simple+We can't easily W/W constructed results like `Stream` because we have no simple way to express existential types in the worker's type signature. Note [WW for calling convention]@@ -804,17 +788,20 @@ Performing W/W might not always be a win. In particular it's easy to break (badly written, but common) rule frameworks by doing additional W/W splits.-See #20364 for a more detailed explaination.+See #20364 for a more detailed explanation. Hence we have the following strategies with different trade-offs:+ A) Never do W/W *just* for unlifting of arguments. + Very conservative - doesn't break any rules - Lot's of performance left on the table+ B) Do W/W on just about anything where it might be beneficial.- + Exploits pretty much every oppertunity for unlifting.+ + Exploits pretty much every opportunity for unlifting. - A bit of compile time/code size cost for all the wrappers. - Can break rules which would otherwise fire. See #20364.+ C) Unlift *any* (non-boot exported) functions arguments if they are strict. That is instead of creating a Worker with the new calling convention we change the calling convention of the binding itself.@@ -824,7 +811,7 @@ - Requires either: ~ Eta-expansion at *all* call sites in order to generate an impedance matcher function. Leading to massive code bloat.- Essentially we end up creating a imprompto wrapper function+ Essentially we end up creating a impromptu wrapper function wherever we wouldn't inline the wrapper with a W/W approach. ~ There is the option of achieving this without eta-expansion if we instead expand the partial application code to check for demands on the calling convention and@@ -835,12 +822,61 @@ Currently we use the first approach A) by default, with a flag that allows users to fall back to the more aggressive approach B).+ I also tried the third approach C) using eta-expansion at call sites to avoid modifying the PAP-handling code which wasn't fruitful. See https://gitlab.haskell.org/ghc/ghc/-/merge_requests/5614#note_389903. We could still try to do C) in the future by having PAP calls which will evaluate the required arguments before calling the partially applied function. But this would be neither a small nor simple change so we stick with A) and a flag for B) for now.+ See also Note [Tag Inference] and Note [CBV Function Ids]++Note [Worker/wrapper for strict arguments]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider+ f x = case x of+ [] -> blah+ (y:ys) -> f ys++Clearly `f` is strict, but its argument is not a product type, so by default+we don't worker/wrapper it. But it is arguably valuable to do so. We could+do this:++ f x = case x of xx { DEFAULT -> $wf xx }+ $wf xx = case xx of+ [] -> blah+ (y:ys) -> f ys++Now the worker `$wf` knows that its argument `xx` will be evaluated+and properly tagged, so the code for the `case xx` does not need to do+an "eval" (see `GHC.StgToCmm.Expr.cgCase`). A call (f (a:as)) will+have the wrapper inlined, and will drop the `case x`, so no eval+happens at all.++The worker `$wf` is a CBV function (see `Note [CBV Function Ids]`+in GHC.Types.Id.Info) and the code generator guarantees that every+call to `$wf` has a properly tagged argument (see `GHC.Stg.InferTags.Rewrite`).++Is this a win? Not always:+* It can cause slight codesize increases. This is since we push evals to every+ call sites which there might be many. And the evals will only disappear at+ call sites where we already known that the argument is evaluated.++* It will also cause many more functions to get a worker/wrapper split+ which can play badly with rules (see Ticket #20364). In particular+ if you depend on rules firing on functions marked as NOINLINE+ without marking use sites of these functions as INLINE or INLINEABLE+ then things will break.+ But if you want a function to match in a RULE, it is /in any case/ good practice to+ have a `INLINE[1]` or `NOINLNE[1]` pragma, to ensure that it doesn't inline until+ the rule has had a chance to fire.++So there is a flag, `-fworker-wrapper-cbv`, to control whether we do+w/w on strict arguments (internally `Opt_WorkerWrapperUnlift`). The+flag is off by default. The choice is made in+GHC.Core.Opt.WorkWrape.Utils.wwUseForUnlifting++See also `Note [WW for calling convention]` in GHC.Core.Opt.WorkWrap.Utils -} {-@@ -854,8 +890,8 @@ mkWWstr :: WwOpts -> [Var] -- Wrapper args; have their demand info on them -- *Includes type variables*- -> [StrictnessMark] -- cbv info for arguments- -> UniqSM (Bool, -- Will this result in a useful worker+ -> [StrictnessMark] -- Strictness-mark info for arguments+ -> UniqSM (WwUse, -- Will this result in a useful worker [(Var,StrictnessMark)], -- Worker args/their call-by-value semantics. CoreExpr -> CoreExpr, -- Wrapper body, lacking the worker call -- and without its lambdas@@ -863,20 +899,19 @@ [CoreExpr]) -- Reboxed args for the call to the -- original RHS. Corresponds one-to-one -- with the wrapper arg vars-mkWWstr opts args cbv_info- = go args cbv_info+mkWWstr opts args str_marks+ = -- pprTrace "mkWWstr" (ppr args) $+ go args str_marks where- go_one arg cbv = mkWWstr_one opts arg cbv-- go [] _ = return (badWorker, [], nop_fn, [])- go (arg : args) (cbv:cbvs)- = do { (useful1, args1, wrap_fn1, wrap_arg) <- go_one arg cbv- ; (useful2, args2, wrap_fn2, wrap_args) <- go args cbvs- ; return ( useful1 || useful2- , args1 ++ args2- , wrap_fn1 . wrap_fn2- , wrap_arg:wrap_args ) }- go _ _ = panic "mkWWstr: Impossible - cbv/arg length missmatch"+ go [] _ = return (boringSplit, [], nop_fn, [])+ go (arg : args) (str:strs)+ = do { (useful1, args1, wrap_fn1, wrap_arg) <- mkWWstr_one opts arg str+ ; (useful2, args2, wrap_fn2, wrap_args) <- go args strs+ ; return ( useful1 || useful2+ , args1 ++ args2+ , wrap_fn1 . wrap_fn2+ , wrap_arg:wrap_args ) }+ go _ _ = panic "mkWWstr: Impossible - str/arg length mismatch" ---------------------- -- mkWWstr_one wrap_var = (useful, work_args, wrap_fn, wrap_arg)@@ -888,65 +923,78 @@ mkWWstr_one :: WwOpts -> Var -> StrictnessMark- -> UniqSM (Bool, [(Var,StrictnessMark)], CoreExpr -> CoreExpr, CoreExpr)-mkWWstr_one opts arg banged =- case wantToUnboxArg True fam_envs arg_ty arg_dmd of+ -> UniqSM (WwUse, [(Var,StrictnessMark)], CoreExpr -> CoreExpr, CoreExpr)+mkWWstr_one opts arg str_mark =+ -- pprTrace "mkWWstr_one" (ppr arg <+> (if isId arg then ppr arg_ty $$ ppr arg_dmd else text "type arg")) $+ case canUnboxArg fam_envs arg_ty arg_dmd of _ | isTyVar arg -> do_nothing DropAbsent- | Just absent_filler <- mkAbsentFiller opts arg banged- -- Absent case. Dropt the argument from the worker.+ | Just absent_filler <- mkAbsentFiller opts arg str_mark+ -- Absent case. Drop the argument from the worker. -- We can't always handle absence for arbitrary -- unlifted types, so we need to choose just the cases we can -- (that's what mkAbsentFiller does)- -> return (goodWorker, [], nop_fn, absent_filler)+ -> return (usefulSplit, [], nop_fn, absent_filler)+ | otherwise -> do_nothing - Unbox dcpc ds -> unbox_one_arg opts arg ds dcpc banged+ DoUnbox dcpc -> -- pprTrace "mkWWstr_one:1" (ppr (dcpc_dc dcpc) <+> ppr (dcpc_tc_args dcpc) $$ ppr (dcpc_args dcpc)) $+ unbox_one_arg opts arg dcpc - Unlift -> return ( wwForUnlifting opts- , [(arg, MarkedStrict)]- , nop_fn- , varToCoreExpr arg)+ DontUnbox+ | isStrictDmd arg_dmd || isMarkedStrict str_mark+ , wwUseForUnlifting opts -- See Note [CBV Function Ids]+ , not (isFunTy arg_ty)+ , not (isUnliftedType arg_ty) -- Already unlifted!+ -- NB: function arguments have a fixed RuntimeRep,+ -- so it's OK to call isUnliftedType here+ -> return (usefulSplit, [(arg, MarkedStrict)], nop_fn, varToCoreExpr arg ) - _ -> do_nothing -- Other cases, like StopUnboxing+ | otherwise -> do_nothing where fam_envs = wo_fam_envs opts arg_ty = idType arg arg_dmd = idDemandInfo arg- -- Type args don't get cbv marks- arg_cbv = if isTyVar arg then NotMarkedStrict else banged-- do_nothing = return (badWorker, [(arg,arg_cbv)], nop_fn, varToCoreExpr arg)+ arg_str | isTyVar arg = NotMarkedStrict -- Type args don't get strictness marks+ | otherwise = str_mark+ do_nothing = return (boringSplit, [(arg,arg_str)], nop_fn, varToCoreExpr arg) unbox_one_arg :: WwOpts- -> Var- -> [Demand]- -> DataConPatContext- -> StrictnessMark- -> UniqSM (Bool, [(Var,StrictnessMark)], CoreExpr -> CoreExpr, CoreExpr)-unbox_one_arg opts arg_var ds- DataConPatContext { dcpc_dc = dc, dcpc_tc_args = tc_args- , dcpc_co = co }- _marked_cbv+ -> Var -> DataConPatContext Demand+ -> UniqSM (WwUse, [(Var,StrictnessMark)], CoreExpr -> CoreExpr, CoreExpr)+unbox_one_arg opts arg_var+ DataConPatContext { dcpc_dc = dc, dcpc_tc_args = tc_args+ , dcpc_co = co, dcpc_args = ds } = do { pat_bndrs_uniqs <- getUniquesM ; let ex_name_fss = map getOccFS $ dataConExTyCoVars dc+ -- Create new arguments we get when unboxing dc- (ex_tvs', arg_ids) =- dataConRepFSInstPat (ex_name_fss ++ repeat ww_prefix) pat_bndrs_uniqs (idMult arg_var) dc tc_args+ (ex_tvs', arg_ids) = dataConRepFSInstPat (ex_name_fss ++ repeat ww_prefix)+ pat_bndrs_uniqs (idMult arg_var) dc tc_args con_str_marks = dataConRepStrictness dc- -- Apply str info to new args. Also remove OtherCon unfoldings so they don't end up in lambda binders- -- of the worker. See Note [Never put `OtherCon` unfoldings on lambda binders]- arg_ids' = map zapIdUnfolding $ zipWithEqual "unbox_one_arg" setIdDemandInfo arg_ids ds++ -- Apply str info to new args. Also remove OtherCon unfoldings so they+ -- don't end up in lambda binders of the worker.+ -- See Note [Never put `OtherCon` unfoldings on lambda binders]+ arg_ids' = map zapIdUnfolding $+ zipWithEqual "unbox_one_arg" setIdDemandInfo arg_ids ds+ unbox_fn = mkUnpackCase (Var arg_var) co (idMult arg_var) dc (ex_tvs' ++ arg_ids')- -- Mark arguments coming out of strict fields so we can make the worker strict on those- -- argumnets later. seq them later. See Note [Call-by-value for worker args]- strict_marks = (map (const NotMarkedStrict) ex_tvs') ++ con_str_marks- ; (_sub_args_quality, worker_args, wrap_fn, wrap_args) <- mkWWstr opts (ex_tvs' ++ arg_ids') strict_marks++ -- Mark arguments coming out of strict fields so we can seq them in the worker+ -- See Note [Call-by-value for worker args]+ all_str_marks = (map (const NotMarkedStrict) ex_tvs') ++ con_str_marks++ ; (nested_useful, worker_args, wrap_fn, wrap_args)+ <- mkWWstr opts (ex_tvs' ++ arg_ids') all_str_marks+ ; let wrap_arg = mkConApp dc (map Type tc_args ++ wrap_args) `mkCast` mkSymCo co- ; return (goodWorker, worker_args, unbox_fn . wrap_fn, wrap_arg) }- -- Don't pass the arg, rebox instead+ -- See Note [Unboxing through unboxed tuples]+ ; return $ if isUnboxedTupleDataCon dc && not nested_useful+ then (boringSplit, [(arg_var,NotMarkedStrict)], nop_fn, varToCoreExpr arg_var)+ else (usefulSplit, worker_args, unbox_fn . wrap_fn, wrap_arg) } -- | Tries to find a suitable absent filler to bind the given absent identifier -- to. See Note [Absent fillers].@@ -988,7 +1036,7 @@ {- Note [Worker/wrapper for Strictness and Absence] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The worker/wrapper transformation, mkWWstr_one, takes concrete action-based on the 'UnboxingDescision' returned by 'wantToUnboxArg'.+based on the 'UnboxingDecision' returned by 'canUnboxArg'. The latter takes into account several possibilities to decide if the function is worthy for splitting: @@ -1002,7 +1050,7 @@ 2. If the argument is evaluated strictly (or known to be eval'd), we can take a view into the product demand ('viewProd'). In accordance- with Note [Boxity analysis], 'wantToUnboxArg' will say 'Unbox'.+ with Note [Boxity analysis], 'canUnboxArg' will say 'DoUnbox'. 'mkWWstr_one' then follows suit it and recurses into the fields of the product demand. For example @@ -1024,7 +1072,7 @@ $gw c a b = if c then a else b 2a But do /not/ unbox if Boxity Analysis said "Boxed".- In this case, 'wantToUnboxArg' returns 'StopUnboxing'.+ In this case, 'canUnboxArg' returns 'DontUnbox'. Otherwise we risk decomposing and reboxing a massive tuple which is barely used. Example: @@ -1045,7 +1093,7 @@ 3. In all other cases (e.g., lazy, used demand and not eval'd), 'finaliseArgBoxities' will have cleared the Boxity flag to 'Boxed' (see Note [Finalising boxity for demand signatures] in GHC.Core.Opt.DmdAnal)- and 'wantToUnboxArg' returns 'StopUnboxing' so that 'mkWWstr_one'+ and 'canUnboxArg' returns 'DontUnbox' so that 'mkWWstr_one' stops unboxing. Note [Worker/wrapper for bottoming functions]@@ -1161,7 +1209,7 @@ NB from Andreas: But I think using an error thunk there would be dodgy no matter what for example if we decide to pass the argument to the bottoming function cbv. As we might do if the function in question is a worker.- See Note [CBV Function Ids] in GHC.CoreToStg.Prep. So I just left the strictness check+ See Note [CBV Function Ids] in GHC.Types.Id.Info. So I just left the strictness check in place on top of threading through the marks from the constructor. It's a *really* cheap and easy check to make anyway. @@ -1196,6 +1244,26 @@ because `MkT` is strict in its Int# argument, so we get an absentError exception when we shouldn't. Very annoying! +Note [Unboxing through unboxed tuples]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+We should not to a worker/wrapper split just for unboxing the components of+an unboxed tuple (in the result *or* argument, #22388). Consider+ boring_res x y = (# y, x #)+It's entirely pointless to split for the constructed unboxed pair to+ $wboring_res x y = (# y, x #)+ boring_res = case $wboring_res x y of (# a, b #) -> (# a, b #)+`boring_res` will immediately simplify to an alias for `$wboring_res`!++Similarly, the unboxed tuple might occur in argument position+ boring_arg (# x, y, z #) = (# z, x, y #)+It's entirely pointless to "unbox" the triple+ $wboring_arg x y z = (# z, x, y #)+ boring_arg (# x, y, z #) = $wboring_arg x y z+because after unarisation, `boring_arg` is just an alias for `$wboring_arg`.++Conclusion: Only consider unboxing an unboxed tuple useful when we will+also unbox its components. That is governed by the `usefulSplit` mechanism.+ ************************************************************************ * * Type scrutiny that is specific to demand analysis@@ -1204,16 +1272,25 @@ -} -- | Exactly 'dataConInstArgTys', but lacks the (ASSERT'ed) precondition that--- the 'DataCon' may not have existentials. The lack of cloning the existentials--- compared to 'dataConInstExAndArgVars' makes this function \"dubious\";--- only use it where type variables aren't substituted for!+-- the 'DataCon' may not have existentials. The lack of cloning the+-- existentials this function \"dubious\"; only use it where type variables+-- aren't substituted for! Why may the data con bind existentials?+-- See Note [Which types are unboxed?] dubiousDataConInstArgTys :: DataCon -> [Type] -> [Type] dubiousDataConInstArgTys dc tc_args = arg_tys where- univ_tvs = dataConUnivTyVars dc- ex_tvs = dataConExTyCoVars dc- subst = extendTCvInScopeList (zipTvSubst univ_tvs tc_args) ex_tvs- arg_tys = map (GHC.Core.Type.substTy subst . scaledThing) (dataConRepArgTys dc)+ univ_tvs = dataConUnivTyVars dc+ ex_tvs = dataConExTyCoVars dc+ univ_subst = zipTvSubst univ_tvs tc_args+ (full_subst, _) = substTyVarBndrs univ_subst ex_tvs+ arg_tys = map (substTy full_subst . scaledThing) $+ dataConRepArgTys dc+ -- NB: use substTyVarBndrs on ex_tvs to ensure that we+ -- substitute in their kinds. For example (#22849)+ -- Consider data T a where+ -- MkT :: forall k (t::k->*) (ix::k). t ix -> T @k a+ -- Then dubiousDataConInstArgTys MkT [Type, Foo] should return+ -- [Foo (ix::Type)], not [Foo (ix::k)]! findTypeShape :: FamInstEnvs -> Type -> TypeShape -- Uncover the arrow and product shape of a type@@ -1227,7 +1304,7 @@ -- to look deep into such products -- see #18034 where go rec_tc ty- | Just (_, _, res) <- splitFunTy_maybe ty+ | Just (_, _, _, res) <- splitFunTy_maybe ty = TsFun (go rec_tc res) | Just (tc, tc_args) <- splitTyConApp_maybe ty@@ -1291,7 +1368,7 @@ -- through one of @dc@'s fields (so surely non-recursive). -- * @NonRecursiveOrUnsure@ when @fuel /= Infinity@ -- and @fuel@ expansions of nested data TyCons were not enough to prove--- non-recursivenss, nor arrive at an occurrence of @tc@ thus proving+-- non-recursiveness, nor arrive at an occurrence of @tc@ thus proving -- recursiveness. (So not sure if non-recursive.) -- * @NonRecursiveOrUnsure@ when we hit an abstract TyCon (one without -- visible DataCons), such as those imported from .hs-boot files.@@ -1377,12 +1454,12 @@ :: WwOpts -> Type -- function body -> Cpr -- CPR analysis results- -> UniqSM (Bool, -- Is w/w'ing useful?+ -> UniqSM (WwUse, -- Is w/w'ing useful? CoreExpr -> CoreExpr, -- New wrapper. 'nop_fn' if not useful CoreExpr -> CoreExpr) -- New worker. 'nop_fn' if not useful -- ^ Entrypoint to CPR W/W. See Note [Worker/wrapper for CPR] for an overview. mkWWcpr_entry opts body_ty body_cpr- | not (wo_cpr_anal opts) = return (badWorker, nop_fn, nop_fn)+ | not (wo_cpr_anal opts) = return (boringSplit, nop_fn, nop_fn) | otherwise = do -- Part (1) res_bndr <- mk_res_bndr body_ty@@ -1399,8 +1476,8 @@ let wrap_fn = unbox_transit_tup rebuilt_result -- 3 2 work_fn body = bind_res_bndr body (work_unpack_res transit_tup) -- 1 2 3 return $ if not useful- then (badWorker, nop_fn, nop_fn)- else (goodWorker, wrap_fn, work_fn)+ then (boringSplit, nop_fn, nop_fn)+ else (usefulSplit, wrap_fn, work_fn) -- | Part (1) of Note [Worker/wrapper for CPR]. mk_res_bndr :: Type -> UniqSM Id@@ -1412,20 +1489,20 @@ -- | What part (2) of Note [Worker/wrapper for CPR] collects. ----- 1. A Bool capturing whether the transformation did anything useful.+-- 1. A 'WwUse' capturing whether the split does anything useful. -- 2. The list of transit variables (see the Note). -- 3. The result builder expression for the wrapper. The original case binder if not useful. -- 4. The result unpacking expression for the worker. 'nop_fn' if not useful.-type CprWwResultOne = (Bool, OrdList Var, CoreExpr , CoreExpr -> CoreExpr)-type CprWwResultMany = (Bool, OrdList Var, [CoreExpr], CoreExpr -> CoreExpr)+type CprWwResultOne = (WwUse, OrdList Var, CoreExpr , CoreExpr -> CoreExpr)+type CprWwResultMany = (WwUse, OrdList Var, [CoreExpr], CoreExpr -> CoreExpr) mkWWcpr :: WwOpts -> [Id] -> [Cpr] -> UniqSM CprWwResultMany mkWWcpr _opts vars [] = -- special case: No CPRs means all top (for example from FlatConCpr), -- hence stop WW.- return (badWorker, toOL vars, map varToCoreExpr vars, nop_fn)+ return (boringSplit, toOL vars, map varToCoreExpr vars, nop_fn) mkWWcpr opts vars cprs = do- -- No existentials in 'vars'. 'wantToUnboxResult' should have checked that.+ -- No existentials in 'vars'. 'canUnboxResult' should have checked that. massertPpr (not (any isTyVar vars)) (ppr vars $$ ppr cprs) massertPpr (equalLength vars cprs) (ppr vars $$ ppr cprs) (usefuls, varss, rebuilt_results, work_unpack_ress) <-@@ -1439,17 +1516,17 @@ -- ^ See if we want to unbox the result and hand off to 'unbox_one_result'. mkWWcpr_one opts res_bndr cpr | assert (not (isTyVar res_bndr) ) True- , Unbox dcpc arg_cprs <- wantToUnboxResult (wo_fam_envs opts) (idType res_bndr) cpr- = unbox_one_result opts res_bndr arg_cprs dcpc+ , DoUnbox dcpc <- canUnboxResult (wo_fam_envs opts) (idType res_bndr) cpr+ = unbox_one_result opts res_bndr dcpc | otherwise- = return (badWorker, unitOL res_bndr, varToCoreExpr res_bndr, nop_fn)+ = return (boringSplit, unitOL res_bndr, varToCoreExpr res_bndr, nop_fn) unbox_one_result- :: WwOpts -> Id -> [Cpr] -> DataConPatContext -> UniqSM CprWwResultOne+ :: WwOpts -> Id -> DataConPatContext Cpr -> UniqSM CprWwResultOne -- ^ Implements the main bits of part (2) of Note [Worker/wrapper for CPR]-unbox_one_result opts res_bndr arg_cprs+unbox_one_result opts res_bndr DataConPatContext { dcpc_dc = dc, dcpc_tc_args = tc_args- , dcpc_co = co } = do+ , dcpc_co = co, dcpc_args = arg_cprs } = do -- unboxer (free in `res_bndr`): | builder (where <i> builds what was -- ( case res_bndr of (i, j) -> ) | bound to i) -- ( case i of I# a -> ) |@@ -1458,7 +1535,7 @@ pat_bndrs_uniqs <- getUniquesM let (_exs, arg_ids) = dataConRepFSInstPat (repeat ww_prefix) pat_bndrs_uniqs cprCaseBndrMult dc tc_args- massert (null _exs) -- Should have been caught by wantToUnboxResult+ massert (null _exs) -- Should have been caught by canUnboxResult (nested_useful, transit_vars, con_args, work_unbox_res) <- mkWWcpr opts arg_ids arg_cprs@@ -1468,11 +1545,10 @@ -- this_work_unbox_res alt = (case res_bndr |> co of C a b -> <alt>[a,b]) this_work_unbox_res = mkUnpackCase (Var res_bndr) co cprCaseBndrMult dc arg_ids - -- Don't try to WW an unboxed tuple return type when there's nothing inside- -- to unbox further.+ -- See Note [Unboxing through unboxed tuples] return $ if isUnboxedTupleDataCon dc && not nested_useful- then ( badWorker, unitOL res_bndr, Var res_bndr, nop_fn )- else ( goodWorker+ then ( boringSplit, unitOL res_bndr, Var res_bndr, nop_fn )+ else ( usefulSplit , transit_vars , rebuilt_result , this_work_unbox_res . work_unbox_res@@ -1506,7 +1582,7 @@ (DataAlt tup_con) vars build_res , ubx_tup_app ) where- ubx_tup_app = mkCoreUbxTup (map idType vars) (map varToCoreExpr vars)+ ubx_tup_app = mkCoreUnboxedTuple (map varToCoreExpr vars) tup_con = tupleDataCon Unboxed (length vars) -- See also Note [Linear types and CPR] case_bndr = mkWildValBinder cprCaseBndrMult (exprType ubx_tup_app)@@ -1595,7 +1671,7 @@ We want `$wh :: Int# -> [Int]`. We'd get `$wh :: Int# -> (# [Int] #)`. -By considering vars as unlifted that satsify 'exprIsHNF', we catch (3).+By considering vars as unlifted that satisfy 'exprIsHNF', we catch (3). Why not check for 'exprOkForSpeculation'? Quite perplexingly, evaluated vars are not ok-for-spec, see Note [exprOkForSpeculation and evaluated variables]. For (1) and (2) we would have to look at the term. WW only looks at the@@ -1607,7 +1683,7 @@ Remark on linearity: in both the case of the wrapper and the worker, we build a linear case to unpack constructed products. All the multiplicity information is kept in the constructors (both C and (#, #)).-In particular (#,#) is parametrised by the multiplicity of its fields.+In particular (#,#) is parameterised by the multiplicity of its fields. Specifically, in this instance, the multiplicity of the fields of (#,#) is chosen to be the same as those of C. @@ -1635,7 +1711,7 @@ -- | The multiplicity of a case binder unboxing a constructed result. -- See Note [Linear types and CPR] cprCaseBndrMult :: Mult-cprCaseBndrMult = One+cprCaseBndrMult = OneTy ww_prefix :: FastString ww_prefix = fsLit "ww"
compiler/GHC/CoreToStg.hs view
@@ -14,13 +14,10 @@ -- And, as we have the info in hand, we may convert some lets to -- let-no-escapes. -module GHC.CoreToStg ( coreToStg ) where+module GHC.CoreToStg ( CoreToStgOpts (..), coreToStg ) where import GHC.Prelude -import GHC.Driver.Session-import GHC.Driver.Config.Stg.Debug- import GHC.Core import GHC.Core.Utils ( exprType, findDefault, isJoinBind , exprIsTickedString_maybe )@@ -41,7 +38,7 @@ import GHC.Types.Tickish import GHC.Types.Var.Env import GHC.Types.Name ( isExternalName, nameModule_maybe )-import GHC.Types.Basic ( Arity )+import GHC.Types.Basic ( Arity, TypeOrConstraint(..) ) import GHC.Types.Literal import GHC.Types.ForeignCall import GHC.Types.IPE@@ -49,21 +46,19 @@ import GHC.Types.SrcLoc ( mkGeneralSrcSpan ) import GHC.Unit.Module-import GHC.Builtin.Types ( unboxedUnitDataCon ) import GHC.Data.FastString+import GHC.Platform ( Platform ) import GHC.Platform.Ways-import GHC.Builtin.PrimOps ( PrimCall(..) )+import GHC.Builtin.PrimOps ( PrimCall(..), primOpWrapperId ) import GHC.Utils.Outputable import GHC.Utils.Monad import GHC.Utils.Misc (HasDebugCallStack) import GHC.Utils.Panic import GHC.Utils.Panic.Plain-import GHC.Utils.Trace import Control.Monad (ap) import Data.Maybe (fromMaybe)-import Data.Tuple (swap) -- Note [Live vs free] -- ~~~~~~~~~~~~~~~~~~~@@ -236,24 +231,29 @@ -- -------------------------------------------------------------- -coreToStg :: DynFlags -> Module -> ModLocation -> CoreProgram+coreToStg :: CoreToStgOpts -> Module -> ModLocation -> CoreProgram -> ([StgTopBinding], InfoTableProvMap, CollectedCCs)-coreToStg dflags this_mod ml pgm+coreToStg opts@CoreToStgOpts+ { coreToStg_ways = ways+ , coreToStg_AutoSccsOnIndividualCafs = opt_AutoSccsOnIndividualCafs+ , coreToStg_InfoTableMap = opt_InfoTableMap+ , coreToStg_stgDebugOpts = stgDebugOpts+ } this_mod ml pgm = (pgm'', denv, final_ccs) where (_, (local_ccs, local_cc_stacks), pgm')- = coreTopBindsToStg dflags this_mod emptyVarEnv emptyCollectedCCs pgm+ = coreTopBindsToStg opts this_mod emptyVarEnv emptyCollectedCCs pgm -- See Note [Mapping Info Tables to Source Positions]- (!pgm'', !denv) =- if gopt Opt_InfoTableMap dflags- then collectDebugInformation (initStgDebugOpts dflags) ml pgm'- else (pgm', emptyInfoTableProvMap)+ (!pgm'', !denv)+ | opt_InfoTableMap+ = collectDebugInformation stgDebugOpts ml pgm'+ | otherwise = (pgm', emptyInfoTableProvMap) - prof = ways dflags `hasWay` WayProf+ prof = hasWay ways WayProf final_ccs- | prof && gopt Opt_AutoSccsOnIndividualCafs dflags+ | prof && opt_AutoSccsOnIndividualCafs = (local_ccs,local_cc_stacks) -- don't need "all CAFs" CC | prof = (all_cafs_cc:local_ccs, all_cafs_ccs:local_cc_stacks)@@ -263,7 +263,7 @@ (all_cafs_cc, all_cafs_ccs) = getAllCAFsCC this_mod coreTopBindsToStg- :: DynFlags+ :: CoreToStgOpts -> Module -> IdEnv HowBound -- environment for the bindings -> CollectedCCs@@ -272,17 +272,17 @@ coreTopBindsToStg _ _ env ccs [] = (env, ccs, [])-coreTopBindsToStg dflags this_mod env ccs (b:bs)+coreTopBindsToStg opts this_mod env ccs (b:bs) | NonRec _ rhs <- b, isTyCoArg rhs- = coreTopBindsToStg dflags this_mod env1 ccs1 bs+ = coreTopBindsToStg opts this_mod env1 ccs1 bs | otherwise = (env2, ccs2, b':bs') where- (env1, ccs1, b' ) = coreTopBindToStg dflags this_mod env ccs b- (env2, ccs2, bs') = coreTopBindsToStg dflags this_mod env1 ccs1 bs+ (env1, ccs1, b' ) = coreTopBindToStg opts this_mod env ccs b+ (env2, ccs2, bs') = coreTopBindsToStg opts this_mod env1 ccs1 bs coreTopBindToStg- :: DynFlags+ :: CoreToStgOpts -> Module -> IdEnv HowBound -> CollectedCCs@@ -298,16 +298,18 @@ how_bound = LetBound TopLet 0 in (env', ccs, StgTopStringLit id str) -coreTopBindToStg dflags this_mod env ccs (NonRec id rhs)+coreTopBindToStg opts@CoreToStgOpts+ { coreToStg_platform = platform+ } this_mod env ccs (NonRec id rhs) = let env' = extendVarEnv env id how_bound how_bound = LetBound TopLet $! manifestArity rhs - (stg_rhs, ccs') =- initCts dflags env $- coreToTopStgRhs dflags ccs this_mod (id,rhs)+ (ccs', (id', stg_rhs)) =+ initCts platform env $+ coreToTopStgRhs opts this_mod ccs (id,rhs) - bind = StgTopLifted $ StgNonRec id stg_rhs+ bind = StgTopLifted $ StgNonRec id' stg_rhs in -- NB: previously the assertion printed 'rhs' and 'bind' -- as well as 'id', but that led to a black hole@@ -315,42 +317,38 @@ -- assertion again! (env', ccs', bind) -coreTopBindToStg dflags this_mod env ccs (Rec pairs)+coreTopBindToStg opts@CoreToStgOpts+ { coreToStg_platform = platform+ } this_mod env ccs (Rec pairs) = assert (not (null pairs)) $ let- binders = map fst pairs- extra_env' = [ (b, LetBound TopLet $! manifestArity rhs) | (b, rhs) <- pairs ] env' = extendVarEnvList env extra_env' -- generate StgTopBindings and CAF cost centres created for CAFs (ccs', stg_rhss)- = initCts dflags env' $- mapAccumLM (\ccs rhs -> swap <$> coreToTopStgRhs dflags ccs this_mod rhs)- ccs- pairs- bind = StgTopLifted $ StgRec (zip binders stg_rhss)+ = initCts platform env' $ mapAccumLM (coreToTopStgRhs opts this_mod) ccs pairs+ bind = StgTopLifted $ StgRec stg_rhss in (env', ccs', bind) coreToTopStgRhs- :: DynFlags- -> CollectedCCs+ :: CoreToStgOpts -> Module+ -> CollectedCCs -> (Id,CoreExpr)- -> CtsM (StgRhs, CollectedCCs)+ -> CtsM (CollectedCCs, (Id, StgRhs)) -coreToTopStgRhs dflags ccs this_mod (bndr, rhs)+coreToTopStgRhs opts this_mod ccs (bndr, rhs) = do { new_rhs <- coreToPreStgRhs rhs ; let (stg_rhs, ccs') =- mkTopStgRhs dflags this_mod ccs bndr new_rhs+ mkTopStgRhs opts this_mod ccs bndr new_rhs stg_arity = stgRhsArity stg_rhs - ; return (assertPpr (arity_ok stg_arity) (mk_arity_msg stg_arity) stg_rhs,- ccs') }+ ; pure (ccs', (bndr, assertPpr (arity_ok stg_arity) (mk_arity_msg stg_arity) stg_rhs)) } where -- It's vital that the arity on a top-level Id matches -- the arity of the generated STG binding, else an importing@@ -402,7 +400,11 @@ = case app_head of Var f -> coreToStgApp f args ticks -- Regular application Lit l | isLitRubbish l -- If there is LitRubbish at the head,- -> return (StgLit l) -- discard the arguments+ -- discard the arguments+ -- Recompute representation, because in+ -- '(RUBBISH[rep] x) :: (T :: TYPE rep2)'+ -- rep might not be equal to rep2+ -> return (StgLit $ LitRubbish TypeLike $ getRuntimeRep (exprType expr)) _ -> pprPanic "coreToStgExpr - Invalid app head:" (ppr expr) where@@ -458,15 +460,6 @@ where vars_alt :: CoreAlt -> CtsM StgAlt vars_alt (Alt con binders rhs)- | DataAlt c <- con, c == unboxedUnitDataCon- = -- This case is a bit smelly.- -- See Note [Nullary unboxed tuple] in GHC.Core.Type- -- where a nullary tuple is mapped to (State# World#)- assert (null binders) $- do { rhs2 <- coreToStgExpr rhs- ; return GenStgAlt{alt_con=DEFAULT,alt_bndrs=mempty,alt_rhs=rhs2}- }- | otherwise = let -- Remove type variables binders' = filterStgBinders binders in@@ -501,8 +494,7 @@ prim_reps = typePrimRep bndr_ty _is_poly_alt_tycon tc- = isFunTyCon tc- || isPrimTyCon tc -- "Any" is lifted but primitive+ = isPrimTyCon tc -- "Any" is lifted but primitive || isFamilyTyCon tc -- Type family; e.g. Any, or arising from strict -- function application where argument has a -- type-family type@@ -554,7 +546,7 @@ -- Some primitive operator that might be implemented as a library call. -- As noted by Note [Eta expanding primops] in GHC.Builtin.PrimOps -- we require that primop applications be saturated.- PrimOpId op -> assert saturated $+ PrimOpId op _ -> -- assertPpr saturated (ppr f <+> ppr args) $ StgOpApp (StgPrimOp op) args' res_ty -- A call to some primitive Cmm function.@@ -606,10 +598,11 @@ let (aticks, arg'') = stripStgTicksTop tickishFloatable arg' stg_arg = case arg'' of- StgApp v [] -> StgVarArg v- StgConApp con _ [] _ -> StgVarArg (dataConWorkId con)- StgLit lit -> StgLitArg lit- _ -> pprPanic "coreToStgArgs" (ppr arg)+ StgApp v [] -> StgVarArg v+ StgConApp con _ [] _ -> StgVarArg (dataConWorkId con)+ StgOpApp (StgPrimOp op) [] _ -> StgVarArg (primOpWrapperId op)+ StgLit lit -> StgLitArg lit+ _ -> pprPanic "coreToStgArgs" (ppr arg $$ pprStgExpr panicStgPprOpts arg' $$ pprStgExpr panicStgPprOpts arg'') -- WARNING: what if we have an argument like (v `cast` co) -- where 'co' changes the representation type?@@ -621,7 +614,7 @@ -- or foreign call. -- Wanted: a better solution than this hacky warning - platform <- targetPlatform <$> getDynFlags+ platform <- getPlatform let arg_rep = typePrimRep (exprType arg) stg_arg_rep = typePrimRep (stgArgType stg_arg)@@ -703,23 +696,24 @@ -- Convert the RHS of a binding from Core to STG. This is a wrapper around -- coreToStgExpr that can handle value lambdas. coreToPreStgRhs :: HasDebugCallStack => CoreExpr -> CtsM PreStgRhs-coreToPreStgRhs (Cast expr _) = coreToPreStgRhs expr-coreToPreStgRhs expr@(Lam _ _) =- let- (args, body) = myCollectBinders expr- args' = filterStgBinders args- in- extendVarEnvCts [ (a, LambdaBound) | a <- args' ] $ do- body' <- coreToStgExpr body- return (PreStgRhs args' body')-coreToPreStgRhs expr = PreStgRhs [] <$> coreToStgExpr expr+coreToPreStgRhs expr+ = extendVarEnvCts [ (a, LambdaBound) | a <- args' ] $+ do { body' <- coreToStgExpr body+ ; return (PreStgRhs args' body') }+ where+ (args, body) = myCollectBinders expr+ args' = filterStgBinders args -- Generate a top-level RHS. Any new cost centres generated for CAFs will be -- appended to `CollectedCCs` argument.-mkTopStgRhs :: DynFlags -> Module -> CollectedCCs+mkTopStgRhs :: CoreToStgOpts -> Module -> CollectedCCs -> Id -> PreStgRhs -> (StgRhs, CollectedCCs) -mkTopStgRhs dflags this_mod ccs bndr (PreStgRhs bndrs rhs)+mkTopStgRhs CoreToStgOpts+ { coreToStg_platform = platform+ , coreToStg_ExternalDynamicRefs = opt_ExternalDynamicRefs+ , coreToStg_AutoSccsOnIndividualCafs = opt_AutoSccsOnIndividualCafs+ } this_mod ccs bndr (PreStgRhs bndrs rhs) | not (null bndrs) = -- The list of arguments is non-empty, so not CAF ( StgRhsClosure noExtFieldSilent@@ -732,14 +726,14 @@ -- so this is not a function binding | StgConApp con mn args _ <- unticked_rhs , -- Dynamic StgConApps are updatable- not (isDllConApp (targetPlatform dflags) (gopt Opt_ExternalDynamicRefs dflags) this_mod con args)+ not (isDllConApp platform opt_ExternalDynamicRefs this_mod con args) = -- CorePrep does this right, but just to make sure assertPpr (not (isUnboxedTupleDataCon con || isUnboxedSumDataCon con)) (ppr bndr $$ ppr con $$ ppr args) ( StgRhsCon dontCareCCS con mn ticks args, ccs ) -- Otherwise it's a CAF, see Note [Cost-centre initialization plan].- | gopt Opt_AutoSccsOnIndividualCafs dflags+ | opt_AutoSccsOnIndividualCafs = ( StgRhsClosure noExtFieldSilent caf_ccs upd_flag [] rhs@@ -863,7 +857,7 @@ -- *down*. newtype CtsM a = CtsM- { unCtsM :: DynFlags -- Needed for checking for bad coercions in coreToStgArgs+ { unCtsM :: Platform -- Needed for checking for bad coercions in coreToStgArgs -> IdEnv HowBound -> a }@@ -901,8 +895,8 @@ -- The std monad functions: -initCts :: DynFlags -> IdEnv HowBound -> CtsM a -> a-initCts dflags env m = unCtsM m dflags env+initCts :: Platform -> IdEnv HowBound -> CtsM a -> a+initCts platform env m = unCtsM m platform env @@ -913,8 +907,8 @@ returnCts e = CtsM $ \_ _ -> e thenCts :: CtsM a -> (a -> CtsM b) -> CtsM b-thenCts m k = CtsM $ \dflags env- -> unCtsM (k (unCtsM m dflags env)) dflags env+thenCts m k = CtsM $ \platform env+ -> unCtsM (k (unCtsM m platform env)) platform env instance Applicative CtsM where pure = returnCts@@ -923,15 +917,15 @@ instance Monad CtsM where (>>=) = thenCts -instance HasDynFlags CtsM where- getDynFlags = CtsM $ \dflags _ -> dflags+getPlatform :: CtsM Platform+getPlatform = CtsM const -- Functions specific to this monad: extendVarEnvCts :: [(Id, HowBound)] -> CtsM a -> CtsM a extendVarEnvCts ids_w_howbound expr- = CtsM $ \dflags env- -> unCtsM expr dflags (extendVarEnvList env ids_w_howbound)+ = CtsM $ \platform env+ -> unCtsM expr platform (extendVarEnvList env ids_w_howbound) lookupVarCts :: Id -> CtsM HowBound lookupVarCts v = CtsM $ \_ env -> lookupBinding env v@@ -1003,3 +997,12 @@ stgArity _ (LetBound _ arity) = arity stgArity f ImportBound = idArity f stgArity _ LambdaBound = 0++data CoreToStgOpts = CoreToStgOpts+ { coreToStg_platform :: Platform+ , coreToStg_ways :: Ways+ , coreToStg_AutoSccsOnIndividualCafs :: Bool+ , coreToStg_InfoTableMap :: Bool+ , coreToStg_ExternalDynamicRefs :: Bool+ , coreToStg_stgDebugOpts :: StgDebugOpts+ }
compiler/GHC/CoreToStg/Prep.hs view
@@ -1,4 +1,3 @@- {-# LANGUAGE BangPatterns #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}@@ -11,7 +10,9 @@ -} module GHC.CoreToStg.Prep- ( corePrepPgm+ ( CorePrepConfig (..)+ , CorePrepPgmConfig (..)+ , corePrepPgm , corePrepExpr , mkConvertNumLiteral )@@ -21,9 +22,7 @@ import GHC.Platform -import GHC.Driver.Session-import GHC.Driver.Env-import GHC.Driver.Ppr+import GHC.Driver.Flags import GHC.Tc.Utils.Env import GHC.Unit@@ -33,9 +32,7 @@ import GHC.Core.Utils import GHC.Core.Opt.Arity-import GHC.Core.FVs-import GHC.Core.Opt.Monad ( CoreToDo(..) )-import GHC.Core.Lint ( endPassIO )+import GHC.Core.Lint ( EndPassConfig(..), endPassIO ) import GHC.Core import GHC.Core.Make hiding( FloatBind(..) ) -- We use our own FloatBind here import GHC.Core.Type@@ -58,17 +55,15 @@ import GHC.Utils.Outputable import GHC.Utils.Monad ( mapAccumLM ) import GHC.Utils.Logger-import GHC.Utils.Trace import GHC.Types.Demand import GHC.Types.Var-import GHC.Types.Var.Set import GHC.Types.Var.Env import GHC.Types.Id import GHC.Types.Id.Info import GHC.Types.Id.Make ( realWorldPrimId ) import GHC.Types.Basic-import GHC.Types.Name ( NamedThing(..), nameSrcSpan, isInternalName )+import GHC.Types.Name ( Name, NamedThing(..), nameSrcSpan, isInternalName ) import GHC.Types.SrcLoc ( SrcSpan(..), realSrcLocSpan, mkRealSrcLoc ) import GHC.Types.Literal import GHC.Types.Tickish@@ -117,6 +112,17 @@ and doing so would be tiresome because then we'd need to substitute in types and coercions. + We need to clone ids for two reasons:+ + Things associated with labels in the final code must be truly unique in+ order to avoid labels being shadowed in the final output.+ + Even binders without info tables like function arguments or alternative+ bound binders must be unique at least in their type/unique combination.+ We only emit a single declaration for each binder when compiling to C+ so if binders are not unique we would either get duplicate declarations+ or misstyped variables. The later happend in #22402.+ + We heavily use unique-keyed maps in the backend which can go wrong when+ ids with the same unique are meant to represent the same variable.+ 7. Give each dynamic CCall occurrence a fresh unique; this is rather like the cloning step above. @@ -126,26 +132,24 @@ We want curried definitions for all of these in case they aren't inlined by some caller. -9. Replace (lazy e) by e. See Note [lazyId magic] in GHC.Types.Id.Make- Also replace (noinline e) by e.--10. Convert bignum literals into their core representation.+ 9. Convert bignum literals into their core representation. -11. Uphold tick consistency while doing this: We move ticks out of+10. Uphold tick consistency while doing this: We move ticks out of (non-type) applications where we can, and make sure that we annotate according to scoping rules when floating. -12. Collect cost centres (including cost centres in unfoldings) if we're in- profiling mode. We have to do this here beucase we won't have unfoldings+11. Collect cost centres (including cost centres in unfoldings) if we're in+ profiling mode. We have to do this here because we won't have unfoldings after this pass (see `trimUnfolding` and Note [Drop unfoldings and rules]. -13. Eliminate case clutter in favour of unsafe coercions.+12. Eliminate case clutter in favour of unsafe coercions. See Note [Unsafe coercions] -14. Eliminate some magic Ids, specifically+13. Eliminate some magic Ids, specifically runRW# (\s. e) ==> e[readWorldId/s]- lazy e ==> e+ lazy e ==> e (see Note [lazyId magic] in GHC.Types.Id.Make) noinline e ==> e+ nospec e ==> e ToDo: keepAlive# ... This is done in cpeApp @@ -185,7 +189,7 @@ Tiresome, but not difficult. These transformations get rid of "case clutter", leaving only casts.-We are doing no further significant tranformations, so the reasons+We are doing no further significant transformations, so the reasons for the case forms have disappeared. And it is extremely helpful for the ANF-ery, CoreToStg, and backends, if trivial expressions really do look trivial. #19700 was an example.@@ -234,17 +238,28 @@ ************************************************************************ -} -corePrepPgm :: HscEnv -> Module -> ModLocation -> CoreProgram -> [TyCon]+data CorePrepPgmConfig = CorePrepPgmConfig+ { cpPgm_endPassConfig :: !EndPassConfig+ , cpPgm_generateDebugInfo :: !Bool+ }++corePrepPgm :: Logger+ -> CorePrepConfig+ -> CorePrepPgmConfig+ -> Module -> ModLocation -> CoreProgram -> [TyCon] -> IO CoreProgram-corePrepPgm hsc_env this_mod mod_loc binds data_tycons =+corePrepPgm logger cp_cfg pgm_cfg+ this_mod mod_loc binds data_tycons = withTiming logger (text "CorePrep"<+>brackets (ppr this_mod)) (\a -> a `seqList` ()) $ do us <- mkSplitUniqSupply 's'- initialCorePrepEnv <- mkInitialCorePrepEnv hsc_env+ let initialCorePrepEnv = mkInitialCorePrepEnv cp_cfg let- implicit_binds = mkDataConWorkers dflags mod_loc data_tycons+ implicit_binds = mkDataConWorkers+ (cpPgm_generateDebugInfo pgm_cfg)+ mod_loc data_tycons -- NB: we must feed mkImplicitBinds through corePrep too -- so that they are suitably cloned and eta-expanded @@ -253,18 +268,15 @@ floats2 <- corePrepTopBinds initialCorePrepEnv implicit_binds return (deFloatTop (floats1 `appendFloats` floats2)) - endPassIO hsc_env alwaysQualify CorePrep binds_out []+ endPassIO logger (cpPgm_endPassConfig pgm_cfg)+ binds_out [] return binds_out- where- dflags = hsc_dflags hsc_env- logger = hsc_logger hsc_env -corePrepExpr :: HscEnv -> CoreExpr -> IO CoreExpr-corePrepExpr hsc_env expr = do- let logger = hsc_logger hsc_env+corePrepExpr :: Logger -> CorePrepConfig -> CoreExpr -> IO CoreExpr+corePrepExpr logger config expr = do withTiming logger (text "CorePrep [expr]") (\e -> e `seq` ()) $ do us <- mkSplitUniqSupply 's'- initialCorePrepEnv <- mkInitialCorePrepEnv hsc_env+ let initialCorePrepEnv = mkInitialCorePrepEnv config let new_expr = initUs_ us (cpeBodyNF initialCorePrepEnv expr) putDumpFileMaybe logger Opt_D_dump_prep "CorePrep" FormatCore (ppr new_expr) return new_expr@@ -283,10 +295,10 @@ floatss <- go env' binds return (floats `appendFloats` floatss) -mkDataConWorkers :: DynFlags -> ModLocation -> [TyCon] -> [CoreBind]+mkDataConWorkers :: Bool -> ModLocation -> [TyCon] -> [CoreBind] -- See Note [Data constructor workers] -- c.f. Note [Injecting implicit bindings] in GHC.Iface.Tidy-mkDataConWorkers dflags mod_loc data_tycons+mkDataConWorkers generate_debug_info mod_loc data_tycons = [ NonRec id (tick_it (getName data_con) (Var id)) -- The ice is thin here, but it works | tycon <- data_tycons, -- CorePrep will eta-expand it@@ -297,11 +309,12 @@ -- If we want to generate debug info, we put a source note on the -- worker. This is useful, especially for heap profiling. tick_it name- | not (needSourceNotes dflags) = id+ | not generate_debug_info = id | RealSrcSpan span _ <- nameSrcSpan name = tick span | Just file <- ml_hs_file mod_loc = tick (span1 file) | otherwise = tick (span1 "???")- where tick span = Tick (SourceNote span $ showSDoc dflags (ppr name))+ where tick span = Tick $ SourceNote span $+ renderWithContext defaultSDocContext $ ppr name span1 file = realSrcLocSpan $ mkRealSrcLoc (mkFastString file) 1 1 {-@@ -777,12 +790,12 @@ cpeRhsE env (Coercion co) = return (emptyFloats, Coercion (cpSubstCo env co)) cpeRhsE env expr@(Lit (LitNumber nt i))- = case cpe_convertNumLit env nt i of+ = case cp_convertNumLit (cpe_config env) nt i of Nothing -> return (emptyFloats, expr) Just e -> cpeRhsE env e cpeRhsE _env expr@(Lit {}) = return (emptyFloats, expr) cpeRhsE env expr@(Var {}) = cpeApp env expr-cpeRhsE env expr@(App {}) = cpeApp env expr+cpeRhsE env expr@(App {}) = cpeApp env expr cpeRhsE env (Let bind body) = do { (env', bind_floats, maybe_bind') <- cpeBind NotTopLevel env bind@@ -793,7 +806,7 @@ cpeRhsE env (Tick tickish expr) -- Pull out ticks if they are allowed to be floated.- | floatableTick tickish+ | tickishFloatable tickish = do { (floats, body) <- cpeRhsE env expr -- See [Floating Ticks in CorePrep] ; return (unitFloat (FloatTick tickish) `appendFloats` floats, body) }@@ -851,18 +864,11 @@ = do { (floats, scrut') <- cpeBody env scrut ; (env', bndr2) <- cpCloneBndr env bndr ; let alts'- -- This flag is intended to aid in debugging strictness- -- analysis bugs. These are particularly nasty to chase down as- -- they may manifest as segmentation faults. When this flag is- -- enabled we instead produce an 'error' expression to catch- -- the case where a function we think should bottom- -- unexpectedly returns.- | gopt Opt_CatchNonexhaustiveCases (cpe_dynFlags env)+ | cp_catchNonexhaustiveCases $ cpe_config env , not (altsAreExhaustive alts) = addDefault alts (Just err) | otherwise = alts- where err = mkRuntimeErrorApp rUNTIME_ERROR_ID ty- "Bottoming expression returned"+ where err = mkImpossibleExpr ty "cpeRhsE: missing case alternative" ; alts'' <- mapM (sat_alt env') alts' ; return (floats, Case scrut' bndr2 ty alts'') }@@ -917,9 +923,7 @@ = do { (floats, e') <- rhsToBody e ; return (floats, Cast e' co) } -rhsToBody expr@(Lam {})- | Just no_lam_result <- tryEtaReducePrep bndrs body- = return (emptyFloats, no_lam_result)+rhsToBody expr@(Lam {}) -- See Note [No eta reduction needed in rhsToBody] | all isTyVar bndrs -- Type lambdas are ok = return (emptyFloats, expr) | otherwise -- Some value lambdas@@ -928,12 +932,30 @@ ; let float = FloatLet (NonRec fn rhs) ; return (unitFloat float, Var fn) } where- (bndrs,body) = collectBinders expr+ (bndrs,_) = collectBinders expr rhsToBody expr = return (emptyFloats, expr) +{- Note [No eta reduction needed in rhsToBody]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Historical note. In the olden days we used to have a Prep-specific+eta-reduction step in rhsToBody:+ rhsToBody expr@(Lam {})+ | Just no_lam_result <- tryEtaReducePrep bndrs body+ = return (emptyFloats, no_lam_result) +The goal was to reduce+ case x of { p -> \xs. map f xs }+ ==> case x of { p -> map f }++to avoid allocating a lambda. Of course, we'd allocate a PAP+instead, which is hardly better, but that's the way it was.++Now we simply don't bother with this. It doesn't seem to be a win,+and it's extra work.+-}+ -- --------------------------------------------------------------------------- -- CpeApp: produces a result satisfying CpeApp -- ---------------------------------------------------------------------------@@ -1013,10 +1035,12 @@ -- Profiling ticks are slightly less strict so we expand their scope -- if they cover partial applications of things like primOps. -- See Note [Ticks and mandatory eta expansion]- | floatableTick tickish || isProfTick tickish- , Var vh <- head+ -- Here we look inside `fun` before we make the final decision about+ -- floating the tick which isn't optimal for perf. But this only makes+ -- a difference if we have a non-floatable tick which is somewhat rare.+ | Var vh <- head , Var head' <- lookupCorePrepEnv top_env vh- , hasNoBinding head'+ , etaExpansionTick head' tickish = (head,as') where (head,as') = go fun (CpeTick tickish : as)@@ -1032,8 +1056,11 @@ cpe_app env (Var f) (CpeApp Type{} : CpeApp arg : args) | f `hasKey` lazyIdKey -- Replace (lazy a) with a, and -- See Note [lazyId magic] in GHC.Types.Id.Make- || f `hasKey` noinlineIdKey -- Replace (noinline a) with a+ || f `hasKey` noinlineIdKey || f `hasKey` noinlineConstraintIdKey+ -- Replace (noinline a) with a -- See Note [noinlineId magic] in GHC.Types.Id.Make+ || f `hasKey` nospecIdKey -- Replace (nospec a) with a+ -- See Note [nospecId magic] in GHC.Types.Id.Make -- Consider the code: --@@ -1084,7 +1111,7 @@ where depth = val_args args stricts = case idDmdSig v of- DmdSig (DmdType _ demands)+ DmdSig (DmdType _ demands _) | listLengthCmp demands depth /= GT -> demands -- length demands <= depth | otherwise -> []@@ -1118,7 +1145,7 @@ case info of CpeCast {} -> go infos n CpeTick tickish- | floatableTick tickish -> go infos n+ | tickishFloatable tickish -> go infos n -- If we can't guarantee a tick will be floated out of the application -- we can't guarantee the value args following it will be applied. | otherwise -> n@@ -1183,12 +1210,9 @@ arg_ty' = cpSubstTy env arg_ty CpeApp (Coercion co)- -> rebuild_app' env as (App fun' (Coercion co')) floats ss' rt_ticks req_depth+ -> rebuild_app' env as (App fun' (Coercion co')) floats (drop 1 ss) rt_ticks req_depth where co' = cpSubstCo env co- ss'- | null ss = []- | otherwise = tail ss CpeApp arg -> do let (ss1, ss_rest) -- See Note [lazyId magic] in GHC.Types.Id.Make@@ -1564,7 +1588,7 @@ NB1:we could refrain when the RHS is trivial (which can happen for exported things). This would reduce the amount of code- generated (a little) and make things a little words for+ generated (a little) and make things a little worse for code compiled without -O. The case in point is data constructor wrappers. @@ -1598,56 +1622,6 @@ | otherwise = etaExpand arity expr {---- -------------------------------------------------------------------------------- Eta reduction--- -------------------------------------------------------------------------------Why try eta reduction? Hasn't the simplifier already done eta?-But the simplifier only eta reduces if that leaves something-trivial (like f, or f Int). But for deLam it would be enough to-get to a partial application:- case x of { p -> \xs. map f xs }- ==> case x of { p -> map f }--}---- When updating this function, make sure it lines up with--- GHC.Core.Utils.tryEtaReduce!-tryEtaReducePrep :: [CoreBndr] -> CoreExpr -> Maybe CoreExpr-tryEtaReducePrep bndrs expr@(App _ _)- | ok_to_eta_reduce f- , n_remaining >= 0- , and (zipWith ok bndrs last_args)- , not (any (`elemVarSet` fvs_remaining) bndrs)- , exprIsHNF remaining_expr -- Don't turn value into a non-value- -- else the behaviour with 'seq' changes- =- -- pprTrace "prep-reduce" (- -- text "reduced:" <> ppr remaining_expr $$- -- ppr (remaining_args)- -- ) $- Just remaining_expr- where- (f, args) = collectArgs expr- remaining_expr = mkApps f remaining_args- fvs_remaining = exprFreeVars remaining_expr- (remaining_args, last_args) = splitAt n_remaining args- n_remaining = length args - length bndrs- n_remaining_vals = length $ filter isRuntimeArg remaining_args-- ok bndr (Var arg) = bndr == arg- ok _ _ = False-- ok_to_eta_reduce (Var f) = canEtaReduceToArity f n_remaining n_remaining_vals- ok_to_eta_reduce _ = False -- Safe. ToDo: generalise---tryEtaReducePrep bndrs (Tick tickish e)- | tickishFloatable tickish- = fmap (mkTick tickish) $ tryEtaReducePrep bndrs e--tryEtaReducePrep _ _ = Nothing--{- ************************************************************************ * * Floats@@ -1990,8 +1964,25 @@ -} +data CorePrepConfig = CorePrepConfig+ { cp_catchNonexhaustiveCases :: !Bool+ -- ^ Whether to generate a default alternative with ``error`` in these+ -- cases. This is helpful when debugging demand analysis or type+ -- checker bugs which can sometimes manifest as segmentation faults.++ , cp_convertNumLit :: !(LitNumType -> Integer -> Maybe CoreExpr)+ -- ^ Convert some numeric literals (Integer, Natural) into their final+ -- Core form.+ }+ data CorePrepEnv- = CPE { cpe_dynFlags :: DynFlags+ = CPE { cpe_config :: !CorePrepConfig+ -- ^ This flag is intended to aid in debugging strictness+ -- analysis bugs. These are particularly nasty to chase down as+ -- they may manifest as segmentation faults. When this flag is+ -- enabled we instead produce an 'error' expression to catch+ -- the case where a function we think should bottom+ -- unexpectedly returns. , cpe_env :: IdEnv CoreExpr -- Clone local Ids -- ^ This environment is used for three operations: --@@ -2007,20 +1998,14 @@ , cpe_tyco_env :: Maybe CpeTyCoEnv -- See Note [CpeTyCoEnv] - , cpe_convertNumLit :: LitNumType -> Integer -> Maybe CoreExpr- -- ^ Convert some numeric literals (Integer, Natural) into their- -- final Core form , cpe_rec_ids :: UnVarSet -- Faster OutIdSet; See Note [Speculative evaluation] } -mkInitialCorePrepEnv :: HscEnv -> IO CorePrepEnv-mkInitialCorePrepEnv hsc_env = do- convertNumLit <- mkConvertNumLiteral hsc_env- return $ CPE- { cpe_dynFlags = hsc_dflags hsc_env+mkInitialCorePrepEnv :: CorePrepConfig -> CorePrepEnv+mkInitialCorePrepEnv cfg = CPE+ { cpe_config = cfg , cpe_env = emptyVarEnv , cpe_tyco_env = Nothing- , cpe_convertNumLit = convertNumLit , cpe_rec_ids = emptyUnVarSet } @@ -2054,7 +2039,7 @@ {- Note [CpeTyCoEnv] ~~~~~~~~~~~~~~~~~~~~ The cpe_tyco_env :: Maybe CpeTyCoEnv field carries a substitution-for type and coercion varibles+for type and coercion variables * We need the coercion substitution to support the elimination of unsafeEqualityProof (see Note [Unsafe coercions])@@ -2179,7 +2164,9 @@ -- Drop (now-useless) rules/unfoldings -- See Note [Drop unfoldings and rules] -- and Note [Preserve evaluatedness] in GHC.Core.Tidy- ; let unfolding' = trimUnfolding (realIdUnfolding bndr)+ -- And force it.. otherwise the old unfolding is just retained.+ -- See #22071+ ; let !unfolding' = trimUnfolding (realIdUnfolding bndr) -- Simplifier will set the Id's unfolding bndr'' = bndr' `setIdUnfolding` unfolding'@@ -2232,9 +2219,7 @@ newVar :: Type -> UniqSM Id newVar ty- = seqType ty `seq` do- uniq <- getUniqueM- return (mkSysLocalOrCoVar (fsLit "sat") uniq Many ty)+ = seqType ty `seq` mkSysLocalOrCoVarM (fsLit "sat") ManyTy ty ------------------------------------------------------------------------------@@ -2292,24 +2277,18 @@ wrapBind t (NonRec binder rhs) = NonRec binder (mkTick t rhs) wrapBind t (Rec pairs) = Rec (mapSnd (mkTick t) pairs) -floatableTick :: GenTickish pass -> Bool-floatableTick tickish =- tickishPlace tickish == PlaceNonLam &&- tickish `tickishScopesLike` SoftScope- ------------------------------------------------------------------------------ -- Numeric literals -- --------------------------------------------------------------------------- -- | Create a function that converts Bignum literals into their final CoreExpr mkConvertNumLiteral- :: HscEnv+ :: Platform+ -> HomeUnit+ -> (Name -> IO TyThing) -> IO (LitNumType -> Integer -> Maybe CoreExpr)-mkConvertNumLiteral hsc_env = do+mkConvertNumLiteral platform home_unit lookup_global = do let- dflags = hsc_dflags hsc_env- platform = targetPlatform dflags- home_unit = hsc_home_unit hsc_env guardBignum act | isHomeUnitInstanceOf home_unit primUnitId = return $ panic "Bignum literals are not supported in ghc-prim"@@ -2317,7 +2296,7 @@ = return $ panic "Bignum literals are not supported in ghc-bignum" | otherwise = act - lookupBignumId n = guardBignum (tyThingId <$> lookupGlobal hsc_env n)+ lookupBignumId n = guardBignum (tyThingId <$> lookup_global n) -- The lookup is done here but the failure (panic) is reported lazily when we -- try to access the `bigNatFromWordList` function.@@ -2334,8 +2313,6 @@ convertBignatPrim i = let- target = targetPlatform dflags- -- ByteArray# literals aren't supported (yet). Were they supported, -- we would use them directly. We would need to handle -- wordSize/endianness conversion between host and target@@ -2351,11 +2328,10 @@ f x = let low = x .&. mask high = x `shiftR` bits in Just (mkConApp wordDataCon [Lit (mkLitWord platform low)], high)- bits = platformWordSizeInBits target+ bits = platformWordSizeInBits platform mask = 2 ^ bits - 1 in mkApps (Var bignatFromWordListId) [words] return convertNumLit-
+ compiler/GHC/Data/Graph/Collapse.hs view
@@ -0,0 +1,264 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++module GHC.Data.Graph.Collapse+ ( PureSupernode(..)+ , Supernode(..)+ , collapseInductiveGraph+ , VizCollapseMonad(..)+ , NullCollapseViz(..)+ , runNullCollapse+ , MonadUniqSM(..)+ )+where++import GHC.Prelude++import Control.Exception+import Control.Monad+import Data.List (delete, union, insert, intersect)+import Data.Semigroup++import GHC.Cmm.Dataflow.Label+import GHC.Data.Graph.Inductive.Graph+import GHC.Types.Unique.Supply+import GHC.Utils.Panic+++{-|+Module : GHC.Data.Graph.Collapse+Description : Implement the "collapsing" algorithm Hecht and Ullman++A control-flow graph is reducible if and only if it is collapsible+according to the definition of Hecht and Ullman (1972). This module+implements the collapsing algorithm of Hecht and Ullman, and if it+encounters a graph that is not collapsible, it splits nodes until the+graph is fully collapsed. It then reports what nodes (if any) had to+be split in order to collapse the graph. The information is used+upstream to node-split Cmm graphs.++The module uses the inductive graph representation cloned from the+Functional Graph Library (Hackage package `fgl`, modules+`GHC.Data.Graph.Inductive.*`.)++-}++-- Full reference to paper: Matthew S. Hecht and Jeffrey D. Ullman+-- (1972). Flow Graph Reducibility. SIAM J. Comput., 1(2), 188–202.+-- https://doi.org/10.1137/0201014+++------------------ Graph-splitting monad -----------------------++-- | If you want to visualize the graph-collapsing algorithm, create+-- an instance of monad `VizCollapseMonad`. Each step in the+-- algorithm is announced to the monad as a side effect. If you don't+-- care about visualization, you would use the `NullCollapseViz`+-- monad, in which these operations are no-ops.++class (Monad m) => MonadUniqSM m where+ liftUniqSM :: UniqSM a -> m a++class (MonadUniqSM m, Graph gr, Supernode s m) => VizCollapseMonad m gr s where+ consumeByInGraph :: Node -> Node -> gr s () -> m ()+ splitGraphAt :: gr s () -> LNode s -> m ()+ finalGraph :: gr s () -> m ()++++-- | The identity monad as a `VizCollapseMonad`. Use this monad when+-- you want efficiency in graph collapse.+newtype NullCollapseViz a = NullCollapseViz { unNCV :: UniqSM a }+ deriving (Functor, Applicative, Monad, MonadUnique)++instance MonadUniqSM NullCollapseViz where+ liftUniqSM = NullCollapseViz++instance (Graph gr, Supernode s NullCollapseViz) =>+ VizCollapseMonad NullCollapseViz gr s where+ consumeByInGraph _ _ _ = return ()+ splitGraphAt _ _ = return ()+ finalGraph _ = return ()++runNullCollapse :: NullCollapseViz a -> UniqSM a+runNullCollapse = unNCV+++------------------ Utility functions on graphs -----------------------+++-- | Tell if a `Node` has a single predecessor.+singlePred :: Graph gr => gr a b -> Node -> Bool+singlePred gr n+ | ([_], _, _, _) <- context gr n = True+ | otherwise = False++-- | Use this function to extract information about a `Node` that you+-- know is in a `Graph`. It's like `match` from `Graph`, but it must+-- succeed.+forceMatch :: (Graph gr)+ => Node -> gr s b -> (Context s b, gr s b)+forceMatch node g = case match node g of (Just c, g') -> (c, g')+ _ -> panicDump node g+ where panicDump :: Graph gr => Node -> gr s b -> any+ panicDump k _g =+ panic $ "GHC.Data.Graph.Collapse failed to match node " ++ show k++-- | Rewrite the label of a given node.+updateNode :: DynGraph gr => (s -> s) -> Node -> gr s b -> gr s b+updateNode relabel node g = (preds, n, relabel this, succs) & g'+ where ((preds, n, this, succs), g') = forceMatch node g+++-- | Test if a graph has but a single node.+singletonGraph :: Graph gr => gr a b -> Bool+singletonGraph g = case labNodes g of [_] -> True+ _ -> False+++---------------- Supernodes ------------------------------------++-- | A "supernode" stands for a collection of one or more nodes (basic+-- blocks) that have been coalesced by the Hecht-Ullman algorithm.+-- A collection in a supernode constitutes a /reducible/ subgraph of a+-- control-flow graph. (When an entire control-flow graph is collapsed+-- to a single supernode, the flow graph is reducible.)+--+-- The idea of node splitting is to collapse a control-flow graph down+-- to a single supernode, then materialize (``inflate'') the reducible+-- equivalent graph from that supernode. The `Supernode` class+-- defines only the methods needed to collapse; rematerialization is+-- the responsiblity of the client.+--+-- During the Hecht-Ullman algorithm, every supernode has a unique+-- entry point, which is given by `superLabel`. But this invariant is+-- not guaranteed by the class methods and is not a law of the class.+-- The `mapLabels` function rewrites all labels that appear in a+-- supernode (both definitions and uses). The `freshen` function+-- replaces every appearance of a /defined/ label with a fresh label.+-- (Appearances include both definitions and uses.)+--+-- Laws:+-- @+-- superLabel (n <> n') == superLabel n+-- blocks (n <> n') == blocks n `union` blocks n'+-- mapLabels f (n <> n') = mapLabels f n <> mapLabels f n'+-- mapLabels id == id+-- mapLabels (f . g) == mapLabels f . mapLabels g+-- @+--+-- (We expect `freshen` to distribute over `<>`, but because of+-- the fresh names involved, formulating a precise law is a bit+-- challenging.)++class (Semigroup node) => PureSupernode node where+ superLabel :: node -> Label+ mapLabels :: (Label -> Label) -> (node -> node)++class (MonadUnique m, PureSupernode node) => Supernode node m where+ freshen :: node -> m node++ -- ghost method+ -- blocks :: node -> Set Block++------------------ Functions specific to the algorithm -----------------------++-- | Merge two nodes, return new graph plus list of nodes that newly have a single+-- predecessor. This function implements transformation $T_2$ from+-- the Hecht and Ullman paper (merge the node into its unique+-- predecessor). It then also removes self-edges (transformation $T_1$ from+-- the Hecht and Ullman paper). There is no need for a separate+-- implementation of $T_1$.+--+-- `consumeBy v u g` returns the graph that results when node v is+-- consumed by node u in graph g. Both v and u are replaced with a new node u'+-- with these properties:+--+-- LABELS(u') = LABELS(u) `union` LABELS(v)+-- SUCC(u') = SUCC(u) `union` SUCC(v) - { u }+-- every node that previously points to u now points to u'+--+-- It also returns a list of nodes in the result graph that+-- are *newly* single-predecessor nodes.++consumeBy :: (DynGraph gr, PureSupernode s)+ => Node -> Node -> gr s () -> (gr s (), [Node])+consumeBy toNode fromNode g =+ assert (toPreds == [((), fromNode)]) $+ (newGraph, newCandidates)+ where ((toPreds, _, to, toSuccs), g') = forceMatch toNode g+ ((fromPreds, _, from, fromSuccs), g'') = forceMatch fromNode g'+ context = ( fromPreds -- by construction, can't have `toNode`+ , fromNode+ , from <> to+ , delete ((), fromNode) toSuccs `union` fromSuccs+ )+ newGraph = context & g''+ newCandidates = filter (singlePred newGraph) changedNodes+ changedNodes = fromNode `insert` map snd (toSuccs `intersect` fromSuccs)++-- | Split a given node. The node is replaced with a collection of replicas,+-- one for each predecessor. After the split, every predecessor+-- points to a unique replica.+split :: forall gr s b m . (DynGraph gr, Supernode s m)+ => Node -> gr s b -> m (gr s b)+split node g = assert (isMultiple preds) $ foldM addReplica g' newNodes+ where ((preds, _, this, succs), g') = forceMatch node g+ newNodes :: [((b, Node), Node)]+ newNodes = zip preds [maxNode+1..]+ (_, maxNode) = nodeRange g+ thisLabel = superLabel this+ addReplica :: gr s b -> ((b, Node), Node) -> m (gr s b)+ addReplica g ((b, pred), newNode) = do+ newSuper <- freshen this+ return $ add newSuper+ where add newSuper =+ updateNode (thisLabel `replacedWith` superLabel newSuper) pred $+ ([(b, pred)], newNode, newSuper, succs) & g++replacedWith :: PureSupernode s => Label -> Label -> s -> s+replacedWith old new = mapLabels (\l -> if l == old then new else l)+++-- | Does a list have more than one element? (in constant time).+isMultiple :: [a] -> Bool+isMultiple [] = False+isMultiple [_] = False+isMultiple (_:_:_) = True++-- | Find a candidate for splitting by finding a node that has multiple predecessors.++anySplittable :: forall gr a b . Graph gr => gr a b -> LNode a+anySplittable g = case splittable of+ n : _ -> n+ [] -> panic "anySplittable found no splittable nodes"+ where splittable = filter (isMultiple . pre g . fst) $ labNodes g+ splittable :: [LNode a]+++------------------ The collapsing algorithm -----------------------++-- | Using the algorithm of Hecht and Ullman (1972), collapse a graph+-- into a single node, splitting nodes as needed. Record+-- visualization events in monad `m`.+collapseInductiveGraph :: (DynGraph gr, Supernode s m, VizCollapseMonad m gr s)+ => gr s () -> m (gr s ())+collapseInductiveGraph g = drain g worklist+ where worklist :: [[Node]] -- nodes with exactly one predecessor+ worklist = [filter (singlePred g) $ nodes g]++ drain g [] = if singletonGraph g then finalGraph g >> return g+ else let (n, super) = anySplittable g+ in do splitGraphAt g (n, super)+ collapseInductiveGraph =<< split n g+ drain g ([]:nss) = drain g nss+ drain g ((n:ns):nss) = let (g', ns') = consumeBy n (theUniquePred n) g+ in do consumeByInGraph n (theUniquePred n) g+ drain g' (ns':ns:nss)+ where theUniquePred n+ | ([(_, p)], _, _, _) <- context g n = p+ | otherwise =+ panic "node claimed to have a unique predecessor; it doesn't"
compiler/GHC/Data/Graph/Color.hs view
@@ -95,7 +95,7 @@ -- try and color the problem nodes -- problem nodes are the ones that were left uncolored because they weren't triv.- -- theres a change we can color them here anyway.+ -- there's a change we can color them here anyway. (graph_prob, ksNoColor) = assignColors colors graph_triv ksProblems @@ -328,8 +328,7 @@ -- See Note [Unique Determinism and code generation] colors_conflict = mkUniqSet- $ catMaybes- $ map nodeColor nsConflicts+ $ mapMaybe nodeColor nsConflicts -- the prefs of our neighbors colors_neighbor_prefs
+ compiler/GHC/Data/Graph/Inductive/Graph.hs view
@@ -0,0 +1,643 @@+-- (c) 1999-2005 by Martin Erwig (see copyright at bottom)+-- | Static and Dynamic Inductive Graphs+--+-- Code is from Hackage `fgl` package version 5.7.0.3+--+module GHC.Data.Graph.Inductive.Graph (+ -- * General Type Defintions+ -- ** Node and Edge Types+ Node,LNode,UNode,+ Edge,LEdge,UEdge,+ -- ** Types Supporting Inductive Graph View+ Adj,Context,MContext,Decomp,GDecomp,UContext,UDecomp,+ Path,LPath(..),UPath,+ -- * Graph Type Classes+ -- | We define two graph classes:+ --+ -- Graph: static, decomposable graphs.+ -- Static means that a graph itself cannot be changed+ --+ -- DynGraph: dynamic, extensible graphs.+ -- Dynamic graphs inherit all operations from static graphs+ -- but also offer operations to extend and change graphs.+ --+ -- Each class contains in addition to its essential operations those+ -- derived operations that might be overwritten by a more efficient+ -- implementation in an instance definition.+ --+ -- Note that labNodes is essentially needed because the default definition+ -- for matchAny is based on it: we need some node from the graph to define+ -- matchAny in terms of match. Alternatively, we could have made matchAny+ -- essential and have labNodes defined in terms of ufold and matchAny.+ -- However, in general, labNodes seems to be (at least) as easy to define+ -- as matchAny. We have chosen labNodes instead of the function nodes since+ -- nodes can be easily derived from labNodes, but not vice versa.+ Graph(..),+ DynGraph(..),+ -- * Operations+ order,+ size,+ -- ** Graph Folds and Maps+ ufold,gmap,nmap,emap,nemap,+ -- ** Graph Projection+ nodes,edges,toEdge,edgeLabel,toLEdge,newNodes,gelem,+ -- ** Graph Construction and Destruction+ insNode,insEdge,delNode,delEdge,delLEdge,delAllLEdge,+ insNodes,insEdges,delNodes,delEdges,+ buildGr,mkUGraph,+ -- ** Subgraphs+ gfiltermap,nfilter,labnfilter,labfilter,subgraph,+ -- ** Graph Inspection+ context,lab,neighbors,lneighbors,+ suc,pre,lsuc,lpre,+ out,inn,outdeg,indeg,deg,+ hasEdge,hasNeighbor,hasLEdge,hasNeighborAdj,+ equal,+ -- ** Context Inspection+ node',lab',labNode',neighbors',lneighbors',+ suc',pre',lpre',lsuc',+ out',inn',outdeg',indeg',deg',+ -- * Pretty-printing+ prettify,+ prettyPrint,+ -- * Ordering of Graphs+ OrdGr(..)+) where++import GHC.Prelude++import Control.Arrow (first)+import Data.Function (on)+import qualified Data.IntSet as IntSet+import Data.List (delete, groupBy, sort, sortBy, (\\))+import Data.Maybe (fromMaybe, isJust)++import GHC.Utils.Panic++-- | Unlabeled node+type Node = Int+-- | Labeled node+type LNode a = (Node,a)+-- | Quasi-unlabeled node+type UNode = LNode ()++-- | Unlabeled edge+type Edge = (Node,Node)+-- | Labeled edge+type LEdge b = (Node,Node,b)+-- | Quasi-unlabeled edge+type UEdge = LEdge ()++-- | Unlabeled path+type Path = [Node]+-- | Labeled path+newtype LPath a = LP { unLPath :: [LNode a] }++instance (Show a) => Show (LPath a) where+ show (LP xs) = show xs++instance (Eq a) => Eq (LPath a) where+ (LP []) == (LP []) = True+ (LP ((_,x):_)) == (LP ((_,y):_)) = x==y+ (LP _) == (LP _) = False++instance (Ord a) => Ord (LPath a) where+ compare (LP []) (LP []) = EQ+ compare (LP ((_,x):_)) (LP ((_,y):_)) = compare x y+ compare _ _ = panic "LPath: cannot compare two empty paths"++-- | Quasi-unlabeled path+type UPath = [UNode]++-- | Labeled links to or from a 'Node'.+type Adj b = [(b,Node)]+-- | Links to the 'Node', the 'Node' itself, a label, links from the 'Node'.+--+-- In other words, this captures all information regarding the+-- specified 'Node' within a graph.+type Context a b = (Adj b,Node,a,Adj b) -- Context a b "=" Context' a b "+" Node+type MContext a b = Maybe (Context a b)+-- | 'Graph' decomposition - the context removed from a 'Graph', and the rest+-- of the 'Graph'.+type Decomp g a b = (MContext a b,g a b)+-- | The same as 'Decomp', only more sure of itself.+type GDecomp g a b = (Context a b,g a b)++-- | Unlabeled context.+type UContext = ([Node],Node,[Node])+-- | Unlabeled decomposition.+type UDecomp g = (Maybe UContext,g)++-- | Minimum implementation: 'empty', 'isEmpty', 'match', 'mkGraph', 'labNodes'+class Graph gr where+ {-# MINIMAL empty, isEmpty, match, mkGraph, labNodes #-}++ -- | An empty 'Graph'.+ empty :: gr a b++ -- | True if the given 'Graph' is empty.+ isEmpty :: gr a b -> Bool++ -- | Decompose a 'Graph' into the 'MContext' found for the given node and the+ -- remaining 'Graph'.+ match :: Node -> gr a b -> Decomp gr a b++ -- | Create a 'Graph' from the list of 'LNode's and 'LEdge's.+ --+ -- For graphs that are also instances of 'DynGraph', @mkGraph ns+ -- es@ should be equivalent to @('insEdges' es . 'insNodes' ns)+ -- 'empty'@.+ mkGraph :: [LNode a] -> [LEdge b] -> gr a b++ -- | A list of all 'LNode's in the 'Graph'.+ labNodes :: gr a b -> [LNode a]++ -- | Decompose a graph into the 'Context' for an arbitrarily-chosen 'Node'+ -- and the remaining 'Graph'.+ matchAny :: gr a b -> GDecomp gr a b+ matchAny g = case labNodes g of+ [] -> panic "Match Exception, Empty Graph"+ (v,_):_ | (Just c,g') <- match v g -> (c,g')+ _ -> panic "This can't happen: failed to match node in graph"+++ -- | The number of 'Node's in a 'Graph'.+ noNodes :: gr a b -> Int+ noNodes = length . labNodes++ -- | The minimum and maximum 'Node' in a 'Graph'.+ nodeRange :: gr a b -> (Node,Node)+ nodeRange g+ | isEmpty g = panic "nodeRange of empty graph"+ | otherwise = (minimum vs, maximum vs)+ where+ vs = nodes g++ -- | A list of all 'LEdge's in the 'Graph'.+ labEdges :: gr a b -> [LEdge b]+ labEdges = ufold (\(_,v,_,s)->(map (\(l,w)->(v,w,l)) s ++)) []++class (Graph gr) => DynGraph gr where+ -- | Merge the 'Context' into the 'DynGraph'.+ --+ -- Context adjacencies should only refer to either a Node already+ -- in a graph or the node in the Context itself (for loops).+ --+ -- Behaviour is undefined if the specified 'Node' already exists+ -- in the graph.+ (&) :: Context a b -> gr a b -> gr a b+++-- | The number of nodes in the graph. An alias for 'noNodes'.+order :: (Graph gr) => gr a b -> Int+order = noNodes++-- | The number of edges in the graph.+--+-- Note that this counts every edge found, so if you are+-- representing an unordered graph by having each edge mirrored this+-- will be incorrect.+--+-- If you created an unordered graph by either mirroring every edge+-- (including loops!) or using the @undir@ function in+-- "Data.Graph.Inductive.Basic" then you can safely halve the value+-- returned by this.+size :: (Graph gr) => gr a b -> Int+size = length . labEdges++-- | Fold a function over the graph by recursively calling 'match'.+ufold :: (Graph gr) => (Context a b -> c -> c) -> c -> gr a b -> c+ufold f u g+ | isEmpty g = u+ | otherwise = f c (ufold f u g')+ where+ (c,g') = matchAny g++-- | Map a function over the graph by recursively calling 'match'.+gmap :: (DynGraph gr) => (Context a b -> Context c d) -> gr a b -> gr c d+gmap f = ufold (\c->(f c&)) empty+{-# NOINLINE [0] gmap #-}++-- | Map a function over the 'Node' labels in a graph.+nmap :: (DynGraph gr) => (a -> c) -> gr a b -> gr c b+nmap f = gmap (\(p,v,l,s)->(p,v,f l,s))+{-# NOINLINE [0] nmap #-}++-- | Map a function over the 'Edge' labels in a graph.+emap :: (DynGraph gr) => (b -> c) -> gr a b -> gr a c+emap f = gmap (\(p,v,l,s)->(map1 f p,v,l,map1 f s))+ where+ map1 g = map (first g)+{-# NOINLINE [0] emap #-}++-- | Map functions over both the 'Node' and 'Edge' labels in a graph.+nemap :: (DynGraph gr) => (a -> c) -> (b -> d) -> gr a b -> gr c d+nemap fn fe = gmap (\(p,v,l,s) -> (fe' p,v,fn l,fe' s))+ where+ fe' = map (first fe)+{-# NOINLINE [0] nemap #-}++-- | List all 'Node's in the 'Graph'.+nodes :: (Graph gr) => gr a b -> [Node]+nodes = map fst . labNodes++-- | List all 'Edge's in the 'Graph'.+edges :: (Graph gr) => gr a b -> [Edge]+edges = map toEdge . labEdges++-- | Drop the label component of an edge.+toEdge :: LEdge b -> Edge+toEdge (v,w,_) = (v,w)++-- | Add a label to an edge.+toLEdge :: Edge -> b -> LEdge b+toLEdge (v,w) l = (v,w,l)++-- | The label in an edge.+edgeLabel :: LEdge b -> b+edgeLabel (_,_,l) = l++-- | List N available 'Node's, i.e. 'Node's that are not used in the 'Graph'.+newNodes :: (Graph gr) => Int -> gr a b -> [Node]+newNodes i g+ | isEmpty g = [0..i-1]+ | otherwise = [n+1..n+i]+ where+ (_,n) = nodeRange g++-- | 'True' if the 'Node' is present in the 'Graph'.+gelem :: (Graph gr) => Node -> gr a b -> Bool+gelem v = isJust . fst . match v++-- | Insert a 'LNode' into the 'Graph'.+insNode :: (DynGraph gr) => LNode a -> gr a b -> gr a b+insNode (v,l) = (([],v,l,[])&)+{-# NOINLINE [0] insNode #-}++-- | Insert a 'LEdge' into the 'Graph'.+insEdge :: (DynGraph gr) => LEdge b -> gr a b -> gr a b+insEdge (v,w,l) g = (pr,v,la,(l,w):su) & g'+ where+ (mcxt,g') = match v g+ (pr,_,la,su) = fromMaybe+ (panic ("insEdge: cannot add edge from non-existent vertex " ++ show v))+ mcxt+{-# NOINLINE [0] insEdge #-}++-- | Remove a 'Node' from the 'Graph'.+delNode :: (Graph gr) => Node -> gr a b -> gr a b+delNode v = delNodes [v]++-- | Remove an 'Edge' from the 'Graph'.+--+-- NOTE: in the case of multiple edges, this will delete /all/ such+-- edges from the graph as there is no way to distinguish between+-- them. If you need to delete only a single such edge, please use+-- 'delLEdge'.+delEdge :: (DynGraph gr) => Edge -> gr a b -> gr a b+delEdge (v,w) g = case match v g of+ (Nothing,_) -> g+ (Just (p,v',l,s),g') -> (p,v',l,filter ((/=w).snd) s) & g'++-- | Remove an 'LEdge' from the 'Graph'.+--+-- NOTE: in the case of multiple edges with the same label, this+-- will only delete the /first/ such edge. To delete all such+-- edges, please use 'delAllLedge'.+delLEdge :: (DynGraph gr, Eq b) => LEdge b -> gr a b -> gr a b+delLEdge = delLEdgeBy delete++-- | Remove all edges equal to the one specified.+delAllLEdge :: (DynGraph gr, Eq b) => LEdge b -> gr a b -> gr a b+delAllLEdge = delLEdgeBy (filter . (/=))++delLEdgeBy :: (DynGraph gr) => ((b,Node) -> Adj b -> Adj b)+ -> LEdge b -> gr a b -> gr a b+delLEdgeBy f (v,w,b) g = case match v g of+ (Nothing,_) -> g+ (Just (p,v',l,s),g') -> (p,v',l,f (b,w) s) & g'++-- | Insert multiple 'LNode's into the 'Graph'.+insNodes :: (DynGraph gr) => [LNode a] -> gr a b -> gr a b+insNodes vs g = foldl' (flip insNode) g vs+{-# INLINABLE insNodes #-}++-- | Insert multiple 'LEdge's into the 'Graph'.+insEdges :: (DynGraph gr) => [LEdge b] -> gr a b -> gr a b+insEdges es g = foldl' (flip insEdge) g es+{-# INLINABLE insEdges #-}++-- | Remove multiple 'Node's from the 'Graph'.+delNodes :: (Graph gr) => [Node] -> gr a b -> gr a b+delNodes vs g = foldl' (snd .: flip match) g vs++-- | Remove multiple 'Edge's from the 'Graph'.+delEdges :: (DynGraph gr) => [Edge] -> gr a b -> gr a b+delEdges es g = foldl' (flip delEdge) g es++-- | Build a 'Graph' from a list of 'Context's.+--+-- The list should be in the order such that earlier 'Context's+-- depend upon later ones (i.e. as produced by @'ufold' (:) []@).+buildGr :: (DynGraph gr) => [Context a b] -> gr a b+buildGr = foldr (&) empty++-- | Build a quasi-unlabeled 'Graph'.+mkUGraph :: (Graph gr) => [Node] -> [Edge] -> gr () ()+mkUGraph vs es = mkGraph (labUNodes vs) (labUEdges es)+ where+ labUEdges = map (`toLEdge` ())+ labUNodes = map (flip (,) ())++-- | Build a graph out of the contexts for which the predicate is+-- satisfied by recursively calling 'match'.+gfiltermap :: DynGraph gr => (Context a b -> MContext c d) -> gr a b -> gr c d+gfiltermap f = ufold (maybe id (&) . f) empty++-- | Returns the subgraph only containing the labelled nodes which+-- satisfy the given predicate.+labnfilter :: Graph gr => (LNode a -> Bool) -> gr a b -> gr a b+labnfilter p gr = delNodes (map fst . filter (not . p) $ labNodes gr) gr++-- | Returns the subgraph only containing the nodes which satisfy the+-- given predicate.+nfilter :: DynGraph gr => (Node -> Bool) -> gr a b -> gr a b+nfilter f = labnfilter (f . fst)++-- | Returns the subgraph only containing the nodes whose labels+-- satisfy the given predicate.+labfilter :: DynGraph gr => (a -> Bool) -> gr a b -> gr a b+labfilter f = labnfilter (f . snd)++-- | Returns the subgraph induced by the supplied nodes.+subgraph :: DynGraph gr => [Node] -> gr a b -> gr a b+subgraph vs = let vs' = IntSet.fromList vs+ in nfilter (`IntSet.member` vs')++-- | Find the context for the given 'Node'. Causes an error if the 'Node' is+-- not present in the 'Graph'.+context :: (Graph gr) => gr a b -> Node -> Context a b+context g v = fromMaybe (panic ("Match Exception, Node: "++show v))+ (fst (match v g))++-- | Find the label for a 'Node'.+lab :: (Graph gr) => gr a b -> Node -> Maybe a+lab g v = fmap lab' . fst $ match v g++-- | Find the neighbors for a 'Node'.+neighbors :: (Graph gr) => gr a b -> Node -> [Node]+neighbors = map snd .: lneighbors++-- | Find the labelled links coming into or going from a 'Context'.+lneighbors :: (Graph gr) => gr a b -> Node -> Adj b+lneighbors = maybe [] lneighbors' .: mcontext++-- | Find all 'Node's that have a link from the given 'Node'.+suc :: (Graph gr) => gr a b -> Node -> [Node]+suc = map snd .: context4l++-- | Find all 'Node's that link to to the given 'Node'.+pre :: (Graph gr) => gr a b -> Node -> [Node]+pre = map snd .: context1l++-- | Find all 'Node's that are linked from the given 'Node' and the label of+-- each link.+lsuc :: (Graph gr) => gr a b -> Node -> [(Node,b)]+lsuc = map flip2 .: context4l++-- | Find all 'Node's that link to the given 'Node' and the label of each link.+lpre :: (Graph gr) => gr a b -> Node -> [(Node,b)]+lpre = map flip2 .: context1l++-- | Find all outward-bound 'LEdge's for the given 'Node'.+out :: (Graph gr) => gr a b -> Node -> [LEdge b]+out g v = map (\(l,w)->(v,w,l)) (context4l g v)++-- | Find all inward-bound 'LEdge's for the given 'Node'.+inn :: (Graph gr) => gr a b -> Node -> [LEdge b]+inn g v = map (\(l,w)->(w,v,l)) (context1l g v)++-- | The outward-bound degree of the 'Node'.+outdeg :: (Graph gr) => gr a b -> Node -> Int+outdeg = length .: context4l++-- | The inward-bound degree of the 'Node'.+indeg :: (Graph gr) => gr a b -> Node -> Int+indeg = length .: context1l++-- | The degree of the 'Node'.+deg :: (Graph gr) => gr a b -> Node -> Int+deg = deg' .: context++-- | The 'Node' in a 'Context'.+node' :: Context a b -> Node+node' (_,v,_,_) = v++-- | The label in a 'Context'.+lab' :: Context a b -> a+lab' (_,_,l,_) = l++-- | The 'LNode' from a 'Context'.+labNode' :: Context a b -> LNode a+labNode' (_,v,l,_) = (v,l)++-- | All 'Node's linked to or from in a 'Context'.+neighbors' :: Context a b -> [Node]+neighbors' (p,_,_,s) = map snd p++map snd s++-- | All labelled links coming into or going from a 'Context'.+lneighbors' :: Context a b -> Adj b+lneighbors' (p,_,_,s) = p ++ s++-- | All 'Node's linked to in a 'Context'.+suc' :: Context a b -> [Node]+suc' = map snd . context4l'++-- | All 'Node's linked from in a 'Context'.+pre' :: Context a b -> [Node]+pre' = map snd . context1l'++-- | All 'Node's linked from in a 'Context', and the label of the links.+lsuc' :: Context a b -> [(Node,b)]+lsuc' = map flip2 . context4l'++-- | All 'Node's linked from in a 'Context', and the label of the links.+lpre' :: Context a b -> [(Node,b)]+lpre' = map flip2 . context1l'++-- | All outward-directed 'LEdge's in a 'Context'.+out' :: Context a b -> [LEdge b]+out' c@(_,v,_,_) = map (\(l,w)->(v,w,l)) (context4l' c)++-- | All inward-directed 'LEdge's in a 'Context'.+inn' :: Context a b -> [LEdge b]+inn' c@(_,v,_,_) = map (\(l,w)->(w,v,l)) (context1l' c)++-- | The outward degree of a 'Context'.+outdeg' :: Context a b -> Int+outdeg' = length . context4l'++-- | The inward degree of a 'Context'.+indeg' :: Context a b -> Int+indeg' = length . context1l'++-- | The degree of a 'Context'.+deg' :: Context a b -> Int+deg' (p,_,_,s) = length p+length s++-- | Checks if there is a directed edge between two nodes.+hasEdge :: Graph gr => gr a b -> Edge -> Bool+hasEdge gr (v,w) = w `elem` suc gr v++-- | Checks if there is an undirected edge between two nodes.+hasNeighbor :: Graph gr => gr a b -> Node -> Node -> Bool+hasNeighbor gr v w = w `elem` neighbors gr v++-- | Checks if there is a labelled edge between two nodes.+hasLEdge :: (Graph gr, Eq b) => gr a b -> LEdge b -> Bool+hasLEdge gr (v,w,l) = (w,l) `elem` lsuc gr v++-- | Checks if there is an undirected labelled edge between two nodes.+hasNeighborAdj :: (Graph gr, Eq b) => gr a b -> Node -> (b,Node) -> Bool+hasNeighborAdj gr v a = a `elem` lneighbors gr v++----------------------------------------------------------------------+-- GRAPH EQUALITY+----------------------------------------------------------------------++slabNodes :: (Graph gr) => gr a b -> [LNode a]+slabNodes = sortBy (compare `on` fst) . labNodes++glabEdges :: (Graph gr) => gr a b -> [GroupEdges b]+glabEdges = map (GEs . groupLabels)+ . groupBy ((==) `on` toEdge)+ . sortBy (compare `on` toEdge)+ . labEdges+ where+ groupLabels les = toLEdge (toEdge (head les)) (map edgeLabel les)++equal :: (Eq a,Eq b,Graph gr) => gr a b -> gr a b -> Bool+equal g g' = slabNodes g == slabNodes g' && glabEdges g == glabEdges g'+-- This assumes that nodes aren't repeated (which shouldn't happen for+-- sane graph instances). If node IDs are repeated, then the usage of+-- slabNodes cannot guarantee stable ordering.++-- Newtype wrapper just to test for equality of multiple edges. This+-- is needed because without an Ord constraint on `b' it is not+-- possible to guarantee a stable ordering on edge labels.+newtype GroupEdges b = GEs (LEdge [b])+ deriving (Show, Read)++instance (Eq b) => Eq (GroupEdges b) where+ (GEs (v1,w1,bs1)) == (GEs (v2,w2,bs2)) = v1 == v2+ && w1 == w2+ && eqLists bs1 bs2++eqLists :: (Eq a) => [a] -> [a] -> Bool+eqLists xs ys = null (xs \\ ys) && null (ys \\ xs)+-- OK to use \\ here as we want each value in xs to cancel a *single*+-- value in ys.++----------------------------------------------------------------------+-- UTILITIES+----------------------------------------------------------------------++-- auxiliary functions used in the implementation of the+-- derived class members+--+(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d+-- f .: g = \x y->f (g x y)+-- f .: g = (f .) . g+-- (.:) f = ((f .) .)+-- (.:) = (.) (.) (.)+(.:) = (.) . (.)++flip2 :: (a,b) -> (b,a)+flip2 (x,y) = (y,x)++-- projecting on context elements+--+context1l :: (Graph gr) => gr a b -> Node -> Adj b+context1l = maybe [] context1l' .: mcontext++context4l :: (Graph gr) => gr a b -> Node -> Adj b+context4l = maybe [] context4l' .: mcontext++mcontext :: (Graph gr) => gr a b -> Node -> MContext a b+mcontext = fst .: flip match++context1l' :: Context a b -> Adj b+context1l' (p,v,_,s) = p++filter ((==v).snd) s++context4l' :: Context a b -> Adj b+context4l' (p,v,_,s) = s++filter ((==v).snd) p++----------------------------------------------------------------------+-- PRETTY PRINTING+----------------------------------------------------------------------++-- | Pretty-print the graph. Note that this loses a lot of+-- information, such as edge inverses, etc.+prettify :: (DynGraph gr, Show a, Show b) => gr a b -> String+prettify g = foldr (showsContext . context g) id (nodes g) ""+ where+ showsContext (_,n,l,s) sg = shows n . (':':) . shows l+ . showString "->" . shows s+ . ('\n':) . sg++-- | Pretty-print the graph to stdout.+prettyPrint :: (DynGraph gr, Show a, Show b) => gr a b -> IO ()+prettyPrint = putStr . prettify++----------------------------------------------------------------------+-- Ordered Graph+----------------------------------------------------------------------++-- | OrdGr comes equipped with an Ord instance, so that graphs can be+-- used as e.g. Map keys.+newtype OrdGr gr a b = OrdGr { unOrdGr :: gr a b }+ deriving (Read,Show)++instance (Graph gr, Ord a, Ord b) => Eq (OrdGr gr a b) where+ g1 == g2 = compare g1 g2 == EQ++instance (Graph gr, Ord a, Ord b) => Ord (OrdGr gr a b) where+ compare (OrdGr g1) (OrdGr g2) =+ (compare `on` sort . labNodes) g1 g2+ `mappend` (compare `on` sort . labEdges) g1 g2+++{-----------------------------------------------------------------++Copyright (c) 1999-2008, Martin Erwig+ 2010, Ivan Lazar Miljenovic+ 2022, Norman Ramsey+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++1. Redistributions of source code must retain the above copyright notice,+ this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.++3. Neither the name of the author nor the names of its contributors may be+ used to endorse or promote products derived from this software without+ specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.++----------------------------------------------------------------}
+ compiler/GHC/Data/Graph/Inductive/PatriciaTree.hs view
@@ -0,0 +1,346 @@+{-# LANGUAGE BangPatterns, ScopedTypeVariables #-}+{-# LANGUAGE DeriveGeneric #-}++-- |An efficient implementation of 'Data.Graph.Inductive.Graph.Graph'+-- using big-endian patricia tree (i.e. "Data.IntMap").+--+-- This module provides the following specialised functions to gain+-- more performance, using GHC's RULES pragma:+--+-- * 'Data.Graph.Inductive.Graph.insNode'+--+-- * 'Data.Graph.Inductive.Graph.insEdge'+--+-- * 'Data.Graph.Inductive.Graph.gmap'+--+-- * 'Data.Graph.Inductive.Graph.nmap'+--+-- * 'Data.Graph.Inductive.Graph.emap'+--+-- Code is from Hackage `fgl` package version 5.7.0.3+++module GHC.Data.Graph.Inductive.PatriciaTree+ ( Gr+ , UGr+ )+ where++import GHC.Prelude++import GHC.Data.Graph.Inductive.Graph++import Data.IntMap (IntMap)+import qualified Data.IntMap as IM+import Data.List (sort)+import Data.Maybe (fromMaybe)+import Data.Tuple (swap)++import qualified Data.IntMap.Strict as IMS++import GHC.Generics (Generic)++import Data.Bifunctor++----------------------------------------------------------------------+-- GRAPH REPRESENTATION+----------------------------------------------------------------------++newtype Gr a b = Gr (GraphRep a b)+ deriving (Generic)++type GraphRep a b = IntMap (Context' a b)+type Context' a b = (IntMap [b], a, IntMap [b])++type UGr = Gr () ()++----------------------------------------------------------------------+-- CLASS INSTANCES+----------------------------------------------------------------------++instance (Eq a, Ord b) => Eq (Gr a b) where+ (Gr g1) == (Gr g2) = fmap sortAdj g1 == fmap sortAdj g2+ where+ sortAdj (p,n,s) = (fmap sort p,n,fmap sort s)++instance (Show a, Show b) => Show (Gr a b) where+ showsPrec d g = showParen (d > 10) $+ showString "mkGraph "+ . shows (labNodes g)+ . showString " "+ . shows (labEdges g)++instance (Read a, Read b) => Read (Gr a b) where+ readsPrec p = readParen (p > 10) $ \ r -> do+ ("mkGraph", s) <- lex r+ (ns,t) <- reads s+ (es,u) <- reads t+ return (mkGraph ns es, u)++instance Graph Gr where+ empty = Gr IM.empty++ isEmpty (Gr g) = IM.null g++ match = matchGr++ mkGraph vs es = insEdges es+ . Gr+ . IM.fromList+ . map (second (\l -> (IM.empty,l,IM.empty)))+ $ vs++ labNodes (Gr g) = [ (node, label)+ | (node, (_, label, _)) <- IM.toList g ]++ noNodes (Gr g) = IM.size g++ nodeRange (Gr g) = fromMaybe (error "nodeRange of empty graph")+ $ liftA2 (,) (ix (IM.minViewWithKey g))+ (ix (IM.maxViewWithKey g))+ where+ ix = fmap (fst . fst)++ labEdges (Gr g) = do (node, (_, _, s)) <- IM.toList g+ (next, labels) <- IM.toList s+ label <- labels+ return (node, next, label)++instance DynGraph Gr where+ (p, v, l, s) & (Gr g)+ = let !g1 = IM.insert v (preds, l, succs) g+ !(np, preds) = fromAdjCounting p+ !(ns, succs) = fromAdjCounting s+ !g2 = addSucc g1 v np preds+ !g3 = addPred g2 v ns succs+ in Gr g3+++instance Functor (Gr a) where+ fmap = fastEMap++instance Bifunctor Gr where+ bimap = fastNEMap++ first = fastNMap++ second = fastEMap+++matchGr :: Node -> Gr a b -> Decomp Gr a b+matchGr node (Gr g)+ = case IM.lookup node g of+ Nothing+ -> (Nothing, Gr g)++ Just (p, label, s)+ -> let !g1 = IM.delete node g+ !p' = IM.delete node p+ !s' = IM.delete node s+ !g2 = clearPred g1 node s'+ !g3 = clearSucc g2 node p'+ in (Just (toAdj p', node, label, toAdj s), Gr g3)++----------------------------------------------------------------------+-- OVERRIDING FUNCTIONS+----------------------------------------------------------------------++{-++{- RULES+ "insNode/Data.Graph.Inductive.PatriciaTree" insNode = fastInsNode+ -}+fastInsNode :: LNode a -> Gr a b -> Gr a b+fastInsNode (v, l) (Gr g) = g' `seq` Gr g'+ where+ g' = IM.insert v (IM.empty, l, IM.empty) g++-}+{-# RULES+ "insEdge/GHC.Data.Graph.Inductive.PatriciaTree" insEdge = fastInsEdge+ #-}+fastInsEdge :: LEdge b -> Gr a b -> Gr a b+fastInsEdge (v, w, l) (Gr g) = g2 `seq` Gr g2+ where+ g1 = IM.adjust addS' v g+ g2 = IM.adjust addP' w g1++ addS' (ps, l', ss) = (ps, l', IM.insertWith addLists w [l] ss)+ addP' (ps, l', ss) = (IM.insertWith addLists v [l] ps, l', ss)++{-++{- RULES+ "gmap/Data.Graph.Inductive.PatriciaTree" gmap = fastGMap+ -}+fastGMap :: forall a b c d. (Context a b -> Context c d) -> Gr a b -> Gr c d+fastGMap f (Gr g) = Gr (IM.mapWithKey f' g)+ where+ f' :: Node -> Context' a b -> Context' c d+ f' = ((fromContext . f) .) . toContext++{- RULES+ "nmap/Data.Graph.Inductive.PatriciaTree" nmap = fastNMap+ -}+-}+fastNMap :: forall a b c. (a -> c) -> Gr a b -> Gr c b+fastNMap f (Gr g) = Gr (IM.map f' g)+ where+ f' :: Context' a b -> Context' c b+ f' (ps, a, ss) = (ps, f a, ss)+{-++{- RULES+ "emap/GHC.Data.Graph.Inductive.PatriciaTree" emap = fastEMap+ -}+-}+fastEMap :: forall a b c. (b -> c) -> Gr a b -> Gr a c+fastEMap f (Gr g) = Gr (IM.map f' g)+ where+ f' :: Context' a b -> Context' a c+ f' (ps, a, ss) = (IM.map (map f) ps, a, IM.map (map f) ss)++{- RULES+ "nemap/GHC.Data.Graph.Inductive.PatriciaTree" nemap = fastNEMap+ -}++fastNEMap :: forall a b c d. (a -> c) -> (b -> d) -> Gr a b -> Gr c d+fastNEMap fn fe (Gr g) = Gr (IM.map f g)+ where+ f :: Context' a b -> Context' c d+ f (ps, a, ss) = (IM.map (map fe) ps, fn a, IM.map (map fe) ss)++++----------------------------------------------------------------------+-- UTILITIES+----------------------------------------------------------------------++toAdj :: IntMap [b] -> Adj b+toAdj = concatMap expand . IM.toList+ where+ expand (n,ls) = map (flip (,) n) ls++--fromAdj :: Adj b -> IntMap [b]+--fromAdj = IM.fromListWith addLists . map (second (:[]) . swap)++data FromListCounting a = FromListCounting !Int !(IntMap a)+ deriving (Eq, Show, Read)++getFromListCounting :: FromListCounting a -> (Int, IntMap a)+getFromListCounting (FromListCounting i m) = (i, m)+{-# INLINE getFromListCounting #-}++fromListWithKeyCounting :: (Int -> a -> a -> a) -> [(Int, a)] -> (Int, IntMap a)+fromListWithKeyCounting f = getFromListCounting . foldl' ins (FromListCounting 0 IM.empty)+ where+ ins (FromListCounting i t) (k,x) = FromListCounting (i + 1) (IM.insertWithKey f k x t)+{-# INLINE fromListWithKeyCounting #-}++fromListWithCounting :: (a -> a -> a) -> [(Int, a)] -> (Int, IntMap a)+fromListWithCounting f = fromListWithKeyCounting (\_ x y -> f x y)+{-# INLINE fromListWithCounting #-}++fromAdjCounting :: Adj b -> (Int, IntMap [b])+fromAdjCounting = fromListWithCounting addLists . map (second (:[]) . swap)++-- We use differenceWith to modify a graph more than bulkThreshold times,+-- and repeated insertWith otherwise.+bulkThreshold :: Int+bulkThreshold = 5++--toContext :: Node -> Context' a b -> Context a b+--toContext v (ps, a, ss) = (toAdj ps, v, a, toAdj ss)++--fromContext :: Context a b -> Context' a b+--fromContext (ps, _, a, ss) = (fromAdj ps, a, fromAdj ss)++-- A version of @++@ where order isn't important, so @xs ++ [x]@+-- becomes @x:xs@. Used when we have to have a function of type @[a]+-- -> [a] -> [a]@ but one of the lists is just going to be a single+-- element (and it isn't possible to tell which).+addLists :: [a] -> [a] -> [a]+addLists [a] as = a : as+addLists as [a] = a : as+addLists xs ys = xs ++ ys++addSucc :: forall a b . GraphRep a b -> Node -> Int -> IM.IntMap [b] -> GraphRep a b+addSucc g0 v numAdd xs+ | numAdd < bulkThreshold = foldlWithKey' go g0 xs+ where+ go :: GraphRep a b -> Node -> [b] -> GraphRep a b+ go g p l = IMS.adjust f p g+ where f (ps, l', ss) = let !ss' = IM.insertWith addLists v l ss+ in (ps, l', ss')+addSucc g v _ xs = IMS.differenceWith go g xs+ where+ go :: Context' a b -> [b] -> Maybe (Context' a b)+ go (ps, l', ss) l = let !ss' = IM.insertWith addLists v l ss+ in Just (ps, l', ss')++foldlWithKey' :: (a -> IM.Key -> b -> a) -> a -> IntMap b -> a+foldlWithKey' =+ IM.foldlWithKey'++addPred :: forall a b . GraphRep a b -> Node -> Int -> IM.IntMap [b] -> GraphRep a b+addPred g0 v numAdd xs+ | numAdd < bulkThreshold = foldlWithKey' go g0 xs+ where+ go :: GraphRep a b -> Node -> [b] -> GraphRep a b+ go g p l = IMS.adjust f p g+ where f (ps, l', ss) = let !ps' = IM.insertWith addLists v l ps+ in (ps', l', ss)+addPred g v _ xs = IMS.differenceWith go g xs+ where+ go :: Context' a b -> [b] -> Maybe (Context' a b)+ go (ps, l', ss) l = let !ps' = IM.insertWith addLists v l ps+ in Just (ps', l', ss)++clearSucc :: forall a b x . GraphRep a b -> Node -> IM.IntMap x -> GraphRep a b+clearSucc g v = IMS.differenceWith go g+ where+ go :: Context' a b -> x -> Maybe (Context' a b)+ go (ps, l, ss) _ = let !ss' = IM.delete v ss+ in Just (ps, l, ss')++clearPred :: forall a b x . GraphRep a b -> Node -> IM.IntMap x -> GraphRep a b+clearPred g v = IMS.differenceWith go g+ where+ go :: Context' a b -> x -> Maybe (Context' a b)+ go (ps, l, ss) _ = let !ps' = IM.delete v ps+ in Just (ps', l, ss)++{-----------------------------------------------------------------++Copyright (c) 1999-2008, Martin Erwig+ 2010, Ivan Lazar Miljenovic+ 2022, Norman Ramsey+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++1. Redistributions of source code must retain the above copyright notice,+ this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.++3. Neither the name of the author nor the names of its contributors may be+ used to endorse or promote products derived from this software without+ specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.++----------------------------------------------------------------}
compiler/GHC/Data/Graph/Ops.hs view
@@ -253,7 +253,7 @@ -- | Add a coalescence edge to the graph, creating nodes if required.--- It is considered adventageous to assign the same color to nodes in a coalesence.+-- It is considered advantageous to assign the same color to nodes in a coalescence. addCoalesce :: Uniquable k => (k, cls) -> (k, cls)@@ -491,7 +491,7 @@ = if elementOfUniqSet k (nodeCoalesce node) then node { nodeCoalesce = delOneFromUniqSet (nodeCoalesce node) k } else node -- panic "GHC.Data.Graph.Ops.freezeNode: edge to freeze wasn't in the coalesce set"- -- If the edge isn't actually in the coelesce set then just ignore it.+ -- If the edge isn't actually in the coalesce set then just ignore it. fm2 = nonDetStrictFoldUniqSet (adjustUFM_C (freezeEdge k)) fm1 -- It's OK to use a non-deterministic fold here because the@@ -609,7 +609,7 @@ $ nonDetEltsUFM $ graphMap graph , not $ null badNodes = pprPanic "GHC.Data.Graph.Ops.validateGraph"- ( text "Supposably colored graph has uncolored nodes."+ ( text "Supposedly colored graph has uncolored nodes." $$ text " uncolored nodes: " <> hcat (map (ppr . nodeId) badNodes) $$ doc ) @@ -633,7 +633,7 @@ $ nonDetEltsUniqSet $ nodeConflicts node -- See Note [Unique Determinism and code generation] - , neighbourColors <- catMaybes $ map nodeColor neighbors+ , neighbourColors <- mapMaybe nodeColor neighbors , elem color neighbourColors = False
+ compiler/GHC/Driver/Backpack.hs view
@@ -0,0 +1,944 @@++{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE NondecreasingIndentation #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-}+++-- | This is the driver for the 'ghc --backpack' mode, which+-- is a reimplementation of the "package manager" bits of+-- Backpack directly in GHC. The basic method of operation+-- is to compile packages and then directly insert them into+-- GHC's in memory database.+--+-- The compilation products of this mode aren't really suitable+-- for Cabal, because GHC makes up component IDs for the things+-- it builds and doesn't serialize out the database contents.+-- But it's still handy for constructing tests.++module GHC.Driver.Backpack (doBackpack) where++import GHC.Prelude++import GHC.Driver.Backend+-- In a separate module because it hooks into the parser.+import GHC.Driver.Backpack.Syntax+import GHC.Driver.Config.Finder (initFinderOpts)+import GHC.Driver.Config.Parser (initParserOpts)+import GHC.Driver.Config.Diagnostic+import GHC.Driver.Monad+import GHC.Driver.Session+import GHC.Driver.Ppr+import GHC.Driver.Main+import GHC.Driver.Make+import GHC.Driver.Env+import GHC.Driver.Errors+import GHC.Driver.Errors.Types++import GHC.Parser+import GHC.Parser.Header+import GHC.Parser.Lexer+import GHC.Parser.Annotation++import GHC.Rename.Names++import GHC hiding (Failed, Succeeded)+import GHC.Tc.Utils.Monad+import GHC.Iface.Recomp+import GHC.Builtin.Names++import GHC.Types.SrcLoc+import GHC.Types.SourceError+import GHC.Types.SourceFile+import GHC.Types.Unique.FM+import GHC.Types.Unique.DFM+import GHC.Types.Unique.DSet++import GHC.Utils.Outputable+import GHC.Utils.Fingerprint+import GHC.Utils.Misc+import GHC.Utils.Panic+import GHC.Utils.Error+import GHC.Utils.Logger++import GHC.Unit+import GHC.Unit.Env+import GHC.Unit.External+import GHC.Unit.Finder+import GHC.Unit.Module.Graph+import GHC.Unit.Module.ModSummary+import GHC.Unit.Home.ModInfo++import GHC.Linker.Types++import qualified GHC.LanguageExtensions as LangExt++import GHC.Data.Maybe+import GHC.Data.StringBuffer+import GHC.Data.FastString+import qualified GHC.Data.EnumSet as EnumSet+import qualified GHC.Data.ShortText as ST++import Data.List ( partition )+import System.Exit+import Control.Monad+import System.FilePath+import Data.Version++-- for the unification+import Data.IORef+import Data.Map (Map)+import qualified Data.Map as Map+import qualified Data.Set as Set++-- | Entry point to compile a Backpack file.+doBackpack :: [FilePath] -> Ghc ()+doBackpack [src_filename] = do+ -- Apply options from file to dflags+ dflags0 <- getDynFlags+ let dflags1 = dflags0+ let parser_opts1 = initParserOpts dflags1+ (p_warns, src_opts) <- liftIO $ getOptionsFromFile parser_opts1 src_filename+ (dflags, unhandled_flags, warns) <- liftIO $ parseDynamicFilePragma dflags1 src_opts+ modifySession (hscSetFlags dflags)+ logger <- getLogger -- Get the logger after having set the session flags,+ -- so that logger options are correctly set.+ -- Not doing so caused #20396.+ -- Cribbed from: preprocessFile / GHC.Driver.Pipeline+ liftIO $ checkProcessArgsResult unhandled_flags+ let print_config = initPrintConfig dflags+ liftIO $ printOrThrowDiagnostics logger print_config (initDiagOpts dflags) (GhcPsMessage <$> p_warns)+ liftIO $ handleFlagWarnings logger print_config (initDiagOpts dflags) warns+ -- TODO: Preprocessing not implemented++ buf <- liftIO $ hGetStringBuffer src_filename+ let loc = mkRealSrcLoc (mkFastString src_filename) 1 1 -- TODO: not great+ case unP parseBackpack (initParserState (initParserOpts dflags) buf loc) of+ PFailed pst -> throwErrors (GhcPsMessage <$> getPsErrorMessages pst)+ POk _ pkgname_bkp -> do+ -- OK, so we have an LHsUnit PackageName, but we want an+ -- LHsUnit HsComponentId. So let's rename it.+ hsc_env <- getSession+ let bkp = renameHsUnits (hsc_units hsc_env) (bkpPackageNameMap pkgname_bkp) pkgname_bkp+ initBkpM src_filename bkp $+ forM_ (zip [1..] bkp) $ \(i, lunit) -> do+ let comp_name = unLoc (hsunitName (unLoc lunit))+ msgTopPackage (i,length bkp) comp_name+ innerBkpM $ do+ let (cid, insts) = computeUnitId lunit+ if null insts+ then if cid == UnitId (fsLit "main")+ then compileExe lunit+ else compileUnit cid []+ else typecheckUnit cid insts+doBackpack _ =+ throwGhcException (CmdLineError "--backpack can only process a single file")++computeUnitId :: LHsUnit HsComponentId -> (UnitId, [(ModuleName, Module)])+computeUnitId (L _ unit) = (cid, [ (r, mkHoleModule r) | r <- reqs ])+ where+ cid = hsComponentId (unLoc (hsunitName unit))+ reqs = uniqDSetToList (unionManyUniqDSets (map (get_reqs . unLoc) (hsunitBody unit)))+ get_reqs (DeclD HsigFile (L _ modname) _) = unitUniqDSet modname+ get_reqs (DeclD HsSrcFile _ _) = emptyUniqDSet+ get_reqs (DeclD HsBootFile _ _) = emptyUniqDSet+ get_reqs (IncludeD (IncludeDecl (L _ hsuid) _ _)) =+ unitFreeModuleHoles (convertHsComponentId hsuid)++-- | Tiny enum for all types of Backpack operations we may do.+data SessionType+ -- | A compilation operation which will result in a+ -- runnable executable being produced.+ = ExeSession+ -- | A type-checking operation which produces only+ -- interface files, no object files.+ | TcSession+ -- | A compilation operation which produces both+ -- interface files and object files.+ | CompSession+ deriving (Eq)++-- | Create a temporary Session to do some sort of type checking or+-- compilation.+withBkpSession :: UnitId+ -> [(ModuleName, Module)]+ -> [(Unit, ModRenaming)]+ -> SessionType -- what kind of session are we doing+ -> BkpM a -- actual action to run+ -> BkpM a+withBkpSession cid insts deps session_type do_this = do+ dflags <- getDynFlags+ let cid_fs = unitFS cid+ is_primary = False+ uid_str = unpackFS (mkInstantiatedUnitHash cid insts)+ cid_str = unpackFS cid_fs+ -- There are multiple units in a single Backpack file, so we+ -- need to separate out the results in those cases. Right now,+ -- we follow this hierarchy:+ -- $outputdir/$compid --> typecheck results+ -- $outputdir/$compid/$unitid --> compile results+ key_base p | Just f <- p dflags = f+ | otherwise = "."+ sub_comp p | is_primary = p+ | otherwise = p </> cid_str+ outdir p | CompSession <- session_type+ -- Special case when package is definite+ , not (null insts) = sub_comp (key_base p) </> uid_str+ | otherwise = sub_comp (key_base p)++ mk_temp_env hsc_env =+ hscUpdateFlags (\dflags -> mk_temp_dflags (hsc_units hsc_env) dflags) hsc_env+ mk_temp_dflags unit_state dflags = dflags+ { backend = case session_type of+ TcSession -> noBackend+ _ -> backend dflags+ , ghcLink = case session_type of+ TcSession -> NoLink+ _ -> ghcLink dflags+ , homeUnitInstantiations_ = insts+ -- if we don't have any instantiation, don't+ -- fill `homeUnitInstanceOfId` as it makes no+ -- sense (we're not instantiating anything)+ , homeUnitInstanceOf_ = if null insts then Nothing else Just cid+ , homeUnitId_ = case session_type of+ TcSession -> newUnitId cid Nothing+ -- No hash passed if no instances+ _ | null insts -> newUnitId cid Nothing+ | otherwise -> newUnitId cid (Just (mkInstantiatedUnitHash cid insts))+++ -- If we're type-checking an indefinite package, we want to+ -- turn on interface writing. However, if the user also+ -- explicitly passed in `-fno-code`, we DON'T want to write+ -- interfaces unless the user also asked for `-fwrite-interface`.+ -- See Note [-fno-code mode]+ , generalFlags = case session_type of+ -- Make sure to write interfaces when we are type-checking+ -- indefinite packages.+ TcSession+ | backendSupportsInterfaceWriting $ backend dflags+ -> EnumSet.insert Opt_WriteInterface (generalFlags dflags)+ _ -> generalFlags dflags++ -- Setup all of the output directories according to our hierarchy+ , objectDir = Just (outdir objectDir)+ , hiDir = Just (outdir hiDir)+ , stubDir = Just (outdir stubDir)+ -- Unset output-file for non exe builds+ , outputFile_ = case session_type of+ ExeSession -> outputFile_ dflags+ _ -> Nothing+ , dynOutputFile_ = case session_type of+ ExeSession -> dynOutputFile_ dflags+ _ -> Nothing+ -- Clear the import path so we don't accidentally grab anything+ , importPaths = []+ -- Synthesize the flags+ , packageFlags = packageFlags dflags ++ map (\(uid0, rn) ->+ let uid = unwireUnit unit_state+ $ improveUnit unit_state+ $ renameHoleUnit unit_state (listToUFM insts) uid0+ in ExposePackage+ (showSDoc dflags+ (text "-unit-id" <+> ppr uid <+> ppr rn))+ (UnitIdArg uid) rn) deps+ }+ withTempSession mk_temp_env $ do+ dflags <- getSessionDynFlags+ -- pprTrace "flags" (ppr insts <> ppr deps) $ return ()+ setSessionDynFlags dflags -- calls initUnits+ do_this++withBkpExeSession :: [(Unit, ModRenaming)] -> BkpM a -> BkpM a+withBkpExeSession deps do_this =+ withBkpSession (UnitId (fsLit "main")) [] deps ExeSession do_this++getSource :: UnitId -> BkpM (LHsUnit HsComponentId)+getSource cid = do+ bkp_env <- getBkpEnv+ case Map.lookup cid (bkp_table bkp_env) of+ Nothing -> pprPanic "missing needed dependency" (ppr cid)+ Just lunit -> return lunit++typecheckUnit :: UnitId -> [(ModuleName, Module)] -> BkpM ()+typecheckUnit cid insts = do+ lunit <- getSource cid+ buildUnit TcSession cid insts lunit++compileUnit :: UnitId -> [(ModuleName, Module)] -> BkpM ()+compileUnit cid insts = do+ -- Let everyone know we're building this unit+ msgUnitId (mkVirtUnit cid insts)+ lunit <- getSource cid+ buildUnit CompSession cid insts lunit++-- | Compute the dependencies with instantiations of a syntactic+-- HsUnit; e.g., wherever you see @dependency p[A=<A>]@ in a+-- unit file, return the 'Unit' corresponding to @p[A=<A>]@.+-- The @include_sigs@ parameter controls whether or not we also+-- include @dependency signature@ declarations in this calculation.+--+-- Invariant: this NEVER returns UnitId.+hsunitDeps :: Bool {- include sigs -} -> HsUnit HsComponentId -> [(Unit, ModRenaming)]+hsunitDeps include_sigs unit = concatMap get_dep (hsunitBody unit)+ where+ get_dep (L _ (IncludeD (IncludeDecl (L _ hsuid) mb_lrn is_sig)))+ | include_sigs || not is_sig = [(convertHsComponentId hsuid, go mb_lrn)]+ | otherwise = []+ where+ go Nothing = ModRenaming True []+ go (Just lrns) = ModRenaming False (map convRn lrns)+ where+ convRn (L _ (Renaming (L _ from) Nothing)) = (from, from)+ convRn (L _ (Renaming (L _ from) (Just (L _ to)))) = (from, to)+ get_dep _ = []++buildUnit :: SessionType -> UnitId -> [(ModuleName, Module)] -> LHsUnit HsComponentId -> BkpM ()+buildUnit session cid insts lunit = do+ -- NB: include signature dependencies ONLY when typechecking.+ -- If we're compiling, it's not necessary to recursively+ -- compile a signature since it isn't going to produce+ -- any object files.+ let deps_w_rns = hsunitDeps (session == TcSession) (unLoc lunit)+ raw_deps = map fst deps_w_rns+ hsc_env <- getSession+ -- The compilation dependencies are just the appropriately filled+ -- in unit IDs which must be compiled before we can compile.+ let hsubst = listToUFM insts+ deps0 = map (renameHoleUnit (hsc_units hsc_env) hsubst) raw_deps++ -- Build dependencies OR make sure they make sense. BUT NOTE,+ -- we can only check the ones that are fully filled; the rest+ -- we have to defer until we've typechecked our local signature.+ -- TODO: work this into GHC.Driver.Make!!+ forM_ (zip [1..] deps0) $ \(i, dep) ->+ case session of+ TcSession -> return ()+ _ -> compileInclude (length deps0) (i, dep)++ -- IMPROVE IT+ let deps = map (improveUnit (hsc_units hsc_env)) deps0++ mb_old_eps <- case session of+ TcSession -> fmap Just getEpsGhc+ _ -> return Nothing++ conf <- withBkpSession cid insts deps_w_rns session $ do++ dflags <- getDynFlags+ mod_graph <- hsunitModuleGraph False (unLoc lunit)++ msg <- mkBackpackMsg+ ok <- load' noIfaceCache LoadAllTargets (Just msg) mod_graph+ when (failed ok) (liftIO $ exitWith (ExitFailure 1))++ let hi_dir = expectJust (panic "hiDir Backpack") $ hiDir dflags+ export_mod ms = (ms_mod_name ms, ms_mod ms)+ -- Export everything!+ mods = [ export_mod ms | ms <- mgModSummaries mod_graph+ , ms_hsc_src ms == HsSrcFile ]++ -- Compile relevant only+ hsc_env <- getSession+ let home_mod_infos = eltsUDFM (hsc_HPT hsc_env)+ linkables = map (expectJust "bkp link" . homeModInfoObject)+ . filter ((==HsSrcFile) . mi_hsc_src . hm_iface)+ $ home_mod_infos+ getOfiles LM{ linkableUnlinked = us } = map nameOfObject (filter isObject us)+ obj_files = concatMap getOfiles linkables+ state = hsc_units hsc_env++ let compat_fs = unitIdFS cid+ compat_pn = PackageName compat_fs+ unit_id = homeUnitId (hsc_home_unit hsc_env)++ return GenericUnitInfo {+ -- Stub data+ unitAbiHash = "",+ unitPackageId = PackageId compat_fs,+ unitPackageName = compat_pn,+ unitPackageVersion = makeVersion [],+ unitId = unit_id,+ unitComponentName = Nothing,+ unitInstanceOf = cid,+ unitInstantiations = insts,+ -- Slight inefficiency here haha+ unitExposedModules = map (\(m,n) -> (m,Just n)) mods,+ unitHiddenModules = [], -- TODO: doc only+ unitDepends = case session of+ -- Technically, we should state that we depend+ -- on all the indefinite libraries we used to+ -- typecheck this. However, this field isn't+ -- really used for anything, so we leave it+ -- blank for now.+ TcSession -> []+ _ -> map (toUnitId . unwireUnit state)+ $ deps ++ [ moduleUnit mod+ | (_, mod) <- insts+ , not (isHoleModule mod) ],+ unitAbiDepends = [],+ unitLinkerOptions = case session of+ TcSession -> []+ _ -> map ST.pack $ obj_files,+ unitImportDirs = [ ST.pack $ hi_dir ],+ unitIsExposed = False,+ unitIsIndefinite = case session of+ TcSession -> True+ _ -> False,+ -- nope+ unitLibraries = [],+ unitExtDepLibsSys = [],+ unitExtDepLibsGhc = [],+ unitLibraryDynDirs = [],+ unitLibraryDirs = [],+ unitExtDepFrameworks = [],+ unitExtDepFrameworkDirs = [],+ unitCcOptions = [],+ unitIncludes = [],+ unitIncludeDirs = [],+ unitHaddockInterfaces = [],+ unitHaddockHTMLs = [],+ unitIsTrusted = False+ }+++ addUnit conf+ case mb_old_eps of+ Just old_eps -> updateEpsGhc_ (const old_eps)+ _ -> return ()++compileExe :: LHsUnit HsComponentId -> BkpM ()+compileExe lunit = do+ msgUnitId mainUnit+ let deps_w_rns = hsunitDeps False (unLoc lunit)+ deps = map fst deps_w_rns+ -- no renaming necessary+ forM_ (zip [1..] deps) $ \(i, dep) ->+ compileInclude (length deps) (i, dep)+ withBkpExeSession deps_w_rns $ do+ mod_graph <- hsunitModuleGraph True (unLoc lunit)+ msg <- mkBackpackMsg+ ok <- load' noIfaceCache LoadAllTargets (Just msg) mod_graph+ when (failed ok) (liftIO $ exitWith (ExitFailure 1))++-- | Register a new virtual unit database containing a single unit+addUnit :: GhcMonad m => UnitInfo -> m ()+addUnit u = do+ hsc_env <- getSession+ logger <- getLogger+ let dflags0 = hsc_dflags hsc_env+ let old_unit_env = hsc_unit_env hsc_env+ newdbs <- case ue_unit_dbs old_unit_env of+ Nothing -> panic "addUnit: called too early"+ Just dbs ->+ let newdb = UnitDatabase+ { unitDatabasePath = "(in memory " ++ showSDoc dflags0 (ppr (unitId u)) ++ ")"+ , unitDatabaseUnits = [u]+ }+ in return (dbs ++ [newdb]) -- added at the end because ordering matters+ (dbs,unit_state,home_unit,mconstants) <- liftIO $ initUnits logger dflags0 (Just newdbs) (hsc_all_home_unit_ids hsc_env)++ -- update platform constants+ dflags <- liftIO $ updatePlatformConstants dflags0 mconstants++ let unit_env = ue_setUnits unit_state $ ue_setUnitDbs (Just dbs) $ UnitEnv+ { ue_platform = targetPlatform dflags+ , ue_namever = ghcNameVersion dflags+ , ue_current_unit = homeUnitId home_unit++ , ue_home_unit_graph =+ unitEnv_singleton+ (homeUnitId home_unit)+ (mkHomeUnitEnv dflags (ue_hpt old_unit_env) (Just home_unit))+ , ue_eps = ue_eps old_unit_env+ }+ setSession $ hscSetFlags dflags $ hsc_env { hsc_unit_env = unit_env }++compileInclude :: Int -> (Int, Unit) -> BkpM ()+compileInclude n (i, uid) = do+ hsc_env <- getSession+ let pkgs = hsc_units hsc_env+ msgInclude (i, n) uid+ -- Check if we've compiled it already+ case uid of+ HoleUnit -> return ()+ RealUnit _ -> return ()+ VirtUnit i -> case lookupUnit pkgs uid of+ Nothing -> innerBkpM $ compileUnit (instUnitInstanceOf i) (instUnitInsts i)+ Just _ -> return ()++-- ----------------------------------------------------------------------------+-- Backpack monad++-- | Backpack monad is a 'GhcMonad' which also maintains a little extra state+-- beyond the 'Session', c.f. 'BkpEnv'.+type BkpM = IOEnv BkpEnv++-- | Backpack environment. NB: this has a 'Session' and not an 'HscEnv',+-- because we are going to update the 'HscEnv' as we go.+data BkpEnv+ = BkpEnv {+ -- | The session+ bkp_session :: Session,+ -- | The filename of the bkp file we're compiling+ bkp_filename :: FilePath,+ -- | Table of source units which we know how to compile+ bkp_table :: Map UnitId (LHsUnit HsComponentId),+ -- | When a package we are compiling includes another package+ -- which has not been compiled, we bump the level and compile+ -- that.+ bkp_level :: Int+ }++-- Blah, to get rid of the default instance for IOEnv+-- TODO: just make a proper new monad for BkpM, rather than use IOEnv+instance {-# OVERLAPPING #-} HasDynFlags BkpM where+ getDynFlags = fmap hsc_dflags getSession+instance {-# OVERLAPPING #-} HasLogger BkpM where+ getLogger = fmap hsc_logger getSession+++instance GhcMonad BkpM where+ getSession = do+ Session s <- fmap bkp_session getEnv+ readMutVar s+ setSession hsc_env = do+ Session s <- fmap bkp_session getEnv+ writeMutVar s hsc_env++-- | Get the current 'BkpEnv'.+getBkpEnv :: BkpM BkpEnv+getBkpEnv = getEnv++-- | Get the nesting level, when recursively compiling modules.+getBkpLevel :: BkpM Int+getBkpLevel = bkp_level `fmap` getBkpEnv++-- | Run a 'BkpM' computation, with the nesting level bumped one.+innerBkpM :: BkpM a -> BkpM a+innerBkpM do_this =+ -- NB: withTempSession mutates, so we don't have to worry+ -- about bkp_session being stale.+ updEnv (\env -> env { bkp_level = bkp_level env + 1 }) do_this++-- | Update the EPS from a 'GhcMonad'. TODO move to appropriate library spot.+updateEpsGhc_ :: GhcMonad m => (ExternalPackageState -> ExternalPackageState) -> m ()+updateEpsGhc_ f = do+ hsc_env <- getSession+ liftIO $ atomicModifyIORef' (euc_eps (ue_eps (hsc_unit_env hsc_env))) (\x -> (f x, ()))++-- | Get the EPS from a 'GhcMonad'.+getEpsGhc :: GhcMonad m => m ExternalPackageState+getEpsGhc = do+ hsc_env <- getSession+ liftIO $ hscEPS hsc_env++-- | Run 'BkpM' in 'Ghc'.+initBkpM :: FilePath -> [LHsUnit HsComponentId] -> BkpM a -> Ghc a+initBkpM file bkp m =+ reifyGhc $ \session -> do+ let env = BkpEnv {+ bkp_session = session,+ bkp_table = Map.fromList [(hsComponentId (unLoc (hsunitName (unLoc u))), u) | u <- bkp],+ bkp_filename = file,+ bkp_level = 0+ }+ runIOEnv env m++-- ----------------------------------------------------------------------------+-- Messaging++-- | Print a compilation progress message, but with indentation according+-- to @level@ (for nested compilation).+backpackProgressMsg :: Int -> Logger -> SDoc -> IO ()+backpackProgressMsg level logger msg =+ compilationProgressMsg logger $ text (replicate (level * 2) ' ') -- TODO: use GHC.Utils.Ppr.RStr+ <> msg++-- | Creates a 'Messager' for Backpack compilation; this is basically+-- a carbon copy of 'batchMsg' but calling 'backpackProgressMsg', which+-- handles indentation.+mkBackpackMsg :: BkpM Messager+mkBackpackMsg = do+ level <- getBkpLevel+ return $ \hsc_env mod_index recomp node ->+ let dflags = hsc_dflags hsc_env+ logger = hsc_logger hsc_env+ state = hsc_units hsc_env+ showMsg msg reason =+ backpackProgressMsg level logger $ pprWithUnitState state $+ showModuleIndex mod_index <>+ msg <> showModMsg dflags (recompileRequired recomp) node+ <> reason+ in case node of+ InstantiationNode _ _ ->+ case recomp of+ UpToDate+ | verbosity (hsc_dflags hsc_env) >= 2 -> showMsg (text "Skipping ") empty+ | otherwise -> return ()+ NeedsRecompile reason0 -> showMsg (text "Instantiating ") $ case reason0 of+ MustCompile -> empty+ RecompBecause reason -> text " [" <> pprWithUnitState state (ppr reason) <> text "]"+ ModuleNode _ _ ->+ case recomp of+ UpToDate+ | verbosity (hsc_dflags hsc_env) >= 2 -> showMsg (text "Skipping ") empty+ | otherwise -> return ()+ NeedsRecompile reason0 -> showMsg (text "Compiling ") $ case reason0 of+ MustCompile -> empty+ RecompBecause reason -> text " [" <> pprWithUnitState state (ppr reason) <> text "]"+ LinkNode _ _ -> showMsg (text "Linking ") empty++-- | 'PprStyle' for Backpack messages; here we usually want the module to+-- be qualified (so we can tell how it was instantiated.) But we try not+-- to qualify packages so we can use simple names for them.+backpackStyle :: PprStyle+backpackStyle =+ mkUserStyle+ (QueryQualify neverQualifyNames+ alwaysQualifyModules+ neverQualifyPackages+ alwaysPrintPromTick)+ AllTheWay++-- | Message when we initially process a Backpack unit.+msgTopPackage :: (Int,Int) -> HsComponentId -> BkpM ()+msgTopPackage (i,n) (HsComponentId (PackageName fs_pn) _) = do+ logger <- getLogger+ level <- getBkpLevel+ liftIO . backpackProgressMsg level logger+ $ showModuleIndex (i, n) <> text "Processing " <> ftext fs_pn++-- | Message when we instantiate a Backpack unit.+msgUnitId :: Unit -> BkpM ()+msgUnitId pk = do+ logger <- getLogger+ hsc_env <- getSession+ level <- getBkpLevel+ let state = hsc_units hsc_env+ liftIO . backpackProgressMsg level logger+ $ pprWithUnitState state+ $ text "Instantiating "+ <> withPprStyle backpackStyle (ppr pk)++-- | Message when we include a Backpack unit.+msgInclude :: (Int,Int) -> Unit -> BkpM ()+msgInclude (i,n) uid = do+ logger <- getLogger+ hsc_env <- getSession+ level <- getBkpLevel+ let state = hsc_units hsc_env+ liftIO . backpackProgressMsg level logger+ $ pprWithUnitState state+ $ showModuleIndex (i, n) <> text "Including "+ <> withPprStyle backpackStyle (ppr uid)++-- ----------------------------------------------------------------------------+-- Conversion from PackageName to HsComponentId++type PackageNameMap a = UniqFM PackageName a++-- For now, something really simple, since we're not actually going+-- to use this for anything+unitDefines :: LHsUnit PackageName -> (PackageName, HsComponentId)+unitDefines (L _ HsUnit{ hsunitName = L _ pn@(PackageName fs) })+ = (pn, HsComponentId pn (UnitId fs))++bkpPackageNameMap :: [LHsUnit PackageName] -> PackageNameMap HsComponentId+bkpPackageNameMap units = listToUFM (map unitDefines units)++renameHsUnits :: UnitState -> PackageNameMap HsComponentId -> [LHsUnit PackageName] -> [LHsUnit HsComponentId]+renameHsUnits pkgstate m units = map (fmap renameHsUnit) units+ where++ renamePackageName :: PackageName -> HsComponentId+ renamePackageName pn =+ case lookupUFM m pn of+ Nothing ->+ case lookupPackageName pkgstate pn of+ Nothing -> error "no package name"+ Just cid -> HsComponentId pn cid+ Just hscid -> hscid++ renameHsUnit :: HsUnit PackageName -> HsUnit HsComponentId+ renameHsUnit u =+ HsUnit {+ hsunitName = fmap renamePackageName (hsunitName u),+ hsunitBody = map (fmap renameHsUnitDecl) (hsunitBody u)+ }++ renameHsUnitDecl :: HsUnitDecl PackageName -> HsUnitDecl HsComponentId+ renameHsUnitDecl (DeclD a b c) = DeclD a b c+ renameHsUnitDecl (IncludeD idecl) =+ IncludeD IncludeDecl {+ idUnitId = fmap renameHsUnitId (idUnitId idecl),+ idModRenaming = idModRenaming idecl,+ idSignatureInclude = idSignatureInclude idecl+ }++ renameHsUnitId :: HsUnitId PackageName -> HsUnitId HsComponentId+ renameHsUnitId (HsUnitId ln subst)+ = HsUnitId (fmap renamePackageName ln) (map (fmap renameHsModuleSubst) subst)++ renameHsModuleSubst :: HsModuleSubst PackageName -> HsModuleSubst HsComponentId+ renameHsModuleSubst (lk, lm)+ = (lk, fmap renameHsModuleId lm)++ renameHsModuleId :: HsModuleId PackageName -> HsModuleId HsComponentId+ renameHsModuleId (HsModuleVar lm) = HsModuleVar lm+ renameHsModuleId (HsModuleId luid lm) = HsModuleId (fmap renameHsUnitId luid) lm++convertHsComponentId :: HsUnitId HsComponentId -> Unit+convertHsComponentId (HsUnitId (L _ hscid) subst)+ = mkVirtUnit (hsComponentId hscid) (map (convertHsModuleSubst . unLoc) subst)++convertHsModuleSubst :: HsModuleSubst HsComponentId -> (ModuleName, Module)+convertHsModuleSubst (L _ modname, L _ m) = (modname, convertHsModuleId m)++convertHsModuleId :: HsModuleId HsComponentId -> Module+convertHsModuleId (HsModuleVar (L _ modname)) = mkHoleModule modname+convertHsModuleId (HsModuleId (L _ hsuid) (L _ modname)) = mkModule (convertHsComponentId hsuid) modname++++{-+************************************************************************+* *+ Module graph construction+* *+************************************************************************+-}++-- | This is our version of GHC.Driver.Make.downsweep, but with a few modifications:+--+-- 1. Every module is required to be mentioned, so we don't do any funny+-- business with targets or recursively grabbing dependencies. (We+-- could support this in principle).+-- 2. We support inline modules, whose summary we have to synthesize ourself.+--+-- We don't bother trying to support GHC.Driver.Make for now, it's more trouble+-- than it's worth for inline modules.+hsunitModuleGraph :: Bool -> HsUnit HsComponentId -> BkpM ModuleGraph+hsunitModuleGraph do_link unit = do+ hsc_env <- getSession++ let decls = hsunitBody unit+ pn = hsPackageName (unLoc (hsunitName unit))+ home_unit = hsc_home_unit hsc_env++ sig_keys = flip map (homeUnitInstantiations home_unit) $ \(mod_name, _) -> NodeKey_Module (ModNodeKeyWithUid (GWIB mod_name NotBoot) (homeUnitId home_unit))+ keys = [NodeKey_Module (ModNodeKeyWithUid gwib (homeUnitId home_unit)) | (DeclD hsc_src lmodname _) <- map unLoc decls, let gwib = GWIB (unLoc lmodname) (hscSourceToIsBoot hsc_src) ]++ -- 1. Create a HsSrcFile/HsigFile summary for every+ -- explicitly mentioned module/signature.+ let get_decl (L _ (DeclD hsc_src lmodname hsmod)) =+ Just <$> summariseDecl pn hsc_src lmodname hsmod (keys ++ sig_keys)+ get_decl _ = return Nothing+ nodes <- mapMaybeM get_decl decls++ -- 2. For each hole which does not already have an hsig file,+ -- create an "empty" hsig file to induce compilation for the+ -- requirement.+ let hsig_set = Set.fromList+ [ ms_mod_name ms+ | ModuleNode _ ms <- nodes+ , ms_hsc_src ms == HsigFile+ ]+ req_nodes <- fmap catMaybes . forM (homeUnitInstantiations home_unit) $ \(mod_name, _) ->+ if Set.member mod_name hsig_set+ then return Nothing+ else fmap Just $ summariseRequirement pn mod_name++ let graph_nodes = nodes ++ req_nodes ++ (instantiationNodes (homeUnitId $ hsc_home_unit hsc_env) (hsc_units hsc_env))+ key_nodes = map mkNodeKey graph_nodes+ all_nodes = graph_nodes ++ [LinkNode key_nodes (homeUnitId $ hsc_home_unit hsc_env) | do_link]+ -- This error message is not very good but .bkp mode is just for testing so+ -- better to be direct rather than pretty.+ when+ (length key_nodes /= length (ordNub key_nodes))+ (pprPanic "Duplicate nodes keys in backpack file" (ppr key_nodes))++ -- 3. Return the kaboodle+ return $ mkModuleGraph $ all_nodes+++summariseRequirement :: PackageName -> ModuleName -> BkpM ModuleGraphNode+summariseRequirement pn mod_name = do+ hsc_env <- getSession+ let dflags = hsc_dflags hsc_env+ let home_unit = hsc_home_unit hsc_env+ let fopts = initFinderOpts dflags++ let PackageName pn_fs = pn+ let location = mkHomeModLocation2 fopts mod_name+ (unpackFS pn_fs </> moduleNameSlashes mod_name) "hsig"++ env <- getBkpEnv+ src_hash <- liftIO $ getFileHash (bkp_filename env)+ hi_timestamp <- liftIO $ modificationTimeIfExists (ml_hi_file location)+ hie_timestamp <- liftIO $ modificationTimeIfExists (ml_hie_file location)+ let loc = srcLocSpan (mkSrcLoc (mkFastString (bkp_filename env)) 1 1)++ let fc = hsc_FC hsc_env+ mod <- liftIO $ addHomeModuleToFinder fc home_unit mod_name location++ extra_sig_imports <- liftIO $ findExtraSigImports hsc_env HsigFile mod_name++ let ms = ModSummary {+ ms_mod = mod,+ ms_hsc_src = HsigFile,+ ms_location = location,+ ms_hs_hash = src_hash,+ ms_obj_date = Nothing,+ ms_dyn_obj_date = Nothing,+ ms_iface_date = hi_timestamp,+ ms_hie_date = hie_timestamp,+ ms_srcimps = [],+ ms_textual_imps = ((,) NoPkgQual . noLoc) <$> extra_sig_imports,+ ms_ghc_prim_import = False,+ ms_parsed_mod = Just (HsParsedModule {+ hpm_module = L loc (HsModule {+ hsmodExt = XModulePs {+ hsmodAnn = noAnn,+ hsmodLayout = NoLayoutInfo,+ hsmodDeprecMessage = Nothing,+ hsmodHaddockModHeader = Nothing+ },+ hsmodName = Just (L (noAnnSrcSpan loc) mod_name),+ hsmodExports = Nothing,+ hsmodImports = [],+ hsmodDecls = []+ }),+ hpm_src_files = []+ }),+ ms_hspp_file = "", -- none, it came inline+ ms_hspp_opts = dflags,+ ms_hspp_buf = Nothing+ }+ let nodes = [NodeKey_Module (ModNodeKeyWithUid (GWIB mn NotBoot) (homeUnitId home_unit)) | mn <- extra_sig_imports ]+ return (ModuleNode nodes ms)++summariseDecl :: PackageName+ -> HscSource+ -> Located ModuleName+ -> Located (HsModule GhcPs)+ -> [NodeKey]+ -> BkpM ModuleGraphNode+summariseDecl pn hsc_src (L _ modname) hsmod home_keys = hsModuleToModSummary home_keys pn hsc_src modname hsmod++-- | Up until now, GHC has assumed a single compilation target per source file.+-- Backpack files with inline modules break this model, since a single file+-- may generate multiple output files. How do we decide to name these files?+-- Should there only be one output file? This function our current heuristic,+-- which is we make a "fake" module and use that.+hsModuleToModSummary :: [NodeKey]+ -> PackageName+ -> HscSource+ -> ModuleName+ -> Located (HsModule GhcPs)+ -> BkpM ModuleGraphNode+hsModuleToModSummary home_keys pn hsc_src modname+ hsmod = do+ let imps = hsmodImports (unLoc hsmod)+ loc = getLoc hsmod+ hsc_env <- getSession+ -- Sort of the same deal as in GHC.Driver.Pipeline's getLocation+ -- Use the PACKAGE NAME to find the location+ let PackageName unit_fs = pn+ dflags = hsc_dflags hsc_env+ fopts = initFinderOpts dflags+ -- Unfortunately, we have to define a "fake" location in+ -- order to appease the various code which uses the file+ -- name to figure out where to put, e.g. object files.+ -- To add insult to injury, we don't even actually use+ -- these filenames to figure out where the hi files go.+ -- A travesty!+ let location0 = mkHomeModLocation2 fopts modname+ (unpackFS unit_fs </>+ moduleNameSlashes modname)+ (case hsc_src of+ HsigFile -> "hsig"+ HsBootFile -> "hs-boot"+ HsSrcFile -> "hs")+ -- DANGEROUS: bootifying can POISON the module finder cache+ let location = case hsc_src of+ HsBootFile -> addBootSuffixLocnOut location0+ _ -> location0+ -- This duplicates a pile of logic in GHC.Driver.Make+ hi_timestamp <- liftIO $ modificationTimeIfExists (ml_hi_file location)+ hie_timestamp <- liftIO $ modificationTimeIfExists (ml_hie_file location)++ -- Also copied from 'getImports'+ let (src_idecls, ord_idecls) = partition ((== IsBoot) . ideclSource . unLoc) imps++ -- GHC.Prim doesn't exist physically, so don't go looking for it.+ (ordinary_imps, ghc_prim_import)+ = partition ((/= moduleName gHC_PRIM) . unLoc . ideclName . unLoc)+ ord_idecls++ implicit_prelude = xopt LangExt.ImplicitPrelude dflags+ implicit_imports = mkPrelImports modname loc+ implicit_prelude imps++ rn_pkg_qual = renameRawPkgQual (hsc_unit_env hsc_env) modname+ convImport (L _ i) = (rn_pkg_qual (ideclPkgQual i), reLoc $ ideclName i)++ extra_sig_imports <- liftIO $ findExtraSigImports hsc_env hsc_src modname++ let normal_imports = map convImport (implicit_imports ++ ordinary_imps)+ (implicit_sigs, inst_deps) <- liftIO $ implicitRequirementsShallow hsc_env normal_imports++ -- So that Finder can find it, even though it doesn't exist...+ this_mod <- liftIO $ do+ let home_unit = hsc_home_unit hsc_env+ let fc = hsc_FC hsc_env+ addHomeModuleToFinder fc home_unit modname location+ let ms = ModSummary {+ ms_mod = this_mod,+ ms_hsc_src = hsc_src,+ ms_location = location,+ ms_hspp_file = (case hiDir dflags of+ Nothing -> ""+ Just d -> d) </> ".." </> moduleNameSlashes modname <.> "hi",+ ms_hspp_opts = dflags,+ ms_hspp_buf = Nothing,+ ms_srcimps = map convImport src_idecls,+ ms_ghc_prim_import = not (null ghc_prim_import),+ ms_textual_imps = normal_imports+ -- We have to do something special here:+ -- due to merging, requirements may end up with+ -- extra imports+ ++ ((,) NoPkgQual . noLoc <$> extra_sig_imports)+ ++ ((,) NoPkgQual . noLoc <$> implicit_sigs),+ -- This is our hack to get the parse tree to the right spot+ ms_parsed_mod = Just (HsParsedModule {+ hpm_module = hsmod,+ hpm_src_files = [] -- TODO if we preprocessed it+ }),+ -- Source hash = fingerprint0, so the recompilation tests do not recompile+ -- too much. In future, if necessary then could get the hash by just hashing the+ -- relevant part of the .bkp file.+ ms_hs_hash = fingerprint0,+ ms_obj_date = Nothing, -- TODO do this, but problem: hi_timestamp is BOGUS+ ms_dyn_obj_date = Nothing, -- TODO do this, but problem: hi_timestamp is BOGUS+ ms_iface_date = hi_timestamp,+ ms_hie_date = hie_timestamp+ }++ -- Now, what are the dependencies.+ let inst_nodes = map NodeKey_Unit inst_deps+ mod_nodes =+ -- hs-boot edge+ [k | k <- [NodeKey_Module (ModNodeKeyWithUid (GWIB (ms_mod_name ms) IsBoot) (moduleUnitId this_mod))], NotBoot == isBootSummary ms, k `elem` home_keys ] +++ -- Normal edges+ [k | (_, mnwib) <- msDeps ms, let k = NodeKey_Module (ModNodeKeyWithUid (fmap unLoc mnwib) (moduleUnitId this_mod)), k `elem` home_keys]+++ return (ModuleNode (mod_nodes ++ inst_nodes) ms)++-- | Create a new, externally provided hashed unit id from+-- a hash.+newUnitId :: UnitId -> Maybe FastString -> UnitId+newUnitId uid mhash = case mhash of+ Nothing -> uid+ Just hash -> UnitId (concatFS [unitIdFS uid, fsLit "+", hash])
compiler/GHC/Driver/CodeOutput.hs view
@@ -17,6 +17,7 @@ import GHC.Prelude import GHC.Platform import GHC.ForeignSrcLang+import GHC.Data.FastString import GHC.CmmToAsm ( nativeCodeGen ) import GHC.CmmToLlvm ( llvmCodeGen )@@ -27,9 +28,10 @@ import GHC.Cmm.CLabel import GHC.Driver.Session-import GHC.Driver.Config.Finder (initFinderOpts)-import GHC.Driver.Config.CmmToAsm (initNCGConfig)-import GHC.Driver.Config.CmmToLlvm (initLlvmCgConfig)+import GHC.Driver.Config.Finder ( initFinderOpts )+import GHC.Driver.Config.CmmToAsm ( initNCGConfig )+import GHC.Driver.Config.CmmToLlvm ( initLlvmCgConfig )+import GHC.Driver.LlvmConfigCache (LlvmConfigCache) import GHC.Driver.Ppr import GHC.Driver.Backend @@ -42,10 +44,10 @@ import GHC.Utils.Error import GHC.Utils.Outputable-import GHC.Utils.Panic import GHC.Utils.Logger-import GHC.Utils.Exception (bracket)+import GHC.Utils.Exception ( bracket ) import GHC.Utils.Ppr (Mode(..))+import GHC.Utils.Panic.Plain ( pgmError ) import GHC.Unit import GHC.Unit.Finder ( mkStubPaths )@@ -73,6 +75,7 @@ :: forall a. Logger -> TmpFs+ -> LlvmConfigCache -> DynFlags -> UnitState -> Module@@ -87,7 +90,7 @@ (Bool{-stub_h_exists-}, Maybe FilePath{-stub_c_exists-}), [(ForeignSrcLang, FilePath)]{-foreign_fps-}, a)-codeOutput logger tmpfs dflags unit_state this_mod filenm location genForeignStubs foreign_fps pkg_deps+codeOutput logger tmpfs llvm_config dflags unit_state this_mod filenm location genForeignStubs foreign_fps pkg_deps cmm_stream = do {@@ -118,13 +121,12 @@ ; emitInitializerDecls this_mod stubs ; return (stubs, a) } - ; (stubs, a) <- case backend dflags of- NCG -> outputAsm logger dflags this_mod location filenm- final_stream- ViaC -> outputC logger dflags filenm final_stream pkg_deps- LLVM -> outputLlvm logger dflags filenm final_stream- Interpreter -> panic "codeOutput: Interpreter"- NoBackend -> panic "codeOutput: NoBackend"+ ; (stubs, a) <- case backendCodeOutput (backend dflags) of+ NcgCodeOutput -> outputAsm logger dflags this_mod location filenm+ final_stream+ ViaCCodeOutput -> outputC logger dflags filenm final_stream pkg_deps+ LlvmCodeOutput -> outputLlvm logger llvm_config dflags filenm final_stream+ JSCodeOutput -> outputJS logger llvm_config dflags filenm final_stream ; stubs_exist <- outputForeignStubs logger tmpfs dflags unit_state this_mod location stubs ; return (filenm, stubs_exist, foreign_fps, a) }@@ -174,7 +176,7 @@ "C backend output" FormatC doc- let ctx = initSDocContext dflags (PprCode CStyle)+ let ctx = initSDocContext dflags PprCode printSDocLn ctx LeftMode h doc Stream.consume cmm_stream id writeC @@ -199,7 +201,7 @@ let ncg_config = initNCGConfig dflags this_mod {-# SCC "OutputAsm" #-} doOutput filenm $ \h -> {-# SCC "NativeCodeGen" #-}- nativeCodeGen logger ncg_config location h ncg_uniqs cmm_stream+ nativeCodeGen logger (toolSettings dflags) ncg_config location h ncg_uniqs cmm_stream {- ************************************************************************@@ -209,9 +211,9 @@ ************************************************************************ -} -outputLlvm :: Logger -> DynFlags -> FilePath -> Stream IO RawCmmGroup a -> IO a-outputLlvm logger dflags filenm cmm_stream = do- lcg_config <- initLlvmCgConfig logger dflags+outputLlvm :: Logger -> LlvmConfigCache -> DynFlags -> FilePath -> Stream IO RawCmmGroup a -> IO a+outputLlvm logger llvm_config dflags filenm cmm_stream = do+ lcg_config <- initLlvmCgConfig logger llvm_config dflags {-# SCC "llvm_output" #-} doOutput filenm $ \f -> {-# SCC "llvm_CodeGen" #-} llvmCodeGen logger lcg_config f cmm_stream@@ -219,6 +221,18 @@ {- ************************************************************************ * *+\subsection{JavaScript}+* *+************************************************************************+-}+outputJS :: Logger -> LlvmConfigCache -> DynFlags -> FilePath -> Stream IO RawCmmGroup a -> IO a+outputJS _ _ _ _ _ = pgmError $ "codeOutput: Hit JavaScript case. We should never reach here!"+ ++ "\nThe JS backend should shortcircuit to StgToJS after Stg."+ ++ "\nIf you reached this point then you've somehow made it to Cmm!"++{-+************************************************************************+* * \subsection{Foreign import/export} * * ************************************************************************@@ -254,11 +268,11 @@ ForeignStubs (CHeader h_code) (CStub c_code _ _) -> do let- stub_c_output_d = pprCode CStyle c_code+ stub_c_output_d = pprCode c_code stub_c_output_w = showSDoc dflags stub_c_output_d -- Header file protos for "foreign export"ed functions.- stub_h_output_d = pprCode CStyle h_code+ stub_h_output_d = pprCode h_code stub_h_output_w = showSDoc dflags stub_h_output_d createDirectoryIfMissing True (takeDirectory stub_h)@@ -281,7 +295,7 @@ -- wrapper code mentions the ffi_arg type, which comes from ffi.h ffi_includes- | platformMisc_libFFI $ platformMisc dflags = "#include \"rts/ghc_ffi.h\"\n"+ | platformMisc_libFFI $ platformMisc dflags = "#include <ffi.h>\n" | otherwise = "" stub_h_file_exists@@ -311,8 +325,7 @@ cplusplus_ftr = "#if defined(__cplusplus)\n}\n#endif\n" --- Don't use doOutput for dumping the f. export stubs--- since it is more than likely that the stubs file will+-- It is more than likely that the stubs file will -- turn out to be empty, in which case no file should be created. outputForeignStubs_help :: FilePath -> String -> String -> String -> IO Bool outputForeignStubs_help _fname "" _header _footer = return False@@ -332,7 +345,8 @@ = {-# SCC profilingInitCode #-} initializerCStub platform fn_name decls body where- fn_name = mkInitializerStubLabel this_mod "prof_init"+ pdocC = pprCLabel platform+ fn_name = mkInitializerStubLabel this_mod (fsLit "prof_init") decls = vcat $ map emit_cc_decl local_CCs ++ map emit_ccs_decl singleton_CCSs@@ -344,22 +358,22 @@ ] emit_cc_decl cc = text "extern CostCentre" <+> cc_lbl <> text "[];"- where cc_lbl = pdoc platform (mkCCLabel cc)+ where cc_lbl = pdocC (mkCCLabel cc) local_cc_list_label = text "local_cc_" <> ppr this_mod emit_cc_list ccs = text "static CostCentre *" <> local_cc_list_label <> text "[] ="- <+> braces (vcat $ [ pdoc platform (mkCCLabel cc) <> comma+ <+> braces (vcat $ [ pdocC (mkCCLabel cc) <> comma | cc <- ccs ] ++ [text "NULL"]) <> semi emit_ccs_decl ccs = text "extern CostCentreStack" <+> ccs_lbl <> text "[];"- where ccs_lbl = pdoc platform (mkCCSLabel ccs)+ where ccs_lbl = pdocC (mkCCSLabel ccs) singleton_cc_list_label = text "singleton_cc_" <> ppr this_mod emit_ccs_list ccs = text "static CostCentreStack *" <> singleton_cc_list_label <> text "[] ="- <+> braces (vcat $ [ pdoc platform (mkCCSLabel cc) <> comma+ <+> braces (vcat $ [ pdocC (mkCCSLabel cc) <> comma | cc <- ccs ] ++ [text "NULL"]) <> semi@@ -375,12 +389,11 @@ | not do_info_table = mempty | otherwise = initializerCStub platform fn_nm ipe_buffer_decl body where- fn_nm = mkInitializerStubLabel this_mod "ip_init"+ fn_nm = mkInitializerStubLabel this_mod (fsLit "ip_init") body = text "registerInfoProvList" <> parens (text "&" <> ipe_buffer_label) <> semi - ipe_buffer_label = pprCLabel platform CStyle (mkIPELabel this_mod)+ ipe_buffer_label = pprCLabel platform (mkIPELabel this_mod) ipe_buffer_decl = text "extern IpeBufferListNode" <+> ipe_buffer_label <> text ";"-
compiler/GHC/Driver/Config/Cmm.hs view
@@ -3,7 +3,6 @@ ) where import GHC.Cmm.Config-import GHC.Cmm.Switch (backendSupportsSwitch) import GHC.Driver.Session import GHC.Driver.Backend@@ -19,10 +18,12 @@ , cmmDoLinting = gopt Opt_DoCmmLinting dflags , cmmOptElimCommonBlks = gopt Opt_CmmElimCommonBlocks dflags , cmmOptSink = gopt Opt_CmmSink dflags+ , cmmOptThreadSanitizer = gopt Opt_CmmThreadSanitizer dflags , cmmGenStackUnwindInstr = debugLevel dflags > 0 , cmmExternalDynamicRefs = gopt Opt_ExternalDynamicRefs dflags- , cmmDoCmmSwitchPlans = not . backendSupportsSwitch . backend $ dflags- , cmmSplitProcPoints = (backend dflags /= NCG)+ , cmmDoCmmSwitchPlans = not (backendHasNativeSwitch (backend dflags))+ || platformArch platform == ArchWasm32+ , cmmSplitProcPoints = not (backendSupportsUnsplitProcPoints (backend dflags)) || not (platformTablesNextToCode platform) || usingInconsistentPicReg }
+ compiler/GHC/Driver/Config/Cmm/Parser.hs view
@@ -0,0 +1,25 @@+module GHC.Driver.Config.Cmm.Parser+ ( initCmmParserConfig+ ) where++import GHC.Cmm.Parser.Config++import GHC.Driver.Config.Parser+import GHC.Driver.Config.StgToCmm+import GHC.Driver.Session++import GHC.Utils.Panic++initPDConfig :: DynFlags -> PDConfig+initPDConfig dflags = PDConfig+ { pdProfile = targetProfile dflags+ , pdSanitizeAlignment = gopt Opt_AlignmentSanitisation dflags+ }++initCmmParserConfig :: DynFlags -> CmmParserConfig+initCmmParserConfig dflags = CmmParserConfig+ { cmmpParserOpts = initParserOpts dflags+ , cmmpPDConfig = initPDConfig dflags+ , cmmpStgToCmmConfig = initStgToCmmConfig dflags (panic "initCmmParserConfig: no module")+ }+
compiler/GHC/Driver/Config/CmmToAsm.hs view
@@ -18,7 +18,7 @@ initNCGConfig dflags this_mod = NCGConfig { ncgPlatform = targetPlatform dflags , ncgThisModule = this_mod- , ncgAsmContext = initSDocContext dflags (PprCode AsmStyle)+ , ncgAsmContext = initSDocContext dflags PprCode , ncgProcAlignment = cmmProcAlignment dflags , ncgExternalDynamicRefs = gopt Opt_ExternalDynamicRefs dflags , ncgPIC = positionIndependent dflags
compiler/GHC/Driver/Config/CmmToLlvm.hs view
@@ -1,22 +1,26 @@ module GHC.Driver.Config.CmmToLlvm ( initLlvmCgConfig- ) where+ )+where import GHC.Prelude import GHC.Driver.Session+import GHC.Driver.LlvmConfigCache import GHC.Platform import GHC.CmmToLlvm.Config import GHC.SysTools.Tasks+ import GHC.Utils.Outputable import GHC.Utils.Logger -- | Initialize the Llvm code generator configuration from DynFlags-initLlvmCgConfig :: Logger -> DynFlags -> IO LlvmCgConfig-initLlvmCgConfig logger dflags = do+initLlvmCgConfig :: Logger -> LlvmConfigCache -> DynFlags -> IO LlvmCgConfig+initLlvmCgConfig logger config_cache dflags = do version <- figureLlvmVersion logger dflags+ llvm_config <- readLlvmConfigCache config_cache pure $! LlvmCgConfig { llvmCgPlatform = targetPlatform dflags- , llvmCgContext = initSDocContext dflags (PprCode CStyle)+ , llvmCgContext = initSDocContext dflags PprCode , llvmCgFillUndefWithGarbage = gopt Opt_LlvmFillUndefWithGarbage dflags , llvmCgSplitSection = gopt Opt_SplitSections dflags , llvmCgBmiVersion = case platformArch (targetPlatform dflags) of@@ -26,5 +30,5 @@ , llvmCgLlvmVersion = version , llvmCgDoWarn = wopt Opt_WarnUnsupportedLlvmVersion dflags , llvmCgLlvmTarget = platformMisc_llvmTarget $! platformMisc dflags- , llvmCgLlvmConfig = llvmConfig dflags+ , llvmCgLlvmConfig = llvm_config }
+ compiler/GHC/Driver/Config/Core/Lint/Interactive.hs view
@@ -0,0 +1,35 @@+module GHC.Driver.Config.Core.Lint.Interactive+ ( lintInteractiveExpr+ ) where++import GHC.Prelude++import GHC.Driver.Env+import GHC.Driver.Session+import GHC.Driver.Config.Core.Lint++import GHC.Core+import GHC.Core.Ppr++import GHC.Core.Lint+import GHC.Core.Lint.Interactive++--import GHC.Runtime.Context++import GHC.Data.Bag++import GHC.Utils.Outputable as Outputable++lintInteractiveExpr :: SDoc -- ^ The source of the linted expression+ -> HscEnv+ -> CoreExpr -> IO ()+lintInteractiveExpr what hsc_env expr+ | not (gopt Opt_DoCoreLinting dflags)+ = return ()+ | Just err <- lintExpr (initLintConfig dflags $ interactiveInScope $ hsc_IC hsc_env) expr+ = displayLintResults logger False what (pprCoreExpr expr) (emptyBag, err)+ | otherwise+ = return ()+ where+ dflags = hsc_dflags hsc_env+ logger = hsc_logger hsc_env
+ compiler/GHC/Driver/Config/Core/Opt/Arity.hs view
@@ -0,0 +1,15 @@+module GHC.Driver.Config.Core.Opt.Arity+ ( initArityOpts+ ) where++import GHC.Prelude ()++import GHC.Driver.Session++import GHC.Core.Opt.Arity++initArityOpts :: DynFlags -> ArityOpts+initArityOpts dflags = ArityOpts+ { ao_ped_bot = gopt Opt_PedanticBottoms dflags+ , ao_dicts_cheap = gopt Opt_DictsCheap dflags+ }
+ compiler/GHC/Driver/Config/Core/Opt/LiberateCase.hs view
@@ -0,0 +1,15 @@+module GHC.Driver.Config.Core.Opt.LiberateCase+ ( initLiberateCaseOpts+ ) where++import GHC.Driver.Session++import GHC.Core.Opt.LiberateCase ( LibCaseOpts(..) )++-- | Initialize configuration for the liberate case Core optomization+-- pass.+initLiberateCaseOpts :: DynFlags -> LibCaseOpts+initLiberateCaseOpts dflags = LibCaseOpts+ { lco_threshold = liberateCaseThreshold dflags+ , lco_unfolding_opts = unfoldingOpts dflags+ }
+ compiler/GHC/Driver/Config/Core/Opt/Simplify.hs view
@@ -0,0 +1,91 @@+module GHC.Driver.Config.Core.Opt.Simplify+ ( initSimplifyExprOpts+ , initSimplifyOpts+ , initSimplMode+ , initGentleSimplMode+ ) where++import GHC.Prelude++import GHC.Core.Rules ( RuleBase )+import GHC.Core.Opt.Pipeline.Types ( CoreToDo(..) )+import GHC.Core.Opt.Simplify ( SimplifyExprOpts(..), SimplifyOpts(..) )+import GHC.Core.Opt.Simplify.Env ( FloatEnable(..), SimplMode(..) )+import GHC.Core.Opt.Simplify.Monad ( TopEnvConfig(..) )++import GHC.Driver.Config ( initOptCoercionOpts )+import GHC.Driver.Config.Core.Lint ( initLintPassResultConfig )+import GHC.Driver.Config.Core.Rules ( initRuleOpts )+import GHC.Driver.Config.Core.Opt.Arity ( initArityOpts )+import GHC.Driver.Session ( DynFlags(..), GeneralFlag(..), gopt )++import GHC.Runtime.Context ( InteractiveContext(..) )++import GHC.Types.Basic ( CompilerPhase(..) )+import GHC.Types.Var ( Var )++initSimplifyExprOpts :: DynFlags -> InteractiveContext -> SimplifyExprOpts+initSimplifyExprOpts dflags ic = SimplifyExprOpts+ { se_fam_inst = snd $ ic_instances ic+ , se_mode = (initSimplMode dflags InitialPhase "GHCi")+ { sm_inline = False+ -- Do not do any inlining, in case we expose some+ -- unboxed tuple stuff that confuses the bytecode+ -- interpreter+ }+ , se_top_env_cfg = TopEnvConfig+ { te_history_size = historySize dflags+ , te_tick_factor = simplTickFactor dflags+ }+ }++initSimplifyOpts :: DynFlags -> [Var] -> Int -> SimplMode -> RuleBase -> SimplifyOpts+initSimplifyOpts dflags extra_vars iterations mode hpt_rule_base = let+ -- This is a particularly ugly construction, but we will get rid of it in !8341.+ opts = SimplifyOpts+ { so_dump_core_sizes = not $ gopt Opt_SuppressCoreSizes dflags+ , so_iterations = iterations+ , so_mode = mode+ , so_pass_result_cfg = if gopt Opt_DoCoreLinting dflags+ then Just $ initLintPassResultConfig dflags extra_vars+ (CoreDoSimplify opts)+ else Nothing+ , so_hpt_rules = hpt_rule_base+ , so_top_env_cfg = TopEnvConfig { te_history_size = historySize dflags+ , te_tick_factor = simplTickFactor dflags }+ }+ in opts++initSimplMode :: DynFlags -> CompilerPhase -> String -> SimplMode+initSimplMode dflags phase name = SimplMode+ { sm_names = [name]+ , sm_phase = phase+ , sm_rules = gopt Opt_EnableRewriteRules dflags+ , sm_eta_expand = gopt Opt_DoLambdaEtaExpansion dflags+ , sm_cast_swizzle = True+ , sm_inline = True+ , sm_uf_opts = unfoldingOpts dflags+ , sm_case_case = True+ , sm_pre_inline = gopt Opt_SimplPreInlining dflags+ , sm_float_enable = floatEnable dflags+ , sm_do_eta_reduction = gopt Opt_DoEtaReduction dflags+ , sm_arity_opts = initArityOpts dflags+ , sm_rule_opts = initRuleOpts dflags+ , sm_case_folding = gopt Opt_CaseFolding dflags+ , sm_case_merge = gopt Opt_CaseMerge dflags+ , sm_co_opt_opts = initOptCoercionOpts dflags+ }++initGentleSimplMode :: DynFlags -> SimplMode+initGentleSimplMode dflags = (initSimplMode dflags InitialPhase "Gentle")+ { -- Don't do case-of-case transformations.+ -- This makes full laziness work better+ sm_case_case = False+ }++floatEnable :: DynFlags -> FloatEnable+floatEnable dflags =+ case (gopt Opt_LocalFloatOut dflags, gopt Opt_LocalFloatOutTopLevel dflags) of+ (True, True) -> FloatEnabled+ (True, False)-> FloatNestedOnly+ (False, _) -> FloatDisabled
+ compiler/GHC/Driver/Config/Core/Opt/WorkWrap.hs view
@@ -0,0 +1,21 @@+module GHC.Driver.Config.Core.Opt.WorkWrap+ ( initWorkWrapOpts+ ) where++import GHC.Prelude ()++import GHC.Driver.Config (initSimpleOpts)+import GHC.Driver.Session++import GHC.Core.FamInstEnv+import GHC.Core.Opt.WorkWrap+import GHC.Unit.Types++initWorkWrapOpts :: Module -> DynFlags -> FamInstEnvs -> WwOpts+initWorkWrapOpts this_mod dflags fam_envs = MkWwOpts+ { wo_fam_envs = fam_envs+ , wo_simple_opts = initSimpleOpts dflags+ , wo_cpr_anal = gopt Opt_CprAnal dflags+ , wo_module = this_mod+ , wo_unlift_strict = gopt Opt_WorkerWrapperUnlift dflags+ }
+ compiler/GHC/Driver/Config/Core/Rules.hs view
@@ -0,0 +1,23 @@+module GHC.Driver.Config.Core.Rules+ ( initRuleOpts+ ) where++import GHC.Prelude++import GHC.Driver.Flags+import GHC.Driver.Session ( DynFlags, gopt, targetPlatform, homeUnitId_ )++import GHC.Core.Rules.Config++import GHC.Unit.Types ( primUnitId, bignumUnitId )++-- | Initialize RuleOpts from DynFlags+initRuleOpts :: DynFlags -> RuleOpts+initRuleOpts dflags = RuleOpts+ { roPlatform = targetPlatform dflags+ , roNumConstantFolding = gopt Opt_NumConstantFolding dflags+ , roExcessRationalPrecision = gopt Opt_ExcessPrecision dflags+ -- disable bignum rules in ghc-prim and ghc-bignum itself+ , roBignumRules = homeUnitId_ dflags /= primUnitId+ && homeUnitId_ dflags /= bignumUnitId+ }
+ compiler/GHC/Driver/Config/CoreToStg.hs view
@@ -0,0 +1,16 @@+module GHC.Driver.Config.CoreToStg where++import GHC.Driver.Config.Stg.Debug+import GHC.Driver.Session++import GHC.CoreToStg++initCoreToStgOpts :: DynFlags -> CoreToStgOpts+initCoreToStgOpts dflags = CoreToStgOpts+ { coreToStg_platform = targetPlatform dflags+ , coreToStg_ways = ways dflags+ , coreToStg_AutoSccsOnIndividualCafs = gopt Opt_AutoSccsOnIndividualCafs dflags+ , coreToStg_InfoTableMap = gopt Opt_InfoTableMap dflags+ , coreToStg_ExternalDynamicRefs = gopt Opt_ExternalDynamicRefs dflags+ , coreToStg_stgDebugOpts = initStgDebugOpts dflags+ }
+ compiler/GHC/Driver/Config/CoreToStg/Prep.hs view
@@ -0,0 +1,34 @@+module GHC.Driver.Config.CoreToStg.Prep+ ( initCorePrepConfig+ , initCorePrepPgmConfig+ ) where++import GHC.Prelude++import GHC.Core.Opt.Pipeline.Types ( CoreToDo(..) )+import GHC.Driver.Env+import GHC.Driver.Session+import GHC.Driver.Config.Core.Lint+import GHC.Tc.Utils.Env+import GHC.Types.Var+import GHC.Utils.Outputable ( alwaysQualify )++import GHC.CoreToStg.Prep++initCorePrepConfig :: HscEnv -> IO CorePrepConfig+initCorePrepConfig hsc_env = do+ convertNumLit <- do+ let platform = targetPlatform $ hsc_dflags hsc_env+ home_unit = hsc_home_unit hsc_env+ lookup_global = lookupGlobal hsc_env+ mkConvertNumLiteral platform home_unit lookup_global+ return $ CorePrepConfig+ { cp_catchNonexhaustiveCases = gopt Opt_CatchNonexhaustiveCases $ hsc_dflags hsc_env+ , cp_convertNumLit = convertNumLit+ }++initCorePrepPgmConfig :: DynFlags -> [Var] -> CorePrepPgmConfig+initCorePrepPgmConfig dflags extra_vars = CorePrepPgmConfig+ { cpPgm_endPassConfig = initEndPassConfig dflags extra_vars alwaysQualify CorePrep+ , cpPgm_generateDebugInfo = needSourceNotes dflags+ }
+ compiler/GHC/Driver/Config/HsToCore/Ticks.hs view
@@ -0,0 +1,28 @@+module GHC.Driver.Config.HsToCore.Ticks+ ( initTicksConfig+ )+where++import GHC.Prelude++import Data.Maybe (catMaybes)++import GHC.Driver.Backend+import GHC.Driver.Session+import GHC.HsToCore.Ticks++initTicksConfig :: DynFlags -> TicksConfig+initTicksConfig dflags = TicksConfig+ { ticks_passes = coveragePasses dflags+ , ticks_profAuto = profAuto dflags+ , ticks_countEntries = gopt Opt_ProfCountEntries dflags+ }++coveragePasses :: DynFlags -> [TickishType]+coveragePasses dflags = catMaybes+ [ ifA Breakpoints $ backendWantsBreakpointTicks $ backend dflags+ , ifA HpcTicks $ gopt Opt_Hpc dflags+ , ifA ProfNotes $ sccProfilingEnabled dflags && profAuto dflags /= NoProfAuto+ , ifA SourceNotes $ needSourceNotes dflags+ ]+ where ifA x cond = if cond then Just x else Nothing
+ compiler/GHC/Driver/Config/HsToCore/Usage.hs view
@@ -0,0 +1,14 @@+module GHC.Driver.Config.HsToCore.Usage+ ( initUsageConfig+ )+where++import GHC.Driver.Env.Types+import GHC.Driver.Session++import GHC.HsToCore.Usage++initUsageConfig :: HscEnv -> UsageConfig+initUsageConfig hsc_env = UsageConfig+ { uc_safe_implicit_imps_req = safeImplicitImpsReq (hsc_dflags hsc_env)+ }
+ compiler/GHC/Driver/Config/Linker.hs view
@@ -0,0 +1,13 @@+module GHC.Driver.Config.Linker+ ( initFrameworkOpts+ ) where++import GHC.Linker.Config++import GHC.Driver.Session++initFrameworkOpts :: DynFlags -> FrameworkOpts+initFrameworkOpts dflags = FrameworkOpts+ { foFrameworkPaths = frameworkPaths dflags+ , foCmdlineFrameworks = cmdlineFrameworks dflags+ }
compiler/GHC/Driver/Config/StgToCmm.hs view
@@ -2,18 +2,18 @@ ( initStgToCmmConfig ) where +import GHC.Prelude.Basic+ import GHC.StgToCmm.Config import GHC.Driver.Backend import GHC.Driver.Session import GHC.Platform import GHC.Platform.Profile+import GHC.Utils.Error import GHC.Unit.Module import GHC.Utils.Outputable -import Data.Maybe-import Prelude- initStgToCmmConfig :: DynFlags -> Module -> StgToCmmConfig initStgToCmmConfig dflags mod = StgToCmmConfig -- settings@@ -21,7 +21,7 @@ , stgToCmmThisModule = mod , stgToCmmTmpDir = tmpDir dflags , stgToCmmContext = initSDocContext dflags defaultDumpStyle- , stgToCmmDebugLevel = debugLevel dflags+ , stgToCmmEmitDebugInfo = debugLevel dflags > 0 , stgToCmmBinBlobThresh = b_blob , stgToCmmMaxInlAllocSize = maxInlineAllocSize dflags -- ticky options@@ -46,12 +46,11 @@ , stgToCmmDoBoundsCheck = gopt Opt_DoBoundsChecking dflags , stgToCmmDoTagCheck = gopt Opt_DoTagInferenceChecks dflags -- backend flags- , stgToCmmAllowBigArith = not ncg+ , stgToCmmAllowBigArith = not ncg || platformArch platform == ArchWasm32 , stgToCmmAllowQuotRemInstr = ncg && (x86ish || ppc) , stgToCmmAllowQuotRem2 = (ncg && (x86ish || ppc)) || llvm , stgToCmmAllowExtendedAddSubInstrs = (ncg && (x86ish || ppc)) || llvm , stgToCmmAllowIntMul2Instr = (ncg && x86ish) || llvm- , stgToCmmAllowFabsInstrs = (ncg && (x86ish || ppc || aarch64)) || llvm -- SIMD flags , stgToCmmVecInstrsErr = vec_err , stgToCmmAvx = isAvxEnabled dflags@@ -61,9 +60,12 @@ } where profile = targetProfile dflags platform = profilePlatform profile bk_end = backend dflags- ncg = bk_end == NCG- llvm = bk_end == LLVM b_blob = if not ncg then Nothing else binBlobThreshold dflags+ (ncg, llvm) = case backendPrimitiveImplementation bk_end of+ GenericPrimitives -> (False, False)+ JSPrimitives -> (False, False)+ NcgPrimitives -> (True, False)+ LlvmPrimitives -> (False, True) x86ish = case platformArch platform of ArchX86 -> True ArchX86_64 -> True@@ -72,7 +74,6 @@ ArchPPC -> True ArchPPC_64 _ -> True _ -> False- aarch64 = platformArch platform == ArchAArch64- vec_err = case backend dflags of- LLVM -> Nothing- _ -> Just (unlines ["SIMD vector instructions require the LLVM back-end.", "Please use -fllvm."])+ vec_err = case backendSimdValidity (backend dflags) of+ IsValid -> Nothing+ NotValid msg -> Just msg
+ compiler/GHC/Driver/Config/StgToJS.hs view
@@ -0,0 +1,32 @@+module GHC.Driver.Config.StgToJS+ ( initStgToJSConfig+ )+where++import GHC.StgToJS.Types++import GHC.Driver.Session+import GHC.Platform.Ways+import GHC.Utils.Outputable++import GHC.Prelude++-- | Initialize StgToJS settings from DynFlags+initStgToJSConfig :: DynFlags -> StgToJSConfig+initStgToJSConfig dflags = StgToJSConfig+ -- flags+ { csInlinePush = False+ , csInlineBlackhole = False+ , csInlineLoadRegs = False+ , csInlineEnter = False+ , csInlineAlloc = False+ , csTraceRts = False+ , csAssertRts = False+ , csBoundsCheck = gopt Opt_DoBoundsChecking dflags+ , csDebugAlloc = False+ , csTraceForeign = False+ , csProf = ways dflags `hasWay` WayProf+ , csRuntimeAssert = False+ -- settings+ , csContext = initSDocContext dflags defaultDumpStyle+ }
compiler/GHC/Driver/Config/Tidy.hs view
@@ -62,12 +62,8 @@ -- If we are compiling for the interpreter we will insert any necessary -- SPT entries dynamically, otherwise we add a C stub to do so- , opt_gen_cstub = case backend dflags of- Interpreter -> False- _ -> True-+ , opt_gen_cstub = backendWritesFiles (backend dflags) , opt_mk_string = mk_string , opt_static_ptr_info_datacon = static_ptr_info_datacon , opt_static_ptr_datacon = static_ptr_datacon }-
compiler/GHC/Driver/GenerateCgIPEStub.hs view
@@ -3,7 +3,7 @@ module GHC.Driver.GenerateCgIPEStub (generateCgIPEStub) where import qualified Data.Map.Strict as Map-import Data.Maybe (catMaybes, listToMaybe)+import Data.Maybe (mapMaybe, listToMaybe) import GHC.Cmm import GHC.Cmm.CLabel (CLabel) import GHC.Cmm.Dataflow (Block, C, O)@@ -12,18 +12,18 @@ import GHC.Cmm.Dataflow.Label (Label) import GHC.Cmm.Info.Build (emptySRT) import GHC.Cmm.Pipeline (cmmPipeline)-import GHC.Cmm.Utils (toBlockList) import GHC.Data.Maybe (firstJusts) import GHC.Data.Stream (Stream, liftIO) import qualified GHC.Data.Stream as Stream-import GHC.Driver.Env (hsc_dflags)+import GHC.Driver.Env (hsc_dflags, hsc_logger) import GHC.Driver.Env.Types (HscEnv) import GHC.Driver.Flags (GeneralFlag (Opt_InfoTableMap)) import GHC.Driver.Session (gopt, targetPlatform) import GHC.Driver.Config.StgToCmm+import GHC.Driver.Config.Cmm import GHC.Prelude import GHC.Runtime.Heap.Layout (isStackRep)-import GHC.Settings (Platform, platformUnregisterised)+import GHC.Settings (Platform, platformTablesNextToCode) import GHC.StgToCmm.Monad (getCmm, initC, runC, initFCodeState) import GHC.StgToCmm.Prof (initInfoTableProv) import GHC.StgToCmm.Types (CmmCgInfos (..), ModuleLFInfos)@@ -41,7 +41,7 @@ But to make them readable / meaningful, one needs to know the source location of each return frame. Every return frame has a distinct info table and thus a distinct code pointer (for tables next to-code) or at least a distict address itself. Info Table Provernance Entries (IPE) are searchable by+code) or at least a distinct address itself. Info Table Provenance Entries (IPEs) are searchable by this pointer and contain a source location. The info table / info table code pointer to source location map is described in:@@ -52,13 +52,13 @@ This leads to the question: How to figure out the source location of a return frame? -While the lookup algorithms for registerised and unregisterised builds differ in details, they have in+While the lookup algorithms when tables-next-to-code is on/off differ in details, they have in common that we want to lookup the `CmmNode.CmmTick` (containing a `SourceNote`) that is nearest (before) the usage of the return frame's label. (Which label and label type is used differs between these two use cases.) -Registerised-~~~~~~~~~~~~~+With tables-next-to-code+~~~~~~~~~~~~~~~~~~~~~~~~ Let's consider this example: ```@@ -117,10 +117,10 @@ `IpeSourceLocation`. (There are other `Tickish` constructors like `ProfNote` or `HpcTick`, these are ignored.) -Unregisterised-~~~~~~~~~~~~~+Without tables-next-to-code+~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In unregisterised builds there is no return frame / continuation label in calls. The continuation (i.e. return+When tables-next-to-code is off, there is no return frame / continuation label in calls. The continuation (i.e. return frame) is set in an explicit Cmm assignment. Thus the tick lookup algorithm has to be slightly different. ```@@ -182,7 +182,9 @@ generateCgIPEStub hsc_env this_mod denv s = do let dflags = hsc_dflags hsc_env platform = targetPlatform dflags+ logger = hsc_logger hsc_env fstate = initFCodeState platform+ cmm_cfg = initCmmConfig dflags cgState <- liftIO initC -- Collect info tables, but only if -finfo-table-map is enabled, otherwise labeledInfoTablesWithTickishes is empty.@@ -193,7 +195,7 @@ let denv' = denv {provInfoTables = Map.fromList (map (\(_, i, t) -> (cit_lbl i, t)) labeledInfoTablesWithTickishes)} ((ipeStub, ipeCmmGroup), _) = runC (initStgToCmmConfig dflags this_mod) fstate cgState $ getCmm (initInfoTableProv (map sndOf3 labeledInfoTablesWithTickishes) denv') - (_, ipeCmmGroupSRTs) <- liftIO $ cmmPipeline hsc_env (emptySRT this_mod) ipeCmmGroup+ (_, ipeCmmGroupSRTs) <- liftIO $ cmmPipeline logger cmm_cfg (emptySRT this_mod) ipeCmmGroup Stream.yield ipeCmmGroupSRTs return CmmCgInfos {cgNonCafs = nonCaffySet, cgLFInfos = moduleLFInfos, cgIPEStub = ipeStub}@@ -208,7 +210,7 @@ collectNothing _ cmmGroupSRTs = pure ([], cmmGroupSRTs) collectInfoTables :: CmmGroupSRTs -> [(Label, CmmInfoTable)]- collectInfoTables cmmGroup = concat $ catMaybes $ map extractInfoTables cmmGroup+ collectInfoTables cmmGroup = concat $ mapMaybe extractInfoTables cmmGroup extractInfoTables :: GenCmmDecl RawCmmStatics CmmTopInfo CmmGraph -> Maybe [(Label, CmmInfoTable)] extractInfoTables (CmmProc h _ _ _) = Just $ mapToList (info_tbls h)@@ -221,9 +223,9 @@ if (isStackRep . cit_rep) infoTable then do let findFun =- if platformUnregisterised platform- then findCmmTickishForForUnregistered (cit_lbl infoTable)- else findCmmTickishForRegistered infoTableLabel+ if platformTablesNextToCode platform+ then findCmmTickishWithTNTC infoTableLabel+ else findCmmTickishSansTNTC (cit_lbl infoTable) blocks = concatMap toBlockList (graphs cmmGroup) firstJusts $ map findFun blocks else Nothing@@ -234,8 +236,8 @@ go acc (CmmProc _ _ _ g) = g : acc go acc _ = acc - findCmmTickishForRegistered :: Label -> Block CmmNode C C -> Maybe IpeSourceLocation- findCmmTickishForRegistered label block = do+ findCmmTickishWithTNTC :: Label -> Block CmmNode C C -> Maybe IpeSourceLocation+ findCmmTickishWithTNTC label block = do let (_, middleBlock, endBlock) = blockSplit block isCallWithReturnFrameLabel endBlock label@@ -247,15 +249,14 @@ lastTickInBlock block = listToMaybe $- catMaybes $- map maybeTick $ (reverse . blockToList) block+ mapMaybe maybeTick $ (reverse . blockToList) block maybeTick :: CmmNode O O -> Maybe IpeSourceLocation maybeTick (CmmTick (SourceNote span name)) = Just (span, name) maybeTick _ = Nothing - findCmmTickishForForUnregistered :: CLabel -> Block CmmNode C C -> Maybe IpeSourceLocation- findCmmTickishForForUnregistered cLabel block = do+ findCmmTickishSansTNTC :: CLabel -> Block CmmNode C C -> Maybe IpeSourceLocation+ findCmmTickishSansTNTC cLabel block = do let (_, middleBlock, _) = blockSplit block find cLabel (blockToList middleBlock) Nothing where
compiler/GHC/Driver/Main.hs view
@@ -1,7 +1,11 @@ {-# LANGUAGE BangPatterns #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE NondecreasingIndentation #-} {-# LANGUAGE GADTs #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE MultiWayIf #-} {-# OPTIONS_GHC -fprof-auto-top #-} @@ -39,16 +43,22 @@ -- * Making an HscEnv newHscEnv , newHscEnvWithHUG+ , initHscEnv -- * Compiling complete source files , Messager, batchMsg, batchMultiMsg , HscBackendAction (..), HscRecompStatus (..) , initModDetails+ , initWholeCoreBindings , hscMaybeWriteIface , hscCompileCmmFile , hscGenHardCode , hscInteractive+ , mkCgInteractiveGuts+ , CgInteractiveGuts+ , generateByteCode+ , generateFreshByteCode -- * Running passes separately , hscRecompStatus@@ -99,21 +109,35 @@ import GHC.Prelude +import GHC.Platform+import GHC.Platform.Ways+ import GHC.Driver.Plugins import GHC.Driver.Session import GHC.Driver.Backend import GHC.Driver.Env+import GHC.Driver.Env.KnotVars import GHC.Driver.Errors import GHC.Driver.Errors.Types import GHC.Driver.CodeOutput+import GHC.Driver.Config.Cmm.Parser (initCmmParserConfig)+import GHC.Driver.Config.Core.Opt.Simplify ( initSimplifyExprOpts )+import GHC.Driver.Config.Core.Lint ( endPassHscEnvIO )+import GHC.Driver.Config.Core.Lint.Interactive ( lintInteractiveExpr )+import GHC.Driver.Config.CoreToStg+import GHC.Driver.Config.CoreToStg.Prep import GHC.Driver.Config.Logger (initLogFlags) import GHC.Driver.Config.Parser (initParserOpts) import GHC.Driver.Config.Stg.Ppr (initStgPprOpts) import GHC.Driver.Config.Stg.Pipeline (initStgPipelineOpts)-import GHC.Driver.Config.StgToCmm (initStgToCmmConfig)+import GHC.Driver.Config.StgToCmm (initStgToCmmConfig)+import GHC.Driver.Config.Cmm (initCmmConfig)+import GHC.Driver.LlvmConfigCache (initLlvmConfigCache)+import GHC.Driver.Config.StgToJS (initStgToJSConfig) import GHC.Driver.Config.Diagnostic import GHC.Driver.Config.Tidy import GHC.Driver.Hooks+import GHC.Driver.GenerateCgIPEStub (generateCgIPEStub) import GHC.Runtime.Context import GHC.Runtime.Interpreter ( addSptEntry )@@ -131,8 +155,9 @@ import GHC.HsToCore import GHC.StgToByteCode ( byteCodeGen )+import GHC.StgToJS ( stgToJS ) -import GHC.IfaceToCore ( typecheckIface )+import GHC.IfaceToCore ( typecheckIface, typecheckWholeCoreBindings ) import GHC.Iface.Load ( ifaceStats, writeIface ) import GHC.Iface.Make@@ -144,14 +169,14 @@ import GHC.Iface.Ext.Debug ( diffFile, validateScopes ) import GHC.Core+import GHC.Core.Lint.Interactive ( interactiveInScope ) import GHC.Core.Tidy ( tidyExpr ) import GHC.Core.Type ( Type, Kind )-import GHC.Core.Lint ( lintInteractiveExpr, endPassIO ) import GHC.Core.Multiplicity import GHC.Core.Utils ( exprType ) import GHC.Core.ConLike-import GHC.Core.Opt.Monad ( CoreToDo (..)) import GHC.Core.Opt.Pipeline+import GHC.Core.Opt.Pipeline.Types ( CoreToDo (..)) import GHC.Core.TyCon import GHC.Core.InstEnv import GHC.Core.FamInstEnv@@ -182,10 +207,10 @@ import GHC.StgToCmm.Types (CmmCgInfos (..), ModuleLFInfos) import GHC.Cmm-import GHC.Cmm.Parser ( parseCmmFile ) import GHC.Cmm.Info.Build import GHC.Cmm.Pipeline import GHC.Cmm.Info+import GHC.Cmm.Parser import GHC.Unit import GHC.Unit.Env@@ -216,6 +241,7 @@ import GHC.Types.Name.Cache ( initNameCache ) import GHC.Types.Name.Reader import GHC.Types.Name.Ppr+import GHC.Types.Name.Set (NonCaffySet) import GHC.Types.TyThing import GHC.Types.HpcInfo @@ -233,25 +259,33 @@ import GHC.Data.StringBuffer import qualified GHC.Data.Stream as Stream import GHC.Data.Stream (Stream)+import GHC.Data.Maybe+ import qualified GHC.SysTools+import GHC.SysTools (initSysTools)+import GHC.SysTools.BaseDir (findTopDir) import Data.Data hiding (Fixity, TyCon) import Data.List ( nub, isPrefixOf, partition )+import qualified Data.List.NonEmpty as NE import Control.Monad import Data.IORef import System.FilePath as FilePath import System.Directory-import System.IO (fixIO) import qualified Data.Set as S import Data.Set (Set) import Data.Functor import Control.DeepSeq (force) import Data.Bifunctor (first)-import GHC.Data.Maybe-import GHC.Driver.Env.KnotVars-import GHC.Types.Name.Set (NonCaffySet)-import GHC.Driver.GenerateCgIPEStub (generateCgIPEStub) import Data.List.NonEmpty (NonEmpty ((:|)))+import GHC.Unit.Module.WholeCoreBindings+import GHC.Types.TypeEnv+import System.IO+import {-# SOURCE #-} GHC.Driver.Pipeline+import Data.Time++import System.IO.Unsafe ( unsafeInterleaveIO )+import GHC.Iface.Env ( trace_if ) import GHC.Stg.InferTags.TagSig (seqTagSig) import GHC.Types.Unique.FM @@ -262,36 +296,80 @@ %* * %********************************************************************* -} -newHscEnv :: DynFlags -> IO HscEnv-newHscEnv dflags = newHscEnvWithHUG dflags (homeUnitId_ dflags) home_unit_graph+newHscEnv :: FilePath -> DynFlags -> IO HscEnv+newHscEnv top_dir dflags = newHscEnvWithHUG top_dir dflags (homeUnitId_ dflags) home_unit_graph where home_unit_graph = unitEnv_singleton (homeUnitId_ dflags) (mkHomeUnitEnv dflags emptyHomePackageTable Nothing) -newHscEnvWithHUG :: DynFlags -> UnitId -> HomeUnitGraph -> IO HscEnv-newHscEnvWithHUG top_dynflags cur_unit home_unit_graph = do+newHscEnvWithHUG :: FilePath -> DynFlags -> UnitId -> HomeUnitGraph -> IO HscEnv+newHscEnvWithHUG top_dir top_dynflags cur_unit home_unit_graph = do nc_var <- initNameCache 'r' knownKeyNames fc_var <- initFinderCache logger <- initLogger tmpfs <- initTmpFs let dflags = homeUnitEnv_dflags $ unitEnv_lookup cur_unit home_unit_graph unit_env <- initUnitEnv cur_unit home_unit_graph (ghcNameVersion dflags) (targetPlatform dflags)- return HscEnv { hsc_dflags = top_dynflags+ llvm_config <- initLlvmConfigCache top_dir+ return HscEnv { hsc_dflags = top_dynflags , hsc_logger = setLogFlags logger (initLogFlags top_dynflags)- , hsc_targets = []- , hsc_mod_graph = emptyMG- , hsc_IC = emptyInteractiveContext dflags- , hsc_NC = nc_var- , hsc_FC = fc_var- , hsc_type_env_vars = emptyKnotVars- , hsc_interp = Nothing- , hsc_unit_env = unit_env- , hsc_plugins = emptyPlugins- , hsc_hooks = emptyHooks- , hsc_tmpfs = tmpfs+ , hsc_targets = []+ , hsc_mod_graph = emptyMG+ , hsc_IC = emptyInteractiveContext dflags+ , hsc_NC = nc_var+ , hsc_FC = fc_var+ , hsc_type_env_vars = emptyKnotVars+ , hsc_interp = Nothing+ , hsc_unit_env = unit_env+ , hsc_plugins = emptyPlugins+ , hsc_hooks = emptyHooks+ , hsc_tmpfs = tmpfs+ , hsc_llvm_config = llvm_config } +-- | Initialize HscEnv from an optional top_dir path+initHscEnv :: Maybe FilePath -> IO HscEnv+initHscEnv mb_top_dir = do+ top_dir <- findTopDir mb_top_dir+ mySettings <- initSysTools top_dir+ dflags <- initDynFlags (defaultDynFlags mySettings)+ hsc_env <- newHscEnv top_dir dflags+ checkBrokenTablesNextToCode (hsc_logger hsc_env) dflags+ setUnsafeGlobalDynFlags dflags+ -- c.f. DynFlags.parseDynamicFlagsFull, which+ -- creates DynFlags and sets the UnsafeGlobalDynFlags+ return hsc_env++-- | The binutils linker on ARM emits unnecessary R_ARM_COPY relocations which+-- breaks tables-next-to-code in dynamically linked modules. This+-- check should be more selective but there is currently no released+-- version where this bug is fixed.+-- See https://sourceware.org/bugzilla/show_bug.cgi?id=16177 and+-- https://gitlab.haskell.org/ghc/ghc/issues/4210#note_78333+checkBrokenTablesNextToCode :: Logger -> DynFlags -> IO ()+checkBrokenTablesNextToCode logger dflags = do+ let invalidLdErr = "Tables-next-to-code not supported on ARM \+ \when using binutils ld (please see: \+ \https://sourceware.org/bugzilla/show_bug.cgi?id=16177)"+ broken <- checkBrokenTablesNextToCode' logger dflags+ when broken (panic invalidLdErr)++checkBrokenTablesNextToCode' :: Logger -> DynFlags -> IO Bool+checkBrokenTablesNextToCode' logger dflags+ | not (isARM arch) = return False+ | ways dflags `hasNotWay` WayDyn = return False+ | not tablesNextToCode = return False+ | otherwise = do+ linkerInfo <- liftIO $ GHC.SysTools.getLinkerInfo logger dflags+ case linkerInfo of+ GnuLD _ -> return True+ _ -> return False+ where platform = targetPlatform dflags+ arch = platformArch platform+ tablesNextToCode = platformTablesNextToCode platform++ -- ----------------------------------------------------------------------------- getDiagnostics :: Hsc (Messages GhcMessage)@@ -309,9 +387,10 @@ handleWarnings :: Hsc () handleWarnings = do diag_opts <- initDiagOpts <$> getDynFlags+ print_config <- initPrintConfig <$> getDynFlags logger <- getLogger w <- getDiagnostics- liftIO $ printOrThrowDiagnostics logger diag_opts w+ liftIO $ printOrThrowDiagnostics logger print_config diag_opts w clearDiagnostics -- | log warning in the monad, and if there are errors then@@ -329,7 +408,7 @@ logDiagnostics (GhcPsMessage <$> warnings) logger <- getLogger let (wWarns, wErrs) = partitionMessages warnings- liftIO $ printMessages logger diag_opts wWarns+ liftIO $ printMessages logger NoDiagnosticOpts diag_opts wWarns throwErrors $ fmap GhcPsMessage $ errors `unionMessages` wErrs -- | Deal with errors and warnings returned by a compilation step@@ -368,11 +447,15 @@ -- ----------------------------------------------------------------------------- -- | Lookup things in the compiler's environment -hscTcRnLookupRdrName :: HscEnv -> LocatedN RdrName -> IO [Name]+hscTcRnLookupRdrName :: HscEnv -> LocatedN RdrName -> IO (NonEmpty Name) hscTcRnLookupRdrName hsc_env0 rdr_name = runInteractiveHsc hsc_env0 $ do { hsc_env <- getHscEnv- ; ioMsgMaybe $ hoistTcRnMessage $ tcRnLookupRdrName hsc_env rdr_name }+ -- tcRnLookupRdrName can return empty list only together with TcRnUnknownMessage.+ -- Once errors has been dealt with in hoistTcRnMessage, we can enforce+ -- this invariant in types by converting to NonEmpty.+ ; ioMsgMaybe $ fmap (fmap (>>= NE.nonEmpty)) $ hoistTcRnMessage $+ tcRnLookupRdrName hsc_env rdr_name } hscTcRcLookupName :: HscEnv -> Name -> IO (Maybe TyThing) hscTcRcLookupName hsc_env0 name = runInteractiveHsc hsc_env0 $ do@@ -616,7 +699,7 @@ Nothing -> hscParse' mod_summary tc_result0 <- tcRnModule' mod_summary keep_rn' hpm if hsc_src == HsigFile- then do (iface, _) <- liftIO $ hscSimpleIface hsc_env tc_result0 mod_summary+ then do (iface, _) <- liftIO $ hscSimpleIface hsc_env Nothing tc_result0 mod_summary ioMsgMaybe $ hoistTcRnMessage $ tcRnMergeSignatures hsc_env hpm tc_result0 iface else return tc_result0@@ -746,7 +829,7 @@ -> HscEnv -> ModSummary -> Maybe ModIface- -> Maybe Linkable+ -> HomeModLinkable -> (Int,Int) -> IO HscRecompStatus hscRecompStatus@@ -772,39 +855,70 @@ return $ HscRecompNeeded $ fmap (mi_iface_hash . mi_final_exts) mb_checked_iface UpToDateItem checked_iface -> do let lcl_dflags = ms_hspp_opts mod_summary- case backend lcl_dflags of- -- No need for a linkable, we're good to go- NoBackend -> do- msg $ UpToDate- return $ HscUpToDate checked_iface Nothing- -- Do need linkable- backend- | not (backendProducesObject backend)- , IsBoot <- isBootSummary mod_summary- -> do- msg $ UpToDate- return $ HscUpToDate checked_iface Nothing- _ -> do- -- Check to see whether the expected build products already exist.- -- If they don't exists then we trigger recompilation.- recomp_linkable_result <- case () of- -- Interpreter can use either already loaded bytecode or loaded object code- _ | Interpreter <- backend lcl_dflags -> do- let res = checkByteCode old_linkable- case res of- UpToDateItem _ -> pure res- _ -> liftIO $ checkObjects lcl_dflags old_linkable mod_summary- -- Need object files for making object files- | backendProducesObject (backend lcl_dflags) -> liftIO $ checkObjects lcl_dflags old_linkable mod_summary- | otherwise -> pprPanic "hscRecompStatus" (text $ show $ backend lcl_dflags)- case recomp_linkable_result of- UpToDateItem linkable -> do- msg $ UpToDate- return $ HscUpToDate checked_iface $ Just linkable- OutOfDateItem reason _ -> do- msg $ NeedsRecompile reason- return $ HscRecompNeeded $ Just $ mi_iface_hash $ mi_final_exts $ checked_iface+ if | not (backendGeneratesCode (backend lcl_dflags)) -> do+ -- No need for a linkable, we're good to go+ msg UpToDate+ return $ HscUpToDate checked_iface emptyHomeModInfoLinkable+ | not (backendGeneratesCodeForHsBoot (backend lcl_dflags))+ , IsBoot <- isBootSummary mod_summary -> do+ msg UpToDate+ return $ HscUpToDate checked_iface emptyHomeModInfoLinkable+ | otherwise -> do+ -- Do need linkable+ -- 1. Just check whether we have bytecode/object linkables and then+ -- we will decide if we need them or not.+ bc_linkable <- checkByteCode checked_iface mod_summary (homeMod_bytecode old_linkable)+ obj_linkable <- liftIO $ checkObjects lcl_dflags (homeMod_object old_linkable) mod_summary+ trace_if (hsc_logger hsc_env) (vcat [text "BCO linkable", nest 2 (ppr bc_linkable), text "Object Linkable", ppr obj_linkable]) + let just_bc = justBytecode <$> bc_linkable+ just_o = justObjects <$> obj_linkable+ _maybe_both_os = case (bc_linkable, obj_linkable) of+ (UpToDateItem bc, UpToDateItem o) -> UpToDateItem (bytecodeAndObjects bc o)+ -- If missing object code, just say we need to recompile because of object code.+ (_, OutOfDateItem reason _) -> OutOfDateItem reason Nothing+ -- If just missing byte code, just use the object code+ -- so you should use -fprefer-byte-code with -fwrite-if-simplified-core or you'll+ -- end up using bytecode on recompilation+ (_, UpToDateItem {} ) -> just_o++ definitely_both_os = case (bc_linkable, obj_linkable) of+ (UpToDateItem bc, UpToDateItem o) -> UpToDateItem (bytecodeAndObjects bc o)+ -- If missing object code, just say we need to recompile because of object code.+ (_, OutOfDateItem reason _) -> OutOfDateItem reason Nothing+ -- If just missing byte code, just use the object code+ -- so you should use -fprefer-byte-code with -fwrite-if-simplified-core or you'll+ -- end up using bytecode on recompilation+ (OutOfDateItem reason _, _ ) -> OutOfDateItem reason Nothing++-- pprTraceM "recomp" (ppr just_bc <+> ppr just_o)+ -- 2. Decide which of the products we will need+ let recomp_linkable_result = case () of+ _ | backendCanReuseLoadedCode (backend lcl_dflags) ->+ case bc_linkable of+ -- If bytecode is available for Interactive then don't load object code+ UpToDateItem _ -> just_bc+ _ -> case obj_linkable of+ -- If o is availabe, then just use that+ UpToDateItem _ -> just_o+ _ -> outOfDateItemBecause MissingBytecode Nothing+ -- Need object files for making object files+ | backendWritesFiles (backend lcl_dflags) ->+ if gopt Opt_ByteCodeAndObjectCode lcl_dflags+ -- We say we are going to write both, so recompile unless we have both+ then definitely_both_os+ -- Only load the object file unless we are saying we need to produce both.+ -- Unless we do this then you can end up using byte-code for a module you specify -fobject-code for.+ else just_o+ | otherwise -> pprPanic "hscRecompStatus" (text $ show $ backend lcl_dflags)+ case recomp_linkable_result of+ UpToDateItem linkable -> do+ msg $ UpToDate+ return $ HscUpToDate checked_iface $ linkable+ OutOfDateItem reason _ -> do+ msg $ NeedsRecompile reason+ return $ HscRecompNeeded $ Just $ mi_iface_hash $ mi_final_exts $ checked_iface+ -- | Check that the .o files produced by compilation are already up-to-date -- or not. checkObjects :: DynFlags -> Maybe Linkable -> ModSummary -> IO (MaybeValidated Linkable)@@ -840,14 +954,24 @@ -- | Check to see if we can reuse the old linkable, by this point we will -- have just checked that the old interface matches up with the source hash, so -- no need to check that again here-checkByteCode :: Maybe Linkable -> MaybeValidated Linkable-checkByteCode mb_old_linkable =+checkByteCode :: ModIface -> ModSummary -> Maybe Linkable -> IO (MaybeValidated Linkable)+checkByteCode iface mod_sum mb_old_linkable = case mb_old_linkable of Just old_linkable | not (isObjectLinkable old_linkable)- -> UpToDateItem old_linkable- _ -> outOfDateItemBecause MissingBytecode Nothing+ -> return $ (UpToDateItem old_linkable)+ _ -> loadByteCode iface mod_sum +loadByteCode :: ModIface -> ModSummary -> IO (MaybeValidated Linkable)+loadByteCode iface mod_sum = do+ let+ this_mod = ms_mod mod_sum+ if_date = fromJust $ ms_iface_date mod_sum+ case mi_extra_decls iface of+ Just extra_decls -> do+ let fi = WholeCoreBindings extra_decls this_mod (ms_location mod_sum)+ return (UpToDateItem (LM if_date this_mod [CoreBindings fi]))+ _ -> return $ outOfDateItemBecause MissingBytecode Nothing -------------------------------------------------------------- -- Compilers --------------------------------------------------------------@@ -855,18 +979,41 @@ -- Knot tying! See Note [Knot-tying typecheckIface] -- See Note [ModDetails and --make mode]-initModDetails :: HscEnv -> ModSummary -> ModIface -> IO ModDetails-initModDetails hsc_env mod_summary iface =+initModDetails :: HscEnv -> ModIface -> IO ModDetails+initModDetails hsc_env iface = fixIO $ \details' -> do- let act hpt = addToHpt hpt (ms_mod_name mod_summary)- (HomeModInfo iface details' Nothing)- let hsc_env' = hscUpdateHPT act hsc_env+ let act hpt = addToHpt hpt (moduleName $ mi_module iface)+ (HomeModInfo iface details' emptyHomeModInfoLinkable)+ let !hsc_env' = hscUpdateHPT act hsc_env -- NB: This result is actually not that useful -- in one-shot mode, since we're not going to do -- any further typechecking. It's much more useful -- in make mode, since this HMI will go into the HPT. genModDetails hsc_env' iface +-- Hydrate any WholeCoreBindings linkables into BCOs+initWholeCoreBindings :: HscEnv -> ModIface -> ModDetails -> Linkable -> IO Linkable+initWholeCoreBindings hsc_env mod_iface details (LM utc_time this_mod uls) = LM utc_time this_mod <$> mapM go uls+ where+ go (CoreBindings fi) = do+ let act hpt = addToHpt hpt (moduleName $ mi_module mod_iface)+ (HomeModInfo mod_iface details emptyHomeModInfoLinkable)+ types_var <- newIORef (md_types details)+ let kv = knotVarsFromModuleEnv (mkModuleEnv [(this_mod, types_var)])+ let hsc_env' = hscUpdateHPT act hsc_env { hsc_type_env_vars = kv }+ core_binds <- initIfaceCheck (text "l") hsc_env' $ typecheckWholeCoreBindings types_var fi+ -- MP: The NoStubs here is only from (I think) the TH `qAddForeignFilePath` feature but it's a bit unclear what to do+ -- with these files, do we have to read and serialise the foreign file? I will leave it for now until someone+ -- reports a bug.+ let cgi_guts = CgInteractiveGuts this_mod core_binds (typeEnvTyCons (md_types details)) NoStubs Nothing []+ -- The bytecode generation itself is lazy because otherwise even when doing+ -- recompilation checking the bytecode will be generated (which slows things down a lot)+ -- the laziness is OK because generateByteCode just depends on things already loaded+ -- in the interface file.+ LoadedBCOs <$> (unsafeInterleaveIO $ do+ trace_if (hsc_logger hsc_env) (text "Generating ByteCode for" <+> (ppr this_mod))+ generateByteCode hsc_env cgi_guts (wcb_mod_location fi))+ go ul = return ul {- Note [ModDetails and --make mode]@@ -934,6 +1081,7 @@ let bcknd = backend dflags hsc_src = ms_hsc_src summary diag_opts = initDiagOpts dflags+ print_config = initPrintConfig dflags -- Desugar, if appropriate --@@ -948,14 +1096,14 @@ -- Report the warnings from both typechecking and desugar together w <- getDiagnostics- liftIO $ printOrThrowDiagnostics logger diag_opts (unionMessages tc_warnings w)+ liftIO $ printOrThrowDiagnostics logger print_config diag_opts (unionMessages tc_warnings w) clearDiagnostics -- Simplify, if appropriate, and (whether we simplified or not) generate an -- interface file. case mb_desugar of -- Just cause we desugared doesn't mean we are generating code, see above.- Just desugared_guts | bcknd /= NoBackend -> do+ Just desugared_guts | backendGeneratesCode bcknd -> do plugins <- liftIO $ readIORef (tcg_th_coreplugins tc_result) simplified_guts <- hscSimplify' plugins desugared_guts @@ -966,7 +1114,7 @@ {-# SCC "GHC.Driver.Main.mkPartialIface" #-} -- This `force` saves 2M residency in test T10370 -- See Note [Avoiding space leaks in toIface*] for details.- force (mkPartialIface hsc_env details summary simplified_guts)+ force (mkPartialIface hsc_env (cg_binds cg_guts) details summary simplified_guts) return HscRecomp { hscs_guts = cg_guts, hscs_mod_location = ms_location summary,@@ -974,11 +1122,29 @@ hscs_old_iface_hash = mb_old_hash } - -- We are not generating code, so we can skip simplification+ Just desugared_guts | gopt Opt_WriteIfSimplifiedCore dflags -> do+ -- If -fno-code is enabled (hence we fall through to this case)+ -- Running the simplifier once is necessary before doing byte code generation+ -- in order to inline data con wrappers but we honour whatever level of simplificication the+ -- user requested. See #22008 for some discussion.+ plugins <- liftIO $ readIORef (tcg_th_coreplugins tc_result)+ simplified_guts <- hscSimplify' plugins desugared_guts+ (cg_guts, _) <-+ liftIO $ hscTidy hsc_env simplified_guts++ (iface, _details) <- liftIO $+ hscSimpleIface hsc_env (Just $ cg_binds cg_guts) tc_result summary++ liftIO $ hscMaybeWriteIface logger dflags True iface mb_old_hash (ms_location summary)++ return $ HscUpdate iface+++ -- We are not generating code or writing an interface with simplified core so we can skip simplification -- and generate a simple interface. _ -> do (iface, _details) <- liftIO $- hscSimpleIface hsc_env tc_result summary+ hscSimpleIface hsc_env Nothing tc_result summary liftIO $ hscMaybeWriteIface logger dflags True iface mb_old_hash (ms_location summary) @@ -1042,10 +1208,7 @@ -> IO () hscMaybeWriteIface logger dflags is_simple iface old_iface mod_location = do let force_write_interface = gopt Opt_WriteInterface dflags- write_interface = case backend dflags of- NoBackend -> False- Interpreter -> False- _ -> True+ write_interface = backendWritesFiles (backend dflags) write_iface dflags' iface = let !iface_name = if dynamicNow dflags' then ml_dyn_hi_file mod_location else ml_hi_file mod_location@@ -1310,10 +1473,11 @@ logDiagnostics oldErrs diag_opts <- initDiagOpts <$> getDynFlags+ print_config <- initPrintConfig <$> getDynFlags logger <- getLogger -- Will throw if failed safe check- liftIO $ printOrThrowDiagnostics logger diag_opts safeErrs+ liftIO $ printOrThrowDiagnostics logger print_config diag_opts safeErrs -- No fatal warnings or errors: passed safe check let infPassed = isEmptyMessages infErrs@@ -1510,7 +1674,7 @@ -- may call it on modules using Trustworthy or Unsafe flags so as to allow -- warning flags for safety to function correctly. See Note [Safe Haskell -- Inference].-markUnsafeInfer :: Diagnostic e => TcGblEnv -> Messages e -> Hsc TcGblEnv+markUnsafeInfer :: forall e . Diagnostic e => TcGblEnv -> Messages e -> Hsc TcGblEnv markUnsafeInfer tcg_env whyUnsafe = do dflags <- getDynFlags @@ -1520,6 +1684,7 @@ (logDiagnostics $ singleMessage $ mkPlainMsgEnvelope diag_opts (warnUnsafeOnLoc dflags) $ GhcDriverMessage $ DriverUnknownMessage $+ UnknownDiagnostic $ mkPlainDiagnostic reason noHints $ whyUnsafe' dflags) @@ -1538,7 +1703,9 @@ whyUnsafe' df = vcat [ quotes pprMod <+> text "has been inferred as unsafe!" , text "Reason:" , nest 4 $ (vcat $ badFlags df) $+$- (vcat $ pprMsgEnvelopeBagWithLoc (getMessages whyUnsafe)) $+$+ -- MP: Using defaultDiagnosticOpts here is not right but it's also not right to handle these+ -- unsafety error messages in an unstructured manner.+ (vcat $ pprMsgEnvelopeBagWithLoc (defaultDiagnosticOpts @e) (getMessages whyUnsafe)) $+$ (vcat $ badInsts $ tcg_insts tcg_env) ] badFlags df = concatMap (badFlag df) unsafeFlagsForInfer@@ -1590,19 +1757,21 @@ -- Interface generators -------------------------------------------------------------- --- | Generate a striped down interface file, e.g. for boot files or when ghci+-- | Generate a stripped down interface file, e.g. for boot files or when ghci -- generates interface files. See Note [simpleTidyPgm - mkBootModDetailsTc] hscSimpleIface :: HscEnv+ -> Maybe CoreProgram -> TcGblEnv -> ModSummary -> IO (ModIface, ModDetails)-hscSimpleIface hsc_env tc_result summary- = runHsc hsc_env $ hscSimpleIface' tc_result summary+hscSimpleIface hsc_env mb_core_program tc_result summary+ = runHsc hsc_env $ hscSimpleIface' mb_core_program tc_result summary -hscSimpleIface' :: TcGblEnv+hscSimpleIface' :: Maybe CoreProgram+ -> TcGblEnv -> ModSummary -> Hsc (ModIface, ModDetails)-hscSimpleIface' tc_result summary = do+hscSimpleIface' mb_core_program tc_result summary = do hsc_env <- getHscEnv logger <- getLogger details <- liftIO $ mkBootModDetailsTc logger tc_result@@ -1610,7 +1779,7 @@ new_iface <- {-# SCC "MkFinalIface" #-} liftIO $- mkIfaceTc hsc_env safe_mode details summary tc_result+ mkIfaceTc hsc_env safe_mode details summary mb_core_program tc_result -- And the answer is ... liftIO $ dumpIfaceStats hsc_env return (new_iface, details)@@ -1633,11 +1802,14 @@ cg_foreign = foreign_stubs0, cg_foreign_files = foreign_files, cg_dep_pkgs = dependencies,- cg_hpc_info = hpc_info } = cgguts+ cg_hpc_info = hpc_info,+ cg_spt_entries = spt_entries+ } = cgguts dflags = hsc_dflags hsc_env logger = hsc_logger hsc_env hooks = hsc_hooks hsc_env tmpfs = hsc_tmpfs hsc_env+ llvm_config = hsc_llvm_config hsc_env profile = targetProfile dflags data_tycons = filter isDataTyCon tycons -- cg_tycons includes newtypes, for the benefit of External Core,@@ -1661,9 +1833,13 @@ ------------------- -- PREPARE FOR CODE GENERATION -- Do saturation and convert to A-normal form- (prepd_binds) <- {-# SCC "CorePrep" #-}- corePrepPgm hsc_env this_mod location- late_cc_binds data_tycons+ (prepd_binds) <- {-# SCC "CorePrep" #-} do+ cp_cfg <- initCorePrepConfig hsc_env+ corePrepPgm+ (hsc_logger hsc_env)+ cp_cfg+ (initCorePrepPgmConfig (hsc_dflags hsc_env) (interactiveInScope $ hsc_IC hsc_env))+ this_mod location late_cc_binds data_tycons ----------------- Convert to STG ------------------ (stg_binds, denv, (caf_ccs, caf_cc_stacks), stg_cg_infos)@@ -1688,56 +1864,84 @@ ------------------ Code generation ------------------ -- The back-end is streamed: each top-level function goes -- from Stg all the way to asm before dealing with the next- -- top-level function, so showPass isn't very useful here.- -- Hence we have one showPass for the whole backend, the- -- next showPass after this will be "Assembler".- withTiming logger- (text "CodeGen"<+>brackets (ppr this_mod))- (const ()) $ do- cmms <- {-# SCC "StgToCmm" #-}- doCodeGen hsc_env this_mod denv data_tycons- cost_centre_info- stg_binds hpc_info+ -- top-level function, so withTiming isn't very useful here.+ -- Hence we have one withTiming for the whole backend, the+ -- next withTiming after this will be "Assembler" (hard code only).+ withTiming logger (text "CodeGen"<+>brackets (ppr this_mod)) (const ())+ $ case backendCodeOutput (backend dflags) of+ JSCodeOutput ->+ do+ let js_config = initStgToJSConfig dflags+ cmm_cg_infos = Nothing+ stub_c_exists = Nothing+ foreign_fps = [] - ------------------ Code output ------------------------ rawcmms0 <- {-# SCC "cmmToRawCmm" #-}- case cmmToRawCmmHook hooks of- Nothing -> cmmToRawCmm logger profile cmms- Just h -> h dflags (Just this_mod) cmms+ putDumpFileMaybe logger Opt_D_dump_stg_final "Final STG:" FormatSTG+ (pprGenStgTopBindings (initStgPprOpts dflags) stg_binds) - let dump a = do- unless (null a) $- putDumpFileMaybe logger Opt_D_dump_cmm_raw "Raw Cmm" FormatCMM (pdoc platform a)- return a- rawcmms1 = Stream.mapM dump rawcmms0+ -- do the unfortunately effectual business+ stgToJS logger js_config stg_binds this_mod spt_entries foreign_stubs0 cost_centre_info output_filename+ return (output_filename, stub_c_exists, foreign_fps, Just stg_cg_infos, cmm_cg_infos) - let foreign_stubs st = foreign_stubs0 `appendStubC` prof_init- `appendStubC` cgIPEStub st+ _ ->+ do+ cmms <- {-# SCC "StgToCmm" #-}+ doCodeGen hsc_env this_mod denv data_tycons+ cost_centre_info+ stg_binds hpc_info - (output_filename, (_stub_h_exists, stub_c_exists), foreign_fps, cmm_cg_infos)- <- {-# SCC "codeOutput" #-}- codeOutput logger tmpfs dflags (hsc_units hsc_env) this_mod output_filename location- foreign_stubs foreign_files dependencies rawcmms1- return ( output_filename, stub_c_exists, foreign_fps- , Just stg_cg_infos, Just cmm_cg_infos)+ ------------------ Code output -----------------------+ rawcmms0 <- {-# SCC "cmmToRawCmm" #-}+ case cmmToRawCmmHook hooks of+ Nothing -> cmmToRawCmm logger profile cmms+ Just h -> h dflags (Just this_mod) cmms + let dump a = do+ unless (null a) $ putDumpFileMaybe logger Opt_D_dump_cmm_raw "Raw Cmm" FormatCMM (pdoc platform a)+ return a+ rawcmms1 = Stream.mapM dump rawcmms0 + let foreign_stubs st = foreign_stubs0+ `appendStubC` prof_init+ `appendStubC` cgIPEStub st++ (output_filename, (_stub_h_exists, stub_c_exists), foreign_fps, cmm_cg_infos)+ <- {-# SCC "codeOutput" #-}+ codeOutput logger tmpfs llvm_config dflags (hsc_units hsc_env) this_mod output_filename location+ foreign_stubs foreign_files dependencies rawcmms1+ return ( output_filename, stub_c_exists, foreign_fps+ , Just stg_cg_infos, Just cmm_cg_infos)+++-- The part of CgGuts that we need for HscInteractive+data CgInteractiveGuts = CgInteractiveGuts { cgi_module :: Module+ , cgi_binds :: CoreProgram+ , cgi_tycons :: [TyCon]+ , cgi_foreign :: ForeignStubs+ , cgi_modBreaks :: Maybe ModBreaks+ , cgi_spt_entries :: [SptEntry]+ }++mkCgInteractiveGuts :: CgGuts -> CgInteractiveGuts+mkCgInteractiveGuts CgGuts{cg_module, cg_binds, cg_tycons, cg_foreign, cg_modBreaks, cg_spt_entries}+ = CgInteractiveGuts cg_module cg_binds cg_tycons cg_foreign cg_modBreaks cg_spt_entries+ hscInteractive :: HscEnv- -> CgGuts+ -> CgInteractiveGuts -> ModLocation -> IO (Maybe FilePath, CompiledByteCode, [SptEntry]) hscInteractive hsc_env cgguts location = do let dflags = hsc_dflags hsc_env let logger = hsc_logger hsc_env let tmpfs = hsc_tmpfs hsc_env- let CgGuts{ -- This is the last use of the ModGuts in a compilation.+ let CgInteractiveGuts{ -- This is the last use of the ModGuts in a compilation. -- From now on, we just use the bits we need.- cg_module = this_mod,- cg_binds = core_binds,- cg_tycons = tycons,- cg_foreign = foreign_stubs,- cg_modBreaks = mod_breaks,- cg_spt_entries = spt_entries } = cgguts+ cgi_module = this_mod,+ cgi_binds = core_binds,+ cgi_tycons = tycons,+ cgi_foreign = foreign_stubs,+ cgi_modBreaks = mod_breaks,+ cgi_spt_entries = spt_entries } = cgguts data_tycons = filter isDataTyCon tycons -- cg_tycons includes newtypes, for the benefit of External Core,@@ -1746,8 +1950,13 @@ ------------------- -- PREPARE FOR CODE GENERATION -- Do saturation and convert to A-normal form- prepd_binds <- {-# SCC "CorePrep" #-}- corePrepPgm hsc_env this_mod location core_binds data_tycons+ prepd_binds <- {-# SCC "CorePrep" #-} do+ cp_cfg <- initCorePrepConfig hsc_env+ corePrepPgm+ (hsc_logger hsc_env)+ cp_cfg+ (initCorePrepPgmConfig (hsc_dflags hsc_env) (interactiveInScope $ hsc_IC hsc_env))+ this_mod location core_binds data_tycons -- The stg cg info only provides a runtime benfit, but is not requires so we just -- omit it here@@ -1761,6 +1970,32 @@ <- outputForeignStubs logger tmpfs dflags (hsc_units hsc_env) this_mod location foreign_stubs return (istub_c_exists, comp_bc, spt_entries) +generateByteCode :: HscEnv+ -> CgInteractiveGuts+ -> ModLocation+ -> IO [Unlinked]+generateByteCode hsc_env cgguts mod_location = do+ (hasStub, comp_bc, spt_entries) <- hscInteractive hsc_env cgguts mod_location++ stub_o <- case hasStub of+ Nothing -> return []+ Just stub_c -> do+ stub_o <- compileForeign hsc_env LangC stub_c+ return [DotO stub_o]++ let hs_unlinked = [BCOs comp_bc spt_entries]+ return (hs_unlinked ++ stub_o)++generateFreshByteCode :: HscEnv+ -> ModuleName+ -> CgInteractiveGuts+ -> ModLocation+ -> IO Linkable+generateFreshByteCode hsc_env mod_name cgguts mod_location = do+ ul <- generateByteCode hsc_env cgguts mod_location+ unlinked_time <- getCurrentTime+ let !linkable = LM unlinked_time (mkHomeModule (hsc_home_unit hsc_env) mod_name) ul+ return linkable ------------------------------ hscCompileCmmFile :: HscEnv -> FilePath -> FilePath -> FilePath -> IO (Maybe FilePath)@@ -1772,15 +2007,18 @@ profile = targetProfile dflags home_unit = hsc_home_unit hsc_env platform = targetPlatform dflags+ llvm_config = hsc_llvm_config hsc_env+ cmm_config = initCmmConfig dflags do_info_table = gopt Opt_InfoTableMap dflags -- Make up a module name to give the NCG. We can't pass bottom here -- lest we reproduce #11784. mod_name = mkModuleName $ "Cmm$" ++ original_filename cmm_mod = mkHomeModule home_unit mod_name+ cmmpConfig = initCmmParserConfig dflags (cmm, ipe_ents) <- ioMsgMaybe $ do (warns,errs,cmm) <- withTiming logger (text "ParseCmm"<+>brackets (text filename)) (\_ -> ())- $ parseCmmFile dflags cmm_mod home_unit filename+ $ parseCmmFile cmmpConfig cmm_mod home_unit filename let msgs = warns `unionMessages` errs return (GhcPsMessage <$> msgs, cmm) liftIO $ do@@ -1793,7 +2031,7 @@ -- in C we must declare before use, but SRT algorithm is free to -- re-order [A, B] (B refers to A) when A is not CAFFY and return [B, A] cmmgroup <-- concatMapM (\cmm -> snd <$> cmmPipeline hsc_env (emptySRT cmm_mod) [cmm]) cmm+ concatMapM (\cmm -> snd <$> cmmPipeline logger cmm_config (emptySRT cmm_mod) [cmm]) cmm unless (null cmmgroup) $ putDumpFileMaybe logger Opt_D_dump_cmm "Output Cmm"@@ -1809,11 +2047,11 @@ in NoStubs `appendStubC` ip_init | otherwise = NoStubs (_output_filename, (_stub_h_exists, stub_c_exists), _foreign_fps, _caf_infos)- <- codeOutput logger tmpfs dflags (hsc_units hsc_env) cmm_mod output_filename no_loc foreign_stubs [] S.empty+ <- codeOutput logger tmpfs llvm_config dflags (hsc_units hsc_env) cmm_mod output_filename no_loc foreign_stubs [] S.empty rawCmms return stub_c_exists where- no_loc = ModLocation{ ml_hs_file = Just filename,+ no_loc = ModLocation{ ml_hs_file = Just original_filename, ml_hi_file = panic "hscCompileCmmFile: no hi file", ml_obj_file = panic "hscCompileCmmFile: no obj file", ml_dyn_obj_file = panic "hscCompileCmmFile: no dyn obj file",@@ -1881,11 +2119,13 @@ ppr_stream1 = Stream.mapM dump1 cmm_stream + cmm_config = initCmmConfig dflags+ pipeline_stream :: Stream IO CmmGroupSRTs (NonCaffySet, ModuleLFInfos) pipeline_stream = do (non_cafs, lf_infos) <- {-# SCC "cmmPipeline" #-}- Stream.mapAccumL_ (cmmPipeline hsc_env) (emptySRT this_mod) ppr_stream1+ Stream.mapAccumL_ (cmmPipeline logger cmm_config) (emptySRT this_mod) ppr_stream1 <&> first (srtMapNonCAFs . moduleSRTMap) return (non_cafs, lf_infos)@@ -1910,7 +2150,7 @@ binding for the stg2stg step) -} let bco_tmp_id = mkSysLocal (fsLit "BCO_toplevel") (mkPseudoUniqueE 0)- Many+ ManyTy (exprType prepd_expr) (stg_binds, prov_map, collected_ccs, stg_cg_infos) <- myCoreToStg logger@@ -1932,11 +2172,11 @@ myCoreToStg logger dflags ictxt for_bytecode this_mod ml prepd_binds = do let (stg_binds, denv, cost_centre_info) = {-# SCC "Core2Stg" #-}- coreToStg dflags this_mod ml prepd_binds+ coreToStg (initCoreToStgOpts dflags) this_mod ml prepd_binds (stg_binds_with_fvs,stg_cg_info) <- {-# SCC "Stg2Stg" #-}- stg2stg logger ictxt (initStgPipelineOpts dflags for_bytecode)+ stg2stg logger (interactiveInScope ictxt) (initStgPipelineOpts dflags for_bytecode) this_mod stg_binds putDumpFileMaybe logger Opt_D_dump_stg_cg "CodeGenInput STG:" FormatSTG@@ -2016,7 +2256,7 @@ -> IO ([TyThing], InteractiveContext) hscDecls hsc_env str = hscDeclsWithLocation hsc_env str "<interactive>" 1 -hscParseModuleWithLocation :: HscEnv -> String -> Int -> String -> IO HsModule+hscParseModuleWithLocation :: HscEnv -> String -> Int -> String -> IO (HsModule GhcPs) hscParseModuleWithLocation hsc_env source line_num str = do L _ mod <- runInteractiveHsc hsc_env $@@ -2085,8 +2325,13 @@ {- Prepare For Code Generation -} -- Do saturation and convert to A-normal form- prepd_binds <- {-# SCC "CorePrep" #-}- liftIO $ corePrepPgm hsc_env this_mod iNTERACTIVELoc core_binds data_tycons+ prepd_binds <- {-# SCC "CorePrep" #-} liftIO $ do+ cp_cfg <- initCorePrepConfig hsc_env+ corePrepPgm+ (hsc_logger hsc_env)+ cp_cfg+ (initCorePrepPgmConfig (hsc_dflags hsc_env) (interactiveInScope $ hsc_IC hsc_env))+ this_mod iNTERACTIVELoc core_binds data_tycons (stg_binds, _infotable_prov, _caf_ccs__caf_cc_stacks, _stg_cg_info) <- {-# SCC "CoreToStg" #-}@@ -2158,14 +2403,18 @@ hscImport :: HscEnv -> String -> IO (ImportDecl GhcPs) hscImport hsc_env str = runInteractiveHsc hsc_env $ do- (L _ (HsModule{hsmodImports=is})) <-- hscParseThing parseModule str- case is of- [L _ i] -> return i- _ -> liftIO $ throwOneError $- mkPlainErrorMsgEnvelope noSrcSpan $- GhcPsMessage $ PsUnknownMessage $ mkPlainError noHints $- text "parse error in import declaration"+ -- Use >>= \case instead of MonadFail desugaring to take into+ -- consideration `instance XXModule p = DataConCantHappen`.+ -- Tracked in #15681+ hscParseThing parseModule str >>= \case+ (L _ (HsModule{hsmodImports=is})) ->+ case is of+ [L _ i] -> return i+ _ -> liftIO $ throwOneError $+ mkPlainErrorMsgEnvelope noSrcSpan $+ GhcPsMessage $ PsUnknownMessage $+ UnknownDiagnostic $ mkPlainError noHints $+ text "parse error in import declaration" -- | Typecheck an expression (but don't run it) hscTcExpr :: HscEnv@@ -2195,7 +2444,7 @@ Just (L _ (BodyStmt _ expr _ _)) -> return expr _ -> throwOneError $ mkPlainErrorMsgEnvelope noSrcSpan $- GhcPsMessage $ PsUnknownMessage $ mkPlainError noHints $+ GhcPsMessage $ PsUnknownMessage $ UnknownDiagnostic $ mkPlainError noHints $ text "not an expression:" <+> quotes (text expr) hscParseStmt :: String -> Hsc (Maybe (GhciLStmt GhcPs))@@ -2254,15 +2503,16 @@ -- post tidy pretty-printing and linting... let tidy_rules = md_rules details let all_tidy_binds = cg_binds cgguts- let print_unqual = mkPrintUnqualified (hsc_unit_env hsc_env) (mg_rdr_env guts)+ let name_ppr_ctx = mkNamePprCtx ptc (hsc_unit_env hsc_env) (mg_rdr_env guts)+ ptc = initPromotionTickContext (hsc_dflags hsc_env) - endPassIO hsc_env print_unqual CoreTidy all_tidy_binds tidy_rules+ endPassHscEnvIO hsc_env name_ppr_ctx CoreTidy all_tidy_binds tidy_rules -- If the endPass didn't print the rules, but ddump-rules is -- on, print now unless (logHasDumpFlag logger Opt_D_dump_simpl) $ putDumpFileMaybe logger Opt_D_dump_rules- (renderWithContext defaultSDocContext (ppr CoreTidy <+> text "rules"))+ "Tidy Core rules" FormatText (pprRulesForUser tidy_rules) @@ -2296,13 +2546,21 @@ = do { {- Simplify it -} -- Question: should we call SimpleOpt.simpleOptExpr here instead? -- It is, well, simpler, and does less inlining etc.- simpl_expr <- simplifyExpr hsc_env ds_expr+ let dflags = hsc_dflags hsc_env+ ; let logger = hsc_logger hsc_env+ ; let ic = hsc_IC hsc_env+ ; let unit_env = hsc_unit_env hsc_env+ ; let simplify_expr_opts = initSimplifyExprOpts dflags ic+ ; simpl_expr <- simplifyExpr logger (ue_eps unit_env) simplify_expr_opts ds_expr {- Tidy it (temporary, until coreSat does cloning) -} ; let tidy_expr = tidyExpr emptyTidyEnv simpl_expr {- Prepare for codegen -}- ; prepd_expr <- corePrepExpr hsc_env tidy_expr+ ; cp_cfg <- initCorePrepConfig hsc_env+ ; prepd_expr <- corePrepExpr+ logger cp_cfg+ tidy_expr {- Lint if necessary -} ; lintInteractiveExpr (text "hscCompileExpr") hsc_env prepd_expr@@ -2315,8 +2573,8 @@ ; let ictxt = hsc_IC hsc_env ; (binding_id, stg_expr, _, _, _stg_cg_info) <-- myCoreToStgExpr (hsc_logger hsc_env)- (hsc_dflags hsc_env)+ myCoreToStgExpr logger+ dflags ictxt True (icInteractiveModule ictxt)@@ -2369,4 +2627,4 @@ writeInterfaceOnlyMode :: DynFlags -> Bool writeInterfaceOnlyMode dflags = gopt Opt_WriteInterface dflags &&- NoBackend == backend dflags+ not (backendGeneratesCode (backend dflags))
compiler/GHC/Driver/Make.hs view
@@ -3,8 +3,8 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NondecreasingIndentation #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-} -{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE MultiParamTypeClasses #-}@@ -55,7 +55,7 @@ import GHC.Platform import GHC.Tc.Utils.Backpack-import GHC.Tc.Utils.Monad ( initIfaceCheck )+import GHC.Tc.Utils.Monad ( initIfaceCheck, concatMapM ) import GHC.Runtime.Interpreter import qualified GHC.Linker.Loader as Linker@@ -148,7 +148,11 @@ import GHC.Runtime.Loader import GHC.Rename.Names import GHC.Utils.Constants+import GHC.Types.Unique.DFM (udfmRestrictKeysSet)+import qualified Data.IntSet as I+import GHC.Types.Unique + -- ----------------------------------------------------------------------------- -- Loading the program @@ -442,7 +446,7 @@ addHmiToCache c (HomeModInfo i _ l) = iface_addToCache c (CachedIface i l) data CachedIface = CachedIface { cached_modiface :: !ModIface- , cached_linkable :: !(Maybe Linkable) }+ , cached_linkable :: !HomeModLinkable } instance Outputable CachedIface where ppr (CachedIface mi ln) = hsep [text "CachedIface", ppr (miKey mi), ppr ln]@@ -533,20 +537,29 @@ then emptyMessages else warn - -- | A ModuleGraphNode which also has a hs-boot file, and the list of nodes on any -- path from module to its boot file. data ModuleGraphNodeWithBootFile- = ModuleGraphNodeWithBootFile ModuleGraphNode [ModuleGraphNode]+ = ModuleGraphNodeWithBootFile+ ModuleGraphNode+ -- ^ The module itself (not the hs-boot module)+ [NodeKey]+ -- ^ The modules in between the module and its hs-boot file,+ -- not including the hs-boot file itself. + instance Outputable ModuleGraphNodeWithBootFile where ppr (ModuleGraphNodeWithBootFile mgn deps) = text "ModeGraphNodeWithBootFile: " <+> ppr mgn $$ ppr deps -getNode :: ModuleGraphNodeWithBootFile -> ModuleGraphNode-getNode (ModuleGraphNodeWithBootFile mgn _) = mgn-data BuildPlan = SingleModule ModuleGraphNode -- A simple, single module all alone but *might* have an hs-boot file which isn't part of a cycle- | ResolvedCycle [Either ModuleGraphNode ModuleGraphNodeWithBootFile] -- A resolved cycle, linearised by hs-boot files- | UnresolvedCycle [ModuleGraphNode] -- An actual cycle, which wasn't resolved by hs-boot files+-- | A 'BuildPlan' is the result of attempting to linearise a single strongly-connected+-- component of the module graph.+data BuildPlan+ -- | A simple, single module all alone (which *might* have an hs-boot file, if it isn't part of a cycle)+ = SingleModule ModuleGraphNode+ -- | A resolved cycle, linearised by hs-boot files+ | ResolvedCycle [Either ModuleGraphNode ModuleGraphNodeWithBootFile]+ -- | An actual cycle, which wasn't resolved by hs-boot files+ | UnresolvedCycle [ModuleGraphNode] instance Outputable BuildPlan where ppr (SingleModule mgn) = text "SingleModule" <> parens (ppr mgn)@@ -609,7 +622,7 @@ select_boot_modules :: [ModuleGraphNode] -> [ModuleGraphNode] select_boot_modules = mapMaybe (fmap fst . get_boot_module) - get_boot_module :: (ModuleGraphNode -> Maybe (ModuleGraphNode, [ModuleGraphNode]))+ get_boot_module :: ModuleGraphNode -> Maybe (ModuleGraphNode, [ModuleGraphNode]) get_boot_module m = case m of ModuleNode _ ms | HsSrcFile <- ms_hsc_src ms -> lookupModuleEnv boot_modules (ms_mod ms); _ -> Nothing -- Any cycles should be resolved now@@ -620,13 +633,13 @@ -- Cyclic collapseSCC _ = Nothing - toNodeWithBoot :: (ModuleGraphNode -> Either ModuleGraphNode ModuleGraphNodeWithBootFile)+ toNodeWithBoot :: ModuleGraphNode -> Either ModuleGraphNode ModuleGraphNodeWithBootFile toNodeWithBoot mn = case get_boot_module mn of -- The node doesn't have a boot file Nothing -> Left mn -- The node does have a boot file- Just path -> Right (ModuleGraphNodeWithBootFile mn (snd path))+ Just path -> Right (ModuleGraphNodeWithBootFile mn (map mkNodeKey (snd path))) -- The toposort and accumulation of acyclic modules is solely to pick-up -- hs-boot files which are **not** part of cycles.@@ -649,6 +662,9 @@ -- produced by calling 'depanal'. load' :: GhcMonad m => Maybe ModIfaceCache -> LoadHowMuch -> Maybe Messager -> ModuleGraph -> m SuccessFlag load' mhmi_cache how_much mHscMessage mod_graph = do+ -- In normal usage plugins are initialised already by ghc/Main.hs this is protective+ -- for any client who might interact with GHC via load'.+ initializeSessionPlugins modifySession $ \hsc_env -> hsc_env { hsc_mod_graph = mod_graph } guessOutputFile hsc_env <- getSession@@ -777,9 +793,10 @@ -- we must add the .exe extension unconditionally here, otherwise -- when name has an extension of its own, the .exe extension will -- not be added by GHC.Driver.Pipeline.exeFileName. See #2248- !name' <- if platformOS platform == OSMinGW32- then fmap (<.> "exe") name- else name+ !name' <- case platformArchOS platform of+ ArchOS _ OSMinGW32 -> fmap (<.> "exe") name+ ArchOS ArchWasm32 _ -> fmap (<.> "wasm") name+ _ -> name mainModuleSrcPath' <- mainModuleSrcPath -- #9930: don't clobber input files (unless they ask for it) if name' == mainModuleSrcPath'@@ -821,7 +838,7 @@ linkable' | Just ms <- M.lookup modl ms_map , mi_src_hash iface /= ms_hs_hash ms- = Nothing+ = emptyHomeModInfoLinkable | otherwise = linkable @@ -854,9 +871,9 @@ blocking. * runPipelines takes this list and eventually passes it to runLoop which executes each action and places the result into the right MVar.-* The amount of parrelism is controlled by a semaphore. This is just used around the+* The amount of parallelism is controlled by a semaphore. This is just used around the module compilation step, so that only the right number of modules are compiled at- the same time which reduces overal memory usage and allocations.+ the same time which reduces overall memory usage and allocations. * Each proper node has a LogQueue, which dictates where to send it's output. * The LogQueue is placed into the LogQueueQueue when the action starts and a worker thread processes the LogQueueQueue printing logs for each module in a stable order.@@ -960,8 +977,7 @@ -- | Simple wrapper around MVar which allows a functor instance. data ResultVar b = forall a . ResultVar (a -> b) (MVar (Maybe a)) -instance Functor ResultVar where- fmap f (ResultVar g var) = ResultVar (f . g) var+deriving instance Functor ResultVar mkResultVar :: MVar (Maybe a) -> ResultVar a mkResultVar = ResultVar id@@ -970,10 +986,22 @@ waitResult :: ResultVar a -> MaybeT IO a waitResult (ResultVar f var) = MaybeT (fmap f <$> readMVar var) +data BuildResult = BuildResult { _resultOrigin :: ResultOrigin+ , resultVar :: ResultVar (Maybe HomeModInfo, ModuleNameSet)+ } -data BuildLoopState = BuildLoopState { buildDep :: M.Map NodeKey (SDoc, ResultVar (Maybe HomeModInfo))+-- The origin of this result var, useful for debugging+data ResultOrigin = NoLoop | Loop ResultLoopOrigin deriving (Show)++data ResultLoopOrigin = Initialise | Rehydrated | Finalised deriving (Show)++mkBuildResult :: ResultOrigin -> ResultVar (Maybe HomeModInfo, ModuleNameSet) -> BuildResult+mkBuildResult = BuildResult+++data BuildLoopState = BuildLoopState { buildDep :: M.Map NodeKey BuildResult -- The current way to build a specific TNodeKey, without cycles this just points to- -- the appropiate result of compiling a module but with+ -- the appropriate result of compiling a module but with -- cycles there can be additional indirection and can point to the result of typechecking a loop , nNODE :: Int , hug_var :: MVar HomeUnitGraph@@ -987,14 +1015,20 @@ modify (\m -> m { nNODE = n + 1 }) return n -setModulePipeline :: NodeKey -> SDoc -> ResultVar (Maybe HomeModInfo) -> BuildM ()-setModulePipeline mgn doc wrapped_pipeline = do- modify (\m -> m { buildDep = M.insert mgn (doc, wrapped_pipeline) (buildDep m) }) -getBuildMap :: BuildM (M.Map- NodeKey (SDoc, ResultVar (Maybe HomeModInfo)))+setModulePipeline :: NodeKey -> BuildResult -> BuildM ()+setModulePipeline mgn build_result = do+ modify (\m -> m { buildDep = M.insert mgn build_result (buildDep m) })++type BuildMap = M.Map NodeKey BuildResult++getBuildMap :: BuildM BuildMap getBuildMap = gets buildDep +getDependencies :: [NodeKey] -> BuildMap -> [BuildResult]+getDependencies direct_deps build_map =+ strictMap (expectJust "dep_map" . flip M.lookup build_map) direct_deps+ type BuildM a = StateT BuildLoopState IO a @@ -1038,7 +1072,7 @@ where collect_results build_map =- sequence (map (\(_doc, res_var) -> collect_result res_var) (M.elems build_map))+ sequence (map (\br -> collect_result (fst <$> resultVar br)) (M.elems build_map)) where collect_result res_var = runMaybeT (waitResult res_var) @@ -1053,7 +1087,7 @@ case plan of -- If there was no cycle, then typecheckLoop is not necessary SingleModule m -> do- (one_plan, _) <- buildSingleModule Nothing m+ one_plan <- buildSingleModule Nothing NoLoop m (cycle, all_plans) <- buildLoop plans return (cycle, one_plan : all_plans) @@ -1068,75 +1102,126 @@ -- Can't continue past this point as the cycle is unresolved. UnresolvedCycle ns -> return (Just ns, []) - buildSingleModule :: Maybe [ModuleGraphNode] -- Modules we need to rehydrate before compiling this module+ buildSingleModule :: Maybe [NodeKey] -- Modules we need to rehydrate before compiling this module+ -> ResultOrigin -> ModuleGraphNode -- The node we are compiling- -> BuildM (MakeAction, ResultVar (Maybe HomeModInfo))- buildSingleModule rehydrate_nodes mod = do+ -> BuildM MakeAction+ buildSingleModule rehydrate_nodes origin mod = do mod_idx <- nodeId- home_mod_map <- getBuildMap+ !build_map <- getBuildMap hug_var <- gets hug_var- -- 1. Get the transitive dependencies of this module, by looking up in the dependency map+ -- 1. Get the direct dependencies of this module let direct_deps = nodeDependencies False mod- doc_build_deps = map (expectJust "dep_map" . flip M.lookup home_mod_map) direct_deps- build_deps = map snd doc_build_deps- -- 2. Set the default way to build this node, not in a loop here- let build_action = withCurrentUnit (moduleGraphNodeUnitId mod) $+ -- It's really important to force build_deps, or the whole buildMap is retained,+ -- which would retain all the result variables, preventing us from collecting them+ -- after they are no longer used.+ !build_deps = getDependencies direct_deps build_map+ let build_action =+ withCurrentUnit (moduleGraphNodeUnitId mod) $ do+ (hug, deps) <- wait_deps_hug hug_var build_deps case mod of- InstantiationNode uid iu ->- const Nothing <$> executeInstantiationNode mod_idx n_mods (wait_deps_hug hug_var build_deps) uid iu+ InstantiationNode uid iu -> do+ executeInstantiationNode mod_idx n_mods hug uid iu+ return (Nothing, deps) ModuleNode _build_deps ms -> do- let !old_hmi = M.lookup (msKey ms) old_hpt- rehydrate_mods = mapMaybe moduleGraphNodeModule <$> rehydrate_nodes- hmi <- executeCompileNode mod_idx n_mods old_hmi (wait_deps_hug hug_var build_deps) rehydrate_mods ms-- -- Write the HMI to an external cache (if one exists)- -- See Note [Caching HomeModInfo]- liftIO $ forM mhmi_cache $ \hmi_cache -> addHmiToCache hmi_cache hmi- -- This global MVar is incrementally modified in order to avoid having to- -- recreate the HPT before compiling each module which leads to a quadratic amount of work.- hsc_env <- asks hsc_env- hmi' <- liftIO $ modifyMVar hug_var (\hug -> do- let new_hpt = addHomeModInfoToHug hmi hug- new_hsc = setHUG new_hpt hsc_env- maybeRehydrateAfter hmi new_hsc rehydrate_mods- )- return (Just hmi')+ let !old_hmi = M.lookup (msKey ms) old_hpt+ rehydrate_mods = mapMaybe nodeKeyModName <$> rehydrate_nodes+ hmi <- executeCompileNode mod_idx n_mods old_hmi hug rehydrate_mods ms+ -- Write the HMI to an external cache (if one exists)+ -- See Note [Caching HomeModInfo]+ liftIO $ forM mhmi_cache $ \hmi_cache -> addHmiToCache hmi_cache hmi+ -- This global MVar is incrementally modified in order to avoid having to+ -- recreate the HPT before compiling each module which leads to a quadratic amount of work.+ liftIO $ modifyMVar_ hug_var (return . addHomeModInfoToHug hmi)+ return (Just hmi, addToModuleNameSet (moduleGraphNodeUnitId mod) (ms_mod_name ms) deps ) LinkNode _nks uid -> do- executeLinkNode (wait_deps_hug hug_var build_deps) (mod_idx, n_mods) uid direct_deps- return Nothing+ executeLinkNode hug (mod_idx, n_mods) uid direct_deps+ return (Nothing, deps) res_var <- liftIO newEmptyMVar let result_var = mkResultVar res_var- setModulePipeline (mkNodeKey mod) (text "N") result_var- return $ (MakeAction build_action res_var, result_var)+ setModulePipeline (mkNodeKey mod) (mkBuildResult origin result_var)+ return $ (MakeAction build_action res_var) - buildOneLoopyModule :: ModuleGraphNodeWithBootFile -> BuildM (MakeAction, (ResultVar (Maybe HomeModInfo)))- buildOneLoopyModule (ModuleGraphNodeWithBootFile mn deps) =- buildSingleModule (Just deps) mn+ buildOneLoopyModule :: ModuleGraphNodeWithBootFile -> BuildM [MakeAction]+ buildOneLoopyModule (ModuleGraphNodeWithBootFile mn deps) = do+ ma <- buildSingleModule (Just deps) (Loop Initialise) mn+ -- Rehydration (1) from Note [Hydrating Modules], "Loops with multiple boot files"+ rehydrate_action <- rehydrateAction Rehydrated ((GWIB (mkNodeKey mn) IsBoot) : (map (\d -> GWIB d NotBoot) deps))+ return $ [ma, rehydrate_action] - buildModuleLoop :: [Either ModuleGraphNode ModuleGraphNodeWithBootFile] -> BuildM [MakeAction]++ buildModuleLoop :: [Either ModuleGraphNode ModuleGraphNodeWithBootFile] -> BuildM [MakeAction] buildModuleLoop ms = do- (build_modules, wait_modules) <- mapAndUnzipM (either (buildSingleModule Nothing) buildOneLoopyModule) ms+ build_modules <- concatMapM (either (fmap (:[]) <$> buildSingleModule Nothing (Loop Initialise)) buildOneLoopyModule) ms+ let extract (Left mn) = GWIB (mkNodeKey mn) NotBoot+ extract (Right (ModuleGraphNodeWithBootFile mn _)) = GWIB (mkNodeKey mn) IsBoot+ let loop_mods = map extract ms+ -- Rehydration (2) from Note [Hydrating Modules], "Loops with multiple boot files"+ -- Fixes the space leak described in that note.+ rehydrate_action <- rehydrateAction Finalised loop_mods++ return $ build_modules ++ [rehydrate_action]++ -- An action which rehydrates the given keys+ rehydrateAction :: ResultLoopOrigin -> [GenWithIsBoot NodeKey] -> BuildM MakeAction+ rehydrateAction origin deps = do+ hug_var <- gets hug_var+ !build_map <- getBuildMap res_var <- liftIO newEmptyMVar- let loop_action = wait_deps wait_modules- let fanout i = Just . (!! i) <$> mkResultVar res_var+ let loop_unit :: UnitId+ !loop_unit = nodeKeyUnitId (gwib_mod (head deps))+ !build_deps = getDependencies (map gwib_mod deps) build_map+ let loop_action = withCurrentUnit loop_unit $ do+ (hug, tdeps) <- wait_deps_hug hug_var build_deps+ hsc_env <- asks hsc_env+ let new_hsc = setHUG hug hsc_env+ mns :: [ModuleName]+ mns = mapMaybe (nodeKeyModName . gwib_mod) deps++ hmis' <- liftIO $ rehydrateAfter new_hsc mns++ checkRehydrationInvariant hmis' deps++ -- Add hydrated interfaces to global variable+ liftIO $ modifyMVar_ hug_var (\hug -> return $ foldr addHomeModInfoToHug hug hmis')+ return (hmis', tdeps)++ let fanout i = first (Just . (!! i)) <$> mkResultVar res_var -- From outside the module loop, anyone must wait for the loop to finish and then -- use the result of the rehydrated iface. This makes sure that things not in the -- module loop will see the updated interfaces for all the identifiers in the loop.- let update_module_pipeline (m, i) = setModulePipeline (NodeKey_Module m) (text "T") (fanout i)+ boot_key :: NodeKey -> NodeKey+ boot_key (NodeKey_Module m) = NodeKey_Module (m { mnkModuleName = (mnkModuleName m) { gwib_isBoot = IsBoot } } )+ boot_key k = pprPanic "boot_key" (ppr k) - let ms_i = zip (mapMaybe (fmap msKey . moduleGraphNodeModSum . either id getNode) ms) [0..]- mapM update_module_pipeline ms_i- return $ build_modules ++ [MakeAction loop_action res_var]+ update_module_pipeline (m, i) =+ case gwib_isBoot m of+ NotBoot -> setModulePipeline (gwib_mod m) (mkBuildResult (Loop origin) (fanout i))+ IsBoot -> do+ setModulePipeline (gwib_mod m) (mkBuildResult (Loop origin) (fanout i))+ -- SPECIAL: Anything outside the loop needs to see A rather than A.hs-boot+ setModulePipeline (boot_key (gwib_mod m)) (mkBuildResult (Loop origin) (fanout i)) + let deps_i = zip deps [0..]+ mapM update_module_pipeline deps_i + return $ MakeAction loop_action res_var++ -- Checks that the interfaces returned from hydration match-up with the names of the+ -- modules which were fed into the function.+ checkRehydrationInvariant hmis deps =+ let hmi_names = map (moduleName . mi_module . hm_iface) hmis+ start = mapMaybe (nodeKeyModName . gwib_mod) deps+ in massertPpr (hmi_names == start) $ (ppr hmi_names $$ ppr start)++ withCurrentUnit :: UnitId -> RunMakeM a -> RunMakeM a withCurrentUnit uid = do local (\env -> env { hsc_env = hscSetActiveUnitId uid (hsc_env env)}) - upsweep :: Int -- ^ The number of workers we wish to run in parallel -> HscEnv -- ^ The base HscEnv, which is augmented for each module@@ -1195,7 +1280,7 @@ -> IO HomeModInfo upsweep_mod hsc_env mHscMessage old_hmi summary mod_index nmods = do hmi <- compileOne' mHscMessage hsc_env summary- mod_index nmods (hm_iface <$> old_hmi) (old_hmi >>= hm_linkable)+ mod_index nmods (hm_iface <$> old_hmi) (maybe emptyHomeModInfoLinkable hm_linkable old_hmi) -- MP: This is a bit janky, because before you add the entries you have to extend the HPT with the module -- you just compiled. Another option, would be delay adding anything until after upsweep has finished, but I@@ -1203,7 +1288,7 @@ -- This function only does anything if the linkable produced is a BCO, which only happens with the -- bytecode backend, no need to guard against the backend type additionally. addSptEntries (hscUpdateHPT (\hpt -> addToHpt hpt (ms_mod_name summary) hmi) hsc_env)- (hm_linkable hmi)+ (homeModInfoByteCode hmi) return hmi @@ -1253,10 +1338,12 @@ that is imported by a module that uses template haskell, to generate object code. -The flavour of generated object code is chosen by defaultObjectTarget for the-target platform. It would likely be faster to generate bytecode, but this is not-supported on all platforms(?Please Confirm?), and does not support the entirety-of GHC haskell. See #1257.+The flavour of the generated code depends on whether `-fprefer-byte-code` is enabled+or not in the module which needs the code generation. If the module requires byte-code then+dependencies will generate byte-code, otherwise they will generate object files.+In the case where some modules require byte-code and some object files, both are+generated by enabling `-fbyte-code-and-object-code`, the test "fat015" tests these+configurations. The object files (and interface files if -fwrite-interface is disabled) produced for template haskell are written to temporary files.@@ -1683,6 +1770,12 @@ enableCodeGenForTH logger tmpfs unit_env = enableCodeGenWhen logger tmpfs TFL_CurrentModule TFL_GhcSession unit_env ++data CodeGenEnable = EnableByteCode | EnableObject | EnableByteCodeAndObject deriving (Eq, Show, Ord)++instance Outputable CodeGenEnable where+ ppr = text . show+ -- | Helper used to implement 'enableCodeGenForTH'. -- In particular, this enables -- unoptimized code generation for all modules that meet some@@ -1708,7 +1801,7 @@ , ms_hsc_src = HsSrcFile , ms_hspp_opts = dflags } <- ms- , mkNodeKey n `Set.member` needs_codegen_set =+ , Just enable_spec <- mkNodeKey n `Map.lookup` needs_codegen_map = if | nocode_enable ms -> do let new_temp_file suf dynsuf = do tn <- newTempName logger tmpfs (tmpDir dflags) staticLife suf@@ -1727,18 +1820,32 @@ , (ml_obj_file ms_location, ml_dyn_obj_file ms_location)) else (,) <$> (new_temp_file (hiSuf_ dflags) (dynHiSuf_ dflags)) <*> (new_temp_file (objectSuf_ dflags) (dynObjectSuf_ dflags))+ let new_dflags = case enable_spec of+ EnableByteCode -> dflags { backend = interpreterBackend }+ EnableObject -> dflags { backend = defaultBackendOf ms }+ EnableByteCodeAndObject -> (gopt_set dflags Opt_ByteCodeAndObjectCode) { backend = defaultBackendOf ms} let ms' = ms { ms_location = ms_location { ml_hi_file = hi_file , ml_obj_file = o_file , ml_dyn_hi_file = dyn_hi_file , ml_dyn_obj_file = dyn_o_file }- , ms_hspp_opts = updOptLevel 0 $ dflags {backend = defaultBackendOf ms}+ , ms_hspp_opts = updOptLevel 0 $ new_dflags } -- Recursive call to catch the other cases enable_code_gen (ModuleNode deps ms')- | dynamic_too_enable ms -> do++ -- If -fprefer-byte-code then satisfy dependency by enabling bytecode (if normal object not enough)+ -- we only get to this case if the default backend is already generating object files, but we need dynamic+ -- objects+ | bytecode_and_enable enable_spec ms -> do let ms' = ms+ { ms_hspp_opts = gopt_set (ms_hspp_opts ms) Opt_ByteCodeAndObjectCode+ }+ -- Recursive call to catch the other cases+ enable_code_gen (ModuleNode deps ms')+ | dynamic_too_enable enable_spec ms -> do+ let ms' = ms { ms_hspp_opts = gopt_set (ms_hspp_opts ms) Opt_BuildDynamicToo } -- Recursive call to catch the other cases@@ -1755,23 +1862,45 @@ enable_code_gen ms = return ms nocode_enable ms@(ModSummary { ms_hspp_opts = dflags }) =- backend dflags == NoBackend &&+ not (backendGeneratesCode (backend dflags)) && -- Don't enable codegen for TH on indefinite packages; we -- can't compile anything anyway! See #16219. isHomeUnitDefinite (ue_unitHomeUnit (ms_unitid ms) unit_env) + bytecode_and_enable enable_spec ms =+ -- In the situation where we **would** need to enable dynamic-too+ -- IF we had decided we needed objects+ dynamic_too_enable EnableObject ms+ -- but we prefer to use bytecode rather than objects+ && prefer_bytecode+ -- and we haven't already turned it on+ && not generate_both+ where+ lcl_dflags = ms_hspp_opts ms+ prefer_bytecode = case enable_spec of+ EnableByteCodeAndObject -> True+ EnableByteCode -> True+ EnableObject -> False++ generate_both = gopt Opt_ByteCodeAndObjectCode lcl_dflags+ -- #8180 - when using TemplateHaskell, switch on -dynamic-too so -- the linker can correctly load the object files. This isn't necessary -- when using -fexternal-interpreter.- dynamic_too_enable ms+ dynamic_too_enable enable_spec ms = hostIsDynamic && internalInterpreter && not isDynWay && not isProfWay && not dyn_too_enabled+ && enable_object where lcl_dflags = ms_hspp_opts ms internalInterpreter = not (gopt Opt_ExternalInterpreter lcl_dflags)- dyn_too_enabled = (gopt Opt_BuildDynamicToo lcl_dflags)+ dyn_too_enabled = gopt Opt_BuildDynamicToo lcl_dflags isDynWay = hasWay (ways lcl_dflags) WayDyn isProfWay = hasWay (ways lcl_dflags) WayProf+ enable_object = case enable_spec of+ EnableByteCode -> False+ EnableByteCodeAndObject -> True+ EnableObject -> True -- #16331 - when no "internal interpreter" is available but we -- need to process some TemplateHaskell or QuasiQuotes, we automatically@@ -1781,19 +1910,44 @@ lcl_dflags = ms_hspp_opts ms internalInterpreter = not (gopt Opt_ExternalInterpreter lcl_dflags) + (mg, lookup_node) = moduleGraphNodes False mod_graph + mk_needed_set roots = Set.fromList $ map (mkNodeKey . node_payload) $ reachablesG mg (map (expectJust "needs_th" . lookup_node) roots) + needs_obj_set, needs_bc_set :: Set.Set NodeKey+ needs_obj_set = mk_needed_set need_obj_set - (mg, lookup_node) = moduleGraphNodes False mod_graph- needs_codegen_set = Set.fromList $ map (mkNodeKey . node_payload) $ reachablesG mg (map (expectJust "needs_th" . lookup_node) has_th_set)+ needs_bc_set = mk_needed_set need_bc_set + -- A map which tells us how to enable code generation for a NodeKey+ needs_codegen_map :: Map.Map NodeKey CodeGenEnable+ needs_codegen_map =+ -- Another option here would be to just produce object code, rather than both object and+ -- byte code+ Map.unionWith (\_ _ -> EnableByteCodeAndObject)+ (Map.fromList $ [(m, EnableObject) | m <- Set.toList needs_obj_set])+ (Map.fromList $ [(m, EnableByteCode) | m <- Set.toList needs_bc_set]) - has_th_set =- [ mkNodeKey mn- | mn@(ModuleNode _ ms) <- mod_graph- , isTemplateHaskellOrQQNonBoot ms- ]+ -- The direct dependencies of modules which require object code+ need_obj_set =+ concat+ -- Note we don't need object code for a module if it uses TemplateHaskell itself. Only+ -- it's dependencies.+ [ deps+ | (ModuleNode deps ms) <- mod_graph+ , isTemplateHaskellOrQQNonBoot ms+ , not (gopt Opt_UseBytecodeRatherThanObjects (ms_hspp_opts ms))+ ] + -- The direct dependencies of modules which require byte code+ need_bc_set =+ concat+ [ deps+ | (ModuleNode deps ms) <- mod_graph+ , isTemplateHaskellOrQQNonBoot ms+ , gopt Opt_UseBytecodeRatherThanObjects (ms_hspp_opts ms)+ ]+ -- | Populate the Downsweep cache with the root modules. mkRootMap :: [ModSummary]@@ -2155,9 +2309,9 @@ let deferDiagnostics _dflags !msgClass !srcSpan !msg = do let action = logMsg logger msgClass srcSpan msg case msgClass of- MCDiagnostic SevWarning _reason+ MCDiagnostic SevWarning _reason _code -> atomicModifyIORef' warnings $ \i -> (action: i, ())- MCDiagnostic SevError _reason+ MCDiagnostic SevError _reason _code -> atomicModifyIORef' errors $ \i -> (action: i, ()) MCFatal -> atomicModifyIORef' fatals $ \i -> (action: i, ())@@ -2178,7 +2332,8 @@ noModError :: HscEnv -> SrcSpan -> ModuleName -> FindResult -> MsgEnvelope GhcMessage -- ToDo: we don't have a proper line number for this error noModError hsc_env loc wanted_mod err- = mkPlainErrorMsgEnvelope loc $ GhcDriverMessage $ DriverUnknownMessage $ mkPlainError noHints $+ = mkPlainErrorMsgEnvelope loc $ GhcDriverMessage $+ DriverUnknownMessage $ UnknownDiagnostic $ mkPlainError noHints $ cannotFindModule hsc_env wanted_mod err {-@@ -2263,7 +2418,8 @@ wrapAction hsc_env k = do let lcl_logger = hsc_logger hsc_env lcl_dynflags = hsc_dflags hsc_env- let logg err = printMessages lcl_logger (initDiagOpts lcl_dynflags) (srcErrorMessages err)+ print_config = initPrintConfig lcl_dynflags+ let logg err = printMessages lcl_logger print_config (initDiagOpts lcl_dynflags) (srcErrorMessages err) -- MP: It is a bit strange how prettyPrintGhcErrors handles some errors but then we handle -- SourceError and ThreadKilled differently directly below. TODO: Refactor to use `catches` -- directly. MP should probably use safeTry here to not catch async exceptions but that will regress performance due to@@ -2304,13 +2460,11 @@ executeInstantiationNode :: Int -> Int- -> RunMakeM HomeUnitGraph+ -> HomeUnitGraph -> UnitId -> InstantiatedUnit -> RunMakeM ()-executeInstantiationNode k n wait_deps uid iu = do- -- Wait for the dependencies of this node- deps <- wait_deps+executeInstantiationNode k n deps uid iu = do env <- ask -- Output of the logger is mediated by a central worker to -- avoid output interleaving@@ -2326,23 +2480,22 @@ executeCompileNode :: Int -> Int -> Maybe HomeModInfo- -> RunMakeM HomeUnitGraph+ -> HomeUnitGraph -> Maybe [ModuleName] -- List of modules we need to rehydrate before compiling -> ModSummary -> RunMakeM HomeModInfo-executeCompileNode k n !old_hmi wait_deps mrehydrate_mods mod = do+executeCompileNode k n !old_hmi hug mrehydrate_mods mod = do me@MakeEnv{..} <- ask- deps <- wait_deps -- Rehydrate any dependencies if this module had a boot file or is a signature file. lift $ MaybeT (withAbstractSem compile_sem $ withLoggerHsc k me $ \hsc_env -> do- hydrated_hsc_env <- liftIO $ maybeRehydrateBefore (setHUG deps hsc_env) mod fixed_mrehydrate_mods+ hydrated_hsc_env <- liftIO $ maybeRehydrateBefore (setHUG hug hsc_env) mod fixed_mrehydrate_mods let -- Use the cached DynFlags which includes OPTIONS_GHC pragmas lcl_dynflags = ms_hspp_opts mod let lcl_hsc_env = -- Localise the hsc_env to use the cached flags hscSetFlags lcl_dynflags $ hydrated_hsc_env- -- Compile the module, locking with a semphore to avoid too many modules+ -- Compile the module, locking with a semaphore to avoid too many modules -- being compiled at the same time leading to high memory usage. wrapAction lcl_hsc_env $ do res <- upsweep_mod lcl_hsc_env env_messager old_hmi mod k n@@ -2364,11 +2517,11 @@ -> [HomeModInfo] -- ^ These are the modules we want to rehydrate. -> IO HscEnv rehydrate hsc_env hmis = do- debugTraceMsg logger 2 $- text "Re-hydrating loop: "+ debugTraceMsg logger 2 $ (+ text "Re-hydrating loop: " <+> (ppr (map (mi_module . hm_iface) hmis))) new_mods <- fixIO $ \new_mods -> do let new_hpt = addListToHpt old_hpt new_mods- let new_hsc_env = hscUpdateHPT (const new_hpt) hsc_env+ let new_hsc_env = hscUpdateHPT_lazy (const new_hpt) hsc_env mds <- initIfaceCheck (text "rehydrate") new_hsc_env $ mapM (typecheckIface . hm_iface) hmis let new_mods = [ (mn,hmi{ hm_details = details })@@ -2398,17 +2551,14 @@ let mod_name = homeModuleInstantiation (hsc_home_unit_maybe hsc_env) (ms_mod mod) in mkModuleEnv . (:[]) . (mod_name,) <$> newIORef emptyTypeEnv -maybeRehydrateAfter :: HomeModInfo- -> HscEnv- -> Maybe [ModuleName]- -> IO (HomeUnitGraph, HomeModInfo)-maybeRehydrateAfter hmi new_hsc Nothing = return (hsc_HUG new_hsc, hmi)-maybeRehydrateAfter hmi new_hsc (Just mns) = do+rehydrateAfter :: HscEnv+ -> [ModuleName]+ -> IO [HomeModInfo]+rehydrateAfter new_hsc mns = do let new_hpt = hsc_HPT new_hsc hmis = map (expectJust "mrAfter" . lookupHpt new_hpt) mns- new_mod_name = moduleName (mi_module (hm_iface hmi))- hsc_env <- rehydrate (new_hsc { hsc_type_env_vars = emptyKnotVars }) (hmi : hmis)- return (hsc_HUG hsc_env, expectJust "rehydrate" $ lookupHpt (hsc_HPT hsc_env) new_mod_name)+ hsc_env <- rehydrate (new_hsc { hsc_type_env_vars = emptyKnotVars }) hmis+ return $ map (\mn -> expectJust "rehydrate" $ lookupHpt (hsc_HPT hsc_env) mn) mns {- Note [Hydrating Modules]@@ -2509,6 +2659,49 @@ There might be many unrelated modules (in the home package) that don't need to be rehydrated. +== Loops with multiple boot files++It is possible for a module graph to have a loop (SCC, when ignoring boot files)+which requires multiple boot files to break. In this case, we must perform+several hydration steps:+ 1. The hydration steps described above, which are necessary for correctness.+ 2. An extra hydration step at the end of compiling the entire SCC, in order to+ remove space leaks, as we explain below.++Consider the following example:++ ┌─────┐ ┌─────┐+ │ A │ │ B │+ └──┬──┘ └──┬──┘+ │ │+ ┌───▼───────────▼───┐+ │ C │+ └───┬───────────┬───┘+ │ │+ ┌────▼───┐ ┌───▼────┐+ │ A-boot │ │ B-boot │+ └────────┘ └────────┘++A, B and C live together in a SCC. Suppose that we compile the modules in the+order:++ A-boot, B-boot, C, A, B.++When we come to compile A, we will perform the necessary hydration steps,+because A has a boot file. This means that C will be hydrated relative to A,+and the ModDetails for A will reference C/A. Then, when B is compiled,+C will be rehydrated again, and so B will reference C/A,B. At this point,+its interface will be hydrated relative to both A and B.+This causes a space leak: there are now two different copies of C's ModDetails,+kept alive by modules A and B. This is especially problematic if C is large.++The way to avoid this space leak is to rehydrate an entire SCC together at the+end of compilation, so that all the ModDetails point to interfaces for .hs files.+In this example, when we hydrate A, B and C together, then both A and B will refer to+C/A,B.++See #21900 for some more discussion.+ == Modules "above" the loop This dark corner is the subject of #14092.@@ -2520,9 +2713,9 @@ data XT = MkX T fx = ...g... ```-If in `--make` we compile R.hs-boot, then A.hs, then X.hs, we'll get a `ModDetails` for `X` that has an AbstractTyCon for `T` in the the argument type of `MkX`. So:+If in `--make` we compile R.hs-boot, then A.hs, then X.hs, we'll get a `ModDetails` for `X` that has an AbstractTyCon for `T` in the argument type of `MkX`. So: -* Either we should delay compiling X until after R has beeen compiled. (This is what we do)+* Either we should delay compiling X until after R has been compiled. (This is what we do) * Or we should rehydrate X after compiling R -- because it transitively depends on R.hs-boot. Ticket #20200 has exposed some issues to do with the knot-tying logic in GHC.Make, in `--make` mode.@@ -2536,11 +2729,10 @@ -} -executeLinkNode :: RunMakeM HomeUnitGraph -> (Int, Int) -> UnitId -> [NodeKey] -> RunMakeM ()-executeLinkNode wait_deps kn uid deps = do+executeLinkNode :: HomeUnitGraph -> (Int, Int) -> UnitId -> [NodeKey] -> RunMakeM ()+executeLinkNode hug kn uid deps = do withCurrentUnit uid $ do MakeEnv{..} <- ask- hug <- wait_deps let dflags = hsc_dflags hsc_env let hsc_env' = setHUG hug hsc_env msg' = (\messager -> \recomp -> messager hsc_env kn recomp (LinkNode deps uid)) <$> env_messager@@ -2559,29 +2751,77 @@ Failed -> fail "Link Failed" Succeeded -> return () +{-+Note [ModuleNameSet, efficiency and space leaks]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +During upsweep, the results of compiling modules are placed into a MVar. When we need+to compute the right compilation environment for a module, we consult this MVar and+set the HomeUnitGraph accordingly. This is done to avoid having to precisely track+module dependencies and recreating the HUG from scratch each time, which is very expensive.++In serial mode (-j1), this all works out fine: a module can only be compiled+after its dependencies have finished compiling, and compilation can't be+interleaved with the compilation of other module loops. This ensures that+the HUG only ever contains finalised interfaces.++In parallel mode, we have to be more careful: the HUG variable can contain non-finalised+interfaces, which have been started by another thread. In order to avoid a space leak+in which a finalised interface is compiled against a HPT which contains a non-finalised+interface, we have to restrict the HUG to only contain the visible modules.++The collection of visible modules explains which transitive modules are visible+from a certain point. It is recorded in the ModuleNameSet.+Before a module is compiled, we use this set to restrict the HUG to the visible+modules only, avoiding this tricky space leak.++Efficiency of the ModuleNameSet is of utmost importance, because a union occurs for+each edge in the module graph. To achieve this, the set is represented directly as an IntSet,+which provides suitable performance – even using a UniqSet (which is backed by an IntMap) is+too slow. The crucial test of performance here is the time taken to a do a no-op build in --make mode.++See test "jspace" for an example which used to trigger this problem.++-}++-- See Note [ModuleNameSet, efficiency and space leaks]+type ModuleNameSet = M.Map UnitId I.IntSet++addToModuleNameSet :: UnitId -> ModuleName -> ModuleNameSet -> ModuleNameSet+addToModuleNameSet uid mn s =+ let k = (getKey $ getUnique $ mn)+ in M.insertWith (I.union) uid (I.singleton k) s+ -- | Wait for some dependencies to finish and then read from the given MVar.-wait_deps_hug :: MVar b -> [ResultVar (Maybe HomeModInfo)] -> ReaderT MakeEnv (MaybeT IO) b+wait_deps_hug :: MVar HomeUnitGraph -> [BuildResult] -> ReaderT MakeEnv (MaybeT IO) (HomeUnitGraph, ModuleNameSet) wait_deps_hug hug_var deps = do- _ <- wait_deps deps- liftIO $ readMVar hug_var-+ (_, module_deps) <- wait_deps deps+ hug <- liftIO $ readMVar hug_var+ let pruneHomeUnitEnv uid hme =+ let -- Restrict to things which are in the transitive closure to avoid retaining+ -- reference to loop modules which have already been compiled by other threads.+ -- See Note [ModuleNameSet, efficiency and space leaks]+ !new = udfmRestrictKeysSet (homeUnitEnv_hpt hme) (fromMaybe I.empty $ M.lookup uid module_deps)+ in hme { homeUnitEnv_hpt = new }+ return (unitEnv_mapWithKey pruneHomeUnitEnv hug, module_deps) -- | Wait for dependencies to finish, and then return their results.-wait_deps :: [ResultVar (Maybe HomeModInfo)] -> RunMakeM [HomeModInfo]-wait_deps [] = return []+wait_deps :: [BuildResult] -> RunMakeM ([HomeModInfo], ModuleNameSet)+wait_deps [] = return ([], M.empty) wait_deps (x:xs) = do- res <- lift $ waitResult x+ (res, deps) <- lift $ waitResult (resultVar x)+ (hmis, all_deps) <- wait_deps xs+ let !new_deps = deps `unionModuleNameSet` all_deps case res of- Nothing -> wait_deps xs- Just hmi -> (hmi:) <$> wait_deps xs+ Nothing -> return (hmis, new_deps)+ Just hmi -> return (hmi:hmis, new_deps)+ where+ unionModuleNameSet = M.unionWith I.union -- Executing the pipelines --- | Start a thread which reads from the LogQueueQueue - label_self :: String -> IO () label_self thread_name = do self_tid <- CC.myThreadId@@ -2591,13 +2831,11 @@ runPipelines :: Int -> HscEnv -> Maybe Messager -> [MakeAction] -> IO () -- Don't even initialise plugins if there are no pipelines runPipelines _ _ _ [] = return ()-runPipelines n_job orig_hsc_env mHscMessager all_pipelines = do+runPipelines n_job hsc_env mHscMessager all_pipelines = do liftIO $ label_self "main --make thread"-- plugins_hsc_env <- initializePlugins orig_hsc_env case n_job of- 1 -> runSeqPipelines plugins_hsc_env mHscMessager all_pipelines- _n -> runParPipelines n_job plugins_hsc_env mHscMessager all_pipelines+ 1 -> runSeqPipelines hsc_env mHscMessager all_pipelines+ _n -> runParPipelines n_job hsc_env mHscMessager all_pipelines runSeqPipelines :: HscEnv -> Maybe Messager -> [MakeAction] -> IO () runSeqPipelines plugin_hsc_env mHscMessager all_pipelines =@@ -2687,7 +2925,7 @@ MC.bracket spawn_actions kill_actions $ \_ -> do mapM_ waitMakeAction acts --- | Execute each action in order, limiting the amount of parrelism by the given+-- | Execute each action in order, limiting the amount of parallelism by the given -- semaphore. runLoop :: (((forall a. IO a -> IO a) -> IO ()) -> IO a) -> MakeEnv -> [MakeAction] -> IO [a] runLoop _ _env [] = return []@@ -2740,4 +2978,9 @@ Where? No one place in the compiler. These leaks can be introduced by not suitable forcing functions which take a TcLclEnv as an argument. +5. At the end of a successful upsweep, the number of live ModDetails equals the+ number of non-boot Modules.+ Why? Each module has a HomeModInfo which contains a ModDetails from that module.+ Where? See Note [ModuleNameSet, efficiency and space leaks], a variety of places+ in the driver are responsible. -}
+ compiler/GHC/Driver/MakeFile.hs view
@@ -0,0 +1,454 @@+++-----------------------------------------------------------------------------+--+-- Makefile Dependency Generation+--+-- (c) The University of Glasgow 2005+--+-----------------------------------------------------------------------------++module GHC.Driver.MakeFile+ ( doMkDependHS+ )+where++import GHC.Prelude++import qualified GHC+import GHC.Driver.Monad+import GHC.Driver.Session+import GHC.Driver.Ppr+import GHC.Utils.Misc+import GHC.Driver.Env+import GHC.Driver.Errors.Types+import qualified GHC.SysTools as SysTools+import GHC.Data.Graph.Directed ( SCC(..) )+import GHC.Utils.Outputable+import GHC.Utils.Panic+import GHC.Utils.Panic.Plain+import GHC.Types.Error (UnknownDiagnostic(..))+import GHC.Types.SourceError+import GHC.Types.SrcLoc+import GHC.Types.PkgQual+import Data.List (partition)+import GHC.Utils.TmpFs++import GHC.Iface.Load (cannotFindModule)++import GHC.Unit.Module+import GHC.Unit.Module.ModSummary+import GHC.Unit.Module.Graph+import GHC.Unit.Finder++import GHC.Utils.Exception+import GHC.Utils.Error+import GHC.Utils.Logger++import System.Directory+import System.FilePath+import System.IO+import System.IO.Error ( isEOFError )+import Control.Monad ( when, forM_ )+import Data.Maybe ( isJust )+import Data.IORef+import qualified Data.Set as Set++-----------------------------------------------------------------+--+-- The main function+--+-----------------------------------------------------------------++doMkDependHS :: GhcMonad m => [FilePath] -> m ()+doMkDependHS srcs = do+ logger <- getLogger++ -- Initialisation+ dflags0 <- GHC.getSessionDynFlags++ -- We kludge things a bit for dependency generation. Rather than+ -- generating dependencies for each way separately, we generate+ -- them once and then duplicate them for each way's osuf/hisuf.+ -- We therefore do the initial dependency generation with an empty+ -- way and .o/.hi extensions, regardless of any flags that might+ -- be specified.+ let dflags1 = dflags0+ { targetWays_ = Set.empty+ , hiSuf_ = "hi"+ , objectSuf_ = "o"+ }+ GHC.setSessionDynFlags dflags1++ -- If no suffix is provided, use the default -- the empty one+ let dflags = if null (depSuffixes dflags1)+ then dflags1 { depSuffixes = [""] }+ else dflags1++ tmpfs <- hsc_tmpfs <$> getSession+ files <- liftIO $ beginMkDependHS logger tmpfs dflags++ -- Do the downsweep to find all the modules+ targets <- mapM (\s -> GHC.guessTarget s Nothing Nothing) srcs+ GHC.setTargets targets+ let excl_mods = depExcludeMods dflags+ module_graph <- GHC.depanal excl_mods True {- Allow dup roots -}++ -- Sort into dependency order+ -- There should be no cycles+ let sorted = GHC.topSortModuleGraph False module_graph Nothing++ -- Print out the dependencies if wanted+ liftIO $ debugTraceMsg logger 2 (text "Module dependencies" $$ ppr sorted)++ -- Process them one by one, dumping results into makefile+ -- and complaining about cycles+ hsc_env <- getSession+ root <- liftIO getCurrentDirectory+ mapM_ (liftIO . processDeps dflags hsc_env excl_mods root (mkd_tmp_hdl files)) sorted++ -- If -ddump-mod-cycles, show cycles in the module graph+ liftIO $ dumpModCycles logger module_graph++ -- Tidy up+ liftIO $ endMkDependHS logger files++ -- Unconditional exiting is a bad idea. If an error occurs we'll get an+ --exception; if that is not caught it's fine, but at least we have a+ --chance to find out exactly what went wrong. Uncomment the following+ --line if you disagree.++ --`GHC.ghcCatch` \_ -> io $ exitWith (ExitFailure 1)++-----------------------------------------------------------------+--+-- beginMkDependHs+-- Create a temporary file,+-- find the Makefile,+-- slurp through it, etc+--+-----------------------------------------------------------------++data MkDepFiles+ = MkDep { mkd_make_file :: FilePath, -- Name of the makefile+ mkd_make_hdl :: Maybe Handle, -- Handle for the open makefile+ mkd_tmp_file :: FilePath, -- Name of the temporary file+ mkd_tmp_hdl :: Handle } -- Handle of the open temporary file++beginMkDependHS :: Logger -> TmpFs -> DynFlags -> IO MkDepFiles+beginMkDependHS logger tmpfs dflags = do+ -- open a new temp file in which to stuff the dependency info+ -- as we go along.+ tmp_file <- newTempName logger tmpfs (tmpDir dflags) TFL_CurrentModule "dep"+ tmp_hdl <- openFile tmp_file WriteMode++ -- open the makefile+ let makefile = depMakefile dflags+ exists <- doesFileExist makefile+ mb_make_hdl <-+ if not exists+ then return Nothing+ else do+ makefile_hdl <- openFile makefile ReadMode++ -- slurp through until we get the magic start string,+ -- copying the contents into dep_makefile+ let slurp = do+ l <- hGetLine makefile_hdl+ if (l == depStartMarker)+ then return ()+ else do hPutStrLn tmp_hdl l; slurp++ -- slurp through until we get the magic end marker,+ -- throwing away the contents+ let chuck = do+ l <- hGetLine makefile_hdl+ if (l == depEndMarker)+ then return ()+ else chuck++ catchIO slurp+ (\e -> if isEOFError e then return () else ioError e)+ catchIO chuck+ (\e -> if isEOFError e then return () else ioError e)++ return (Just makefile_hdl)+++ -- write the magic marker into the tmp file+ hPutStrLn tmp_hdl depStartMarker++ return (MkDep { mkd_make_file = makefile, mkd_make_hdl = mb_make_hdl,+ mkd_tmp_file = tmp_file, mkd_tmp_hdl = tmp_hdl})+++-----------------------------------------------------------------+--+-- processDeps+--+-----------------------------------------------------------------++processDeps :: DynFlags+ -> HscEnv+ -> [ModuleName]+ -> FilePath+ -> Handle -- Write dependencies to here+ -> SCC ModuleGraphNode+ -> IO ()+-- Write suitable dependencies to handle+-- Always:+-- this.o : this.hs+--+-- If the dependency is on something other than a .hi file:+-- this.o this.p_o ... : dep+-- otherwise+-- this.o ... : dep.hi+-- this.p_o ... : dep.p_hi+-- ...+-- (where .o is $osuf, and the other suffixes come from+-- the cmdline -s options).+--+-- For {-# SOURCE #-} imports the "hi" will be "hi-boot".++processDeps dflags _ _ _ _ (CyclicSCC nodes)+ = -- There shouldn't be any cycles; report them+ throwGhcExceptionIO $ ProgramError $+ showSDoc dflags $ GHC.cyclicModuleErr nodes++processDeps dflags _ _ _ _ (AcyclicSCC (InstantiationNode _uid node))+ = -- There shouldn't be any backpack instantiations; report them as well+ throwGhcExceptionIO $ ProgramError $+ showSDoc dflags $+ vcat [ text "Unexpected backpack instantiation in dependency graph while constructing Makefile:"+ , nest 2 $ ppr node ]+processDeps _dflags _ _ _ _ (AcyclicSCC (LinkNode {})) = return ()++processDeps dflags hsc_env excl_mods root hdl (AcyclicSCC (ModuleNode _ node))+ = do { let extra_suffixes = depSuffixes dflags+ include_pkg_deps = depIncludePkgDeps dflags+ src_file = msHsFilePath node+ obj_file = msObjFilePath node+ obj_files = insertSuffixes obj_file extra_suffixes++ do_imp loc is_boot pkg_qual imp_mod+ = do { mb_hi <- findDependency hsc_env loc pkg_qual imp_mod+ is_boot include_pkg_deps+ ; case mb_hi of {+ Nothing -> return () ;+ Just hi_file -> do+ { let hi_files = insertSuffixes hi_file extra_suffixes+ write_dep (obj,hi) = writeDependency root hdl [obj] hi++ -- Add one dependency for each suffix;+ -- e.g. A.o : B.hi+ -- A.x_o : B.x_hi+ ; mapM_ write_dep (obj_files `zip` hi_files) }}}+++ -- Emit std dependency of the object(s) on the source file+ -- Something like A.o : A.hs+ ; writeDependency root hdl obj_files src_file++ -- add dependency between objects and their corresponding .hi-boot+ -- files if the module has a corresponding .hs-boot file (#14482)+ ; when (isBootSummary node == IsBoot) $ do+ let hi_boot = msHiFilePath node+ let obj = removeBootSuffix (msObjFilePath node)+ forM_ extra_suffixes $ \suff -> do+ let way_obj = insertSuffixes obj [suff]+ let way_hi_boot = insertSuffixes hi_boot [suff]+ mapM_ (writeDependency root hdl way_obj) way_hi_boot++ -- Emit a dependency for each CPP import+ ; when (depIncludeCppDeps dflags) $ do+ -- CPP deps are discovered in the module parsing phase by parsing+ -- comment lines left by the preprocessor.+ -- Note that GHC.parseModule may throw an exception if the module+ -- fails to parse, which may not be desirable (see #16616).+ { session <- Session <$> newIORef hsc_env+ ; parsedMod <- reflectGhc (GHC.parseModule node) session+ ; mapM_ (writeDependency root hdl obj_files)+ (GHC.pm_extra_src_files parsedMod)+ }++ -- Emit a dependency for each import++ ; let do_imps is_boot idecls = sequence_+ [ do_imp loc is_boot mb_pkg mod+ | (mb_pkg, L loc mod) <- idecls,+ mod `notElem` excl_mods ]++ ; do_imps IsBoot (ms_srcimps node)+ ; do_imps NotBoot (ms_imps node)+ }+++findDependency :: HscEnv+ -> SrcSpan+ -> PkgQual -- package qualifier, if any+ -> ModuleName -- Imported module+ -> IsBootInterface -- Source import+ -> Bool -- Record dependency on package modules+ -> IO (Maybe FilePath) -- Interface file+findDependency hsc_env srcloc pkg imp is_boot include_pkg_deps = do+ -- Find the module; this will be fast because+ -- we've done it once during downsweep+ r <- findImportedModule hsc_env imp pkg+ case r of+ Found loc _+ -- Home package: just depend on the .hi or hi-boot file+ | isJust (ml_hs_file loc) || include_pkg_deps+ -> return (Just (addBootSuffix_maybe is_boot (ml_hi_file loc)))++ -- Not in this package: we don't need a dependency+ | otherwise+ -> return Nothing++ fail ->+ throwOneError $+ mkPlainErrorMsgEnvelope srcloc $+ GhcDriverMessage $ DriverUnknownMessage $+ UnknownDiagnostic $ mkPlainError noHints $+ cannotFindModule hsc_env imp fail++-----------------------------+writeDependency :: FilePath -> Handle -> [FilePath] -> FilePath -> IO ()+-- (writeDependency r h [t1,t2] dep) writes to handle h the dependency+-- t1 t2 : dep+writeDependency root hdl targets dep+ = do let -- We need to avoid making deps on+ -- c:/foo/...+ -- on cygwin as make gets confused by the :+ -- Making relative deps avoids some instances of this.+ dep' = makeRelative root dep+ forOutput = escapeSpaces . reslash Forwards . normalise+ output = unwords (map forOutput targets) ++ " : " ++ forOutput dep'+ hPutStrLn hdl output++-----------------------------+insertSuffixes+ :: FilePath -- Original filename; e.g. "foo.o"+ -> [String] -- Suffix prefixes e.g. ["x_", "y_"]+ -> [FilePath] -- Zapped filenames e.g. ["foo.x_o", "foo.y_o"]+ -- Note that the extra bit gets inserted *before* the old suffix+ -- We assume the old suffix contains no dots, so we know where to+ -- split it+insertSuffixes file_name extras+ = [ basename <.> (extra ++ suffix) | extra <- extras ]+ where+ (basename, suffix) = case splitExtension file_name of+ -- Drop the "." from the extension+ (b, s) -> (b, drop 1 s)+++-----------------------------------------------------------------+--+-- endMkDependHs+-- Complete the makefile, close the tmp file etc+--+-----------------------------------------------------------------++endMkDependHS :: Logger -> MkDepFiles -> IO ()++endMkDependHS logger+ (MkDep { mkd_make_file = makefile, mkd_make_hdl = makefile_hdl,+ mkd_tmp_file = tmp_file, mkd_tmp_hdl = tmp_hdl })+ = do+ -- write the magic marker into the tmp file+ hPutStrLn tmp_hdl depEndMarker++ case makefile_hdl of+ Nothing -> return ()+ Just hdl -> do+ -- slurp the rest of the original makefile and copy it into the output+ SysTools.copyHandle hdl tmp_hdl+ hClose hdl++ hClose tmp_hdl -- make sure it's flushed++ -- Create a backup of the original makefile+ when (isJust makefile_hdl) $ do+ showPass logger ("Backing up " ++ makefile)+ SysTools.copyFile makefile (makefile++".bak")++ -- Copy the new makefile in place+ showPass logger "Installing new makefile"+ SysTools.copyFile tmp_file makefile+++-----------------------------------------------------------------+-- Module cycles+-----------------------------------------------------------------++dumpModCycles :: Logger -> ModuleGraph -> IO ()+dumpModCycles logger module_graph+ | not (logHasDumpFlag logger Opt_D_dump_mod_cycles)+ = return ()++ | null cycles+ = putMsg logger (text "No module cycles")++ | otherwise+ = putMsg logger (hang (text "Module cycles found:") 2 pp_cycles)+ where+ topoSort = GHC.topSortModuleGraph True module_graph Nothing++ cycles :: [[ModuleGraphNode]]+ cycles =+ [ c | CyclicSCC c <- topoSort ]++ pp_cycles = vcat [ (text "---------- Cycle" <+> int n <+> text "----------")+ $$ pprCycle c $$ blankLine+ | (n,c) <- [1..] `zip` cycles ]++pprCycle :: [ModuleGraphNode] -> SDoc+-- Print a cycle, but show only the imports within the cycle+pprCycle summaries = pp_group (CyclicSCC summaries)+ where+ cycle_mods :: [ModuleName] -- The modules in this cycle+ cycle_mods = map (moduleName . ms_mod) [ms | ModuleNode _ ms <- summaries]++ pp_group :: SCC ModuleGraphNode -> SDoc+ pp_group (AcyclicSCC (ModuleNode _ ms)) = pp_ms ms+ pp_group (AcyclicSCC _) = empty+ pp_group (CyclicSCC mss)+ = assert (not (null boot_only)) $+ -- The boot-only list must be non-empty, else there would+ -- be an infinite chain of non-boot imports, and we've+ -- already checked for that in processModDeps+ pp_ms loop_breaker $$ vcat (map pp_group groups)+ where+ (boot_only, others) = partition is_boot_only mss+ is_boot_only (ModuleNode _ ms) = not (any in_group (map snd (ms_imps ms)))+ is_boot_only _ = False+ in_group (L _ m) = m `elem` group_mods+ group_mods = map (moduleName . ms_mod) [ms | ModuleNode _ ms <- mss]++ loop_breaker = head ([ms | ModuleNode _ ms <- boot_only])+ all_others = tail boot_only ++ others+ groups =+ GHC.topSortModuleGraph True (mkModuleGraph all_others) Nothing++ pp_ms summary = text mod_str <> text (take (20 - length mod_str) (repeat ' '))+ <+> (pp_imps empty (map snd (ms_imps summary)) $$+ pp_imps (text "{-# SOURCE #-}") (map snd (ms_srcimps summary)))+ where+ mod_str = moduleNameString (moduleName (ms_mod summary))++ pp_imps :: SDoc -> [Located ModuleName] -> SDoc+ pp_imps _ [] = empty+ pp_imps what lms+ = case [m | L _ m <- lms, m `elem` cycle_mods] of+ [] -> empty+ ms -> what <+> text "imports" <+>+ pprWithCommas ppr ms++-----------------------------------------------------------------+--+-- Flags+--+-----------------------------------------------------------------++depStartMarker, depEndMarker :: String+depStartMarker = "# DO NOT DELETE: Beginning of Haskell dependencies"+depEndMarker = "# DO NOT DELETE: End of Haskell dependencies"
compiler/GHC/Driver/Pipeline.hs view
@@ -1,12 +1,14 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE BangPatterns #-}-{-# LANGUAGE TypeApplications #-}+ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NondecreasingIndentation #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE TypeApplications #-} ----------------------------------------------------------------------------- --@@ -40,7 +42,7 @@ TPipelineClass, MonadUse(..), preprocessPipeline, fullPipeline, hscPipeline, hscBackendPipeline, hscPostBackendPipeline,- hscGenBackendPipeline, asPipeline, viaCPipeline, cmmCppPipeline, cmmPipeline,+ hscGenBackendPipeline, asPipeline, viaCPipeline, cmmCppPipeline, cmmPipeline, jsPipeline, llvmPipeline, llvmLlcPipeline, llvmManglePipeline, pipelineStart, -- * Default method of running a pipeline@@ -60,6 +62,7 @@ import GHC.Driver.Errors.Types import GHC.Driver.Pipeline.Monad import GHC.Driver.Config.Diagnostic+import GHC.Driver.Config.StgToJS import GHC.Driver.Phases import GHC.Driver.Pipeline.Execute import GHC.Driver.Pipeline.Phases@@ -71,6 +74,7 @@ import GHC.Platform.Ways import GHC.SysTools+import GHC.SysTools.Cpp import GHC.Utils.TmpFs import GHC.Linker.ExtraObj@@ -78,6 +82,9 @@ import GHC.Linker.Static.Utils import GHC.Linker.Types +import GHC.StgToJS.Linker.Linker+import GHC.StgToJS.Linker.Types (defaultJSLinkConfig)+ import GHC.Utils.Outputable import GHC.Utils.Error import GHC.Utils.Panic@@ -96,7 +103,7 @@ import GHC.Types.Basic ( SuccessFlag(..), ForeignSrcLang(..) )-import GHC.Types.Error ( singleMessage, getMessages )+import GHC.Types.Error ( singleMessage, getMessages, UnknownDiagnostic (..) ) import GHC.Types.Target import GHC.Types.SrcLoc import GHC.Types.SourceFile@@ -122,7 +129,6 @@ import Data.Time ( getCurrentTime ) import GHC.Iface.Recomp-import GHC.Types.Unique.DSet -- Simpler type synonym for actions in the pipeline monad type P m = TPipelineClass TPhase m@@ -156,13 +162,14 @@ handler (ProgramError msg) = return $ Left $ singleMessage $ mkPlainErrorMsgEnvelope srcspan $- DriverUnknownMessage $ mkPlainError noHints $ text msg+ DriverUnknownMessage $ UnknownDiagnostic $ mkPlainError noHints $ text msg handler ex = throwGhcExceptionIO ex to_driver_messages :: Messages GhcMessage -> Messages DriverMessage to_driver_messages msgs = case traverse to_driver_message msgs of Nothing -> pprPanic "non-driver message in preprocess"- (vcat $ pprMsgEnvelopeBagWithLoc (getMessages msgs))+ -- MP: Default config is fine here as it's just in a panic.+ (vcat $ pprMsgEnvelopeBagWithLoc (defaultDiagnosticOpts @GhcMessage) (getMessages msgs)) Just msgs' -> msgs' to_driver_message = \case@@ -210,7 +217,7 @@ -> Int -- ^ module N ... -> Int -- ^ ... of M -> Maybe ModIface -- ^ old interface, if we have one- -> Maybe Linkable -- ^ old linkable, if we have one+ -> HomeModLinkable -- ^ old linkable, if we have one -> IO HomeModInfo -- ^ the complete HomeModInfo, if successful compileOne = compileOne' (Just batchMsg)@@ -221,7 +228,7 @@ -> Int -- ^ module N ... -> Int -- ^ ... of M -> Maybe ModIface -- ^ old interface, if we have one- -> Maybe Linkable -- ^ old linkable, if we have one+ -> HomeModLinkable -> IO HomeModInfo -- ^ the complete HomeModInfo, if successful compileOne' mHscMessage@@ -230,33 +237,31 @@ debugTraceMsg logger 2 (text "compile: input file" <+> text input_fnpp) - let flags = hsc_dflags hsc_env0- in do unless (gopt Opt_KeepHiFiles flags) $- addFilesToClean tmpfs TFL_CurrentModule $- [ml_hi_file $ ms_location summary]- unless (gopt Opt_KeepOFiles flags) $- addFilesToClean tmpfs TFL_GhcSession $- [ml_obj_file $ ms_location summary]+ unless (gopt Opt_KeepHiFiles lcl_dflags) $+ addFilesToClean tmpfs TFL_CurrentModule $+ [ml_hi_file $ ms_location summary]+ unless (gopt Opt_KeepOFiles lcl_dflags) $+ addFilesToClean tmpfs TFL_GhcSession $+ [ml_obj_file $ ms_location summary] + -- Initialise plugins here for any plugins enabled locally for a module. plugin_hsc_env <- initializePlugins hsc_env let pipe_env = mkPipeEnv NoStop input_fn Nothing pipelineOutput status <- hscRecompStatus mHscMessage plugin_hsc_env upd_summary mb_old_iface mb_old_linkable (mod_index, nmods) let pipeline = hscPipeline pipe_env (setDumpPrefix pipe_env plugin_hsc_env, upd_summary, status)- (iface, linkable) <- runPipeline (hsc_hooks hsc_env) pipeline+ (iface, linkable) <- runPipeline (hsc_hooks plugin_hsc_env) pipeline -- See Note [ModDetails and --make mode]- details <- initModDetails plugin_hsc_env upd_summary iface- return $! HomeModInfo iface details linkable+ details <- initModDetails plugin_hsc_env iface+ linkable' <- traverse (initWholeCoreBindings plugin_hsc_env iface details) (homeMod_bytecode linkable)+ return $! HomeModInfo iface details (linkable { homeMod_bytecode = linkable' }) where lcl_dflags = ms_hspp_opts summary location = ms_location summary input_fn = expectJust "compile:hs" (ml_hs_file location) input_fnpp = ms_hspp_file summary - pipelineOutput = case bcknd of- Interpreter -> NoOutputFile- NoBackend -> NoOutputFile- _ -> Persistent+ pipelineOutput = backendPipelineOutput bcknd logger = hsc_logger hsc_env0 tmpfs = hsc_tmpfs hsc_env0@@ -280,7 +285,10 @@ -- was set), force it to generate byte-code. This is NOT transitive and -- only applies to direct targets. | loadAsByteCode- = (Interpreter, gopt_set (lcl_dflags { backend = Interpreter }) Opt_ForceRecomp)+ = ( interpreterBackend+ , gopt_set (lcl_dflags { backend = interpreterBackend }) Opt_ForceRecomp+ )+ | otherwise = (backend dflags, lcl_dflags) -- See Note [Filepaths and Multiple Home Units]@@ -361,17 +369,17 @@ link ghcLink logger tmpfs hooks dflags unit_env batch_attempt_linking mHscMessage hpt = case linkHook hooks of Nothing -> case ghcLink of- NoLink -> return Succeeded- LinkBinary -> normal_link- LinkStaticLib -> normal_link- LinkDynLib -> normal_link- LinkMergedObj -> normal_link- LinkInMemory- | platformMisc_ghcWithInterpreter $ platformMisc dflags- -> -- Not Linking...(demand linker will do the job)- return Succeeded- | otherwise- -> panicBadLink LinkInMemory+ NoLink -> return Succeeded+ LinkBinary -> normal_link+ LinkStaticLib -> normal_link+ LinkDynLib -> normal_link+ LinkMergedObj -> normal_link+ LinkInMemory+ | platformMisc_ghcWithInterpreter $ platformMisc dflags+ -- Not Linking...(demand linker will do the job)+ -> return Succeeded+ | otherwise+ -> panicBadLink LinkInMemory Just h -> h ghcLink dflags batch_attempt_linking hpt where normal_link = link' logger tmpfs dflags unit_env batch_attempt_linking mHscMessage hpt@@ -407,9 +415,11 @@ $ home_mod_infos -- the linkables to link- linkables = map (expectJust "link".hm_linkable) home_mod_infos+ linkables = map (expectJust "link". homeModInfoObject) home_mod_infos + debugTraceMsg logger 3 (text "link: hmi ..." $$ vcat (map (ppr . mi_module . hm_iface) home_mod_infos)) debugTraceMsg logger 3 (text "link: linkables are ..." $$ vcat (map ppr linkables))+ debugTraceMsg logger 3 (text "link: pkg deps are ..." $$ vcat (map ppr pkg_deps)) -- check for the -no-link flag if isNoLink (ghcLink dflags)@@ -420,7 +430,8 @@ let getOfiles LM{ linkableUnlinked } = map nameOfObject (filter isObject linkableUnlinked) obj_files = concatMap getOfiles linkables platform = targetPlatform dflags- exe_file = exeFileName platform staticLink (outputFile_ dflags)+ arch_os = platformArchOS platform+ exe_file = exeFileName arch_os staticLink (outputFile_ dflags) linking_needed <- linkingNeeded logger dflags unit_env staticLink linkables pkg_deps @@ -432,12 +443,13 @@ -- Don't showPass in Batch mode; doLink will do that for us.- let link = case ghcLink dflags of- LinkBinary -> linkBinary logger tmpfs- LinkStaticLib -> linkStaticLib logger- LinkDynLib -> linkDynLibCheck logger tmpfs- other -> panicBadLink other- link dflags unit_env obj_files pkg_deps+ case ghcLink dflags of+ LinkBinary+ | backendUseJSLinker (backend dflags) -> linkJSBinary logger dflags unit_env obj_files pkg_deps+ | otherwise -> linkBinary logger tmpfs dflags unit_env obj_files pkg_deps+ LinkStaticLib -> linkStaticLib logger dflags unit_env obj_files pkg_deps+ LinkDynLib -> linkDynLibCheck logger tmpfs dflags unit_env obj_files pkg_deps+ other -> panicBadLink other debugTraceMsg logger 3 (text "link: done") @@ -450,6 +462,15 @@ return Succeeded +linkJSBinary :: Logger -> DynFlags -> UnitEnv -> [FilePath] -> [UnitId] -> IO ()+linkJSBinary logger dflags unit_env obj_files pkg_deps = do+ -- we use the default configuration for now. In the future we may expose+ -- settings to the user via DynFlags.+ let lc_cfg = defaultJSLinkConfig+ let cfg = initStgToJSConfig dflags+ let extra_js = mempty+ jsLinkBinary lc_cfg cfg extra_js logger dflags unit_env obj_files pkg_deps+ linkingNeeded :: Logger -> DynFlags -> UnitEnv -> Bool -> [Linkable] -> [UnitId] -> IO RecompileRequired linkingNeeded logger dflags unit_env staticLink linkables pkg_deps = do -- if the modification time on the executable is later than the@@ -457,7 +478,8 @@ -- linking (unless the -fforce-recomp flag was given). let platform = ue_platform unit_env unit_state = ue_units unit_env- exe_file = exeFileName platform staticLink (outputFile_ dflags)+ arch_os = platformArchOS platform+ exe_file = exeFileName arch_os staticLink (outputFile_ dflags) e_exe_time <- tryIO $ getModificationUTCTime exe_file case e_exe_time of Left _ -> return $ NeedsRecompile MustCompile@@ -473,18 +495,8 @@ -- next, check libraries. XXX this only checks Haskell libraries, -- not extra_libraries or -l things from the command line.- -- pkg_deps is just the direct dependencies so take the transitive closure here- -- to decide if we need to relink or not.- let pkg_hslibs acc uid- | uid `elementOfUniqDSet` acc = acc- | Just c <- lookupUnitId unit_state uid =- foldl' @[] pkg_hslibs (addOneToUniqDSet acc uid) (unitDepends c)- | otherwise = acc-- all_pkg_deps = foldl' @[] pkg_hslibs emptyUniqDSet pkg_deps- let pkg_hslibs = [ (collectLibraryDirs (ways dflags) [c], lib)- | Just c <- map (lookupUnitId unit_state) (uniqDSetToList all_pkg_deps),+ | Just c <- map (lookupUnitId unit_state) pkg_deps, lib <- unitHsLibs (ghcNameVersion dflags) (ways dflags) c ] pkg_libfiles <- mapM (uncurry (findHSLib platform (ways dflags))) pkg_hslibs@@ -515,7 +527,11 @@ -- Compile files in one-shot mode. oneShot :: HscEnv -> StopPhase -> [(String, Maybe Phase)] -> IO ()-oneShot hsc_env stop_phase srcs = do+oneShot orig_hsc_env stop_phase srcs = do+ -- In oneshot mode, initialise plugins specified on command line+ -- we also initialise in ghc/Main but this might be used as an entry point by API clients who+ -- should initialise their own plugins but may not.+ hsc_env <- initializePlugins orig_hsc_env o_files <- mapMaybeM (compileFile hsc_env stop_phase) srcs case stop_phase of StopPreprocess -> return ()@@ -538,7 +554,7 @@ -- When linking, the -o argument refers to the linker's output. -- otherwise, we use it as the name for the pipeline's output. output- | NoBackend <- backend dflags, notStopPreprocess = NoOutputFile+ | not (backendGeneratesCode (backend dflags)), notStopPreprocess = NoOutputFile -- avoid -E -fno-code undesirable interactions. see #20439 | NoStop <- stop_phase, not (isNoLink ghc_link) = Persistent -- -o foo applies to linker@@ -551,24 +567,28 @@ doLink :: HscEnv -> [FilePath] -> IO ()-doLink hsc_env o_files =- let- dflags = hsc_dflags hsc_env- logger = hsc_logger hsc_env- unit_env = hsc_unit_env hsc_env- tmpfs = hsc_tmpfs hsc_env- in case ghcLink dflags of- NoLink -> return ()- LinkBinary -> linkBinary logger tmpfs dflags unit_env o_files []- LinkStaticLib -> linkStaticLib logger dflags unit_env o_files []- LinkDynLib -> linkDynLibCheck logger tmpfs dflags unit_env o_files []- LinkMergedObj- | Just out <- outputFile dflags- , let objs = [ f | FileOption _ f <- ldInputs dflags ]- -> joinObjectFiles hsc_env (o_files ++ objs) out- | otherwise -> panic "Output path must be specified for LinkMergedObj"- other -> panicBadLink other+doLink hsc_env o_files = do+ let+ dflags = hsc_dflags hsc_env+ logger = hsc_logger hsc_env+ unit_env = hsc_unit_env hsc_env+ tmpfs = hsc_tmpfs hsc_env + case ghcLink dflags of+ NoLink -> return ()+ LinkBinary+ | backendUseJSLinker (backend dflags)+ -> linkJSBinary logger dflags unit_env o_files []+ | otherwise -> linkBinary logger tmpfs dflags unit_env o_files []+ LinkStaticLib -> linkStaticLib logger dflags unit_env o_files []+ LinkDynLib -> linkDynLibCheck logger tmpfs dflags unit_env o_files []+ LinkMergedObj+ | Just out <- outputFile dflags+ , let objs = [ f | FileOption _ f <- ldInputs dflags ]+ -> joinObjectFiles hsc_env (o_files ++ objs) out+ | otherwise -> panic "Output path must be specified for LinkMergedObj"+ other -> panicBadLink other+ ----------------------------------------------------------------------------- -- stub .h and .c files (for foreign export support), and cc files. @@ -592,6 +612,7 @@ LangObjc -> viaCPipeline Cobjc LangObjcxx -> viaCPipeline Cobjcxx LangAsm -> \pe hsc_env ml fp -> asPipeline True pe hsc_env ml fp+ LangJs -> \pe hsc_env ml fp -> Just <$> foreignJsPipeline pe hsc_env ml fp #if __GLASGOW_HASKELL__ < 811 RawObject -> panic "compileForeign: should be unreachable" #endif@@ -601,7 +622,7 @@ -- This should never happen as viaCPipeline should only return `Nothing` when the stop phase is `StopC`. -- and the same should never happen for asPipeline -- Future refactoring to not check StopC for this case- Nothing -> pprPanic "compileForeign" (ppr stub_c)+ Nothing -> pprPanic "compileForeign" (text stub_c) Just fp -> return fp compileEmptyStub :: DynFlags -> HscEnv -> FilePath -> ModLocation -> ModuleName -> IO ()@@ -615,16 +636,29 @@ -- and https://github.com/haskell/cabal/issues/2257 let logger = hsc_logger hsc_env let tmpfs = hsc_tmpfs hsc_env- empty_stub <- newTempName logger tmpfs (tmpDir dflags) TFL_CurrentModule "c" let home_unit = hsc_home_unit hsc_env- src = text "int" <+> ppr (mkHomeModule home_unit mod_name) <+> text "= 0;"- writeFile empty_stub (showSDoc dflags (pprCode CStyle src))- let pipe_env = (mkPipeEnv NoStop empty_stub Nothing Persistent) { src_basename = basename}- pipeline = viaCPipeline HCc pipe_env hsc_env (Just location) empty_stub- _ <- runPipeline (hsc_hooks hsc_env) pipeline- return () + case backendCodeOutput (backend dflags) of+ JSCodeOutput -> do+ empty_stub <- newTempName logger tmpfs (tmpDir dflags) TFL_CurrentModule "js"+ let src = ppr (mkHomeModule home_unit mod_name) <+> text "= 0;"+ writeFile empty_stub (showSDoc dflags (pprCode src))+ let pipe_env = (mkPipeEnv NoStop empty_stub Nothing Persistent) { src_basename = basename}+ pipeline = Just <$> foreignJsPipeline pipe_env hsc_env (Just location) empty_stub+ _ <- runPipeline (hsc_hooks hsc_env) pipeline+ pure () + _ -> do+ empty_stub <- newTempName logger tmpfs (tmpDir dflags) TFL_CurrentModule "c"+ let src = text "int" <+> ppr (mkHomeModule home_unit mod_name) <+> text "= 0;"+ writeFile empty_stub (showSDoc dflags (pprCode src))+ let pipe_env = (mkPipeEnv NoStop empty_stub Nothing Persistent) { src_basename = basename}+ pipeline = viaCPipeline HCc pipe_env hsc_env (Just location) empty_stub+ _ <- runPipeline (hsc_hooks hsc_env) pipeline+ pure ()+++ {- Environment Initialisation -} mkPipeEnv :: StopPhase -- End phase@@ -700,8 +734,9 @@ -- Reparse with original hsc_env so that we don't get duplicated options use (T_FileArgs hsc_env pp_fn) - liftIO (printOrThrowDiagnostics (hsc_logger hsc_env) (initDiagOpts dflags3) (GhcPsMessage <$> p_warns3))- liftIO (handleFlagWarnings (hsc_logger hsc_env) (initDiagOpts dflags3) warns3)+ let print_config = initPrintConfig dflags3+ liftIO (printOrThrowDiagnostics (hsc_logger hsc_env) print_config (initDiagOpts dflags3) (GhcPsMessage <$> p_warns3))+ liftIO (handleFlagWarnings (hsc_logger hsc_env) print_config (initDiagOpts dflags3) warns3) return (dflags3, pp_fn) @@ -722,7 +757,7 @@ $ phaseIfFlag hsc_env flag def action -- | The complete compilation pipeline, from start to finish-fullPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> HscSource -> m (ModIface, Maybe Linkable)+fullPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> HscSource -> m (ModIface, HomeModLinkable) fullPipeline pipe_env hsc_env pp_fn src_flavour = do (dflags, input_fn) <- preprocessPipeline pipe_env hsc_env pp_fn let hsc_env' = hscSetFlags dflags hsc_env@@ -731,7 +766,7 @@ hscPipeline pipe_env (hsc_env_with_plugins, mod_sum, hsc_recomp_status) -- | Everything after preprocess-hscPipeline :: P m => PipeEnv -> ((HscEnv, ModSummary, HscRecompStatus)) -> m (ModIface, Maybe Linkable)+hscPipeline :: P m => PipeEnv -> ((HscEnv, ModSummary, HscRecompStatus)) -> m (ModIface, HomeModLinkable) hscPipeline pipe_env (hsc_env_with_plugins, mod_sum, hsc_recomp_status) = do case hsc_recomp_status of HscUpToDate iface mb_linkable -> return (iface, mb_linkable)@@ -740,28 +775,28 @@ hscBackendAction <- use (T_HscPostTc hsc_env_with_plugins mod_sum tc_result warnings mb_old_hash ) hscBackendPipeline pipe_env hsc_env_with_plugins mod_sum hscBackendAction -hscBackendPipeline :: P m => PipeEnv -> HscEnv -> ModSummary -> HscBackendAction -> m (ModIface, Maybe Linkable)+hscBackendPipeline :: P m => PipeEnv -> HscEnv -> ModSummary -> HscBackendAction -> m (ModIface, HomeModLinkable) hscBackendPipeline pipe_env hsc_env mod_sum result =- case backend (hsc_dflags hsc_env) of- NoBackend ->- case result of- HscUpdate iface -> return (iface, Nothing)- HscRecomp {} -> (,) <$> liftIO (mkFullIface hsc_env (hscs_partial_iface result) Nothing Nothing) <*> pure Nothing- -- TODO: Why is there not a linkable?- -- Interpreter -> (,) <$> use (T_IO (mkFullIface hsc_env (hscs_partial_iface result) Nothing)) <*> pure Nothing- _ -> do+ if backendGeneratesCode (backend (hsc_dflags hsc_env)) then+ do res <- hscGenBackendPipeline pipe_env hsc_env mod_sum result when (gopt Opt_BuildDynamicToo (hsc_dflags hsc_env)) $ do let dflags' = setDynamicNow (hsc_dflags hsc_env) -- set "dynamicNow" () <$ hscGenBackendPipeline pipe_env (hscSetFlags dflags' hsc_env) mod_sum result return res+ else+ case result of+ HscUpdate iface -> return (iface, emptyHomeModInfoLinkable)+ HscRecomp {} -> (,) <$> liftIO (mkFullIface hsc_env (hscs_partial_iface result) Nothing Nothing) <*> pure emptyHomeModInfoLinkable+ -- TODO: Why is there not a linkable?+ -- Interpreter -> (,) <$> use (T_IO (mkFullIface hsc_env (hscs_partial_iface result) Nothing)) <*> pure Nothing hscGenBackendPipeline :: P m => PipeEnv -> HscEnv -> ModSummary -> HscBackendAction- -> m (ModIface, Maybe Linkable)+ -> m (ModIface, HomeModLinkable) hscGenBackendPipeline pipe_env hsc_env mod_sum result = do let mod_name = moduleName (ms_mod mod_sum) src_flavour = (ms_hsc_src mod_sum)@@ -776,7 +811,8 @@ unlinked_time <- liftIO (liftIO getCurrentTime) final_unlinked <- DotO <$> use (T_MergeForeign pipe_env hsc_env o_fp fos) let !linkable = LM unlinked_time (ms_mod mod_sum) [final_unlinked]- return (Just linkable)+ -- Add the object linkable to the potential bytecode linkable which was generated in HscBackend.+ return (mlinkable { homeMod_object = Just linkable }) return (miface, final_linkable) asPipeline :: P m => Bool -> PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> m (Maybe ObjFile)@@ -787,10 +823,10 @@ viaCPipeline :: P m => Phase -> PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> m (Maybe FilePath) viaCPipeline c_phase pipe_env hsc_env location input_fn = do- out_fn <- use (T_Cc c_phase pipe_env hsc_env input_fn)+ out_fn <- use (T_Cc c_phase pipe_env hsc_env location input_fn) case stop_phase pipe_env of StopC -> return Nothing- _ -> asPipeline False pipe_env hsc_env location out_fn+ _ -> return $ Just out_fn llvmPipeline :: P m => PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> m (Maybe FilePath) llvmPipeline pipe_env hsc_env location fp = do@@ -810,30 +846,46 @@ else use (T_LlvmMangle pipe_env hsc_env llc_fn) asPipeline False pipe_env hsc_env location mangled_fn -cmmCppPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> m (Maybe FilePath)+cmmCppPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> m FilePath cmmCppPipeline pipe_env hsc_env input_fn = do output_fn <- use (T_CmmCpp pipe_env hsc_env input_fn) cmmPipeline pipe_env hsc_env output_fn -cmmPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> m (Maybe FilePath)+cmmPipeline :: P m => PipeEnv -> HscEnv -> FilePath -> m FilePath cmmPipeline pipe_env hsc_env input_fn = do (fos, output_fn) <- use (T_Cmm pipe_env hsc_env input_fn) mo_fn <- hscPostBackendPipeline pipe_env hsc_env HsSrcFile (backend (hsc_dflags hsc_env)) Nothing output_fn case mo_fn of- Nothing -> return Nothing- Just mo_fn -> Just <$> use (T_MergeForeign pipe_env hsc_env mo_fn fos)+ Nothing -> panic "CMM pipeline - produced no .o file"+ Just mo_fn -> use (T_MergeForeign pipe_env hsc_env mo_fn fos) +jsPipeline :: P m => PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> m FilePath+jsPipeline pipe_env hsc_env location input_fn = do+ use (T_Js pipe_env hsc_env location input_fn)++foreignJsPipeline :: P m => PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> m FilePath+foreignJsPipeline pipe_env hsc_env location input_fn = do+ use (T_ForeignJs pipe_env hsc_env location input_fn)+ hscPostBackendPipeline :: P m => PipeEnv -> HscEnv -> HscSource -> Backend -> Maybe ModLocation -> FilePath -> m (Maybe FilePath) hscPostBackendPipeline _ _ HsBootFile _ _ _ = return Nothing hscPostBackendPipeline _ _ HsigFile _ _ _ = return Nothing hscPostBackendPipeline pipe_env hsc_env _ bcknd ml input_fn =- case bcknd of- ViaC -> viaCPipeline HCc pipe_env hsc_env ml input_fn- NCG -> asPipeline False pipe_env hsc_env ml input_fn- LLVM -> llvmPipeline pipe_env hsc_env ml input_fn- NoBackend -> return Nothing- Interpreter -> return Nothing+ applyPostHscPipeline (backendPostHscPipeline bcknd) pipe_env hsc_env ml input_fn +applyPostHscPipeline+ :: TPipelineClass TPhase m+ => DefunctionalizedPostHscPipeline+ -> PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> m (Maybe FilePath)+applyPostHscPipeline NcgPostHscPipeline =+ \pe he ml fp -> asPipeline False pe he ml fp+applyPostHscPipeline ViaCPostHscPipeline = viaCPipeline HCc+applyPostHscPipeline LlvmPostHscPipeline =+ \pe he ml fp -> llvmPipeline pe he ml fp+applyPostHscPipeline JSPostHscPipeline =+ \pe he ml fp -> Just <$> jsPipeline pe he ml fp+applyPostHscPipeline NoPostHscPipeline = \_ _ _ _ -> return Nothing+ -- Pipeline from a given suffix pipelineStart :: P m => PipeEnv -> HscEnv -> FilePath -> Maybe Phase -> m (Maybe FilePath) pipelineStart pipe_env hsc_env input_fn mb_phase =@@ -865,10 +917,9 @@ as :: P m => Bool -> m (Maybe FilePath) as use_cpp = asPipeline use_cpp pipe_env hsc_env Nothing input_fn - objFromLinkable (_, Just (LM _ _ [DotO lnk])) = Just lnk+ objFromLinkable (_, homeMod_object -> Just (LM _ _ [DotO lnk])) = Just lnk objFromLinkable _ = Nothing - fromPhase :: P m => Phase -> m (Maybe FilePath) fromPhase (Unlit p) = frontend p fromPhase (Cpp p) = frontend p@@ -884,8 +935,9 @@ fromPhase LlvmLlc = llvmLlcPipeline pipe_env hsc_env Nothing input_fn fromPhase LlvmMangle = llvmManglePipeline pipe_env hsc_env Nothing input_fn fromPhase StopLn = return (Just input_fn)- fromPhase CmmCpp = cmmCppPipeline pipe_env hsc_env input_fn- fromPhase Cmm = cmmPipeline pipe_env hsc_env input_fn+ fromPhase CmmCpp = Just <$> cmmCppPipeline pipe_env hsc_env input_fn+ fromPhase Cmm = Just <$> cmmPipeline pipe_env hsc_env input_fn+ fromPhase Js = Just <$> foreignJsPipeline pipe_env hsc_env Nothing input_fn fromPhase MergeForeign = panic "fromPhase: MergeForeign" {-@@ -902,7 +954,7 @@ `TPipelineClass` to give different behaviours that the default `HookedPhase` implementation: * Additional logging of different phases-* Automatic parrelism (in the style of shake)+* Automatic parallelism (in the style of shake) * Easy consumption by external tools such as ghcide * Easier to create your own pipeline and extend existing pipelines.
compiler/GHC/Driver/Pipeline.hs-boot view
@@ -5,8 +5,9 @@ import GHC.ForeignSrcLang ( ForeignSrcLang ) import GHC.Prelude (FilePath, IO) import GHC.Unit.Module.Location (ModLocation)-import GHC.Unit.Module.Name (ModuleName) import GHC.Driver.Session (DynFlags)++import Language.Haskell.Syntax.Module.Name -- These are used in GHC.Driver.Pipeline.Execute, but defined in terms of runPipeline compileForeign :: HscEnv -> ForeignSrcLang -> FilePath -> IO FilePath
compiler/GHC/Driver/Pipeline/Execute.hs view
@@ -22,12 +22,10 @@ import GHC.Driver.Env hiding (Hsc) import GHC.Unit.Module.Location import GHC.Driver.Phases-import GHC.Unit.Module.Name ( ModuleName ) import GHC.Unit.Types import GHC.Types.SourceFile import GHC.Unit.Module.Status import GHC.Unit.Module.ModIface-import GHC.Linker.Types import GHC.Driver.Backend import GHC.Driver.Session import GHC.Driver.CmdLine@@ -44,11 +42,11 @@ import GHC.Platform import Data.List (intercalate, isInfixOf) import GHC.Unit.Env-import GHC.SysTools.Info import GHC.Utils.Error import Data.Maybe import GHC.CmmToLlvm.Mangler import GHC.SysTools+import GHC.SysTools.Cpp import GHC.Utils.Panic.Plain import System.Directory import System.FilePath@@ -59,30 +57,32 @@ import GHC.Unit.Home import GHC.Data.Maybe import GHC.Iface.Make-import Data.Time import GHC.Driver.Config.Parser import GHC.Parser.Header import GHC.Data.StringBuffer import GHC.Types.SourceError import GHC.Unit.Finder-import GHC.Runtime.Loader import Data.IORef import GHC.Types.Name.Env import GHC.Platform.Ways-import GHC.Platform.ArchOS-import GHC.CmmToLlvm.Base ( llvmVersionList )+import GHC.Driver.LlvmConfigCache (readLlvmConfigCache)+import GHC.CmmToLlvm.Config (LlvmTarget (..), LlvmConfig (..)) import {-# SOURCE #-} GHC.Driver.Pipeline (compileForeign, compileEmptyStub) import GHC.Settings import System.IO import GHC.Linker.ExtraObj import GHC.Linker.Dynamic-import Data.Version import GHC.Utils.Panic import GHC.Unit.Module.Env import GHC.Driver.Env.KnotVars import GHC.Driver.Config.Finder import GHC.Rename.Names+import GHC.StgToJS.Linker.Linker (embedJsFile) +import Language.Haskell.Syntax.Module.Name+import GHC.Unit.Home.ModInfo+import GHC.Runtime.Loader (initializePlugins)+ newtype HookedUse a = HookedUse { runHookedUse :: (Hooks, PhaseHook) -> IO a } deriving (Functor, Applicative, Monad, MonadIO, MonadThrow, MonadCatch) via (ReaderT (Hooks, PhaseHook) IO) @@ -121,9 +121,16 @@ (hsc_tmpfs hsc_env) (hsc_dflags hsc_env) (hsc_unit_env hsc_env)- False{-not raw-}+ (CppOpts+ { cppUseCc = True+ , cppLinePragmas = True+ }) input_fn output_fn return output_fn+runPhase (T_Js pipe_env hsc_env location js_src) =+ runJsPhase pipe_env hsc_env location js_src+runPhase (T_ForeignJs pipe_env hsc_env location js_src) =+ runForeignJsPhase pipe_env hsc_env location js_src runPhase (T_Cmm pipe_env hsc_env input_fn) = do let dflags = hsc_dflags hsc_env let next_phase = hscPostBackendPhase HsSrcFile (backend dflags)@@ -133,7 +140,7 @@ let foreign_os = maybeToList stub_o return (foreign_os, output_fn) -runPhase (T_Cc phase pipe_env hsc_env input_fn) = runCcPhase phase pipe_env hsc_env input_fn+runPhase (T_Cc phase pipe_env hsc_env location input_fn) = runCcPhase phase pipe_env hsc_env location input_fn runPhase (T_As cpp pipe_env hsc_env location input_fn) = do runAsPhase cpp pipe_env hsc_env location input_fn runPhase (T_LlvmOpt pipe_env hsc_env input_fn) =@@ -209,6 +216,7 @@ -- -- Observed at least with -mtriple=arm-unknown-linux-gnueabihf -enable-tbaa --+ llvm_config <- readLlvmConfigCache (hsc_llvm_config hsc_env) let dflags = hsc_dflags hsc_env logger = hsc_logger hsc_env llvmOpts = case llvmOptLevel dflags of@@ -217,7 +225,7 @@ _ -> "-O2" defaultOptions = map GHC.SysTools.Option . concatMap words . snd- $ unzip (llvmOptions dflags)+ $ unzip (llvmOptions llvm_config dflags) optFlag = if null (getOpts dflags opt_lc) then map GHC.SysTools.Option $ words llvmOpts else []@@ -243,16 +251,17 @@ runLlvmOptPhase pipe_env hsc_env input_fn = do let dflags = hsc_dflags hsc_env logger = hsc_logger hsc_env+ llvm_config <- readLlvmConfigCache (hsc_llvm_config hsc_env) let -- we always (unless -optlo specified) run Opt since we rely on it to -- fix up some pretty big deficiencies in the code we generate optIdx = max 0 $ min 2 $ llvmOptLevel dflags -- ensure we're in [0,2]- llvmOpts = case lookup optIdx $ llvmPasses $ llvmConfig dflags of+ llvmOpts = case lookup optIdx $ llvmPasses llvm_config of Just passes -> passes Nothing -> panic ("runPhase LlvmOpt: llvm-passes file " ++ "is missing passes for level " ++ show optIdx) defaultOptions = map GHC.SysTools.Option . concat . fmap words . fst- $ unzip (llvmOptions dflags)+ $ unzip (llvmOptions llvm_config dflags) -- don't specify anything if user has specified commands. We do this -- for opt but not llc since opt is very specifically for optimisation@@ -284,13 +293,11 @@ -- LLVM from version 3.0 onwards doesn't support the OS X system -- assembler, so we use clang as the assembler instead. (#5636)- let (as_prog, get_asm_info) | backend dflags == LLVM- , platformOS platform == OSDarwin- = (GHC.SysTools.runClang, pure Clang)- | otherwise- = (GHC.SysTools.runAs, getAssemblerInfo logger dflags)-- asmInfo <- get_asm_info+ let (as_prog, get_asm_info) =+ ( applyAssemblerProg $ backendAssemblerProg (backend dflags)+ , applyAssemblerInfoGetter $ backendAssemblerInfoGetter (backend dflags)+ )+ asmInfo <- get_asm_info logger dflags platform let cmdline_include_paths = includePaths dflags let pic_c_flags = picCCOpts dflags@@ -310,6 +317,7 @@ = withAtomicRename outputFilename $ \temp_outputFilename -> as_prog logger dflags+ platform (local_includes ++ global_includes -- See Note [-fPIC for assembler] ++ map GHC.SysTools.Option pic_c_flags@@ -319,6 +327,10 @@ , not $ target32Bit (targetPlatform dflags) ] + -- See Note [-Wa,--no-type-check on wasm32]+ ++ [ GHC.SysTools.Option "-Wa,--no-type-check"+ | platformArch (targetPlatform dflags) == ArchWasm32]+ ++ (if any (asmInfo ==) [Clang, AppleClang, AppleClang51] then [GHC.SysTools.Option "-Qunused-arguments"] else [])@@ -338,8 +350,87 @@ return output_fn -runCcPhase :: Phase -> PipeEnv -> HscEnv -> FilePath -> IO FilePath-runCcPhase cc_phase pipe_env hsc_env input_fn = do+-- Note [JS Backend .o file procedure]+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+--+-- The JS backend breaks some of the assumptions on file generation order+-- because it directly produces .o files. This violation breaks some of the+-- assumptions on file timestamps, particularly in the postHsc phase. The+-- postHsc phase for the JS backend is performed in 'runJsPhase'. Consider+-- what the NCG does:+--+-- With other NCG backends we have the following order:+-- 1. The backend produces a .s file+-- 2. Then we write the interface file, .hi+-- 3. Then we generate a .o file in a postHsc phase (calling the asm phase etc.)+--+-- For the JS Backend this order is different+-- 1. The JS Backend _directly_ produces .o files+-- 2. Then we write the interface file. Notice that this breaks the ordering+-- of .hi > .o (step 2 and step 3 in the NCG above).+--+-- This violation results in timestamp checks which pass on the NCG but fail+-- in the JS backend. In particular, checks that compare 'ms_obj_date', and+-- 'ms_iface_date' in 'GHC.Unit.Module.ModSummary'.+--+-- Thus to fix this ordering we touch the object files we generated earlier+-- to ensure these timestamps abide by the proper ordering.++-- | Run the JS Backend postHsc phase.+runJsPhase :: PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> IO FilePath+runJsPhase _pipe_env hsc_env _location input_fn = do+ let dflags = hsc_dflags hsc_env+ let logger = hsc_logger hsc_env++ -- The object file is already generated. We only touch it to ensure the+ -- timestamp is refreshed, see Note [JS Backend .o file procedure].+ touchObjectFile logger dflags input_fn++ return input_fn++-- | Deal with foreign JS files (embed them into .o files)+runForeignJsPhase :: PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> IO FilePath+runForeignJsPhase pipe_env hsc_env _location input_fn = do+ let dflags = hsc_dflags hsc_env+ let logger = hsc_logger hsc_env+ let tmpfs = hsc_tmpfs hsc_env+ let unit_env = hsc_unit_env hsc_env++ output_fn <- phaseOutputFilenameNew StopLn pipe_env hsc_env Nothing+ embedJsFile logger dflags tmpfs unit_env input_fn output_fn+ return output_fn+++applyAssemblerInfoGetter+ :: DefunctionalizedAssemblerInfoGetter+ -> Logger -> DynFlags -> Platform -> IO CompilerInfo+applyAssemblerInfoGetter StandardAssemblerInfoGetter logger dflags _platform =+ getAssemblerInfo logger dflags+applyAssemblerInfoGetter JSAssemblerInfoGetter _ _ _ =+ pure Emscripten+applyAssemblerInfoGetter DarwinClangAssemblerInfoGetter logger dflags platform =+ if platformOS platform == OSDarwin then+ pure Clang+ else+ getAssemblerInfo logger dflags++applyAssemblerProg+ :: DefunctionalizedAssemblerProg+ -> Logger -> DynFlags -> Platform -> [Option] -> IO ()+applyAssemblerProg StandardAssemblerProg logger dflags _platform =+ runAs logger dflags+applyAssemblerProg JSAssemblerProg logger dflags _platform =+ runEmscripten logger dflags+applyAssemblerProg DarwinClangAssemblerProg logger dflags platform =+ if platformOS platform == OSDarwin then+ runClang logger dflags+ else+ runAs logger dflags++++runCcPhase :: Phase -> PipeEnv -> HscEnv -> Maybe ModLocation -> FilePath -> IO FilePath+runCcPhase cc_phase pipe_env hsc_env location input_fn = do let dflags = hsc_dflags hsc_env let logger = hsc_logger hsc_env let unit_env = hsc_unit_env hsc_env@@ -402,10 +493,12 @@ | llvmOptLevel dflags >= 1 = [ "-O" ] | otherwise = [] - -- Decide next phase- let next_phase = As False- output_fn <- phaseOutputFilenameNew next_phase pipe_env hsc_env Nothing+ output_fn <- phaseOutputFilenameNew StopLn pipe_env hsc_env location + -- we create directories for the object file, because it+ -- might be a hierarchical module.+ createDirectoryIfMissing True (takeDirectory output_fn)+ let more_hcc_opts = -- on x86 the floating point regs have greater precision@@ -425,14 +518,22 @@ ghcVersionH <- getGhcVersionPathName dflags unit_env - GHC.SysTools.runCc (phaseForeignLanguage cc_phase) logger tmpfs dflags (- [ GHC.SysTools.FileOption "" input_fn+ withAtomicRename output_fn $ \temp_outputFilename ->+ GHC.SysTools.runCc (phaseForeignLanguage cc_phase) logger tmpfs dflags (+ [ GHC.SysTools.Option "-c"+ , GHC.SysTools.FileOption "" input_fn , GHC.SysTools.Option "-o"- , GHC.SysTools.FileOption "" output_fn+ , GHC.SysTools.FileOption "" temp_outputFilename ] ++ map GHC.SysTools.Option ( pic_c_flags + -- See Note [Produce big objects on Windows]+ ++ [ "-Wa,-mbig-obj"+ | platformOS (targetPlatform dflags) == OSMinGW32+ , not $ target32Bit (targetPlatform dflags)+ ]+ -- Stub files generated for foreign exports references the runIO_closure -- and runNonIO_closure symbols, which are defined in the base package. -- These symbols are imported into the stub.c file via RtsAPI.h, and the@@ -452,7 +553,6 @@ then gcc_extra_viac_flags ++ more_hcc_opts else []) ++ verbFlags- ++ [ "-S" ] ++ cc_opt ++ [ "-include", ghcVersionH ] ++ framework_paths@@ -470,7 +570,7 @@ -> HscSource -> ModLocation -> HscBackendAction- -> IO ([FilePath], ModIface, Maybe Linkable, FilePath)+ -> IO ([FilePath], ModIface, HomeModLinkable, FilePath) runHscBackendPhase pipe_env hsc_env mod_name src_flavour location result = do let dflags = hsc_dflags hsc_env logger = hsc_logger hsc_env@@ -478,13 +578,13 @@ next_phase = hscPostBackendPhase src_flavour (backend dflags) case result of HscUpdate iface ->- if | NoBackend <- backend dflags ->+ if | not (backendGeneratesCode (backend dflags)) -> panic "HscUpdate not relevant for NoBackend"- | Interpreter <- backend dflags -> do+ | not (backendGeneratesCodeForHsBoot (backend dflags)) -> do -- In Interpreter way, there is just no linkable for hs-boot files -- and we don't want to write an empty `o-boot` file when we're not -- supposed to be writing any .o files (#22669)- return ([], iface, Nothing, o_file)+ return ([], iface, emptyHomeModInfoLinkable, o_file) | otherwise -> do case src_flavour of HsigFile -> do@@ -499,47 +599,38 @@ HsBootFile -> touchObjectFile logger dflags o_file HsSrcFile -> panic "HscUpdate not relevant for HscSrcFile" - -- MP: I wonder if there are any lurking bugs here because we- -- return Linkable == emptyHomeModInfoLinkable, despite the fact that there is a- -- linkable (.o-boot) which we check for in `Iface/Recomp.hs` and- -- then will carry around the linkable if we're doing- -- recompilation.- return ([], iface, Nothing, o_file)+ -- MP: I wonder if there are any lurking bugs here because we+ -- return Linkable == emptyHomeModInfoLinkable, despite the fact that there is a+ -- linkable (.o-boot) which we check for in `Iface/Recomp.hs` and+ -- then will carry around the linkable if we're doing+ -- recompilation.+ return ([], iface, emptyHomeModInfoLinkable, o_file) HscRecomp { hscs_guts = cgguts, hscs_mod_location = mod_location, hscs_partial_iface = partial_iface, hscs_old_iface_hash = mb_old_iface_hash }- -> case backend dflags of- NoBackend -> panic "HscRecomp not relevant for NoBackend"- Interpreter -> do- -- In interpreted mode the regular codeGen backend is not run so we- -- generate a interface without codeGen info.- final_iface <- mkFullIface hsc_env partial_iface Nothing Nothing- hscMaybeWriteIface logger dflags True final_iface mb_old_iface_hash location-- (hasStub, comp_bc, spt_entries) <- hscInteractive hsc_env cgguts mod_location-- stub_o <- case hasStub of- Nothing -> return []- Just stub_c -> do- stub_o <- compileStub hsc_env stub_c- return [DotO stub_o]-- let hs_unlinked = [BCOs comp_bc spt_entries]- unlinked_time <- getCurrentTime- let !linkable = LM unlinked_time (mkHomeModule (hsc_home_unit hsc_env) mod_name)- (hs_unlinked ++ stub_o)- return ([], final_iface, Just linkable, panic "interpreter")- _ -> do+ -> if not (backendGeneratesCode (backend dflags)) then+ panic "HscRecomp not relevant for NoBackend"+ else if backendWritesFiles (backend dflags) then+ do output_fn <- phaseOutputFilenameNew next_phase pipe_env hsc_env (Just location)- (outputFilename, mStub, foreign_files, mb_stg_infos, mb_cg_infos) <-+ (outputFilename, mStub, foreign_files, stg_infos, cg_infos) <-+ hscGenHardCode hsc_env cgguts mod_location output_fn- final_iface <- mkFullIface hsc_env partial_iface mb_stg_infos mb_cg_infos+ final_iface <- mkFullIface hsc_env partial_iface stg_infos cg_infos -- See Note [Writing interface files] hscMaybeWriteIface logger dflags False final_iface mb_old_iface_hash mod_location+ mlinkable <-+ if backendGeneratesCode (backend dflags) && gopt Opt_ByteCodeAndObjectCode dflags+ then do+ bc <- generateFreshByteCode hsc_env mod_name (mkCgInteractiveGuts cgguts) mod_location+ return $ emptyHomeModInfoLinkable { homeMod_bytecode = Just bc } + else return emptyHomeModInfoLinkable++ stub_o <- mapM (compileStub hsc_env) mStub foreign_os <- mapM (uncurry (compileForeign hsc_env)) foreign_files@@ -549,17 +640,26 @@ -- have some way to do before the object file is produced -- In future we can split up the driver logic more so that this function -- is in TPipeline and in this branch we can invoke the rest of the backend phase.- return (fos, final_iface, Nothing, outputFilename)+ return (fos, final_iface, mlinkable, outputFilename) + else+ -- In interpreted mode the regular codeGen backend is not run so we+ -- generate a interface without codeGen info.+ do+ final_iface <- mkFullIface hsc_env partial_iface Nothing Nothing+ hscMaybeWriteIface logger dflags True final_iface mb_old_iface_hash location+ bc <- generateFreshByteCode hsc_env mod_name (mkCgInteractiveGuts cgguts) mod_location+ return ([], final_iface, emptyHomeModInfoLinkable { homeMod_bytecode = Just bc } , panic "interpreter") + runUnlitPhase :: HscEnv -> FilePath -> FilePath -> IO FilePath runUnlitPhase hsc_env input_fn output_fn = do let -- escape the characters \, ", and ', but don't try to escape -- Unicode or anything else (so we don't use Util.charToC -- here). If we get this wrong, then in- -- GHC.HsToCore.Coverage.isGoodTickSrcSpan where we check that the filename in- -- a SrcLoc is the same as the source filenaame, the two will+ -- GHC.HsToCore.Ticks.isGoodTickSrcSpan where we check that the filename in+ -- a SrcLoc is the same as the source filename, the two will -- look bogusly different. See test: -- libraries/hpc/tests/function/subdir/tough2.hs escape ('\\':cs) = '\\':'\\': escape cs@@ -599,7 +699,10 @@ (hsc_tmpfs hsc_env) (hsc_dflags hsc_env) (hsc_unit_env hsc_env)- True{-raw-}+ (CppOpts+ { cppUseCc = False+ , cppLinePragmas = True+ }) input_fn output_fn return output_fn @@ -621,9 +724,11 @@ new_includes = addImplicitQuoteInclude paths [current_dir] paths = includePaths dflags0 dflags = dflags0 { includePaths = new_includes }- hsc_env = hscSetFlags dflags hsc_env0-+ hsc_env1 = hscSetFlags dflags hsc_env0 + -- Initialise plugins as the flags passed into runHscPhase might have local plugins just+ -- specific to this module.+ hsc_env <- initializePlugins hsc_env1 -- gather the imports and module name (hspp_buf,mod_name,imps,src_imps, ghc_prim_imp) <- do@@ -683,18 +788,17 @@ -- run the compiler! let msg :: Messager msg hsc_env _ what _ = oneShotMsg (hsc_logger hsc_env) what- plugin_hsc_env' <- initializePlugins hsc_env -- Need to set the knot-tying mutable variable for interface -- files. See GHC.Tc.Utils.TcGblEnv.tcg_type_env_var. -- See also Note [hsc_type_env_var hack] type_env_var <- newIORef emptyNameEnv- let plugin_hsc_env = plugin_hsc_env' { hsc_type_env_vars = knotVarsFromModuleEnv (mkModuleEnv [(mod, type_env_var)]) }+ let hsc_env' = hsc_env { hsc_type_env_vars = knotVarsFromModuleEnv (mkModuleEnv [(mod, type_env_var)]) } - status <- hscRecompStatus (Just msg) plugin_hsc_env mod_summary- Nothing Nothing (1, 1)+ status <- hscRecompStatus (Just msg) hsc_env' mod_summary+ Nothing emptyHomeModInfoLinkable (1, 1) - return (plugin_hsc_env, mod_summary, status)+ return (hsc_env', mod_summary, status) -- | Calculate the ModLocation from the provided DynFlags. This function is only used -- in one-shot mode and therefore takes into account the effect of -o/-ohi flags@@ -813,7 +917,7 @@ | StopLn <- next_phase, Just loc <- maybe_location = return $ if dynamicNow dflags then ml_dyn_obj_file loc else ml_obj_file loc- -- 2. If output style is persistant then+ -- 2. If output style is persistent then | is_last_phase, Persistent <- output = persistent_fn -- 3. Specific file is only set when outputFile is set by -o -- If we are in dynamic mode but -dyno is not set then write to the same path as@@ -835,9 +939,11 @@ | otherwise = newTempName logger tmpfs (tmpDir dflags) TFL_CurrentModule suffix where- getOutputFile_ dflags = case outputFile_ dflags of- Nothing -> pprPanic "SpecificFile: No filename" (ppr $ (dynamicNow dflags, outputFile_ dflags, dynOutputFile_ dflags))- Just fn -> fn+ getOutputFile_ dflags =+ case outputFile_ dflags of+ Nothing -> pprPanic "SpecificFile: No filename" (ppr (dynamicNow dflags) $$+ text (fromMaybe "-" (dynOutputFile_ dflags)))+ Just fn -> fn hcsuf = hcSuf dflags odir = objectDir dflags@@ -879,9 +985,10 @@ -- | LLVM Options. These are flags to be passed to opt and llc, to ensure -- consistency we list them in pairs, so that they form groups.-llvmOptions :: DynFlags+llvmOptions :: LlvmConfig+ -> DynFlags -> [(String, String)] -- ^ pairs of (opt, llc) arguments-llvmOptions dflags =+llvmOptions llvm_config dflags = [("-enable-tbaa -tbaa", "-enable-tbaa") | gopt Opt_LlvmTBAA dflags ] ++ [("-relocation-model=" ++ rmodel ,"-relocation-model=" ++ rmodel) | not (null rmodel)]@@ -895,7 +1002,7 @@ ++ [("", "-target-abi=" ++ abi) | not (null abi) ] where target = platformMisc_llvmTarget $ platformMisc dflags- Just (LlvmTarget _ mcpu mattr) = lookup target (llvmTargets $ llvmConfig dflags)+ Just (LlvmTarget _ mcpu mattr) = lookup target (llvmTargets llvm_config) -- Relocation models rmodel | gopt Opt_PIC dflags = "pic"@@ -927,156 +1034,14 @@ abi :: String abi = case platformArch (targetPlatform dflags) of ArchRISCV64 -> "lp64d"+ ArchLoongArch64 -> "lp64d" _ -> "" ---- Note [Filepaths and Multiple Home Units]-offsetIncludePaths :: DynFlags -> IncludeSpecs -> IncludeSpecs-offsetIncludePaths dflags (IncludeSpecs incs quotes impl) =- let go = map (augmentByWorkingDirectory dflags)- in IncludeSpecs (go incs) (go quotes) (go impl)--- -------------------------------------------------------------------------------- Running CPP---- | Run CPP------ UnitEnv is needed to compute MIN_VERSION macros-doCpp :: Logger -> TmpFs -> DynFlags -> UnitEnv -> Bool -> FilePath -> FilePath -> IO ()-doCpp logger tmpfs dflags unit_env raw input_fn output_fn = do- let hscpp_opts = picPOpts dflags- let cmdline_include_paths = offsetIncludePaths dflags (includePaths dflags)- let unit_state = ue_units unit_env- pkg_include_dirs <- mayThrowUnitErr- (collectIncludeDirs <$> preloadUnitsInfo unit_env)- -- MP: This is not quite right, the headers which are supposed to be installed in- -- the package might not be the same as the provided include paths, but it's a close- -- enough approximation for things to work. A proper solution would be to have to declare which paths should- -- be propagated to dependent packages.- let home_pkg_deps =- [homeUnitEnv_dflags . ue_findHomeUnitEnv uid $ unit_env | uid <- ue_transitiveHomeDeps (ue_currentUnit unit_env) unit_env]- dep_pkg_extra_inputs = [offsetIncludePaths fs (includePaths fs) | fs <- home_pkg_deps]-- let include_paths_global = foldr (\ x xs -> ("-I" ++ x) : xs) []- (includePathsGlobal cmdline_include_paths ++ pkg_include_dirs- ++ concatMap includePathsGlobal dep_pkg_extra_inputs)- let include_paths_quote = foldr (\ x xs -> ("-iquote" ++ x) : xs) []- (includePathsQuote cmdline_include_paths ++- includePathsQuoteImplicit cmdline_include_paths)- let include_paths = include_paths_quote ++ include_paths_global-- let verbFlags = getVerbFlags dflags-- let cpp_prog args | raw = GHC.SysTools.runCpp logger dflags args- | otherwise = GHC.SysTools.runCc Nothing logger tmpfs dflags- (GHC.SysTools.Option "-E" : args)-- let platform = targetPlatform dflags- targetArch = stringEncodeArch $ platformArch platform- targetOS = stringEncodeOS $ platformOS platform- isWindows = platformOS platform == OSMinGW32- let target_defs =- [ "-D" ++ HOST_OS ++ "_BUILD_OS",- "-D" ++ HOST_ARCH ++ "_BUILD_ARCH",- "-D" ++ targetOS ++ "_HOST_OS",- "-D" ++ targetArch ++ "_HOST_ARCH" ]- -- remember, in code we *compile*, the HOST is the same our TARGET,- -- and BUILD is the same as our HOST.-- let io_manager_defs =- [ "-D__IO_MANAGER_WINIO__=1" | isWindows ] ++- [ "-D__IO_MANAGER_MIO__=1" ]-- let sse_defs =- [ "-D__SSE__" | isSseEnabled platform ] ++- [ "-D__SSE2__" | isSse2Enabled platform ] ++- [ "-D__SSE4_2__" | isSse4_2Enabled dflags ]-- let avx_defs =- [ "-D__AVX__" | isAvxEnabled dflags ] ++- [ "-D__AVX2__" | isAvx2Enabled dflags ] ++- [ "-D__AVX512CD__" | isAvx512cdEnabled dflags ] ++- [ "-D__AVX512ER__" | isAvx512erEnabled dflags ] ++- [ "-D__AVX512F__" | isAvx512fEnabled dflags ] ++- [ "-D__AVX512PF__" | isAvx512pfEnabled dflags ]-- backend_defs <- getBackendDefs logger dflags-- let th_defs = [ "-D__GLASGOW_HASKELL_TH__" ]- -- Default CPP defines in Haskell source- ghcVersionH <- getGhcVersionPathName dflags unit_env- let hsSourceCppOpts = [ "-include", ghcVersionH ]-- -- MIN_VERSION macros- let uids = explicitUnits unit_state- pkgs = mapMaybe (lookupUnit unit_state . fst) uids- mb_macro_include <-- if not (null pkgs) && gopt Opt_VersionMacros dflags- then do macro_stub <- newTempName logger tmpfs (tmpDir dflags) TFL_CurrentModule "h"- writeFile macro_stub (generatePackageVersionMacros pkgs)- -- Include version macros for every *exposed* package.- -- Without -hide-all-packages and with a package database- -- size of 1000 packages, it takes cpp an estimated 2- -- milliseconds to process this file. See #10970- -- comment 8.- return [GHC.SysTools.FileOption "-include" macro_stub]- else return []-- cpp_prog ( map GHC.SysTools.Option verbFlags- ++ map GHC.SysTools.Option include_paths- ++ map GHC.SysTools.Option hsSourceCppOpts- ++ map GHC.SysTools.Option target_defs- ++ map GHC.SysTools.Option backend_defs- ++ map GHC.SysTools.Option th_defs- ++ map GHC.SysTools.Option hscpp_opts- ++ map GHC.SysTools.Option sse_defs- ++ map GHC.SysTools.Option avx_defs- ++ map GHC.SysTools.Option io_manager_defs- ++ mb_macro_include- -- Set the language mode to assembler-with-cpp when preprocessing. This- -- alleviates some of the C99 macro rules relating to whitespace and the hash- -- operator, which we tend to abuse. Clang in particular is not very happy- -- about this.- ++ [ GHC.SysTools.Option "-x"- , GHC.SysTools.Option "assembler-with-cpp"- , GHC.SysTools.Option input_fn- -- We hackily use Option instead of FileOption here, so that the file- -- name is not back-slashed on Windows. cpp is capable of- -- dealing with / in filenames, so it works fine. Furthermore- -- if we put in backslashes, cpp outputs #line directives- -- with *double* backslashes. And that in turn means that- -- our error messages get double backslashes in them.- -- In due course we should arrange that the lexer deals- -- with these \\ escapes properly.- , GHC.SysTools.Option "-o"- , GHC.SysTools.FileOption "" output_fn- ])--getBackendDefs :: Logger -> DynFlags -> IO [String]-getBackendDefs logger dflags | backend dflags == LLVM = do- llvmVer <- figureLlvmVersion logger dflags- return $ case fmap llvmVersionList llvmVer of- Just [m] -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (m,0) ]- Just (m:n:_) -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (m,n) ]- _ -> []- where- format (major, minor)- | minor >= 100 = error "getBackendDefs: Unsupported minor version"- | otherwise = show $ (100 * major + minor :: Int) -- Contract is Int--getBackendDefs _ _ =- return []- -- | What phase to run after one of the backend code generators has run hscPostBackendPhase :: HscSource -> Backend -> Phase hscPostBackendPhase HsBootFile _ = StopLn hscPostBackendPhase HsigFile _ = StopLn-hscPostBackendPhase _ bcknd =- case bcknd of- ViaC -> HCc- NCG -> As False- LLVM -> LlvmOpt- NoBackend -> StopLn- Interpreter -> StopLn+hscPostBackendPhase _ bcknd = backendNormalSuccessorPhase bcknd compileStub :: HscEnv -> FilePath -> IO FilePath@@ -1117,7 +1082,7 @@ Note [Object merging] ~~~~~~~~~~~~~~~~~~~~~ On most platforms one can "merge" a set of relocatable object files into a new,-partiall-linked-but-still-relocatable object. In a typical UNIX-style linker,+partially-linked-but-still-relocatable object. In a typical UNIX-style linker, this is accomplished with the `ld -r` command. We rely on this for two ends: * We rely on `ld -r` to squash together split sections, making GHCi loading@@ -1161,21 +1126,14 @@ -- | See Note [Object merging]. joinObjectFiles :: HscEnv -> [FilePath] -> FilePath -> IO () joinObjectFiles hsc_env o_files output_fn- | can_merge_objs = do+ | can_merge_objs && not dashLSupported = do let toolSettings' = toolSettings dflags ldIsGnuLd = toolSettings_ldIsGnuLd toolSettings' ld_r args = GHC.SysTools.runMergeObjects (hsc_logger hsc_env) (hsc_tmpfs hsc_env) (hsc_dflags hsc_env) (- map GHC.SysTools.Option ld_build_id- ++ [ GHC.SysTools.Option "-o",+ [ GHC.SysTools.Option "-o", GHC.SysTools.FileOption "" output_fn ] ++ args) - -- suppress the generation of the .note.gnu.build-id section,- -- which we don't need and sometimes causes ld to emit a- -- warning:- ld_build_id | toolSettings_ldSupportsBuildId toolSettings' = ["--build-id=none"]- | otherwise = []- if ldIsGnuLd then do script <- newTempName logger tmpfs (tmpDir dflags) TFL_CurrentModule "ldscript"@@ -1229,36 +1187,6 @@ --- ------------------------------------------------------------------------------ Macros (cribbed from Cabal)--generatePackageVersionMacros :: [UnitInfo] -> String-generatePackageVersionMacros pkgs = concat- -- Do not add any C-style comments. See #3389.- [ generateMacros "" pkgname version- | pkg <- pkgs- , let version = unitPackageVersion pkg- pkgname = map fixchar (unitPackageNameString pkg)- ]--fixchar :: Char -> Char-fixchar '-' = '_'-fixchar c = c--generateMacros :: String -> String -> Version -> String-generateMacros prefix name version =- concat- ["#define ", prefix, "VERSION_",name," ",show (showVersion version),"\n"- ,"#define MIN_", prefix, "VERSION_",name,"(major1,major2,minor) (\\\n"- ," (major1) < ",major1," || \\\n"- ," (major1) == ",major1," && (major2) < ",major2," || \\\n"- ," (major1) == ",major1," && (major2) == ",major2," && (minor) <= ",minor,")"- ,"\n\n"- ]- where- (major1:major2:minor:_) = map show (versionBranch version ++ repeat 0)-- -- ----------------------------------------------------------------------------- -- Misc. @@ -1269,22 +1197,6 @@ createDirectoryIfMissing True $ takeDirectory path GHC.SysTools.touch logger dflags "Touching object file" path --- | Find out path to @ghcversion.h@ file-getGhcVersionPathName :: DynFlags -> UnitEnv -> IO FilePath-getGhcVersionPathName dflags unit_env = do- candidates <- case ghcVersionFile dflags of- Just path -> return [path]- Nothing -> do- ps <- mayThrowUnitErr (preloadUnitsInfo' unit_env [rtsUnitId])- return ((</> "ghcversion.h") <$> collectIncludeDirs ps)-- found <- filterM doesFileExist candidates- case found of- [] -> throwGhcExceptionIO (InstallationError- ("ghcversion.h missing; tried: "- ++ intercalate ", " candidates))- (x:_) -> return x- -- Note [-fPIC for assembler] -- ~~~~~~~~~~~~~~~~~~~~~~~~~~ -- When compiling .c source file GHC's driver pipeline basically@@ -1353,9 +1265,9 @@ 4) -fhpc At some point during compilation with -fhpc, in the function- `GHC.HsToCore.Coverage.isGoodTickSrcSpan`, we compare the filename that a+ `GHC.HsToCore.Ticks.isGoodTickSrcSpan`, we compare the filename that a `SrcSpan` refers to with the name of the file we are currently compiling.- For some reason I don't yet understand, they can sometimes legitimally be+ For some reason I don't yet understand, they can sometimes legitimately be different, and then hpc ignores that SrcSpan. Problem@@ -1382,4 +1294,31 @@ that commit was addressing has since been solved in a different manner, in a commit called "Fix the filename passed to unlit" (1eedbc6b). So the `normalise` is no longer necessary.+-}++{-+Note [-Wa,--no-type-check on wasm32]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++Wasm32 has a type system and corresponding validation rules, so it's+possible to produce syntactically valid object code that doesn't pass+validation.++We have no problem with that, but we do have a problem with clang.+When clang takes an assembly input for wasm32, it uses its internal+type-checker, which is a huge source of trouble (see llvm ticket+#56935 #58438): it may reject valid assembly, and even worse, it may+silently alter the output object code!!! The worsest of all, is the+person that added the wasm32 asm typechecker logic has moved on from+Google/LLVM, and while other LLVM devs may be knowledgable enough to+fix this mess, they likely got tons of other stuff on their table and+don't care enough.++We do have an escape hatch, just pass -Wa,--no-type-check to clang to+bypass the entire wasm32 asm typechecking logic. There's little point+in type-checking object code at compile-time anyway, the wasm engines+will do type-checking at run-time. And even if we want to add some+linting flag to do compile-time checks, we should just rely on+battle-tested external tools instead of a completely broken horror+story. -}
compiler/GHC/Hs/Stats.hs view
@@ -22,7 +22,7 @@ import Data.Char -- | Source Statistics-ppSourceStats :: Bool -> Located HsModule -> SDoc+ppSourceStats :: Bool -> Located (HsModule GhcPs) -> SDoc ppSourceStats short (L _ (HsModule{ hsmodExports = exports, hsmodImports = imports, hsmodDecls = ldecls })) = (if short then hcat else vcat) (map pp_val@@ -122,7 +122,7 @@ import_info :: LImportDecl GhcPs -> (Int, Int, Int, Int, Int, Int, Int) import_info (L _ (ImportDecl { ideclSafe = safe, ideclQualified = qual- , ideclAs = as, ideclHiding = spec }))+ , ideclAs = as, ideclImportList = spec })) = add7 (1, safe_info safe, qual_info qual, as_info as, 0,0,0) (spec_info spec) safe_info False = 0@@ -132,8 +132,8 @@ as_info Nothing = 0 as_info (Just _) = 1 spec_info Nothing = (0,0,0,0,1,0,0)- spec_info (Just (False, _)) = (0,0,0,0,0,1,0)- spec_info (Just (True, _)) = (0,0,0,0,0,0,1)+ spec_info (Just (Exactly, _)) = (0,0,0,0,0,1,0)+ spec_info (Just (EverythingBut, _)) = (0,0,0,0,0,0,1) data_info (DataDecl { tcdDataDefn = HsDataDefn { dd_cons = cs
compiler/GHC/Hs/Syn/Type.hs view
@@ -21,10 +21,10 @@ import GHC.Core.PatSyn import GHC.Core.TyCo.Rep import GHC.Core.Type-import GHC.Core.Utils import GHC.Hs import GHC.Tc.Types.Evidence import GHC.Types.Id+import GHC.Types.Var( VarBndr(..) ) import GHC.Types.SrcLoc import GHC.Utils.Outputable import GHC.Utils.Panic@@ -48,7 +48,7 @@ hsPatType (BangPat _ pat) = hsLPatType pat hsPatType (LazyPat _ pat) = hsLPatType pat hsPatType (LitPat _ lit) = hsLitType lit-hsPatType (AsPat _ var _) = idType (unLoc var)+hsPatType (AsPat _ var _ _) = idType (unLoc var) hsPatType (ViewPat ty _ _) = ty hsPatType (ListPat ty _) = mkListTy ty hsPatType (TuplePat tys _ bx) = mkTupleTy1 bx tys@@ -94,14 +94,14 @@ hsExprType (HsVar _ (L _ id)) = idType id hsExprType (HsUnboundVar (HER _ ty _) _) = ty hsExprType (HsRecSel _ (FieldOcc id _)) = idType id-hsExprType (HsOverLabel v _) = dataConCantHappen v+hsExprType (HsOverLabel v _ _) = dataConCantHappen v hsExprType (HsIPVar v _) = dataConCantHappen v hsExprType (HsOverLit _ lit) = overLitType lit hsExprType (HsLit _ lit) = hsLitType lit hsExprType (HsLam _ (MG { mg_ext = match_group })) = matchGroupTcType match_group hsExprType (HsLamCase _ _ (MG { mg_ext = match_group })) = matchGroupTcType match_group hsExprType (HsApp _ f _) = funResultTy $ lhsExprType f-hsExprType (HsAppType x f _) = piResultTy (lhsExprType f) x+hsExprType (HsAppType x f _ _) = piResultTy (lhsExprType f) x hsExprType (OpApp v _ _ _) = dataConCantHappen v hsExprType (NegApp _ _ se) = syntaxExprType se hsExprType (HsPar _ _ e _) = lhsExprType e@@ -116,11 +116,7 @@ hsExprType (HsDo ty _ _) = ty hsExprType (ExplicitList ty _) = mkListTy ty hsExprType (RecordCon con_expr _ _) = hsExprType con_expr-hsExprType e@(RecordUpd (RecordUpdTc { rupd_cons = cons, rupd_out_tys = out_tys }) _ _) =- case cons of- con_like:_ -> conLikeResTy con_like out_tys- [] -> pprPanic "hsExprType: RecordUpdTc with empty rupd_cons"- (ppr e)+hsExprType (RecordUpd v _ _) = dataConCantHappen v hsExprType (HsGetField { gf_ext = v }) = dataConCantHappen v hsExprType (HsProjection { proj_ext = v }) = dataConCantHappen v hsExprType (ExprWithTySig _ e _) = lhsExprType e@@ -129,13 +125,14 @@ Nothing -> asi_ty where asi_ty = arithSeqInfoType asi-hsExprType (HsTypedBracket (HsBracketTc _ ty _wrap _pending) _) = ty-hsExprType (HsUntypedBracket (HsBracketTc _ ty _wrap _pending) _) = ty-hsExprType e@(HsSpliceE{}) = pprPanic "hsExprType: Unexpected HsSpliceE"- (ppr e)- -- Typed splices should have been eliminated during zonking, but we- -- can't use `dataConCantHappen` since they are still present before- -- than in the typechecked AST+hsExprType (HsTypedBracket (HsBracketTc { hsb_ty = ty }) _) = ty+hsExprType (HsUntypedBracket (HsBracketTc { hsb_ty = ty }) _) = ty+hsExprType e@(HsTypedSplice{}) = pprPanic "hsExprType: Unexpected HsTypedSplice"+ (ppr e)+ -- Typed splices should have been eliminated during zonking, but we+ -- can't use `dataConCantHappen` since they are still present before+ -- than in the typechecked AST.+hsExprType (HsUntypedSplice ext _) = dataConCantHappen ext hsExprType (HsProc _ _ lcmd_top) = lhsCmdTopType lcmd_top hsExprType (HsStatic (_, ty) _s) = ty hsExprType (HsPragE _ _ e) = lhsExprType e@@ -185,9 +182,9 @@ exp_res = hsWrapperType w2 act_res in mkFunctionType m exp_arg exp_res go (WpCast co) = liftPRType $ \_ -> coercionRKind co- go (WpEvLam v) = liftPRType $ mkInvisFunTyMany (idType v)+ go (WpEvLam v) = liftPRType $ mkInvisFunTy (idType v) go (WpEvApp _) = liftPRType $ funResultTy- go (WpTyLam tv) = liftPRType $ mkForAllTy tv Inferred+ go (WpTyLam tv) = liftPRType $ mkForAllTy (Bndr tv Inferred) go (WpTyApp ta) = \(ty,tas) -> (ty, ta:tas) go (WpLet _) = id go (WpMultCoercion _) = id@@ -196,7 +193,7 @@ lhsCmdTopType (L _ (HsCmdTop (CmdTopTc _ ret_ty _) _)) = ret_ty matchGroupTcType :: MatchGroupTc -> Type-matchGroupTcType (MatchGroupTc args res) = mkVisFunTys args res+matchGroupTcType (MatchGroupTc args res _) = mkScaledFunTys args res syntaxExprType :: SyntaxExpr GhcTc -> Type syntaxExprType (SyntaxExprTc e _ _) = hsExprType e
compiler/GHC/HsToCore.hs view
@@ -1,4 +1,4 @@-+{-# LANGUAGE MonadComprehensions #-} {-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}@@ -20,6 +20,9 @@ import GHC.Driver.Session import GHC.Driver.Config+import GHC.Driver.Config.Core.Lint ( endPassHscEnvIO )+import GHC.Driver.Config.HsToCore.Ticks+import GHC.Driver.Config.HsToCore.Usage import GHC.Driver.Env import GHC.Driver.Backend import GHC.Driver.Plugins@@ -32,17 +35,20 @@ import GHC.HsToCore.Expr import GHC.HsToCore.Binds import GHC.HsToCore.Foreign.Decl+import GHC.HsToCore.Ticks+import GHC.HsToCore.Breakpoints import GHC.HsToCore.Coverage import GHC.HsToCore.Docs import GHC.Tc.Types-import GHC.Tc.Utils.Monad ( finalSafeMode, fixSafeInstances )+import GHC.Tc.Utils.Monad ( finalSafeMode, fixSafeInstances, initIfaceLoad ) import GHC.Tc.Module ( runTcInteractive ) import GHC.Core.Type-import GHC.Core.TyCon ( tyConDataCons )+import GHC.Core.TyCo.Compare( eqType )+import GHC.Core.TyCon ( tyConDataCons ) import GHC.Core-import GHC.Core.FVs ( exprsSomeFreeVarsList )+import GHC.Core.FVs ( exprsSomeFreeVarsList, exprFreeVars ) import GHC.Core.SimpleOpt ( simpleOptPgm, simpleOptExpr ) import GHC.Core.Utils import GHC.Core.Unfold.Make@@ -50,8 +56,7 @@ import GHC.Core.DataCon ( dataConWrapId ) import GHC.Core.Make import GHC.Core.Rules-import GHC.Core.Opt.Monad ( CoreToDo(..) )-import GHC.Core.Lint ( endPassIO )+import GHC.Core.Opt.Pipeline.Types ( CoreToDo(..) ) import GHC.Core.Ppr import GHC.Builtin.Names@@ -61,6 +66,7 @@ import GHC.Data.FastString import GHC.Data.Maybe ( expectJust ) import GHC.Data.OrdList+import GHC.Data.SizedSeq ( sizeSS ) import GHC.Utils.Error import GHC.Utils.Outputable@@ -91,6 +97,7 @@ import Data.List (partition) import Data.IORef+import Data.Traversable (for) {- ************************************************************************@@ -139,26 +146,41 @@ = do { let dflags = hsc_dflags hsc_env logger = hsc_logger hsc_env- print_unqual = mkPrintUnqualified (hsc_unit_env hsc_env) rdr_env+ ptc = initPromotionTickContext (hsc_dflags hsc_env)+ name_ppr_ctx = mkNamePprCtx ptc (hsc_unit_env hsc_env) rdr_env ; withTiming logger (text "Desugar"<+>brackets (ppr mod)) (const ()) $ do { -- Desugar the program ; let export_set = availsToNameSet exports bcknd = backend dflags- hpcInfo = emptyHpcInfo other_hpc_info - ; (binds_cvr, ds_hpc_info, modBreaks)+ ; (binds_cvr, m_tickInfo) <- if not (isHsBootOrSig hsc_src) then addTicksToBinds- (CoverageConfig- { coverageConfig_logger = hsc_logger hsc_env- , coverageConfig_dynFlags = hsc_dflags hsc_env- , coverageConfig_mInterp = hsc_interp hsc_env- })+ (hsc_logger hsc_env)+ (initTicksConfig (hsc_dflags hsc_env)) mod mod_loc export_set (typeEnvTyCons type_env) binds- else return (binds, hpcInfo, Nothing)+ else return (binds, Nothing)+ ; modBreaks <- for+ [ (i, s)+ | i <- hsc_interp hsc_env+ , (_, s) <- m_tickInfo+ , backendWantsBreakpointTicks (backend dflags)+ ]+ $ \(interp, specs) -> mkModBreaks interp mod specs++ ; ds_hpc_info <- case m_tickInfo of+ Just (orig_file2, ticks)+ | gopt Opt_Hpc $ hsc_dflags hsc_env+ -> do+ hashNo <- if gopt Opt_Hpc $ hsc_dflags hsc_env+ then writeMixEntries (hpcDir dflags) mod ticks orig_file2+ else return 0 -- dummy hash when none are written+ pure $ HpcInfo (fromIntegral $ sizeSS ticks) hashNo+ _ -> pure $ emptyHpcInfo other_hpc_info+ ; (msgs, mb_res) <- initDs hsc_env tcg_env $ do { ds_ev_binds <- dsEvBinds ev_binds ; core_prs <- dsTopLHsBinds binds_cvr@@ -191,7 +213,7 @@ -- You might think it doesn't matter, but the simplifier brings all top-level -- things into the in-scope set before simplifying; so we get no unfolding for F#! - ; endPassIO hsc_env print_unqual CoreDesugar final_pgm rules_for_imps+ ; endPassHscEnvIO hsc_env name_ppr_ctx CoreDesugar final_pgm rules_for_imps ; let simpl_opts = initSimpleOpts dflags ; let (ds_binds, ds_rules_for_imps, occ_anald_binds) = simpleOptPgm simpl_opts mod final_pgm rules_for_imps@@ -200,7 +222,7 @@ ; putDumpFileMaybe logger Opt_D_dump_occur_anal "Occurrence analysis" FormatCore (pprCoreBindings occ_anald_binds $$ pprRules ds_rules_for_imps ) - ; endPassIO hsc_env print_unqual CoreDesugarOpt ds_binds ds_rules_for_imps+ ; endPassHscEnvIO hsc_env name_ppr_ctx CoreDesugarOpt ds_binds ds_rules_for_imps ; let used_names = mkUsedNames tcg_env pluginModules = map lpModule (loadedPlugins (hsc_plugins hsc_env))@@ -215,8 +237,13 @@ ; safe_mode <- finalSafeMode dflags tcg_env ; (needed_mods, needed_pkgs) <- readIORef (tcg_th_needed_deps tcg_env) - ; usages <- mkUsageInfo hsc_env mod (imp_mods imports) used_names- dep_files merged needed_mods needed_pkgs+ ; let uc = initUsageConfig hsc_env+ ; let plugins = hsc_plugins hsc_env+ ; let fc = hsc_FC hsc_env+ ; let unit_env = hsc_unit_env hsc_env+ ; usages <- initIfaceLoad hsc_env $+ mkUsageInfo uc plugins fc unit_env mod (imp_mods imports) used_names+ dep_files merged needed_mods needed_pkgs -- id_mod /= mod when we are processing an hsig, but hsigs -- never desugared and compiled (there's no code!) -- Consequently, this should hold for any ModGuts that make@@ -335,33 +362,28 @@ addExportFlagsAndRules :: Backend -> NameSet -> NameSet -> [CoreRule] -> [(Id, t)] -> [(Id, t)]-addExportFlagsAndRules bcknd exports keep_alive rules prs- = mapFst add_one prs+addExportFlagsAndRules bcknd exports keep_alive rules+ = mapFst (addRulesToId rule_base . add_export_flag)+ -- addRulesToId: see Note [Attach rules to local ids]+ -- NB: the binder might have some existing rules,+ -- arising from specialisation pragmas+ where- add_one bndr = add_rules name (add_export name bndr)- where- name = idName bndr ---------- Rules --------- -- See Note [Attach rules to local ids]- -- NB: the binder might have some existing rules,- -- arising from specialisation pragmas- add_rules name bndr- | Just rules <- lookupNameEnv rule_base name- = bndr `addIdSpecialisations` rules- | otherwise- = bndr rule_base = extendRuleBaseList emptyRuleBase rules ---------- Export flag -------- -- See Note [Adding export flags]- add_export name bndr- | dont_discard name = setIdExported bndr+ add_export_flag bndr+ | dont_discard bndr = setIdExported bndr | otherwise = bndr - dont_discard :: Name -> Bool- dont_discard name = is_exported name+ dont_discard :: Id -> Bool+ dont_discard bndr = is_exported name || name `elemNameSet` keep_alive+ where+ name = idName bndr -- In interactive mode, we don't want to discard any top-level -- entities at all (eg. do not inline them away during@@ -371,7 +393,7 @@ -- isExternalName separates the user-defined top-level names from those -- introduced by the type checker. is_exported :: Name -> Bool- is_exported | backendRetainsAllBindings bcknd = isExternalName+ is_exported | backendWantsGlobalBindings bcknd = isExternalName | otherwise = (`elemNameSet` exports) {-@@ -439,7 +461,7 @@ -- Substitute the dict bindings eagerly, -- and take the body apart into a (f args) form ; dflags <- getDynFlags- ; case decomposeRuleLhs dflags bndrs'' lhs'' of {+ ; case decomposeRuleLhs dflags bndrs'' lhs'' (exprFreeVars rhs'') of { Left msg -> do { diagnosticDs msg; return Nothing } ; Right (final_bndrs, fn_id, args) -> do @@ -450,27 +472,28 @@ fn_name = idName fn_id simpl_opts = initSimpleOpts dflags final_rhs = simpleOptExpr simpl_opts rhs'' -- De-crap it- rule_name = snd (unLoc name)- final_bndrs_set = mkVarSet final_bndrs- arg_ids = filterOut (`elemVarSet` final_bndrs_set) $- exprsSomeFreeVarsList isId args-- ; rule <- dsMkUserRule this_mod is_local- rule_name rule_act fn_name final_bndrs args- final_rhs- ; warnRuleShadowing rule_name rule_act fn_id arg_ids+ rule_name = unLoc name+ rule = mkRule this_mod False is_local rule_name rule_act+ fn_name final_bndrs args final_rhs+ ; dsWarnOrphanRule rule+ ; dsWarnRuleShadowing fn_id rule ; return (Just rule) } } } -warnRuleShadowing :: RuleName -> Activation -> Id -> [Id] -> DsM ()+dsWarnRuleShadowing :: Id -> CoreRule -> DsM () -- See Note [Rules and inlining/other rules]-warnRuleShadowing rule_name rule_act fn_id arg_ids+dsWarnRuleShadowing fn_id+ (Rule { ru_name = rule_name, ru_act = rule_act, ru_bndrs = bndrs, ru_args = args}) = do { check False fn_id -- We often have multiple rules for the same Id in a -- module. Maybe we should check that they don't overlap -- but currently we don't ; mapM_ (check True) arg_ids } where+ bndrs_set = mkVarSet bndrs+ arg_ids = filterOut (`elemVarSet` bndrs_set) $+ exprsSomeFreeVarsList isId args+ check check_rules_too lhs_id | isLocalId lhs_id || canUnfold (idUnfolding lhs_id) -- If imported with no unfolding, no worries@@ -486,6 +509,8 @@ = [ rule | rule <- idCoreRules lhs_id , ruleActivation rule `competesWith` rule_act ] +dsWarnRuleShadowing _ _ = return () -- Not expecting built-in rules here+ -- See Note [Desugaring coerce as cast] unfold_coerce :: [Id] -> CoreExpr -> CoreExpr -> DsM ([Var], CoreExpr, CoreExpr) unfold_coerce bndrs lhs rhs = do@@ -720,11 +745,11 @@ , openAlphaTyVar, openBetaTyVar , x ] $ mkSingleAltCase scrut1- (mkWildValBinder Many scrut1_ty)+ (mkWildValBinder ManyTy scrut1_ty) (DataAlt unsafe_refl_data_con) [rr_cv] $ mkSingleAltCase scrut2- (mkWildValBinder Many scrut2_ty)+ (mkWildValBinder ManyTy scrut2_ty) (DataAlt unsafe_refl_data_con) [ab_cv] $ Var x `mkCast` x_co@@ -761,7 +786,7 @@ info = noCafIdInfo `setInlinePragInfo` alwaysInlinePragma- `setUnfoldingInfo` mkCompulsoryUnfolding' rhs+ `setUnfoldingInfo` mkCompulsoryUnfolding rhs `setArityInfo` arity ty = mkSpecForAllTys [ runtimeRep1TyVar, runtimeRep2TyVar
compiler/GHC/HsToCore/Arrows.hs view
@@ -2,8 +2,6 @@ {-# LANGUAGE BlockArguments #-} {-# LANGUAGE TupleSections #-} -{-# OPTIONS_GHC -Wno-incomplete-record-updates #-}- {- (c) The University of Glasgow 2006 (c) The GRASP/AQUA Project, Glasgow University, 1992-1998@@ -87,7 +85,7 @@ where mk_bind (std_name, expr) = do { rhs <- dsExpr expr- ; id <- newSysLocalDs Many (exprType rhs)+ ; id <- newSysLocalDs ManyTy (exprType rhs) -- no check needed; these are functions ; return (NonRec id rhs, (std_name, id)) } @@ -136,18 +134,18 @@ -- construct CoreExpr for \ (a :: a_ty, b :: b_ty) -> a mkFstExpr :: Type -> Type -> DsM CoreExpr mkFstExpr a_ty b_ty = do- a_var <- newSysLocalDs Many a_ty- b_var <- newSysLocalDs Many b_ty- pair_var <- newSysLocalDs Many (mkCorePairTy a_ty b_ty)+ a_var <- newSysLocalDs ManyTy a_ty+ b_var <- newSysLocalDs ManyTy b_ty+ pair_var <- newSysLocalDs ManyTy (mkCorePairTy a_ty b_ty) return (Lam pair_var (coreCasePair pair_var a_var b_var (Var a_var))) -- construct CoreExpr for \ (a :: a_ty, b :: b_ty) -> b mkSndExpr :: Type -> Type -> DsM CoreExpr mkSndExpr a_ty b_ty = do- a_var <- newSysLocalDs Many a_ty- b_var <- newSysLocalDs Many b_ty- pair_var <- newSysLocalDs Many (mkCorePairTy a_ty b_ty)+ a_var <- newSysLocalDs ManyTy a_ty+ b_var <- newSysLocalDs ManyTy b_ty+ pair_var <- newSysLocalDs ManyTy (mkCorePairTy a_ty b_ty) return (Lam pair_var (coreCasePair pair_var a_var b_var (Var b_var))) @@ -162,7 +160,7 @@ coreCaseTuple :: UniqSupply -> Id -> [Id] -> CoreExpr -> CoreExpr coreCaseTuple uniqs scrut_var vars body- = mkTupleCase uniqs vars body scrut_var (Var scrut_var)+ = mkBigTupleCase uniqs vars body (Var scrut_var) coreCasePair :: Id -> Id -> Id -> CoreExpr -> CoreExpr coreCasePair scrut_var var1 var2 body@@ -178,11 +176,20 @@ mkCoreUnitExpr :: CoreExpr mkCoreUnitExpr = mkCoreTup [] -{--The input is divided into a local environment, which is a flat tuple-(unless it's too big), and a stack, which is a right-nested pair.-In general, the input has the form+{- Note [Environment and stack]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+The input is divided into +* A local environment, which is a flat tuple (unless it's too big)+ The elements of the local environment can be+ - of kind Type (for ordinary variables), or+ - of kind Constraint (for dictionaries bound by patterns)++* A stack, which is a right-nested pair.+ The elements on the stack are always of kind Type.++So in general, the input has the form+ ((x1,...,xn), (s1,...(sk,())...)) where xi are the environment values, and si the ones on the stack,@@ -225,9 +232,9 @@ -> DsM CoreExpr matchEnvStack env_ids stack_id body = do uniqs <- newUniqueSupply- tup_var <- newSysLocalDs Many (mkBigCoreVarTupTy env_ids)+ tup_var <- newSysLocalDs ManyTy (mkBigCoreVarTupTy env_ids) let match_env = coreCaseTuple uniqs tup_var env_ids body- pair_id <- newSysLocalDs Many (mkCorePairTy (idType tup_var) (idType stack_id))+ pair_id <- newSysLocalDs ManyTy (mkCorePairTy (idType tup_var) (idType stack_id)) return (Lam pair_id (coreCasePair pair_id tup_var stack_id match_env)) ----------------------------------------------@@ -244,7 +251,7 @@ -> DsM CoreExpr matchEnv env_ids body = do uniqs <- newUniqueSupply- tup_id <- newSysLocalDs Many (mkBigCoreVarTupTy env_ids)+ tup_id <- newSysLocalDs ManyTy (mkBigCoreVarTupTy env_ids) return (Lam tup_id (coreCaseTuple uniqs tup_id env_ids body)) ----------------------------------------------@@ -259,7 +266,7 @@ matchVarStack [] stack_id body = return (stack_id, body) matchVarStack (param_id:param_ids) stack_id body = do (tail_id, tail_code) <- matchVarStack param_ids stack_id body- pair_id <- newSysLocalDs Many (mkCorePairTy (idType param_id) (idType tail_id))+ pair_id <- newSysLocalDs ManyTy (mkCorePairTy (idType param_id) (idType tail_id)) return (pair_id, coreCasePair pair_id param_id tail_id tail_code) mkHsEnvStackExpr :: [Id] -> Id -> LHsExpr GhcTc@@ -288,7 +295,7 @@ let env_stk_ty = mkCorePairTy env_ty unitTy let env_stk_expr = mkCorePairExpr (mkBigCoreVarTup env_ids) mkCoreUnitExpr fail_expr <- mkFailExpr (ArrowMatchCtxt ProcExpr) env_stk_ty- var <- selectSimpleMatchVarL Many pat+ var <- selectSimpleMatchVarL ManyTy pat match_code <- matchSimply (Var var) (ArrowMatchCtxt ProcExpr) pat env_stk_expr fail_expr let pat_ty = hsLPatType pat let proc_code = do_premap meth_ids pat_ty env_stk_ty cmd_ty@@ -337,7 +344,7 @@ (_a_ty, arg_ty) = tcSplitAppTy a_arg_ty core_arrow <- dsLExpr arrow core_arg <- dsLExpr arg- stack_id <- newSysLocalDs Many stack_ty+ stack_id <- newSysLocalDs ManyTy stack_ty core_make_arg <- matchEnvStack env_ids stack_id core_arg return (do_premap ids (envStackType env_ids stack_ty)@@ -363,7 +370,7 @@ core_arrow <- dsLExpr arrow core_arg <- dsLExpr arg- stack_id <- newSysLocalDs Many stack_ty+ stack_id <- newSysLocalDs ManyTy stack_ty core_make_pair <- matchEnvStack env_ids stack_id (mkCorePairExpr core_arrow core_arg) @@ -390,8 +397,8 @@ stack_ty' = mkCorePairTy arg_ty stack_ty (core_cmd, free_vars, env_ids') <- dsfixCmd ids local_vars stack_ty' res_ty cmd- stack_id <- newSysLocalDs Many stack_ty- arg_id <- newSysLocalDs Many arg_ty+ stack_id <- newSysLocalDs ManyTy stack_ty+ arg_id <- newSysLocalDs ManyTy arg_ty -- push the argument expression onto the stack let stack' = mkCorePairExpr (Var arg_id) (Var stack_id)@@ -436,7 +443,7 @@ <- dsfixCmd ids local_vars stack_ty res_ty then_cmd (core_else, fvs_else, else_ids) <- dsfixCmd ids local_vars stack_ty res_ty else_cmd- stack_id <- newSysLocalDs Many stack_ty+ stack_id <- newSysLocalDs ManyTy stack_ty either_con <- dsLookupTyCon eitherTyConName left_con <- dsLookupDataCon leftDataConName right_con <- dsLookupDataCon rightDataConName@@ -498,10 +505,10 @@ -} dsCmd ids local_vars stack_ty res_ty (HsCmdCase _ exp match) env_ids = do- stack_id <- newSysLocalDs Many stack_ty+ stack_id <- newSysLocalDs ManyTy stack_ty (match', core_choices) <- dsCases ids local_vars stack_id stack_ty res_ty match- let MG{ mg_ext = MatchGroupTc _ sum_ty } = match'+ let MG{ mg_ext = MatchGroupTc _ sum_ty _ } = match' in_ty = envStackType env_ids stack_ty core_body <- dsExpr (HsCase noExtField exp match')@@ -540,11 +547,11 @@ -- construct and desugar a case expression with multiple scrutinees (core_body, free_vars, env_ids') <- trimInput \env_ids -> do- stack_id <- newSysLocalDs Many stack_ty'+ stack_id <- newSysLocalDs ManyTy stack_ty' (match', core_choices) <- dsCases ids local_vars' stack_id stack_ty' res_ty match - let MG{ mg_ext = MatchGroupTc _ sum_ty } = match'+ let MG{ mg_ext = MatchGroupTc _ sum_ty _ } = match' in_ty = envStackType env_ids stack_ty' discrims = map nlHsVar arg_ids (discrim_vars, matching_code)@@ -556,8 +563,8 @@ return (do_premap ids in_ty sum_ty res_ty core_matches core_choices, exprFreeIdsDSet core_body `uniqDSetIntersectUniqSet` local_vars') - param_ids <- mapM (newSysLocalDs Many) pat_tys- stack_id' <- newSysLocalDs Many stack_ty'+ param_ids <- mapM (newSysLocalDs ManyTy) pat_tys+ stack_id' <- newSysLocalDs ManyTy stack_ty' -- the expression is built from the inside out, so the actions -- are presented in reverse order@@ -592,7 +599,7 @@ (core_body, _free_vars, env_ids') <- dsfixCmd ids local_vars' stack_ty res_ty body- stack_id <- newSysLocalDs Many stack_ty+ stack_id <- newSysLocalDs ManyTy stack_ty -- build a new environment, plus the stack, using the let bindings core_binds <- dsLocalBinds lbinds (buildEnvStack env_ids' stack_id) -- match the old environment and stack against the input@@ -658,7 +665,7 @@ (meth_binds, meth_ids) <- mkCmdEnv ids (core_cmd, free_vars, env_ids') <- dsfixCmd meth_ids local_vars stack_ty cmd_ty cmd- stack_id <- newSysLocalDs Many stack_ty+ stack_id <- newSysLocalDs ManyTy stack_ty trim_code <- matchEnvStack env_ids stack_id (buildEnvStack env_ids' stack_id) let@@ -722,8 +729,8 @@ (pat_tys, stack_ty') = splitTypeAt (length pats) stack_ty (core_body, free_vars, env_ids') <- dsfixCmd ids local_vars' stack_ty' res_ty body- param_ids <- mapM (newSysLocalDs Many) pat_tys- stack_id' <- newSysLocalDs Many stack_ty'+ param_ids <- mapM (newSysLocalDs ManyTy) pat_tys+ stack_id' <- newSysLocalDs ManyTy stack_ty' -- the expression is built from the inside out, so the actions -- are presented in reverse order@@ -756,8 +763,8 @@ CoreExpr) -- desugared choices dsCases ids local_vars stack_id stack_ty res_ty (MG { mg_alts = L l matches- , mg_ext = MatchGroupTc arg_tys _- , mg_origin = origin }) = do+ , mg_ext = MatchGroupTc arg_tys _ origin+ }) = do -- Extract and desugar the leaf commands in the case, building tuple -- expressions that will (after tagging) replace these leaves@@ -805,8 +812,8 @@ Nothing -> ([], void_ty,) . do_arr ids void_ty res_ty <$> dsExpr (HsLamCase EpAnnNotUsed LamCase (MG { mg_alts = noLocA []- , mg_ext = MatchGroupTc [Scaled Many void_ty] res_ty- , mg_origin = Generated }))+ , mg_ext = MatchGroupTc [Scaled ManyTy void_ty] res_ty Generated+ })) -- Replace the commands in the case with these tagged tuples, -- yielding a HsExpr Id we can feed to dsExpr.@@ -816,8 +823,8 @@ -- Note that we replace the MatchGroup result type by sum_ty, -- which is the type of matches' return (MG { mg_alts = L l matches'- , mg_ext = MatchGroupTc arg_tys sum_ty- , mg_origin = origin },+ , mg_ext = MatchGroupTc arg_tys sum_ty origin+ }, core_choices) {-@@ -847,7 +854,7 @@ dsCmdDo ids local_vars res_ty [L _ (LastStmt _ body _ _)] env_ids = do (core_body, env_ids') <- dsLCmd ids local_vars unitTy res_ty body env_ids let env_ty = mkBigCoreVarTupTy env_ids- env_var <- newSysLocalDs Many env_ty+ env_var <- newSysLocalDs ManyTy env_ty let core_map = Lam env_var (mkCorePairExpr (Var env_var) mkCoreUnitExpr) return (do_premap ids env_ty@@ -949,7 +956,7 @@ -- projection function -- \ (p, (xs2)) -> (zs) - env_id <- newSysLocalDs Many env_ty2+ env_id <- newSysLocalDs ManyTy env_ty2 uniqs <- newUniqueSupply let after_c_ty = mkCorePairTy pat_ty env_ty2@@ -957,10 +964,10 @@ body_expr = coreCaseTuple uniqs env_id env_ids2 (mkBigCoreVarTup out_ids) fail_expr <- mkFailExpr (StmtCtxt (HsDoStmt (DoExpr Nothing))) out_ty- pat_id <- selectSimpleMatchVarL Many pat+ pat_id <- selectSimpleMatchVarL ManyTy pat match_code <- matchSimply (Var pat_id) (StmtCtxt (HsDoStmt (DoExpr Nothing))) pat body_expr fail_expr- pair_id <- newSysLocalDs Many after_c_ty+ pair_id <- newSysLocalDs ManyTy after_c_ty let proj_expr = Lam pair_id (coreCasePair pair_id pat_id env_id match_code) @@ -1023,7 +1030,7 @@ -- post_loop_fn = \((later_ids),(env2_ids)) -> (out_ids) uniqs <- newUniqueSupply- env2_id <- newSysLocalDs Many env2_ty+ env2_id <- newSysLocalDs ManyTy env2_ty let later_ty = mkBigCoreVarTupTy later_ids post_pair_ty = mkCorePairTy later_ty env2_ty@@ -1110,7 +1117,7 @@ -- squash_pair_fn = \ ((env1_ids), ~(rec_ids)) -> (env_ids) - rec_id <- newSysLocalDs Many rec_ty+ rec_id <- newSysLocalDs ManyTy rec_ty let env1_id_set = fv_stmts `uniqDSetMinusUniqSet` rec_id_set env1_ids = dVarSetElems env1_id_set@@ -1120,7 +1127,7 @@ where selectVar v | v `elemVarSet` rec_id_set- = mkTupleSelector rec_ids v rec_id (Var rec_id)+ = mkBigTupleSelector rec_ids v rec_id (Var rec_id) | otherwise = Var v squash_pair_fn <- matchEnvStack env1_ids rec_id core_body
compiler/GHC/HsToCore/Binds.hs view
@@ -2,8 +2,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-} -{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}- {- (c) The University of Glasgow 2006 (c) The GRASP/AQUA Project, Glasgow University, 1992-1998@@ -18,14 +16,14 @@ module GHC.HsToCore.Binds ( dsTopLHsBinds, dsLHsBinds, decomposeRuleLhs, dsSpec- , dsHsWrapper, dsEvTerm, dsTcEvBinds, dsTcEvBinds_s, dsEvBinds, dsMkUserRule+ , dsHsWrapper, dsEvTerm, dsTcEvBinds, dsTcEvBinds_s, dsEvBinds+ , dsWarnOrphanRule ) where import GHC.Prelude import GHC.Driver.Session-import GHC.Driver.Ppr import GHC.Driver.Config import qualified GHC.LanguageExtensions as LangExt import GHC.Unit.Module@@ -54,6 +52,7 @@ import GHC.Core.Coercion import GHC.Core.Multiplicity import GHC.Core.Rules+import GHC.Core.TyCo.Compare( eqType ) import GHC.Builtin.Names import GHC.Builtin.Types ( naturalTy, typeSymbolKind, charTy )@@ -73,7 +72,6 @@ import GHC.Data.OrdList import GHC.Data.Graph.Directed import GHC.Data.Bag-import GHC.Data.FastString import GHC.Utils.Constants (debugIsOn) import GHC.Utils.Misc@@ -81,7 +79,6 @@ import GHC.Utils.Outputable import GHC.Utils.Panic import GHC.Utils.Panic.Plain-import GHC.Utils.Trace import Control.Monad @@ -154,8 +151,8 @@ dsHsBind dflags b@(FunBind { fun_id = L loc fun , fun_matches = matches- , fun_ext = co_fn- , fun_tick = tick })+ , fun_ext = (co_fn, tick)+ }) = do { (args, body) <- addTyCs FromSource (hsWrapDictBinders co_fn) $ -- FromSource might not be accurate (we don't have any -- origin annotations for things in this module), but at@@ -185,8 +182,8 @@ return (force_var, [core_binds]) } dsHsBind dflags (PatBind { pat_lhs = pat, pat_rhs = grhss- , pat_ext = ty- , pat_ticks = (rhs_tick, var_ticks) })+ , pat_ext = (ty, (rhs_tick, var_ticks))+ }) = do { rhss_nablas <- pmcGRHSs PatBindGuards grhss ; body_expr <- dsGuarded grhss ty rhss_nablas ; let body' = mkOptTickBox rhs_tick body_expr@@ -300,7 +297,7 @@ mkLet aux_binds $ tup_expr - ; poly_tup_id <- newSysLocalDs Many (exprType poly_tup_rhs)+ ; poly_tup_id <- newSysLocalDs ManyTy (exprType poly_tup_rhs) -- Find corresponding global or make up a new one: sometimes -- we need to make new export to desugar strict binds, see@@ -311,10 +308,10 @@ , abe_poly = global , abe_mono = local, abe_prags = spec_prags }) -- See Note [AbsBinds wrappers] in "GHC.Hs.Binds"- = do { tup_id <- newSysLocalDs Many tup_ty+ = do { tup_id <- newSysLocalDs ManyTy tup_ty ; core_wrap <- dsHsWrapper wrap ; let rhs = core_wrap $ mkLams tyvars $ mkLams dicts $- mkTupleSelector all_locals local tup_id $+ mkBigTupleSelector all_locals local tup_id $ mkVarApps (Var poly_tup_id) (tyvars ++ dicts) rhs_for_spec = Let (NonRec poly_tup_id poly_tup_rhs) rhs ; (spec_binds, rules) <- dsSpecs rhs_for_spec spec_prags@@ -371,7 +368,7 @@ ([],[]) lcls mk_export local =- do global <- newSysLocalDs Many+ do global <- newSysLocalDs ManyTy (exprType (mkLams tyvars (mkLams dicts (Var local)))) return (ABE { abe_poly = global , abe_mono = local@@ -391,7 +388,7 @@ makeCorePair dflags gbl_id is_default_method dict_arity rhs | is_default_method -- Default methods are *always* inlined -- See Note [INLINE and default methods] in GHC.Tc.TyCl.Instance- = (gbl_id `setIdUnfolding` mkCompulsoryUnfolding simpl_opts rhs, rhs)+ = (gbl_id `setIdUnfolding` mkCompulsoryUnfolding' simpl_opts rhs, rhs) | otherwise = case inlinePragmaSpec inline_prag of@@ -403,19 +400,20 @@ where simpl_opts = initSimpleOpts dflags inline_prag = idInlinePragma gbl_id- inlinable_unf = mkInlinableUnfolding simpl_opts rhs+ inlinable_unf = mkInlinableUnfolding simpl_opts StableUserSrc rhs inline_pair | Just arity <- inlinePragmaSat inline_prag -- Add an Unfolding for an INLINE (but not for NOINLINE) -- And eta-expand the RHS; see Note [Eta-expanding INLINE things] , let real_arity = dict_arity + arity- -- NB: The arity in the InlineRule takes account of the dictionaries- = ( gbl_id `setIdUnfolding` mkInlineUnfoldingWithArity real_arity simpl_opts rhs+ -- NB: The arity passed to mkInlineUnfoldingWithArity+ -- must take account of the dictionaries+ = ( gbl_id `setIdUnfolding` mkInlineUnfoldingWithArity simpl_opts StableUserSrc real_arity rhs , etaExpand real_arity rhs) | otherwise = pprTrace "makeCorePair: arity missing" (ppr gbl_id) $- (gbl_id `setIdUnfolding` mkInlineUnfolding simpl_opts rhs, rhs)+ (gbl_id `setIdUnfolding` mkInlineUnfoldingNoArity simpl_opts StableUserSrc rhs, rhs) dictArity :: [Var] -> Arity -- Don't count coercion variables in arity@@ -527,7 +525,7 @@ get to do the inlining, which is a Terribly Bad thing given that the user said "inline"! -To avoid this we pre-emptively eta-expand the definition, so that foo+To avoid this we preemptively eta-expand the definition, so that foo has the arity with which it is declared in the source code. In this example it has arity 2 (one for the Eq and one for x). Doing this should mean that (foo d) is a PAP and we don't share it.@@ -543,7 +541,7 @@ fromT :: T Bool -> Bool { fromT_1 ((TBool b)) = not b } } } -Note the nested AbsBind. The arity for the InlineRule on $cfromT should be+Note the nested AbsBind. The arity for the unfolding on $cfromT should be gotten from the binding for fromT_1. It might be better to have just one level of AbsBinds, but that requires more@@ -708,7 +706,7 @@ -- , text "spec_co:" <+> ppr spec_co -- , text "ds_rhs:" <+> ppr ds_lhs ]) $ dflags <- getDynFlags- ; case decomposeRuleLhs dflags spec_bndrs ds_lhs of {+ ; case decomposeRuleLhs dflags spec_bndrs ds_lhs (mkVarSet spec_bndrs) of { Left msg -> do { diagnosticDs msg; return Nothing } ; Right (rule_bndrs, _fn, rule_lhs_args) -> do @@ -716,22 +714,16 @@ ; let fn_unf = realIdUnfolding poly_id simpl_opts = initSimpleOpts dflags spec_unf = specUnfolding simpl_opts spec_bndrs core_app rule_lhs_args fn_unf- spec_id = mkLocalId spec_name Many spec_ty -- Specialised binding is toplevel, hence Many.+ spec_id = mkLocalId spec_name ManyTy spec_ty -- Specialised binding is toplevel, hence Many. `setInlinePragma` inl_prag `setIdUnfolding` spec_unf - ; rule <- dsMkUserRule this_mod is_local_id- (mkFastString ("SPEC " ++ showPpr dflags poly_name))- rule_act poly_name- rule_bndrs rule_lhs_args- (mkVarApps (Var spec_id) spec_bndrs)-- ; let spec_rhs = mkLams spec_bndrs (core_app poly_rhs)+ rule = mkSpecRule dflags this_mod False rule_act (text "USPEC")+ poly_id rule_bndrs rule_lhs_args+ (mkVarApps (Var spec_id) spec_bndrs)+ spec_rhs = mkLams spec_bndrs (core_app poly_rhs) --- Commented out: see Note [SPECIALISE on INLINE functions]--- ; when (isInlinePragma id_inl)--- (diagnosticDs $ text "SPECIALISE pragma on INLINE function probably won't fire:"--- <+> quotes (ppr poly_name))+ ; dsWarnOrphanRule rule ; return (Just (unitOL (spec_id, spec_rhs), rule)) -- NB: do *not* use makeCorePair on (spec_id,spec_rhs), because@@ -774,13 +766,10 @@ | otherwise = spec_prag_act -- Specified by user -dsMkUserRule :: Module -> Bool -> RuleName -> Activation- -> Name -> [CoreBndr] -> [CoreExpr] -> CoreExpr -> DsM CoreRule-dsMkUserRule this_mod is_local name act fn bndrs args rhs = do- let rule = mkRule this_mod False is_local name act fn bndrs args rhs- when (isOrphan (ru_orphan rule)) $- diagnosticDs (DsOrphanRule rule)- return rule+dsWarnOrphanRule :: CoreRule -> DsM ()+dsWarnOrphanRule rule+ = when (isOrphan (ru_orphan rule)) $+ diagnosticDs (DsOrphanRule rule) {- Note [SPECIALISE on INLINE functions] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -844,56 +833,73 @@ -} decomposeRuleLhs :: DynFlags -> [Var] -> CoreExpr+ -> VarSet -- Free vars of the RHS -> Either DsMessage ([Var], Id, [CoreExpr]) -- (decomposeRuleLhs bndrs lhs) takes apart the LHS of a RULE, -- The 'bndrs' are the quantified binders of the rules, but decomposeRuleLhs--- may add some extra dictionary binders (see Note [Free dictionaries])+-- may add some extra dictionary binders (see Note [Free dictionaries on rule LHS]) -- -- Returns an error message if the LHS isn't of the expected shape -- Note [Decomposing the left-hand side of a RULE]-decomposeRuleLhs dflags orig_bndrs orig_lhs- | not (null unbound) -- Check for things unbound on LHS- -- See Note [Unused spec binders]- = Left (DsRuleBindersNotBound unbound orig_bndrs orig_lhs lhs2)+decomposeRuleLhs dflags orig_bndrs orig_lhs rhs_fvs | Var funId <- fun2 , Just con <- isDataConId_maybe funId = Left (DsRuleIgnoredDueToConstructor con) -- See Note [No RULES on datacons]- | Just (fn_id, args) <- decompose fun2 args2- , let extra_bndrs = mk_extra_bndrs fn_id args- = -- pprTrace "decomposeRuleLhs" (vcat [ text "orig_bndrs:" <+> ppr orig_bndrs- -- , text "orig_lhs:" <+> ppr orig_lhs- -- , text "lhs1:" <+> ppr lhs1- -- , text "extra_bndrs:" <+> ppr extra_bndrs- -- , text "fn_id:" <+> ppr fn_id- -- , text "args:" <+> ppr args]) $- Right (orig_bndrs ++ extra_bndrs, fn_id, args) - | otherwise- = Left (DsRuleLhsTooComplicated orig_lhs lhs2)- where- simpl_opts = initSimpleOpts dflags- lhs1 = drop_dicts orig_lhs- lhs2 = simpleOptExpr simpl_opts lhs1 -- See Note [Simplify rule LHS]- (fun2,args2) = collectArgs lhs2+ | otherwise = case decompose fun2 args2 of+ Nothing -> Left (DsRuleLhsTooComplicated orig_lhs lhs2)+ Just (fn_id, args)+ | not (null unbound) ->+ -- Check for things unbound on LHS+ -- See Note [Unused spec binders]+ -- pprTrace "decomposeRuleLhs 1" (vcat [ text "orig_bndrs:" <+> ppr orig_bndrs+ -- , text "orig_lhs:" <+> ppr orig_lhs+ -- , text "lhs_fvs:" <+> ppr lhs_fvs+ -- , text "rhs_fvs:" <+> ppr rhs_fvs+ -- , text "unbound:" <+> ppr unbound+ -- ]) $+ Left (DsRuleBindersNotBound unbound orig_bndrs orig_lhs lhs2)+ | otherwise ->+ -- pprTrace "decomposeRuleLhs 2" (vcat [ text "orig_bndrs:" <+> ppr orig_bndrs+ -- , text "orig_lhs:" <+> ppr orig_lhs+ -- , text "lhs1:" <+> ppr lhs1+ -- , text "extra_bndrs:" <+> ppr extra_bndrs+ -- , text "fn_id:" <+> ppr fn_id+ -- , text "args:" <+> ppr args+ -- , text "args fvs:" <+> ppr (exprsFreeVarsList args)+ -- ]) $+ Right (trimmed_bndrs ++ extra_bndrs, fn_id, args) - lhs_fvs = exprFreeVars lhs2- unbound = filterOut (`elemVarSet` lhs_fvs) orig_bndrs+ where -- See Note [Variables unbound on the LHS]+ lhs_fvs = exprsFreeVars args+ all_fvs = lhs_fvs `unionVarSet` rhs_fvs+ trimmed_bndrs = filter (`elemVarSet` all_fvs) orig_bndrs+ unbound = filterOut (`elemVarSet` lhs_fvs) trimmed_bndrs+ -- Needed on RHS but not bound on LHS + -- Add extra tyvar binders: Note [Free tyvars on rule LHS]+ -- and extra dict binders: Note [Free dictionaries on rule LHS]+ extra_bndrs = scopedSort extra_tvs ++ extra_dicts+ where+ extra_tvs = [ v | v <- extra_vars, isTyVar v ]+ extra_dicts =+ [ mkLocalId (localiseName (idName d)) ManyTy (idType d)+ | d <- extra_vars, isDictId d ]+ extra_vars =+ [ v+ | v <- exprsFreeVarsList args+ , not (v `elemVarSet` orig_bndr_set)+ , not (v == fn_id) ]+ -- fn_id: do not quantify over the function itself, which may+ -- itself be a dictionary (in pathological cases, #10251)++ where+ simpl_opts = initSimpleOpts dflags orig_bndr_set = mkVarSet orig_bndrs - -- Add extra tyvar binders: Note [Free tyvars in rule LHS]- -- and extra dict binders: Note [Free dictionaries in rule LHS]- mk_extra_bndrs fn_id args- = scopedSort unbound_tvs ++ unbound_dicts- where- unbound_tvs = [ v | v <- unbound_vars, isTyVar v ]- unbound_dicts = [ mkLocalId (localiseName (idName d)) Many (idType d)- | d <- unbound_vars, isDictId d ]- unbound_vars = [ v | v <- exprsFreeVarsList args- , not (v `elemVarSet` orig_bndr_set)- , not (v == fn_id) ]- -- fn_id: do not quantify over the function itself, which may- -- itself be a dictionary (in pathological cases, #10251)+ lhs1 = drop_dicts orig_lhs+ lhs2 = simpleOptExpr simpl_opts lhs1 -- See Note [Simplify rule LHS]+ (fun2,args2) = collectArgs lhs2 decompose (Var fn_id) args | not (fn_id `elemVarSet` orig_bndr_set)@@ -935,12 +941,33 @@ needed' = (needed `minusVarSet` rhs_fvs) `extendVarSet` d {-+Note [Variables unbound on the LHS]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+We obviously want to complain about+ RULE forall x. f True = not x+because the forall'd variable `x` is not bound on the LHS.++It can be a bit delicate when dictionaries are involved.+Consider #22471+ {-# RULES "foo" forall (f :: forall a. [a] -> Int).+ foo (\xs. 1 + f xs) = 2 + foo f #-}++We get two dicts on the LHS, one from `1` and one from `+`.+For reasons described in Note [The SimplifyRule Plan] in+GHC.Tc.Gen.Rule, we quantify separately over those dictionaries:+ forall f (d1::Num Int) (d2 :: Num Int).+ foo (\xs. (+) d1 (fromInteger d2 1) xs) = ...++Now the desugarer shortcircuits (fromInteger d2 1) to (I# 1); so d2 is+not mentioned at all (on LHS or RHS)! We don't want to complain about+and unbound d2. Hence the trimmed_bndrs.+ Note [Decomposing the left-hand side of a RULE] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are several things going on here. * drop_dicts: see Note [Drop dictionary bindings on rule LHS] * simpleOptExpr: see Note [Simplify rule LHS]-* extra_dict_bndrs: see Note [Free dictionaries]+* extra_dict_bndrs: see Note [Free dictionaries on rule LHS] Note [Free tyvars on rule LHS] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -965,6 +992,9 @@ see Note [Free dictionaries on rule LHS]. So that's why we look for type variables free on the LHS, and quantify over them. +This relies on there not being any in-scope tyvars, which is true for+user-defined RULEs, which are always top-level.+ Note [Free dictionaries on rule LHS] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When the LHS of a specialisation rule, (/\as\ds. f es) has a free dict,@@ -986,7 +1016,7 @@ But be careful! That dInt might be GHC.Base.$fOrdInt, which is an External Name, and you can't bind them in a lambda or forall without getting things-confused. Likewise it might have an InlineRule or something, which would be+confused. Likewise it might have a stable unfolding or something, which would be utterly bogus. So we really make a fresh Id, with the same unique and type as the old one, but with an Internal name and no IdInfo. @@ -997,7 +1027,7 @@ --> f d Reasoning here is that there is only one d:Eq [Int], and so we can quantify over it. That makes 'd' free in the LHS, but that is later- picked up by extra_dict_bndrs (Note [Dead spec binders]).+ picked up by extra_dict_bndrs (see Note [Unused spec binders]). NB 1: We can only drop the binding if the RHS doesn't bind one of the orig_bndrs, which we assume occur on RHS.@@ -1105,6 +1135,21 @@ * * ************************************************************************ +Note [Desugaring WpFun]+~~~~~~~~~~~~~~~~~~~~~~~+See comments on WpFun in GHC.Tc.Types.Evidence for what WpFun means.+Roughly:++ (WpFun w_arg w_res)[ e ] = \x. w_res[ e w_arg[x] ]++This eta-expansion risk duplicating work, if `e` is not in HNF.+At one stage I thought we could avoid that by desugaring to+ let f = e in \x. w_res[ f w_arg[x] ]+But that /fundamentally/ doesn't work, because `w_res` may bind+evidence that is used in `e`.++This question arose when thinking about deep subsumption; see+https://github.com/ghc-proposals/ghc-proposals/pull/287#issuecomment-1125419649). -} dsHsWrapper :: HsWrapper -> DsM (CoreExpr -> CoreExpr)@@ -1117,9 +1162,7 @@ dsHsWrapper (WpCompose c1 c2) = do { w1 <- dsHsWrapper c1 ; w2 <- dsHsWrapper c2 ; return (w1 . w2) }- -- See comments on WpFun in GHC.Tc.Types.Evidence for an explanation of what- -- the specification of this clause is-dsHsWrapper (WpFun c1 c2 (Scaled w t1))+dsHsWrapper (WpFun c1 c2 (Scaled w t1)) -- See Note [Desugaring WpFun] = do { x <- newSysLocalDs w t1 ; w1 <- dsHsWrapper c1 ; w2 <- dsHsWrapper c2@@ -1208,8 +1251,7 @@ dsEvTypeable ty ev = do { tyCl <- dsLookupTyCon typeableClassName -- Typeable ; let kind = typeKind ty- Just typeable_data_con- = tyConSingleDataCon_maybe tyCl -- "Data constructor"+ typeable_data_con = tyConSingleDataCon tyCl -- "Data constructor" -- for Typeable ; rep_expr <- ds_ev_typeable ty ev -- :: TypeRep a@@ -1266,7 +1308,7 @@ } ds_ev_typeable ty (EvTypeableTrFun evm ev1 ev2)- | Just (m,t1,t2) <- splitFunTy_maybe ty+ | Just (_af,m,t1,t2) <- splitFunTy_maybe ty = do { e1 <- getRep ev1 t1 ; e2 <- getRep ev2 t2 ; em <- getRep evm m
+ compiler/GHC/HsToCore/Breakpoints.hs view
@@ -0,0 +1,54 @@+module GHC.HsToCore.Breakpoints+ ( mkModBreaks+ ) where++import GHC.Prelude++import qualified GHC.Runtime.Interpreter as GHCi+import GHC.Runtime.Interpreter.Types+import GHCi.RemoteTypes+import GHC.ByteCode.Types+import GHC.Stack.CCS+import GHC.Unit++import GHC.HsToCore.Ticks (Tick (..))++import GHC.Data.SizedSeq+import GHC.Utils.Outputable as Outputable++import Data.List (intersperse)+import Data.Array++mkModBreaks :: Interp -> Module -> SizedSeq Tick -> IO ModBreaks+mkModBreaks interp mod extendedMixEntries+ = do+ let count = fromIntegral $ sizeSS extendedMixEntries+ entries = ssElts extendedMixEntries++ breakArray <- GHCi.newBreakArray interp count+ ccs <- mkCCSArray interp mod count entries+ let+ locsTicks = listArray (0,count-1) [ tick_loc t | t <- entries ]+ varsTicks = listArray (0,count-1) [ tick_ids t | t <- entries ]+ declsTicks = listArray (0,count-1) [ tick_path t | t <- entries ]+ return $ emptyModBreaks+ { modBreaks_flags = breakArray+ , modBreaks_locs = locsTicks+ , modBreaks_vars = varsTicks+ , modBreaks_decls = declsTicks+ , modBreaks_ccs = ccs+ }++mkCCSArray+ :: Interp -> Module -> Int -> [Tick]+ -> IO (Array BreakIndex (RemotePtr GHC.Stack.CCS.CostCentre))+mkCCSArray interp modul count entries+ | GHCi.interpreterProfiled interp = do+ let module_str = moduleNameString (moduleName modul)+ costcentres <- GHCi.mkCostCentres interp module_str (map mk_one entries)+ return (listArray (0,count-1) costcentres)+ | otherwise = return (listArray (0,-1) [])+ where+ mk_one t = (name, src)+ where name = concat $ intersperse "." $ tick_path t+ src = renderWithContext defaultSDocContext $ ppr $ tick_loc t
compiler/GHC/HsToCore/Coverage.hs view
@@ -1,1361 +1,132 @@-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE NondecreasingIndentation #-}-{-# LANGUAGE TypeFamilies #-}--{-# OPTIONS_GHC -Wno-incomplete-record-updates #-}--{--(c) Galois, 2006-(c) University of Glasgow, 2007--}--module GHC.HsToCore.Coverage- ( CoverageConfig (..)- , addTicksToBinds- , hpcInitCode- ) where--import GHC.Prelude as Prelude--import GHC.Driver.Session-import GHC.Driver.Backend--import qualified GHC.Runtime.Interpreter as GHCi-import GHCi.RemoteTypes-import GHC.ByteCode.Types-import GHC.Stack.CCS-import GHC.Hs-import GHC.Unit-import GHC.Cmm.CLabel--import GHC.Core.Type-import GHC.Core.TyCon--import GHC.Data.Maybe-import GHC.Data.FastString-import GHC.Data.Bag--import GHC.Platform--import GHC.Runtime.Interpreter.Types--import GHC.Utils.Misc-import GHC.Utils.Outputable as Outputable-import GHC.Utils.Panic-import GHC.Utils.Monad-import GHC.Utils.Logger--import GHC.Types.SrcLoc-import GHC.Types.Basic-import GHC.Types.Id-import GHC.Types.Var.Set-import GHC.Types.Name.Set hiding (FreeVars)-import GHC.Types.Name-import GHC.Types.HpcInfo-import GHC.Types.CostCentre-import GHC.Types.CostCentre.State-import GHC.Types.ForeignStubs-import GHC.Types.Tickish--import Control.Monad-import Data.List (isSuffixOf, intersperse)-import Data.Array-import Data.Time-import System.Directory--import Trace.Hpc.Mix-import Trace.Hpc.Util--import qualified Data.ByteString as BS-import Data.Set (Set)-import qualified Data.Set as Set--{--************************************************************************-* *-* The main function: addTicksToBinds-* *-************************************************************************--}--data CoverageConfig = CoverageConfig- { coverageConfig_logger :: Logger-- -- FIXME: replace this with the specific fields of DynFlags we care about.- , coverageConfig_dynFlags :: DynFlags-- , coverageConfig_mInterp :: Maybe Interp- }--addTicksToBinds- :: CoverageConfig- -> Module- -> ModLocation -- ... off the current module- -> NameSet -- Exported Ids. When we call addTicksToBinds,- -- isExportedId doesn't work yet (the desugarer- -- hasn't set it), so we have to work from this set.- -> [TyCon] -- Type constructor in this module- -> LHsBinds GhcTc- -> IO (LHsBinds GhcTc, HpcInfo, Maybe ModBreaks)--addTicksToBinds (CoverageConfig- { coverageConfig_logger = logger- , coverageConfig_dynFlags = dflags- , coverageConfig_mInterp = m_interp- })- mod mod_loc exports tyCons binds- | let passes = coveragePasses dflags- , not (null passes)- , Just orig_file <- ml_hs_file mod_loc = do-- let orig_file2 = guessSourceFile binds orig_file-- tickPass tickish (binds,st) =- let env = TTE- { fileName = mkFastString orig_file2- , declPath = []- , tte_countEntries = gopt Opt_ProfCountEntries dflags- , exports = exports- , inlines = emptyVarSet- , inScope = emptyVarSet- , blackList = Set.fromList $- mapMaybe (\tyCon -> case getSrcSpan (tyConName tyCon) of- RealSrcSpan l _ -> Just l- UnhelpfulSpan _ -> Nothing)- tyCons- , density = mkDensity tickish dflags- , this_mod = mod- , tickishType = tickish- }- (binds',_,st') = unTM (addTickLHsBinds binds) env st- in (binds', st')-- initState = TT { tickBoxCount = 0- , mixEntries = []- , ccIndices = newCostCentreState- }-- (binds1,st) = foldr tickPass (binds, initState) passes-- let tickCount = tickBoxCount st- entries = reverse $ mixEntries st- hashNo <- writeMixEntries dflags mod tickCount entries orig_file2- modBreaks <- mkModBreaks m_interp dflags mod tickCount entries-- putDumpFileMaybe logger Opt_D_dump_ticked "HPC" FormatHaskell- (pprLHsBinds binds1)-- return (binds1, HpcInfo tickCount hashNo, modBreaks)-- | otherwise = return (binds, emptyHpcInfo False, Nothing)--guessSourceFile :: LHsBinds GhcTc -> FilePath -> FilePath-guessSourceFile binds orig_file =- -- Try look for a file generated from a .hsc file to a- -- .hs file, by peeking ahead.- let top_pos = catMaybes $ foldr (\ (L pos _) rest ->- srcSpanFileName_maybe (locA pos) : rest) [] binds- in- case top_pos of- (file_name:_) | ".hsc" `isSuffixOf` unpackFS file_name- -> unpackFS file_name- _ -> orig_file---mkModBreaks :: Maybe Interp -> DynFlags -> Module -> Int -> [MixEntry_] -> IO (Maybe ModBreaks)-mkModBreaks m_interp dflags mod count entries- | Just interp <- m_interp- , breakpointsEnabled dflags = do- breakArray <- GHCi.newBreakArray interp (length entries)- ccs <- mkCCSArray interp mod count entries- let- locsTicks = listArray (0,count-1) [ span | (span,_,_,_) <- entries ]- varsTicks = listArray (0,count-1) [ vars | (_,_,vars,_) <- entries ]- declsTicks = listArray (0,count-1) [ decls | (_,decls,_,_) <- entries ]- return $ Just $ emptyModBreaks- { modBreaks_flags = breakArray- , modBreaks_locs = locsTicks- , modBreaks_vars = varsTicks- , modBreaks_decls = declsTicks- , modBreaks_ccs = ccs- }- | otherwise = return Nothing--mkCCSArray- :: Interp -> Module -> Int -> [MixEntry_]- -> IO (Array BreakIndex (RemotePtr GHC.Stack.CCS.CostCentre))-mkCCSArray interp modul count entries- | GHCi.interpreterProfiled interp = do- let module_str = moduleNameString (moduleName modul)- costcentres <- GHCi.mkCostCentres interp module_str (map mk_one entries)- return (listArray (0,count-1) costcentres)- | otherwise = return (listArray (0,-1) [])- where- mk_one (srcspan, decl_path, _, _) = (name, src)- where name = concat (intersperse "." decl_path)- src = renderWithContext defaultSDocContext (ppr srcspan)---writeMixEntries- :: DynFlags -> Module -> Int -> [MixEntry_] -> FilePath -> IO Int-writeMixEntries dflags mod count entries filename- | not (gopt Opt_Hpc dflags) = return 0- | otherwise = do- let- hpc_dir = hpcDir dflags- mod_name = moduleNameString (moduleName mod)-- hpc_mod_dir- | moduleUnit mod == mainUnit = hpc_dir- | otherwise = hpc_dir ++ "/" ++ unitString (moduleUnit mod)-- tabStop = 8 -- <tab> counts as a normal char in GHC's- -- location ranges.-- createDirectoryIfMissing True hpc_mod_dir- modTime <- getModificationUTCTime filename- let entries' = [ (hpcPos, box)- | (span,_,_,box) <- entries, hpcPos <- [mkHpcPos span] ]- when (entries' `lengthIsNot` count) $- panic "the number of .mix entries are inconsistent"- let hashNo = mixHash filename modTime tabStop entries'- mixCreate hpc_mod_dir mod_name- $ Mix filename modTime (toHash hashNo) tabStop entries'- return hashNo----- -------------------------------------------------------------------------------- TickDensity: where to insert ticks--data TickDensity- = TickForCoverage -- for Hpc- | TickForBreakPoints -- for GHCi- | TickAllFunctions -- for -prof-auto-all- | TickTopFunctions -- for -prof-auto-top- | TickExportedFunctions -- for -prof-auto-exported- | TickCallSites -- for stack tracing- deriving Eq--mkDensity :: TickishType -> DynFlags -> TickDensity-mkDensity tickish dflags = case tickish of- HpcTicks -> TickForCoverage- SourceNotes -> TickForCoverage- Breakpoints -> TickForBreakPoints- ProfNotes ->- case profAuto dflags of- ProfAutoAll -> TickAllFunctions- ProfAutoTop -> TickTopFunctions- ProfAutoExports -> TickExportedFunctions- ProfAutoCalls -> TickCallSites- _other -> panic "mkDensity"---- | Decide whether to add a tick to a binding or not.-shouldTickBind :: TickDensity- -> Bool -- top level?- -> Bool -- exported?- -> Bool -- simple pat bind?- -> Bool -- INLINE pragma?- -> Bool--shouldTickBind density top_lev exported _simple_pat inline- = case density of- TickForBreakPoints -> False- -- we never add breakpoints to simple pattern bindings- -- (there's always a tick on the rhs anyway).- TickAllFunctions -> not inline- TickTopFunctions -> top_lev && not inline- TickExportedFunctions -> exported && not inline- TickForCoverage -> True- TickCallSites -> False--shouldTickPatBind :: TickDensity -> Bool -> Bool-shouldTickPatBind density top_lev- = case density of- TickForBreakPoints -> False- TickAllFunctions -> True- TickTopFunctions -> top_lev- TickExportedFunctions -> False- TickForCoverage -> False- TickCallSites -> False---- -------------------------------------------------------------------------------- Adding ticks to bindings--addTickLHsBinds :: LHsBinds GhcTc -> TM (LHsBinds GhcTc)-addTickLHsBinds = mapBagM addTickLHsBind--addTickLHsBind :: LHsBind GhcTc -> TM (LHsBind GhcTc)-addTickLHsBind (L pos (XHsBindsLR bind@(AbsBinds { abs_binds = binds- , abs_exports = abs_exports- }))) =- withEnv add_exports $- withEnv add_inlines $ do- binds' <- addTickLHsBinds binds- return $ L pos $ XHsBindsLR $ bind { abs_binds = binds' }- where- -- in AbsBinds, the Id on each binding is not the actual top-level- -- Id that we are defining, they are related by the abs_exports- -- field of AbsBinds. So if we're doing TickExportedFunctions we need- -- to add the local Ids to the set of exported Names so that we know to- -- tick the right bindings.- add_exports env =- env{ exports = exports env `extendNameSetList`- [ idName mid- | ABE{ abe_poly = pid, abe_mono = mid } <- abs_exports- , idName pid `elemNameSet` (exports env) ] }-- -- See Note [inline sccs]- add_inlines env =- env{ inlines = inlines env `extendVarSetList`- [ mid- | ABE{ abe_poly = pid, abe_mono = mid } <- abs_exports- , isInlinePragma (idInlinePragma pid) ] }--addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id }))) = do- let name = getOccString id- decl_path <- getPathEntry- density <- getDensity-- inline_ids <- liftM inlines getEnv- -- See Note [inline sccs]- let inline = isInlinePragma (idInlinePragma id)- || id `elemVarSet` inline_ids-- -- See Note [inline sccs]- tickish <- tickishType `liftM` getEnv- if inline && tickish == ProfNotes then return (L pos funBind) else do-- (fvs, mg) <-- getFreeVars $- addPathEntry name $- addTickMatchGroup False (fun_matches funBind)-- blackListed <- isBlackListed (locA pos)- exported_names <- liftM exports getEnv-- -- We don't want to generate code for blacklisted positions- -- We don't want redundant ticks on simple pattern bindings- -- We don't want to tick non-exported bindings in TickExportedFunctions- let simple = isSimplePatBind funBind- toplev = null decl_path- exported = idName id `elemNameSet` exported_names-- tick <- if not blackListed &&- shouldTickBind density toplev exported simple inline- then- bindTick density name (locA pos) fvs- else- return Nothing-- let mbCons = maybe Prelude.id (:)- return $ L pos $ funBind { fun_matches = mg- , fun_tick = tick `mbCons` fun_tick funBind }-- where- -- a binding is a simple pattern binding if it is a funbind with- -- zero patterns- isSimplePatBind :: HsBind GhcTc -> Bool- isSimplePatBind funBind = matchGroupArity (fun_matches funBind) == 0---- TODO: Revisit this-addTickLHsBind (L pos (pat@(PatBind { pat_lhs = lhs- , pat_rhs = rhs }))) = do-- let simplePatId = isSimplePat lhs-- -- TODO: better name for rhs's for non-simple patterns?- let name = maybe "(...)" getOccString simplePatId-- (fvs, rhs') <- getFreeVars $ addPathEntry name $ addTickGRHSs False False rhs- let pat' = pat { pat_rhs = rhs'}-- -- Should create ticks here?- density <- getDensity- decl_path <- getPathEntry- let top_lev = null decl_path- if not (shouldTickPatBind density top_lev)- then return (L pos pat')- else do-- let mbCons = maybe id (:)-- let (initial_rhs_ticks, initial_patvar_tickss) = pat_ticks pat'-- -- Allocate the ticks-- rhs_tick <- bindTick density name (locA pos) fvs- let rhs_ticks = rhs_tick `mbCons` initial_rhs_ticks-- patvar_tickss <- case simplePatId of- Just{} -> return initial_patvar_tickss- Nothing -> do- let patvars = map getOccString (collectPatBinders CollNoDictBinders lhs)- patvar_ticks <- mapM (\v -> bindTick density v (locA pos) fvs) patvars- return- (zipWith mbCons patvar_ticks- (initial_patvar_tickss ++ repeat []))-- return $ L pos $ pat' { pat_ticks = (rhs_ticks, patvar_tickss) }---- Only internal stuff, not from source, uses VarBind, so we ignore it.-addTickLHsBind var_bind@(L _ (VarBind {})) = return var_bind-addTickLHsBind patsyn_bind@(L _ (PatSynBind {})) = return patsyn_bind--bindTick- :: TickDensity -> String -> SrcSpan -> FreeVars -> TM (Maybe CoreTickish)-bindTick density name pos fvs = do- decl_path <- getPathEntry- let- toplev = null decl_path- count_entries = toplev || density == TickAllFunctions- top_only = density /= TickAllFunctions- box_label = if toplev then TopLevelBox [name]- else LocalBox (decl_path ++ [name])- --- allocATickBox box_label count_entries top_only pos fvs----- Note [inline sccs]--- ~~~~~~~~~~~~~~~~~~--- The reason not to add ticks to INLINE functions is that this is--- sometimes handy for avoiding adding a tick to a particular function--- (see #6131)------ So for now we do not add any ticks to INLINE functions at all.------ We used to use isAnyInlinePragma to figure out whether to avoid adding--- ticks for this purpose. However, #12962 indicates that this contradicts--- the documentation on profiling (which only mentions INLINE pragmas).--- So now we're more careful about what we avoid adding ticks to.---- -------------------------------------------------------------------------------- Decorate an LHsExpr with ticks---- selectively add ticks to interesting expressions-addTickLHsExpr :: LHsExpr GhcTc -> TM (LHsExpr GhcTc)-addTickLHsExpr e@(L pos e0) = do- d <- getDensity- case d of- TickForBreakPoints | isGoodBreakExpr e0 -> tick_it- TickForCoverage -> tick_it- TickCallSites | isCallSite e0 -> tick_it- _other -> dont_tick_it- where- tick_it = allocTickBox (ExpBox False) False False (locA pos)- $ addTickHsExpr e0- dont_tick_it = addTickLHsExprNever e---- Add a tick to an expression which is the RHS of an equation or a binding.--- We always consider these to be breakpoints, unless the expression is a 'let'--- (because the body will definitely have a tick somewhere). ToDo: perhaps--- we should treat 'case' and 'if' the same way?-addTickLHsExprRHS :: LHsExpr GhcTc -> TM (LHsExpr GhcTc)-addTickLHsExprRHS e@(L pos e0) = do- d <- getDensity- case d of- TickForBreakPoints | HsLet{} <- e0 -> dont_tick_it- | otherwise -> tick_it- TickForCoverage -> tick_it- TickCallSites | isCallSite e0 -> tick_it- _other -> dont_tick_it- where- tick_it = allocTickBox (ExpBox False) False False (locA pos)- $ addTickHsExpr e0- dont_tick_it = addTickLHsExprNever e---- The inner expression of an evaluation context:--- let binds in [], ( [] )--- we never tick these if we're doing HPC, but otherwise--- we treat it like an ordinary expression.-addTickLHsExprEvalInner :: LHsExpr GhcTc -> TM (LHsExpr GhcTc)-addTickLHsExprEvalInner e = do- d <- getDensity- case d of- TickForCoverage -> addTickLHsExprNever e- _otherwise -> addTickLHsExpr e---- | A let body is treated differently from addTickLHsExprEvalInner--- above with TickForBreakPoints, because for breakpoints we always--- want to tick the body, even if it is not a redex. See test--- break012. This gives the user the opportunity to inspect the--- values of the let-bound variables.-addTickLHsExprLetBody :: LHsExpr GhcTc -> TM (LHsExpr GhcTc)-addTickLHsExprLetBody e@(L pos e0) = do- d <- getDensity- case d of- TickForBreakPoints | HsLet{} <- e0 -> dont_tick_it- | otherwise -> tick_it- _other -> addTickLHsExprEvalInner e- where- tick_it = allocTickBox (ExpBox False) False False (locA pos)- $ addTickHsExpr e0- dont_tick_it = addTickLHsExprNever e---- version of addTick that does not actually add a tick,--- because the scope of this tick is completely subsumed by--- another.-addTickLHsExprNever :: LHsExpr GhcTc -> TM (LHsExpr GhcTc)-addTickLHsExprNever (L pos e0) = do- e1 <- addTickHsExpr e0- return $ L pos e1---- General heuristic: expressions which are calls (do not denote--- values) are good break points.-isGoodBreakExpr :: HsExpr GhcTc -> Bool-isGoodBreakExpr e = isCallSite e--isCallSite :: HsExpr GhcTc -> Bool-isCallSite HsApp{} = True-isCallSite HsAppType{} = True-isCallSite (XExpr (ExpansionExpr (HsExpanded _ e)))- = isCallSite e--- NB: OpApp, SectionL, SectionR are all expanded out-isCallSite _ = False--addTickLHsExprOptAlt :: Bool -> LHsExpr GhcTc -> TM (LHsExpr GhcTc)-addTickLHsExprOptAlt oneOfMany (L pos e0)- = ifDensity TickForCoverage- (allocTickBox (ExpBox oneOfMany) False False (locA pos)- $ addTickHsExpr e0)- (addTickLHsExpr (L pos e0))--addBinTickLHsExpr :: (Bool -> BoxLabel) -> LHsExpr GhcTc -> TM (LHsExpr GhcTc)-addBinTickLHsExpr boxLabel (L pos e0)- = ifDensity TickForCoverage- (allocBinTickBox boxLabel (locA pos) $ addTickHsExpr e0)- (addTickLHsExpr (L pos e0))----- -------------------------------------------------------------------------------- Decorate the body of an HsExpr with ticks.--- (Whether to put a tick around the whole expression was already decided,--- in the addTickLHsExpr family of functions.)--addTickHsExpr :: HsExpr GhcTc -> TM (HsExpr GhcTc)-addTickHsExpr e@(HsVar _ (L _ id)) = do freeVar id; return e-addTickHsExpr e@(HsUnboundVar {}) = return e-addTickHsExpr e@(HsRecSel _ (FieldOcc id _)) = do freeVar id; return e--addTickHsExpr e@(HsIPVar {}) = return e-addTickHsExpr e@(HsOverLit {}) = return e-addTickHsExpr e@(HsOverLabel{}) = return e-addTickHsExpr e@(HsLit {}) = return e-addTickHsExpr (HsLam x mg) = liftM (HsLam x)- (addTickMatchGroup True mg)-addTickHsExpr (HsLamCase x lc_variant mgs) = liftM (HsLamCase x lc_variant)- (addTickMatchGroup True mgs)-addTickHsExpr (HsApp x e1 e2) = liftM2 (HsApp x) (addTickLHsExprNever e1)- (addTickLHsExpr e2)-addTickHsExpr (HsAppType x e ty) = liftM3 HsAppType (return x)- (addTickLHsExprNever e)- (return ty)-addTickHsExpr (OpApp fix e1 e2 e3) =- liftM4 OpApp- (return fix)- (addTickLHsExpr e1)- (addTickLHsExprNever e2)- (addTickLHsExpr e3)-addTickHsExpr (NegApp x e neg) =- liftM2 (NegApp x)- (addTickLHsExpr e)- (addTickSyntaxExpr hpcSrcSpan neg)-addTickHsExpr (HsPar x lpar e rpar) = do- e' <- addTickLHsExprEvalInner e- return (HsPar x lpar e' rpar)-addTickHsExpr (SectionL x e1 e2) =- liftM2 (SectionL x)- (addTickLHsExpr e1)- (addTickLHsExprNever e2)-addTickHsExpr (SectionR x e1 e2) =- liftM2 (SectionR x)- (addTickLHsExprNever e1)- (addTickLHsExpr e2)-addTickHsExpr (ExplicitTuple x es boxity) =- liftM2 (ExplicitTuple x)- (mapM addTickTupArg es)- (return boxity)-addTickHsExpr (ExplicitSum ty tag arity e) = do- e' <- addTickLHsExpr e- return (ExplicitSum ty tag arity e')-addTickHsExpr (HsCase x e mgs) =- liftM2 (HsCase x)- (addTickLHsExpr e) -- not an EvalInner; e might not necessarily- -- be evaluated.- (addTickMatchGroup False mgs)-addTickHsExpr (HsIf x e1 e2 e3) =- liftM3 (HsIf x)- (addBinTickLHsExpr (BinBox CondBinBox) e1)- (addTickLHsExprOptAlt True e2)- (addTickLHsExprOptAlt True e3)-addTickHsExpr (HsMultiIf ty alts)- = do { let isOneOfMany = case alts of [_] -> False; _ -> True- ; alts' <- mapM (liftL $ addTickGRHS isOneOfMany False) alts- ; return $ HsMultiIf ty alts' }-addTickHsExpr (HsLet x tkLet binds tkIn e) =- bindLocals (collectLocalBinders CollNoDictBinders binds) $ do- binds' <- addTickHsLocalBinds binds -- to think about: !patterns.- e' <- addTickLHsExprLetBody e- return (HsLet x tkLet binds' tkIn e')-addTickHsExpr (HsDo srcloc cxt (L l stmts))- = do { (stmts', _) <- addTickLStmts' forQual stmts (return ())- ; return (HsDo srcloc cxt (L l stmts')) }- where- forQual = case cxt of- ListComp -> Just $ BinBox QualBinBox- _ -> Nothing-addTickHsExpr (ExplicitList ty es)- = liftM2 ExplicitList (return ty) (mapM (addTickLHsExpr) es)--addTickHsExpr (HsStatic fvs e) = HsStatic fvs <$> addTickLHsExpr e--addTickHsExpr expr@(RecordCon { rcon_flds = rec_binds })- = do { rec_binds' <- addTickHsRecordBinds rec_binds- ; return (expr { rcon_flds = rec_binds' }) }--addTickHsExpr expr@(RecordUpd { rupd_expr = e, rupd_flds = Left flds })- = do { e' <- addTickLHsExpr e- ; flds' <- mapM addTickHsRecField flds- ; return (expr { rupd_expr = e', rupd_flds = Left flds' }) }-addTickHsExpr expr@(RecordUpd { rupd_expr = e, rupd_flds = Right flds })- = do { e' <- addTickLHsExpr e- ; flds' <- mapM addTickHsRecField flds- ; return (expr { rupd_expr = e', rupd_flds = Right flds' }) }--addTickHsExpr (ExprWithTySig x e ty) =- liftM3 ExprWithTySig- (return x)- (addTickLHsExprNever e) -- No need to tick the inner expression- -- for expressions with signatures- (return ty)-addTickHsExpr (ArithSeq ty wit arith_seq) =- liftM3 ArithSeq- (return ty)- (addTickWit wit)- (addTickArithSeqInfo arith_seq)- where addTickWit Nothing = return Nothing- addTickWit (Just fl) = do fl' <- addTickSyntaxExpr hpcSrcSpan fl- return (Just fl')--addTickHsExpr (HsPragE x p e) =- liftM (HsPragE x p) (addTickLHsExpr e)-addTickHsExpr e@(HsTypedBracket {}) = return e-addTickHsExpr e@(HsUntypedBracket{}) = return e-addTickHsExpr e@(HsSpliceE {}) = return e-addTickHsExpr e@(HsGetField {}) = return e-addTickHsExpr e@(HsProjection {}) = return e-addTickHsExpr (HsProc x pat cmdtop) =- liftM2 (HsProc x)- (addTickLPat pat)- (liftL (addTickHsCmdTop) cmdtop)-addTickHsExpr (XExpr (WrapExpr (HsWrap w e))) =- liftM (XExpr . WrapExpr . HsWrap w) $- (addTickHsExpr e) -- Explicitly no tick on inside-addTickHsExpr (XExpr (ExpansionExpr (HsExpanded a b))) =- liftM (XExpr . ExpansionExpr . HsExpanded a) $- (addTickHsExpr b)--addTickHsExpr e@(XExpr (ConLikeTc {})) = return e- -- We used to do a freeVar on a pat-syn builder, but actually- -- such builders are never in the inScope env, which- -- doesn't include top level bindings---- We might encounter existing ticks (multiple Coverage passes)-addTickHsExpr (XExpr (HsTick t e)) =- liftM (XExpr . HsTick t) (addTickLHsExprNever e)-addTickHsExpr (XExpr (HsBinTick t0 t1 e)) =- liftM (XExpr . HsBinTick t0 t1) (addTickLHsExprNever e)--addTickTupArg :: HsTupArg GhcTc -> TM (HsTupArg GhcTc)-addTickTupArg (Present x e) = do { e' <- addTickLHsExpr e- ; return (Present x e') }-addTickTupArg (Missing ty) = return (Missing ty)---addTickMatchGroup :: Bool{-is lambda-} -> MatchGroup GhcTc (LHsExpr GhcTc)- -> TM (MatchGroup GhcTc (LHsExpr GhcTc))-addTickMatchGroup is_lam mg@(MG { mg_alts = L l matches }) = do- let isOneOfMany = matchesOneOfMany matches- matches' <- mapM (liftL (addTickMatch isOneOfMany is_lam)) matches- return $ mg { mg_alts = L l matches' }--addTickMatch :: Bool -> Bool -> Match GhcTc (LHsExpr GhcTc)- -> TM (Match GhcTc (LHsExpr GhcTc))-addTickMatch isOneOfMany isLambda match@(Match { m_pats = pats- , m_grhss = gRHSs }) =- bindLocals (collectPatsBinders CollNoDictBinders pats) $ do- gRHSs' <- addTickGRHSs isOneOfMany isLambda gRHSs- return $ match { m_grhss = gRHSs' }--addTickGRHSs :: Bool -> Bool -> GRHSs GhcTc (LHsExpr GhcTc)- -> TM (GRHSs GhcTc (LHsExpr GhcTc))-addTickGRHSs isOneOfMany isLambda (GRHSs x guarded local_binds) =- bindLocals binders $ do- local_binds' <- addTickHsLocalBinds local_binds- guarded' <- mapM (liftL (addTickGRHS isOneOfMany isLambda)) guarded- return $ GRHSs x guarded' local_binds'- where- binders = collectLocalBinders CollNoDictBinders local_binds--addTickGRHS :: Bool -> Bool -> GRHS GhcTc (LHsExpr GhcTc)- -> TM (GRHS GhcTc (LHsExpr GhcTc))-addTickGRHS isOneOfMany isLambda (GRHS x stmts expr) = do- (stmts',expr') <- addTickLStmts' (Just $ BinBox $ GuardBinBox) stmts- (addTickGRHSBody isOneOfMany isLambda expr)- return $ GRHS x stmts' expr'--addTickGRHSBody :: Bool -> Bool -> LHsExpr GhcTc -> TM (LHsExpr GhcTc)-addTickGRHSBody isOneOfMany isLambda expr@(L pos e0) = do- d <- getDensity- case d of- TickForCoverage -> addTickLHsExprOptAlt isOneOfMany expr- TickAllFunctions | isLambda ->- addPathEntry "\\" $- allocTickBox (ExpBox False) True{-count-} False{-not top-} (locA pos) $- addTickHsExpr e0- _otherwise ->- addTickLHsExprRHS expr--addTickLStmts :: (Maybe (Bool -> BoxLabel)) -> [ExprLStmt GhcTc]- -> TM [ExprLStmt GhcTc]-addTickLStmts isGuard stmts = do- (stmts, _) <- addTickLStmts' isGuard stmts (return ())- return stmts--addTickLStmts' :: (Maybe (Bool -> BoxLabel)) -> [ExprLStmt GhcTc] -> TM a- -> TM ([ExprLStmt GhcTc], a)-addTickLStmts' isGuard lstmts res- = bindLocals (collectLStmtsBinders CollNoDictBinders lstmts) $- do { lstmts' <- mapM (liftL (addTickStmt isGuard)) lstmts- ; a <- res- ; return (lstmts', a) }--addTickStmt :: (Maybe (Bool -> BoxLabel)) -> Stmt GhcTc (LHsExpr GhcTc)- -> TM (Stmt GhcTc (LHsExpr GhcTc))-addTickStmt _isGuard (LastStmt x e noret ret) =- liftM3 (LastStmt x)- (addTickLHsExpr e)- (pure noret)- (addTickSyntaxExpr hpcSrcSpan ret)-addTickStmt _isGuard (BindStmt xbs pat e) =- liftM4 (\b f -> BindStmt $ XBindStmtTc- { xbstc_bindOp = b- , xbstc_boundResultType = xbstc_boundResultType xbs- , xbstc_boundResultMult = xbstc_boundResultMult xbs- , xbstc_failOp = f- })- (addTickSyntaxExpr hpcSrcSpan (xbstc_bindOp xbs))- (mapM (addTickSyntaxExpr hpcSrcSpan) (xbstc_failOp xbs))- (addTickLPat pat)- (addTickLHsExprRHS e)-addTickStmt isGuard (BodyStmt x e bind' guard') =- liftM3 (BodyStmt x)- (addTick isGuard e)- (addTickSyntaxExpr hpcSrcSpan bind')- (addTickSyntaxExpr hpcSrcSpan guard')-addTickStmt _isGuard (LetStmt x binds) =- liftM (LetStmt x)- (addTickHsLocalBinds binds)-addTickStmt isGuard (ParStmt x pairs mzipExpr bindExpr) =- liftM3 (ParStmt x)- (mapM (addTickStmtAndBinders isGuard) pairs)- (unLoc <$> addTickLHsExpr (L (noAnnSrcSpan hpcSrcSpan) mzipExpr))- (addTickSyntaxExpr hpcSrcSpan bindExpr)-addTickStmt isGuard (ApplicativeStmt body_ty args mb_join) = do- args' <- mapM (addTickApplicativeArg isGuard) args- return (ApplicativeStmt body_ty args' mb_join)--addTickStmt isGuard stmt@(TransStmt { trS_stmts = stmts- , trS_by = by, trS_using = using- , trS_ret = returnExpr, trS_bind = bindExpr- , trS_fmap = liftMExpr }) = do- t_s <- addTickLStmts isGuard stmts- t_y <- fmapMaybeM addTickLHsExprRHS by- t_u <- addTickLHsExprRHS using- t_f <- addTickSyntaxExpr hpcSrcSpan returnExpr- t_b <- addTickSyntaxExpr hpcSrcSpan bindExpr- t_m <- fmap unLoc (addTickLHsExpr (L (noAnnSrcSpan hpcSrcSpan) liftMExpr))- return $ stmt { trS_stmts = t_s, trS_by = t_y, trS_using = t_u- , trS_ret = t_f, trS_bind = t_b, trS_fmap = t_m }--addTickStmt isGuard stmt@(RecStmt {})- = do { stmts' <- addTickLStmts isGuard (unLoc $ recS_stmts stmt)- ; ret' <- addTickSyntaxExpr hpcSrcSpan (recS_ret_fn stmt)- ; mfix' <- addTickSyntaxExpr hpcSrcSpan (recS_mfix_fn stmt)- ; bind' <- addTickSyntaxExpr hpcSrcSpan (recS_bind_fn stmt)- ; return (stmt { recS_stmts = noLocA stmts', recS_ret_fn = ret'- , recS_mfix_fn = mfix', recS_bind_fn = bind' }) }--addTick :: Maybe (Bool -> BoxLabel) -> LHsExpr GhcTc -> TM (LHsExpr GhcTc)-addTick isGuard e | Just fn <- isGuard = addBinTickLHsExpr fn e- | otherwise = addTickLHsExprRHS e--addTickApplicativeArg- :: Maybe (Bool -> BoxLabel) -> (SyntaxExpr GhcTc, ApplicativeArg GhcTc)- -> TM (SyntaxExpr GhcTc, ApplicativeArg GhcTc)-addTickApplicativeArg isGuard (op, arg) =- liftM2 (,) (addTickSyntaxExpr hpcSrcSpan op) (addTickArg arg)- where- addTickArg (ApplicativeArgOne m_fail pat expr isBody) =- ApplicativeArgOne- <$> mapM (addTickSyntaxExpr hpcSrcSpan) m_fail- <*> addTickLPat pat- <*> addTickLHsExpr expr- <*> pure isBody- addTickArg (ApplicativeArgMany x stmts ret pat ctxt) =- (ApplicativeArgMany x)- <$> addTickLStmts isGuard stmts- <*> (unLoc <$> addTickLHsExpr (L (noAnnSrcSpan hpcSrcSpan) ret))- <*> addTickLPat pat- <*> pure ctxt--addTickStmtAndBinders :: Maybe (Bool -> BoxLabel) -> ParStmtBlock GhcTc GhcTc- -> TM (ParStmtBlock GhcTc GhcTc)-addTickStmtAndBinders isGuard (ParStmtBlock x stmts ids returnExpr) =- liftM3 (ParStmtBlock x)- (addTickLStmts isGuard stmts)- (return ids)- (addTickSyntaxExpr hpcSrcSpan returnExpr)--addTickHsLocalBinds :: HsLocalBinds GhcTc -> TM (HsLocalBinds GhcTc)-addTickHsLocalBinds (HsValBinds x binds) =- liftM (HsValBinds x)- (addTickHsValBinds binds)-addTickHsLocalBinds (HsIPBinds x binds) =- liftM (HsIPBinds x)- (addTickHsIPBinds binds)-addTickHsLocalBinds (EmptyLocalBinds x) = return (EmptyLocalBinds x)--addTickHsValBinds :: HsValBindsLR GhcTc (GhcPass a)- -> TM (HsValBindsLR GhcTc (GhcPass b))-addTickHsValBinds (XValBindsLR (NValBinds binds sigs)) = do- b <- liftM2 NValBinds- (mapM (\ (rec,binds') ->- liftM2 (,)- (return rec)- (addTickLHsBinds binds'))- binds)- (return sigs)- return $ XValBindsLR b-addTickHsValBinds _ = panic "addTickHsValBinds"--addTickHsIPBinds :: HsIPBinds GhcTc -> TM (HsIPBinds GhcTc)-addTickHsIPBinds (IPBinds dictbinds ipbinds) =- liftM2 IPBinds- (return dictbinds)- (mapM (liftL (addTickIPBind)) ipbinds)--addTickIPBind :: IPBind GhcTc -> TM (IPBind GhcTc)-addTickIPBind (IPBind x nm e) =- liftM (IPBind x nm)- (addTickLHsExpr e)---- There is no location here, so we might need to use a context location??-addTickSyntaxExpr :: SrcSpan -> SyntaxExpr GhcTc -> TM (SyntaxExpr GhcTc)-addTickSyntaxExpr pos syn@(SyntaxExprTc { syn_expr = x }) = do- x' <- fmap unLoc (addTickLHsExpr (L (noAnnSrcSpan pos) x))- return $ syn { syn_expr = x' }-addTickSyntaxExpr _ NoSyntaxExprTc = return NoSyntaxExprTc---- we do not walk into patterns.-addTickLPat :: LPat GhcTc -> TM (LPat GhcTc)-addTickLPat pat = return pat--addTickHsCmdTop :: HsCmdTop GhcTc -> TM (HsCmdTop GhcTc)-addTickHsCmdTop (HsCmdTop x cmd) =- liftM2 HsCmdTop- (return x)- (addTickLHsCmd cmd)--addTickLHsCmd :: LHsCmd GhcTc -> TM (LHsCmd GhcTc)-addTickLHsCmd (L pos c0) = do- c1 <- addTickHsCmd c0- return $ L pos c1--addTickHsCmd :: HsCmd GhcTc -> TM (HsCmd GhcTc)-addTickHsCmd (HsCmdLam x matchgroup) =- liftM (HsCmdLam x) (addTickCmdMatchGroup matchgroup)-addTickHsCmd (HsCmdApp x c e) =- liftM2 (HsCmdApp x) (addTickLHsCmd c) (addTickLHsExpr e)-{--addTickHsCmd (OpApp e1 c2 fix c3) =- liftM4 OpApp- (addTickLHsExpr e1)- (addTickLHsCmd c2)- (return fix)- (addTickLHsCmd c3)--}-addTickHsCmd (HsCmdPar x lpar e rpar) = do- e' <- addTickLHsCmd e- return (HsCmdPar x lpar e' rpar)-addTickHsCmd (HsCmdCase x e mgs) =- liftM2 (HsCmdCase x)- (addTickLHsExpr e)- (addTickCmdMatchGroup mgs)-addTickHsCmd (HsCmdLamCase x lc_variant mgs) =- liftM (HsCmdLamCase x lc_variant) (addTickCmdMatchGroup mgs)-addTickHsCmd (HsCmdIf x cnd e1 c2 c3) =- liftM3 (HsCmdIf x cnd)- (addBinTickLHsExpr (BinBox CondBinBox) e1)- (addTickLHsCmd c2)- (addTickLHsCmd c3)-addTickHsCmd (HsCmdLet x tkLet binds tkIn c) =- bindLocals (collectLocalBinders CollNoDictBinders binds) $ do- binds' <- addTickHsLocalBinds binds -- to think about: !patterns.- c' <- addTickLHsCmd c- return (HsCmdLet x tkLet binds' tkIn c')-addTickHsCmd (HsCmdDo srcloc (L l stmts))- = do { (stmts', _) <- addTickLCmdStmts' stmts (return ())- ; return (HsCmdDo srcloc (L l stmts')) }--addTickHsCmd (HsCmdArrApp arr_ty e1 e2 ty1 lr) =- liftM5 HsCmdArrApp- (return arr_ty)- (addTickLHsExpr e1)- (addTickLHsExpr e2)- (return ty1)- (return lr)-addTickHsCmd (HsCmdArrForm x e f fix cmdtop) =- liftM4 (HsCmdArrForm x)- (addTickLHsExpr e)- (return f)- (return fix)- (mapM (liftL (addTickHsCmdTop)) cmdtop)--addTickHsCmd (XCmd (HsWrap w cmd)) =- liftM XCmd $- liftM (HsWrap w) (addTickHsCmd cmd)---- Others should never happen in a command context.---addTickHsCmd e = pprPanic "addTickHsCmd" (ppr e)--addTickCmdMatchGroup :: MatchGroup GhcTc (LHsCmd GhcTc)- -> TM (MatchGroup GhcTc (LHsCmd GhcTc))-addTickCmdMatchGroup mg@(MG { mg_alts = (L l matches) }) = do- matches' <- mapM (liftL addTickCmdMatch) matches- return $ mg { mg_alts = L l matches' }--addTickCmdMatch :: Match GhcTc (LHsCmd GhcTc) -> TM (Match GhcTc (LHsCmd GhcTc))-addTickCmdMatch match@(Match { m_pats = pats, m_grhss = gRHSs }) =- bindLocals (collectPatsBinders CollNoDictBinders pats) $ do- gRHSs' <- addTickCmdGRHSs gRHSs- return $ match { m_grhss = gRHSs' }--addTickCmdGRHSs :: GRHSs GhcTc (LHsCmd GhcTc) -> TM (GRHSs GhcTc (LHsCmd GhcTc))-addTickCmdGRHSs (GRHSs x guarded local_binds) =- bindLocals binders $ do- local_binds' <- addTickHsLocalBinds local_binds- guarded' <- mapM (liftL addTickCmdGRHS) guarded- return $ GRHSs x guarded' local_binds'- where- binders = collectLocalBinders CollNoDictBinders local_binds--addTickCmdGRHS :: GRHS GhcTc (LHsCmd GhcTc) -> TM (GRHS GhcTc (LHsCmd GhcTc))--- The *guards* are *not* Cmds, although the body is--- C.f. addTickGRHS for the BinBox stuff-addTickCmdGRHS (GRHS x stmts cmd)- = do { (stmts',expr') <- addTickLStmts' (Just $ BinBox $ GuardBinBox)- stmts (addTickLHsCmd cmd)- ; return $ GRHS x stmts' expr' }--addTickLCmdStmts :: [LStmt GhcTc (LHsCmd GhcTc)]- -> TM [LStmt GhcTc (LHsCmd GhcTc)]-addTickLCmdStmts stmts = do- (stmts, _) <- addTickLCmdStmts' stmts (return ())- return stmts--addTickLCmdStmts' :: [LStmt GhcTc (LHsCmd GhcTc)] -> TM a- -> TM ([LStmt GhcTc (LHsCmd GhcTc)], a)-addTickLCmdStmts' lstmts res- = bindLocals binders $ do- lstmts' <- mapM (liftL addTickCmdStmt) lstmts- a <- res- return (lstmts', a)- where- binders = collectLStmtsBinders CollNoDictBinders lstmts--addTickCmdStmt :: Stmt GhcTc (LHsCmd GhcTc) -> TM (Stmt GhcTc (LHsCmd GhcTc))-addTickCmdStmt (BindStmt x pat c) =- liftM2 (BindStmt x)- (addTickLPat pat)- (addTickLHsCmd c)-addTickCmdStmt (LastStmt x c noret ret) =- liftM3 (LastStmt x)- (addTickLHsCmd c)- (pure noret)- (addTickSyntaxExpr hpcSrcSpan ret)-addTickCmdStmt (BodyStmt x c bind' guard') =- liftM3 (BodyStmt x)- (addTickLHsCmd c)- (addTickSyntaxExpr hpcSrcSpan bind')- (addTickSyntaxExpr hpcSrcSpan guard')-addTickCmdStmt (LetStmt x binds) =- liftM (LetStmt x)- (addTickHsLocalBinds binds)-addTickCmdStmt stmt@(RecStmt {})- = do { stmts' <- addTickLCmdStmts (unLoc $ recS_stmts stmt)- ; ret' <- addTickSyntaxExpr hpcSrcSpan (recS_ret_fn stmt)- ; mfix' <- addTickSyntaxExpr hpcSrcSpan (recS_mfix_fn stmt)- ; bind' <- addTickSyntaxExpr hpcSrcSpan (recS_bind_fn stmt)- ; return (stmt { recS_stmts = noLocA stmts', recS_ret_fn = ret'- , recS_mfix_fn = mfix', recS_bind_fn = bind' }) }-addTickCmdStmt ApplicativeStmt{} =- panic "ToDo: addTickCmdStmt ApplicativeLastStmt"---- Others should never happen in a command context.-addTickCmdStmt stmt = pprPanic "addTickHsCmd" (ppr stmt)--addTickHsRecordBinds :: HsRecordBinds GhcTc -> TM (HsRecordBinds GhcTc)-addTickHsRecordBinds (HsRecFields fields dd)- = do { fields' <- mapM addTickHsRecField fields- ; return (HsRecFields fields' dd) }--addTickHsRecField :: LHsFieldBind GhcTc id (LHsExpr GhcTc)- -> TM (LHsFieldBind GhcTc id (LHsExpr GhcTc))-addTickHsRecField (L l (HsFieldBind x id expr pun))- = do { expr' <- addTickLHsExpr expr- ; return (L l (HsFieldBind x id expr' pun)) }--addTickArithSeqInfo :: ArithSeqInfo GhcTc -> TM (ArithSeqInfo GhcTc)-addTickArithSeqInfo (From e1) =- liftM From- (addTickLHsExpr e1)-addTickArithSeqInfo (FromThen e1 e2) =- liftM2 FromThen- (addTickLHsExpr e1)- (addTickLHsExpr e2)-addTickArithSeqInfo (FromTo e1 e2) =- liftM2 FromTo- (addTickLHsExpr e1)- (addTickLHsExpr e2)-addTickArithSeqInfo (FromThenTo e1 e2 e3) =- liftM3 FromThenTo- (addTickLHsExpr e1)- (addTickLHsExpr e2)- (addTickLHsExpr e3)--data TickTransState = TT { tickBoxCount:: !Int- , mixEntries :: [MixEntry_]- , ccIndices :: !CostCentreState- }--addMixEntry :: MixEntry_ -> TM Int-addMixEntry ent = do- c <- tickBoxCount <$> getState- setState $ \st ->- st { tickBoxCount = c + 1- , mixEntries = ent : mixEntries st- }- return c--data TickTransEnv = TTE { fileName :: FastString- , density :: TickDensity- , tte_countEntries :: !Bool- -- ^ Whether the number of times functions are- -- entered should be counted.- , exports :: NameSet- , inlines :: VarSet- , declPath :: [String]- , inScope :: VarSet- , blackList :: Set RealSrcSpan- , this_mod :: Module- , tickishType :: TickishType- }---- deriving Show--data TickishType = ProfNotes | HpcTicks | Breakpoints | SourceNotes- deriving (Eq)--coveragePasses :: DynFlags -> [TickishType]-coveragePasses dflags =- ifa (breakpointsEnabled dflags) Breakpoints $- ifa (gopt Opt_Hpc dflags) HpcTicks $- ifa (sccProfilingEnabled dflags &&- profAuto dflags /= NoProfAuto) ProfNotes $- ifa (needSourceNotes dflags) SourceNotes []- where ifa f x xs | f = x:xs- | otherwise = xs---- | Should we produce 'Breakpoint' ticks?-breakpointsEnabled :: DynFlags -> Bool-breakpointsEnabled dflags = backend dflags == Interpreter---- | Tickishs that only make sense when their source code location--- refers to the current file. This might not always be true due to--- LINE pragmas in the code - which would confuse at least HPC.-tickSameFileOnly :: TickishType -> Bool-tickSameFileOnly HpcTicks = True-tickSameFileOnly _other = False--type FreeVars = OccEnv Id-noFVs :: FreeVars-noFVs = emptyOccEnv---- Note [freevars]--- ~~~~~~~~~~~~~~~--- For breakpoints we want to collect the free variables of an--- expression for pinning on the HsTick. We don't want to collect--- *all* free variables though: in particular there's no point pinning--- on free variables that are will otherwise be in scope at the GHCi--- prompt, which means all top-level bindings. Unfortunately detecting--- top-level bindings isn't easy (collectHsBindsBinders on the top-level--- bindings doesn't do it), so we keep track of a set of "in-scope"--- variables in addition to the free variables, and the former is used--- to filter additions to the latter. This gives us complete control--- over what free variables we track.--newtype TM a = TM { unTM :: TickTransEnv -> TickTransState -> (a,FreeVars,TickTransState) }- deriving (Functor)- -- a combination of a state monad (TickTransState) and a writer- -- monad (FreeVars).--instance Applicative TM where- pure a = TM $ \ _env st -> (a,noFVs,st)- (<*>) = ap--instance Monad TM where- (TM m) >>= k = TM $ \ env st ->- case m env st of- (r1,fv1,st1) ->- case unTM (k r1) env st1 of- (r2,fv2,st2) ->- (r2, fv1 `plusOccEnv` fv2, st2)---- | Get the next HPC cost centre index for a given centre name-getCCIndexM :: FastString -> TM CostCentreIndex-getCCIndexM n = TM $ \_ st -> let (idx, is') = getCCIndex n $- ccIndices st- in (idx, noFVs, st { ccIndices = is' })--getState :: TM TickTransState-getState = TM $ \ _ st -> (st, noFVs, st)--setState :: (TickTransState -> TickTransState) -> TM ()-setState f = TM $ \ _ st -> ((), noFVs, f st)--getEnv :: TM TickTransEnv-getEnv = TM $ \ env st -> (env, noFVs, st)--withEnv :: (TickTransEnv -> TickTransEnv) -> TM a -> TM a-withEnv f (TM m) = TM $ \ env st ->- case m (f env) st of- (a, fvs, st') -> (a, fvs, st')--getDensity :: TM TickDensity-getDensity = TM $ \env st -> (density env, noFVs, st)--ifDensity :: TickDensity -> TM a -> TM a -> TM a-ifDensity d th el = do d0 <- getDensity; if d == d0 then th else el--getFreeVars :: TM a -> TM (FreeVars, a)-getFreeVars (TM m)- = TM $ \ env st -> case m env st of (a, fv, st') -> ((fv,a), fv, st')--freeVar :: Id -> TM ()-freeVar id = TM $ \ env st ->- if id `elemVarSet` inScope env- then ((), unitOccEnv (nameOccName (idName id)) id, st)- else ((), noFVs, st)--addPathEntry :: String -> TM a -> TM a-addPathEntry nm = withEnv (\ env -> env { declPath = declPath env ++ [nm] })--getPathEntry :: TM [String]-getPathEntry = declPath `liftM` getEnv--getFileName :: TM FastString-getFileName = fileName `liftM` getEnv--isGoodSrcSpan' :: SrcSpan -> Bool-isGoodSrcSpan' pos@(RealSrcSpan _ _) = srcSpanStart pos /= srcSpanEnd pos-isGoodSrcSpan' (UnhelpfulSpan _) = False--isGoodTickSrcSpan :: SrcSpan -> TM Bool-isGoodTickSrcSpan pos = do- file_name <- getFileName- tickish <- tickishType `liftM` getEnv- let need_same_file = tickSameFileOnly tickish- same_file = Just file_name == srcSpanFileName_maybe pos- return (isGoodSrcSpan' pos && (not need_same_file || same_file))--ifGoodTickSrcSpan :: SrcSpan -> TM a -> TM a -> TM a-ifGoodTickSrcSpan pos then_code else_code = do- good <- isGoodTickSrcSpan pos- if good then then_code else else_code--bindLocals :: [Id] -> TM a -> TM a-bindLocals new_ids (TM m)- = TM $ \ env st ->- case m env{ inScope = inScope env `extendVarSetList` new_ids } st of- (r, fv, st') -> (r, fv `delListFromOccEnv` occs, st')- where occs = [ nameOccName (idName id) | id <- new_ids ]--isBlackListed :: SrcSpan -> TM Bool-isBlackListed (RealSrcSpan pos _) = TM $ \ env st -> (Set.member pos (blackList env), noFVs, st)-isBlackListed (UnhelpfulSpan _) = return False---- the tick application inherits the source position of its--- expression argument to support nested box allocations-allocTickBox :: BoxLabel -> Bool -> Bool -> SrcSpan -> TM (HsExpr GhcTc)- -> TM (LHsExpr GhcTc)-allocTickBox boxLabel countEntries topOnly pos m =- ifGoodTickSrcSpan pos (do- (fvs, e) <- getFreeVars m- env <- getEnv- tickish <- mkTickish boxLabel countEntries topOnly pos fvs (declPath env)- return (L (noAnnSrcSpan pos) (XExpr $ HsTick tickish $ L (noAnnSrcSpan pos) e))- ) (do- e <- m- return (L (noAnnSrcSpan pos) e)- )---- the tick application inherits the source position of its--- expression argument to support nested box allocations-allocATickBox :: BoxLabel -> Bool -> Bool -> SrcSpan -> FreeVars- -> TM (Maybe CoreTickish)-allocATickBox boxLabel countEntries topOnly pos fvs =- ifGoodTickSrcSpan pos (do- let- mydecl_path = case boxLabel of- TopLevelBox x -> x- LocalBox xs -> xs- _ -> panic "allocATickBox"- tickish <- mkTickish boxLabel countEntries topOnly pos fvs mydecl_path- return (Just tickish)- ) (return Nothing)---mkTickish :: BoxLabel -> Bool -> Bool -> SrcSpan -> OccEnv Id -> [String]- -> TM CoreTickish-mkTickish boxLabel countEntries topOnly pos fvs decl_path = do-- let ids = filter (not . mightBeUnliftedType . idType) $ nonDetOccEnvElts fvs- -- unlifted types cause two problems here:- -- * we can't bind them at the GHCi prompt- -- (bindLocalsAtBreakpoint already filters them out),- -- * the simplifier might try to substitute a literal for- -- the Id, and we can't handle that.-- me = (pos, decl_path, map (nameOccName.idName) ids, boxLabel)-- cc_name | topOnly = head decl_path- | otherwise = concat (intersperse "." decl_path)-- env <- getEnv- case tickishType env of- HpcTicks -> HpcTick (this_mod env) <$> addMixEntry me-- ProfNotes -> do- let nm = mkFastString cc_name- flavour <- HpcCC <$> getCCIndexM nm- let cc = mkUserCC nm (this_mod env) pos flavour- count = countEntries && tte_countEntries env- return $ ProfNote cc count True{-scopes-}-- Breakpoints -> Breakpoint noExtField <$> addMixEntry me <*> pure ids-- SourceNotes | RealSrcSpan pos' _ <- pos ->- return $ SourceNote pos' cc_name-- _otherwise -> panic "mkTickish: bad source span!"---allocBinTickBox :: (Bool -> BoxLabel) -> SrcSpan -> TM (HsExpr GhcTc)- -> TM (LHsExpr GhcTc)-allocBinTickBox boxLabel pos m = do- env <- getEnv- case tickishType env of- HpcTicks -> do e <- liftM (L (noAnnSrcSpan pos)) m- ifGoodTickSrcSpan pos- (mkBinTickBoxHpc boxLabel pos e)- (return e)- _other -> allocTickBox (ExpBox False) False False pos m--mkBinTickBoxHpc :: (Bool -> BoxLabel) -> SrcSpan -> LHsExpr GhcTc- -> TM (LHsExpr GhcTc)-mkBinTickBoxHpc boxLabel pos e = do- env <- getEnv- binTick <- HsBinTick- <$> addMixEntry (pos,declPath env, [],boxLabel True)- <*> addMixEntry (pos,declPath env, [],boxLabel False)- <*> pure e- tick <- HpcTick (this_mod env)- <$> addMixEntry (pos,declPath env, [],ExpBox False)- let pos' = noAnnSrcSpan pos- return $ L pos' $ XExpr $ HsTick tick (L pos' (XExpr binTick))--mkHpcPos :: SrcSpan -> HpcPos-mkHpcPos pos@(RealSrcSpan s _)- | isGoodSrcSpan' pos = toHpcPos (srcSpanStartLine s,- srcSpanStartCol s,- srcSpanEndLine s,- srcSpanEndCol s - 1)- -- the end column of a SrcSpan is one- -- greater than the last column of the- -- span (see SrcLoc), whereas HPC- -- expects to the column range to be- -- inclusive, hence we subtract one above.-mkHpcPos _ = panic "bad source span; expected such spans to be filtered out"--hpcSrcSpan :: SrcSpan-hpcSrcSpan = mkGeneralSrcSpan (fsLit "Haskell Program Coverage internals")--matchesOneOfMany :: [LMatch GhcTc body] -> Bool-matchesOneOfMany lmatches = sum (map matchCount lmatches) > 1- where- matchCount :: LMatch GhcTc body -> Int- matchCount (L _ (Match { m_grhss = GRHSs _ grhss _ }))- = length grhss--type MixEntry_ = (SrcSpan, [String], [OccName], BoxLabel)---- For the hash value, we hash everything: the file name,--- the timestamp of the original source file, the tab stop,--- and the mix entries. We cheat, and hash the show'd string.--- This hash only has to be hashed at Mix creation time,--- and is for sanity checking only.--mixHash :: FilePath -> UTCTime -> Int -> [MixEntry] -> Int-mixHash file tm tabstop entries = fromIntegral $ hashString- (show $ Mix file tm 0 tabstop entries)--{--************************************************************************-* *-* initialisation-* *-************************************************************************--Each module compiled with -fhpc declares an initialisation function of-the form `hpc_init_<module>()`, which is emitted into the _stub.c file-and annotated with __attribute__((constructor)) so that it gets-executed at startup time.--The function's purpose is to call hs_hpc_module to register this-module with the RTS, and it looks something like this:--static void hpc_init_Main(void) __attribute__((constructor));-static void hpc_init_Main(void)-{extern StgWord64 _hpc_tickboxes_Main_hpc[];- hs_hpc_module("Main",8,1150288664,_hpc_tickboxes_Main_hpc);}--}--hpcInitCode :: Platform -> Module -> HpcInfo -> CStub-hpcInitCode _ _ (NoHpcInfo {}) = mempty-hpcInitCode platform this_mod (HpcInfo tickCount hashNo)- = initializerCStub platform fn_name decls body- where- fn_name = mkInitializerStubLabel this_mod "hpc"- decls = text "extern StgWord64 " <> tickboxes <> text "[]" <> semi- body = text "hs_hpc_module" <>- parens (hcat (punctuate comma [- doubleQuotes full_name_str,- int tickCount, -- really StgWord32- int hashNo, -- really StgWord32- tickboxes- ])) <> semi-- tickboxes = pprCLabel platform CStyle (mkHpcTicksLabel $ this_mod)+{-+(c) Galois, 2006+(c) University of Glasgow, 2007+-}++module GHC.HsToCore.Coverage+ ( writeMixEntries+ , hpcInitCode+ ) where++import GHC.Prelude as Prelude++import GHC.Unit++import GHC.HsToCore.Ticks++import GHC.Platform++import GHC.Data.FastString+import GHC.Data.SizedSeq++import GHC.Cmm.CLabel++import GHC.Utils.Misc+import GHC.Utils.Panic+import GHC.Utils.Outputable+import GHC.Types.ForeignStubs+import GHC.Types.HpcInfo+import GHC.Types.SrcLoc++import Control.Monad+import Data.Time+import System.Directory++import Trace.Hpc.Mix+import Trace.Hpc.Util++import qualified Data.ByteString as BS++writeMixEntries+ :: FilePath -> Module -> SizedSeq Tick -> FilePath -> IO Int+writeMixEntries hpc_dir mod extendedMixEntries filename+ = do+ let count = fromIntegral $ sizeSS extendedMixEntries+ entries = ssElts extendedMixEntries++ mod_name = moduleNameString (moduleName mod)++ hpc_mod_dir+ | moduleUnit mod == mainUnit = hpc_dir+ | otherwise = hpc_dir ++ "/" ++ unitString (moduleUnit mod)++ tabStop = 8 -- <tab> counts as a normal char in GHC's+ -- location ranges.++ createDirectoryIfMissing True hpc_mod_dir+ modTime <- getModificationUTCTime filename+ let entries' = [ (hpcPos, tick_label t)+ | t <- entries, hpcPos <- [mkHpcPos $ tick_loc t] ]+ when (entries' `lengthIsNot` count) $+ panic "the number of .mix entries are inconsistent"+ let hashNo = mixHash filename modTime tabStop entries'+ mixCreate hpc_mod_dir mod_name+ $ Mix filename modTime (toHash hashNo) tabStop entries'+ return hashNo++mkHpcPos :: SrcSpan -> HpcPos+mkHpcPos pos@(RealSrcSpan s _)+ | isGoodSrcSpan' pos = toHpcPos (srcSpanStartLine s,+ srcSpanStartCol s,+ srcSpanEndLine s,+ srcSpanEndCol s - 1)+ -- the end column of a SrcSpan is one+ -- greater than the last column of the+ -- span (see SrcLoc), whereas HPC+ -- expects to the column range to be+ -- inclusive, hence we subtract one above.+mkHpcPos _ = panic "bad source span; expected such spans to be filtered out"++-- For the hash value, we hash everything: the file name,+-- the timestamp of the original source file, the tab stop,+-- and the mix entries. We cheat, and hash the show'd string.+-- This hash only has to be hashed at Mix creation time,+-- and is for sanity checking only.+mixHash :: FilePath -> UTCTime -> Int -> [MixEntry] -> Int+mixHash file tm tabstop entries = fromIntegral $ hashString+ (show $ Mix file tm 0 tabstop entries)++{-+************************************************************************+* *+* initialisation+* *+************************************************************************+-}++{- | Create HPC initialization C code for a module++Each module compiled with -fhpc declares an initialisation function of+the form `hpc_init_<module>()`, which is emitted into the _stub.c file+and annotated with __attribute__((constructor)) so that it gets+executed at startup time.++The function's purpose is to call hs_hpc_module to register this+module with the RTS, and it looks something like this:++> static void hpc_init_Main(void) __attribute__((constructor));+> static void hpc_init_Main(void)+> {+> extern StgWord64 _hpc_tickboxes_Main_hpc[];+> hs_hpc_module("Main",8,1150288664,_hpc_tickboxes_Main_hpc);+> }+-}+hpcInitCode :: Platform -> Module -> HpcInfo -> CStub+hpcInitCode _ _ (NoHpcInfo {}) = mempty+hpcInitCode platform this_mod (HpcInfo tickCount hashNo)+ = initializerCStub platform fn_name decls body+ where+ fn_name = mkInitializerStubLabel this_mod (fsLit "hpc")+ decls = text "extern StgWord64 " <> tickboxes <> text "[]" <> semi+ body = text "hs_hpc_module" <>+ parens (hcat (punctuate comma [+ doubleQuotes full_name_str,+ int tickCount, -- really StgWord32+ int hashNo, -- really StgWord32+ tickboxes+ ])) <> semi++ tickboxes = pprCLabel platform (mkHpcTicksLabel $ this_mod) module_name = hcat (map (text.charToC) $ BS.unpack $ bytesFS (moduleNameFS (moduleName this_mod)))
compiler/GHC/HsToCore/Docs.hs view
@@ -28,6 +28,7 @@ import Control.Applicative import Control.Monad.IO.Class import Data.Bifunctor (first)+import Data.Foldable (toList) import Data.IntMap (IntMap) import qualified Data.IntMap as IM import Data.Map.Strict (Map)@@ -273,8 +274,8 @@ names _ decl = getMainDeclBinder env decl {--Note [1]:----------+Note [1]+~~~~~~~~ We relate ClsInsts to InstDecls and DerivDecls using the SrcSpans buried inside them. That should work for normal user-written instances (from looking at GHC sources). We can assume that commented instances are@@ -366,13 +367,13 @@ -> [(Name, [HsDoc GhcRn], IntMap (HsDoc GhcRn))] dataSubs dd = constrs ++ fields ++ derivs where- cons = map unLoc $ (dd_cons dd)+ cons = unLoc <$> dd_cons dd constrs = [ ( unLoc cname , maybeToList $ fmap unLoc $ con_doc c , conArgDocs c)- | c <- cons, cname <- getConNames c ]+ | c <- toList cons, cname <- getConNames c ] fields = [ (foExt n, maybeToList $ fmap unLoc doc, IM.empty)- | Just flds <- map getRecConArgs_maybe cons+ | Just flds <- toList $ fmap getRecConArgs_maybe cons , (L _ (ConDeclField _ ns _ doc)) <- (unLoc flds) , (L _ n) <- ns ] derivs = [ (instName, [unLoc doc], IM.empty)@@ -534,7 +535,7 @@ -- | Go through all class declarations and filter their sub-declarations filterClasses :: forall p doc. (IsPass p) => [(LHsDecl (GhcPass p), doc)] -> [(LHsDecl (GhcPass p), doc)]-filterClasses = map (first (mapLoc filterClass))+filterClasses = map (first (fmap filterClass)) where filterClass (TyClD x c@(ClassDecl {})) = TyClD x $ c { tcdSigs =@@ -554,7 +555,7 @@ -> (decl -> hsDecl) -> struct -> [GenLocated l hsDecl]-mkDecls field con = map (mapLoc con) . field+mkDecls field con = map (fmap con) . field -- | Extracts out individual maps of documentation added via Template Haskell's -- @putDoc@.
compiler/GHC/HsToCore/Expr.hs view
@@ -1,8 +1,8 @@ {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ViewPatterns #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}-{-# OPTIONS_GHC -Wno-incomplete-record-updates #-} {- (c) The University of Glasgow 2006@@ -32,7 +32,6 @@ import GHC.HsToCore.Errors.Types import GHC.Types.SourceText import GHC.Types.Name-import GHC.Types.Name.Env import GHC.Core.FamInstEnv( topNormaliseType ) import GHC.HsToCore.Quote import GHC.Hs@@ -44,7 +43,6 @@ import GHC.Tc.Utils.Monad import GHC.Core.Type import GHC.Core.TyCo.Rep-import GHC.Core.Multiplicity import GHC.Core import GHC.Core.Utils import GHC.Core.Make@@ -53,14 +51,12 @@ import GHC.Types.CostCentre import GHC.Types.Id import GHC.Types.Id.Make-import GHC.Types.Var.Env import GHC.Unit.Module import GHC.Core.ConLike import GHC.Core.DataCon import GHC.Builtin.Types import GHC.Builtin.Names import GHC.Types.Basic-import GHC.Data.Maybe import GHC.Types.SrcLoc import GHC.Types.Tickish import GHC.Utils.Misc@@ -70,6 +66,7 @@ import GHC.Utils.Panic.Plain import GHC.Core.PatSyn import Control.Monad+import GHC.HsToCore.Ticks (stripTicksTopHsExpr) {- ************************************************************************@@ -190,8 +187,8 @@ dsUnliftedBind (FunBind { fun_id = L l fun , fun_matches = matches- , fun_ext = co_fn- , fun_tick = tick }) body+ , fun_ext = (co_fn, tick)+ }) body -- Can't be a bang pattern (that looks like a PatBind) -- so must be simply unboxed = do { (args, rhs) <- matchWrapper (mkPrefixFunRhs (L l $ idName fun)) Nothing matches@@ -201,7 +198,7 @@ ; return (bindNonRec fun rhs' body) } dsUnliftedBind (PatBind {pat_lhs = pat, pat_rhs = grhss- , pat_ext = ty }) body+ , pat_ext = (ty, _) }) body = -- let C x# y# = rhs in body -- ==> case rhs of C x# y# -> body do { match_nablas <- pmcGRHSs PatBindGuards grhss@@ -210,7 +207,7 @@ eqn = EqnInfo { eqn_pats = [upat], eqn_orig = FromSource, eqn_rhs = cantFailMatchResult body }- ; var <- selectMatchVar Many upat+ ; var <- selectMatchVar ManyTy upat -- `var` will end up in a let binder, so the multiplicity -- doesn't matter. ; result <- matchEquations PatBindRhs [var] [eqn] (exprType body)@@ -284,14 +281,18 @@ mkBinaryTickBox ixT ixF e2 } +-- Strip ticks due to #21701, need to be invariant about warnings we produce whether+-- this is enabled or not. dsExpr (NegApp _ (L loc- (HsOverLit _ lit@(OverLit { ol_val = HsIntegral i})))- neg_expr)+ (stripTicksTopHsExpr -> (ts, (HsOverLit _ lit@(OverLit { ol_val = HsIntegral i})))))+ neg_expr) = do { expr' <- putSrcSpanDsA loc $ do { warnAboutOverflowedOverLit+ -- See Note [Checking "negative literals"] (lit { ol_val = HsIntegral (negateIntegralLit i) }) ; dsOverLit lit }- ; dsSyntaxExpr neg_expr [expr'] }+ ;+ ; dsSyntaxExpr neg_expr [mkTicks ts expr'] } dsExpr (NegApp _ expr neg_expr) = do { expr' <- dsLExpr expr@@ -311,6 +312,27 @@ dsExpr e@(HsAppType {}) = dsHsWrapped e {-+Note [Checking "negative literals"]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++As observed in #13257 it's desirable to warn about overflowing negative literals+in some situations where the user thinks they are writing a negative literal (ie -1)+but without `-XNegativeLiterals` enabled.++This catches cases such as (-1 :: Word8) which overflow, because (negate 1 == 255) but+which we desugar to `negate (fromIntegral 1)`.++Notice it's crucial we still desugar to the correct (negate (fromIntegral ...)) despite+performing the negation in order to check whether the application of negate will overflow.+For a user written Integer instance we can't predict the interaction of negate and fromIntegral.++Also note that this works for detecting the right result for `-128 :: Int8`.. which is+in-range for Int8 but the correct result is achieved via two overflows.++negate (fromIntegral 128 :: Int8)+= negate (-128 :: Int8)+= -128 :: Int8+ Note [Desugaring vars] ~~~~~~~~~~~~~~~~~~~~~~ In one situation we can get a *coercion* variable in a HsVar, namely@@ -346,7 +368,7 @@ -- See Note [Don't flatten tuples from HsSyn] in GHC.Core.Make dsExpr (ExplicitSum types alt arity expr)- = mkCoreUbxSum arity alt types <$> dsLExpr expr+ = mkCoreUnboxedSum arity alt types <$> dsLExpr expr dsExpr (HsPragE _ prag expr) = ds_prag_expr prag expr@@ -445,12 +467,12 @@ \begin{verbatim} T { op2 = e } ==>- let err = /\a -> recConErr a- T (recConErr t1 "M.hs/230/op1")+ let err = /\a -> recConError a+ T (recConError t1 "M.hs/230/op1") e- (recConErr t1 "M.hs/230/op3")+ (recConError t1 "M.hs/230/op3") \end{verbatim}-@recConErr@ then converts its argument string into a proper message+@recConError@ then converts its argument string into a proper message before printing it as \begin{verbatim} M.hs, line 230: missing field op1 was evaluated@@ -484,270 +506,17 @@ ; return (mkCoreApps con_expr' con_args) } -{--Record update is a little harder. Suppose we have the decl:-\begin{verbatim}- data T = T1 {op1, op2, op3 :: Int}- | T2 {op4, op2 :: Int}- | T3-\end{verbatim}-Then we translate as follows:-\begin{verbatim}- r { op2 = e }-===>- let op2 = e in- case r of- T1 op1 _ op3 -> T1 op1 op2 op3- T2 op4 _ -> T2 op4 op2- other -> recUpdError "M.hs/230"-\end{verbatim}-It's important that we use the constructor Ids for @T1@, @T2@ etc on the-RHSs, and do not generate a Core constructor application directly, because the constructor-might do some argument-evaluation first; and may have to throw away some-dictionaries.--Note [Update for GADTs]-~~~~~~~~~~~~~~~~~~~~~~~-Consider- data T a b where- MkT :: { foo :: a } -> T a Int-- upd :: T s t -> s -> T s t- upd z y = z { foo = y}--We need to get this:- $WMkT :: a -> T a Int- MkT :: (b ~# Int) => a -> T a b-- upd = /\s t. \(z::T s t) (y::s) ->- case z of- MkT (co :: t ~# Int) _ -> $WMkT @s y |> T (Refl s) (Sym co)--Note the final cast- T (Refl s) (Sym co) :: T s Int ~ T s t-which uses co, bound by the GADT match. This is the wrap_co coercion-in wrapped_rhs. How do we produce it?--* Start with raw materials- tc, the tycon: T- univ_tvs, the universally quantified tyvars of MkT: a,b- NB: these are in 1-1 correspondence with the tyvars of tc--* Form univ_cos, a coercion for each of tc's args: (Refl s) (Sym co)- We replaced- a by (Refl s) since 's' instantiates 'a'- b by (Sym co) since 'b' is in the data-con's EqSpec--* Then form the coercion T (Refl s) (Sym co)--It gets more complicated when data families are involved (#18809).-Consider- data family F x- data instance F (a,b) where- MkF :: { foo :: Int } -> F (Int,b)-- bar :: F (s,t) -> Int -> F (s,t)- bar z y = z { foo = y}--We have- data R:FPair a b where- MkF :: { foo :: Int } -> R:FPair Int b-- $WMkF :: Int -> F (Int,b)- MkF :: forall a b. (a ~# Int) => Int -> R:FPair a b-- bar :: F (s,t) -> Int -> F (s,t)- bar = /\s t. \(z::F (s,t)) \(y::Int) ->- case z |> co1 of- MkF (co2::s ~# Int) _ -> $WMkF @t y |> co3--(Side note: here (z |> co1) is built by typechecking the scrutinee, so-we ignore it here. In general the scrutinee is an arbitrary expression.)--The question is: what is co3, the cast for the RHS?- co3 :: F (Int,t) ~ F (s,t)-Again, we can construct it using co2, bound by the GADT match.-We do /exactly/ the same as the non-family case up to building-univ_cos. But that gives us- rep_tc: R:FPair- univ_cos: (Sym co2) (Refl t)-But then we use mkTcFamilyTyConAppCo to "lift" this to the coercion-we want, namely- F (Sym co2, Refl t) :: F (Int,t) ~ F (s,t)---}--dsExpr RecordUpd { rupd_flds = Right _} =- -- Not possible due to elimination in the renamer. See Note- -- [Handling overloaded and rebindable constructs]- panic "The impossible happened"-dsExpr expr@(RecordUpd { rupd_expr = record_expr, rupd_flds = Left fields- , rupd_ext = RecordUpdTc- { rupd_cons = cons_to_upd- , rupd_in_tys = in_inst_tys- , rupd_out_tys = out_inst_tys- , rupd_wrap = dict_req_wrap }} )- | null fields- = dsLExpr record_expr- | otherwise- = assertPpr (notNull cons_to_upd) (ppr expr) $-- do { record_expr' <- dsLExpr record_expr- ; field_binds' <- mapM ds_field fields- ; let upd_fld_env :: NameEnv Id -- Maps field name to the LocalId of the field binding- upd_fld_env = mkNameEnv [(f,l) | (f,l,_) <- field_binds']-- -- It's important to generate the match with matchWrapper,- -- and the right hand sides with applications of the wrapper Id- -- so that everything works when we are doing fancy unboxing on the- -- constructor arguments.- ; alts <- mapM (mk_alt upd_fld_env) cons_to_upd- ; ([discrim_var], matching_code)- <- matchWrapper RecUpd (Just [record_expr]) -- See Note [Scrutinee in Record updates]- (MG { mg_alts = noLocA alts- , mg_ext = MatchGroupTc [unrestricted in_ty] out_ty- , mg_origin = FromSource- })- -- FromSource is not strictly right, but we- -- want incomplete pattern-match warnings-- ; return (add_field_binds field_binds' $- bindNonRec discrim_var record_expr' matching_code) }- where- ds_field :: LHsRecUpdField GhcTc -> DsM (Name, Id, CoreExpr)- -- Clone the Id in the HsRecField, because its Name is that- -- of the record selector, and we must not make that a local binder- -- else we shadow other uses of the record selector- -- Hence 'lcl_id'. Cf #2735- ds_field (L _ rec_field)- = do { rhs <- dsLExpr (hfbRHS rec_field)- ; let fld_id = unLoc (hsRecUpdFieldId rec_field)- ; lcl_id <- newSysLocalDs (idMult fld_id) (idType fld_id)- ; return (idName fld_id, lcl_id, rhs) }-- add_field_binds [] expr = expr- add_field_binds ((_,b,r):bs) expr = bindNonRec b r (add_field_binds bs expr)-- -- Awkwardly, for families, the match goes- -- from instance type to family type- (in_ty, out_ty) =- case (head cons_to_upd) of- RealDataCon data_con ->- let tycon = dataConTyCon data_con in- (mkTyConApp tycon in_inst_tys, mkFamilyTyConApp tycon out_inst_tys)- PatSynCon pat_syn ->- ( patSynInstResTy pat_syn in_inst_tys- , patSynInstResTy pat_syn out_inst_tys)- mk_alt upd_fld_env con- = do { let (univ_tvs, ex_tvs, eq_spec,- prov_theta, _req_theta, arg_tys, _) = conLikeFullSig con- arg_tys' = map (scaleScaled Many) arg_tys- -- Record updates consume the source record with multiplicity- -- Many. Therefore all the fields need to be scaled thus.- user_tvs = binderVars $ conLikeUserTyVarBinders con-- in_subst :: TCvSubst- in_subst = extendTCvInScopeList (zipTvSubst univ_tvs in_inst_tys) ex_tvs- -- The in_subst clones the universally quantified type- -- variables. It will be used to substitute into types that- -- contain existentials, however, so make sure to extend the- -- in-scope set with ex_tvs (#20278).-- out_tv_env :: TvSubstEnv- out_tv_env = zipTyEnv univ_tvs out_inst_tys-- -- I'm not bothering to clone the ex_tvs- ; eqs_vars <- mapM newPredVarDs (substTheta in_subst (eqSpecPreds eq_spec))- ; theta_vars <- mapM newPredVarDs (substTheta in_subst prov_theta)- ; arg_ids <- newSysLocalsDs (substScaledTysUnchecked in_subst arg_tys')- ; let field_labels = conLikeFieldLabels con- val_args = zipWithEqual "dsExpr:RecordUpd" mk_val_arg- field_labels arg_ids- mk_val_arg fl pat_arg_id- = nlHsVar (lookupNameEnv upd_fld_env (flSelector fl) `orElse` pat_arg_id)-- inst_con = noLocA $ mkHsWrap wrap (mkConLikeTc con)- -- Reconstruct with the WrapId so that unpacking happens- wrap = mkWpEvVarApps theta_vars <.>- dict_req_wrap <.>- mkWpTyApps [ lookupVarEnv out_tv_env tv- `orElse` mkTyVarTy tv- | tv <- user_tvs ]- -- Be sure to use user_tvs (which may be ordered- -- differently than `univ_tvs ++ ex_tvs) above.- -- See Note [DataCon user type variable binders]- -- in GHC.Core.DataCon.- rhs = foldl' (\a b -> nlHsApp a b) inst_con val_args-- -- Tediously wrap the application in a cast- -- Note [Update for GADTs]- wrapped_rhs =- case con of- RealDataCon data_con- | null eq_spec -> rhs- | otherwise -> mkLHsWrap (mkWpCastN wrap_co) rhs- -- This wrap is the punchline: Note [Update for GADTs]- where- rep_tc = dataConTyCon data_con- wrap_co = mkTcFamilyTyConAppCo rep_tc univ_cos- univ_cos = zipWithEqual "dsExpr:upd" mk_univ_co univ_tvs out_inst_tys-- mk_univ_co :: TyVar -- Universal tyvar from the DataCon- -> Type -- Corresponding instantiating type- -> Coercion- mk_univ_co univ_tv inst_ty- = case lookupVarEnv eq_spec_env univ_tv of- Just co -> co- Nothing -> mkTcNomReflCo inst_ty-- eq_spec_env :: VarEnv Coercion- eq_spec_env = mkVarEnv [ (eqSpecTyVar spec, mkTcSymCo (mkTcCoVarCo eqs_var))- | (spec,eqs_var) <- zipEqual "dsExpr:upd2" eq_spec eqs_vars ]-- -- eq_spec is always null for a PatSynCon- PatSynCon _ -> rhs--- req_wrap = dict_req_wrap <.> mkWpTyApps in_inst_tys-- pat = noLocA $ ConPat { pat_con = noLocA con- , pat_args = PrefixCon [] $ map nlVarPat arg_ids- , pat_con_ext = ConPatTc- { cpt_tvs = ex_tvs- , cpt_dicts = eqs_vars ++ theta_vars- , cpt_binds = emptyTcEvBinds- , cpt_arg_tys = in_inst_tys- , cpt_wrap = req_wrap- }- }- ; return (mkSimpleMatch RecUpd [pat] wrapped_rhs) }--{- Note [Scrutinee in Record updates]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider #17783:-- data PartialRec = No- | Yes { a :: Int, b :: Bool }- update No = No- update r@(Yes {}) = r { b = False }--In the context of pattern-match checking, the occurrence of @r@ in-@r { b = False }@ is to be treated as if it was a scrutinee, as can be seen by-the following desugaring:-- r { b = False } ==> case r of Yes a b -> Yes a False--Thus, we pass @r@ as the scrutinee expression to @matchWrapper@ above.--}+dsExpr (RecordUpd x _ _) = dataConCantHappen x -- Here is where we desugar the Template Haskell brackets and escapes -- Template Haskell stuff -- See Note [The life cycle of a TH quotation] -dsExpr (HsTypedBracket (HsBracketTc q _ hs_wrapper ps) _) = dsBracket hs_wrapper q ps-dsExpr (HsUntypedBracket (HsBracketTc q _ hs_wrapper ps) _) = dsBracket hs_wrapper q ps-dsExpr (HsSpliceE _ s) = pprPanic "dsExpr:splice" (ppr s)+dsExpr (HsTypedBracket bracket_tc _) = dsBracket bracket_tc+dsExpr (HsUntypedBracket bracket_tc _) = dsBracket bracket_tc+dsExpr (HsTypedSplice _ s) = pprPanic "dsExpr:typed splice" (pprTypedSplice Nothing s)+dsExpr (HsUntypedSplice ext _) = dataConCantHappen ext -- Arrow notation extension dsExpr (HsProc _ pat cmd) = dsProcExpr pat cmd@@ -756,20 +525,20 @@ -- HsSyn constructs that just shouldn't be here, because -- the renamer removed them. See GHC.Rename.Expr. -- Note [Handling overloaded and rebindable constructs]-dsExpr (HsOverLabel x _) = dataConCantHappen x-dsExpr (OpApp x _ _ _) = dataConCantHappen x-dsExpr (SectionL x _ _) = dataConCantHappen x-dsExpr (SectionR x _ _) = dataConCantHappen x+dsExpr (HsOverLabel x _ _) = dataConCantHappen x+dsExpr (OpApp x _ _ _) = dataConCantHappen x+dsExpr (SectionL x _ _) = dataConCantHappen x+dsExpr (SectionR x _ _) = dataConCantHappen x ds_prag_expr :: HsPragE GhcTc -> LHsExpr GhcTc -> DsM CoreExpr-ds_prag_expr (HsPragSCC _ _ cc) expr = do+ds_prag_expr (HsPragSCC _ cc) expr = do dflags <- getDynFlags if sccProfilingEnabled dflags && gopt Opt_ProfManualCcs dflags then do mod_name <- getModule count <- goptM Opt_ProfCountEntries let nm = sl_fs cc- flavour <- ExprCC <$> getCCIndexDsM nm+ flavour <- mkExprCCFlavour <$> getCCIndexDsM nm Tick (ProfNote (mkUserCC nm mod_name (getLocA expr) flavour) count True) <$> dsLExpr expr else dsLExpr expr@@ -822,7 +591,7 @@ At the moment we use a simple heuristic to determine whether build will be fruitful: for small lists we assume the benefits of fusion will be worthwhile;-for long lists we assume that the benefits will be outweighted by the cost of+for long lists we assume that the benefits will be outweighed by the cost of code duplication. This magic length threshold is @maxBuildLength@. Also, fusion won't work at all if rewrite rules are disabled, so we don't use the build-based desugaring in this case.@@ -946,7 +715,7 @@ ; let match_args (pat, fail_op) (vs,body) = putSrcSpanDs (getLocA pat) $- do { var <- selectSimpleMatchVarL Many pat+ do { var <- selectSimpleMatchVarL ManyTy pat ; match <- matchSinglePatVar var Nothing (StmtCtxt (HsDoStmt ctx)) pat body_ty (cantFailMatchResult body) ; match_code <- dsHandleMonadicFailure ctx pat match fail_op@@ -972,10 +741,10 @@ where new_bind_stmt = L loc $ BindStmt XBindStmtTc- { xbstc_bindOp = bind_op+ { xbstc_bindOp = bind_op , xbstc_boundResultType = bind_ty- , xbstc_boundResultMult = Many- , xbstc_failOp = Nothing -- Tuple cannot fail+ , xbstc_boundResultMult = ManyTy+ , xbstc_failOp = Nothing -- Tuple cannot fail } (mkBigLHsPatTupId later_pats) mfix_app@@ -990,8 +759,8 @@ (MG { mg_alts = noLocA [mkSimpleMatch LambdaExpr [mfix_pat] body]- , mg_ext = MatchGroupTc [unrestricted tup_ty] body_ty- , mg_origin = Generated })+ , mg_ext = MatchGroupTc [unrestricted tup_ty] body_ty Generated+ }) mfix_pat = noLocA $ LazyPat noExtField $ mkBigLHsPatTupId rec_tup_pats body = noLocA $ HsDo body_ty ctx (noLocA (rec_stmts ++ [ret_stmt]))@@ -1026,28 +795,26 @@ = do { builder_id <- dsLookupGlobalId builder_name ; return (if add_void then mkCoreApp (text "dsConLike" <+> ppr ps)- (Var builder_id) (Var voidPrimId)+ (Var builder_id) unboxedUnitExpr else Var builder_id) } | otherwise = pprPanic "dsConLike" (ppr ps) -dsConLike :: ConLike -> [TcTyVar] -> [Scaled Type] -> DsM CoreExpr--- This function desugars ConLikeTc+-- | This function desugars 'ConLikeTc': it eta-expands+-- data constructors to make linear types work.+-- -- See Note [Typechecking data constructors] in GHC.Tc.Gen.Head--- for what is going on here+dsConLike :: ConLike -> [TcTyVar] -> [Scaled Type] -> DsM CoreExpr dsConLike con tvs tys = do { ds_con <- dsHsConLike con ; ids <- newSysLocalsDs tys- -- newSysLocalDs: /can/ be lev-poly; see+ -- NB: these 'Id's may be representation-polymorphic;+ -- see Wrinkle [Representation-polymorphic lambda] in+ -- Note [Typechecking data constructors] in GHC.Tc.Gen.Head. ; return (mkLams tvs $ mkLams ids $ ds_con `mkTyApps` mkTyVarTys tvs- `mkVarApps` drop_stupid ids) }- where-- drop_stupid = dropList (conLikeStupidTheta con)- -- drop_stupid: see Note [Instantiating stupid theta]- -- in GHC.Tc.Gen.Head+ `mkVarApps` ids) } {- ************************************************************************@@ -1101,7 +868,7 @@ = go wrap hs_e go wrap1 (XExpr (WrapExpr (HsWrap wrap2 hs_e))) = go (wrap1 <.> wrap2) hs_e- go wrap (HsAppType ty (L _ hs_e) _)+ go wrap (HsAppType ty (L _ hs_e) _ _) = go (wrap <.> WpTyApp ty) hs_e go wrap (HsVar _ (L _ var))
+ compiler/GHC/HsToCore/Foreign/C.hs view
@@ -0,0 +1,632 @@+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}++-- | Handling of C foreign imports/exports+module GHC.HsToCore.Foreign.C+ ( dsCImport+ , dsCFExport+ , dsCFExportDynamic+ )+where++import GHC.Prelude++import GHC.Platform++import GHC.Tc.Utils.Monad -- temp+import GHC.Tc.Utils.Env+import GHC.Tc.Utils.TcType++import GHC.Core+import GHC.Core.Unfold.Make+import GHC.Core.Type+import GHC.Core.TyCon+import GHC.Core.Coercion+import GHC.Core.Multiplicity++import GHC.HsToCore.Foreign.Call+import GHC.HsToCore.Foreign.Prim+import GHC.HsToCore.Foreign.Utils+import GHC.HsToCore.Monad+import GHC.HsToCore.Types (ds_next_wrapper_num)++import GHC.Hs++import GHC.Types.Id+import GHC.Types.Literal+import GHC.Types.ForeignStubs+import GHC.Types.SourceText+import GHC.Types.Name+import GHC.Types.RepType+import GHC.Types.ForeignCall+import GHC.Types.Basic++import GHC.Unit.Module++import GHC.Driver.Session+import GHC.Driver.Config++import GHC.Cmm.Expr+import GHC.Cmm.Utils++import GHC.Builtin.Types+import GHC.Builtin.Types.Prim+import GHC.Builtin.Names++import GHC.Data.FastString++import GHC.Utils.Outputable+import GHC.Utils.Panic+import GHC.Utils.Panic.Plain+import GHC.Utils.Encoding++import Data.Maybe+import Data.List (nub)++dsCFExport:: Id -- Either the exported Id,+ -- or the foreign-export-dynamic constructor+ -> Coercion -- Coercion between the Haskell type callable+ -- from C, and its representation type+ -> CLabelString -- The name to export to C land+ -> CCallConv+ -> Bool -- True => foreign export dynamic+ -- so invoke IO action that's hanging off+ -- the first argument's stable pointer+ -> DsM ( CHeader -- contents of Module_stub.h+ , CStub -- contents of Module_stub.c+ , String -- string describing type to pass to createAdj.+ , Int -- size of args to stub function+ )++dsCFExport fn_id co ext_name cconv isDyn = do+ let+ ty = coercionRKind co+ (bndrs, orig_res_ty) = tcSplitPiTys ty+ fe_arg_tys' = mapMaybe anonPiTyBinderType_maybe bndrs+ -- We must use tcSplits here, because we want to see+ -- the (IO t) in the corner of the type!+ fe_arg_tys | isDyn = tail fe_arg_tys'+ | otherwise = fe_arg_tys'++ -- Look at the result type of the exported function, orig_res_ty+ -- If it's IO t, return (t, True)+ -- If it's plain t, return (t, False)+ (res_ty, is_IO_res_ty) = case tcSplitIOType_maybe orig_res_ty of+ -- The function already returns IO t+ Just (_ioTyCon, res_ty) -> (res_ty, True)+ -- The function returns t+ Nothing -> (orig_res_ty, False)++ dflags <- getDynFlags+ return $+ mkFExportCBits dflags ext_name+ (if isDyn then Nothing else Just fn_id)+ fe_arg_tys res_ty is_IO_res_ty cconv++dsCImport :: Id+ -> Coercion+ -> CImportSpec+ -> CCallConv+ -> Safety+ -> Maybe Header+ -> DsM ([Binding], CHeader, CStub)+dsCImport id co (CLabel cid) cconv _ _ = do+ dflags <- getDynFlags+ let ty = coercionLKind co+ platform = targetPlatform dflags+ fod = case tyConAppTyCon_maybe (dropForAlls ty) of+ Just tycon+ | tyConUnique tycon == funPtrTyConKey ->+ IsFunction+ _ -> IsData+ (resTy, foRhs) <- resultWrapper ty+ assert (fromJust resTy `eqType` addrPrimTy) $ -- typechecker ensures this+ let+ rhs = foRhs (Lit (LitLabel cid stdcall_info fod))+ rhs' = Cast rhs co+ stdcall_info = fun_type_arg_stdcall_info platform cconv ty+ in+ return ([(id, rhs')], mempty, mempty)++dsCImport id co (CFunction target) cconv@PrimCallConv safety _+ = dsPrimCall id co (CCall (CCallSpec target cconv safety))+dsCImport id co (CFunction target) cconv safety mHeader+ = dsFCall id co (CCall (CCallSpec target cconv safety)) mHeader+dsCImport id co CWrapper cconv _ _+ = dsCFExportDynamic id co cconv+++{-+@foreign import "wrapper"@ (previously "foreign export dynamic") lets+you dress up Haskell IO actions of some fixed type behind an+externally callable interface (i.e., as a C function pointer). Useful+for callbacks and stuff.++\begin{verbatim}+type Fun = Bool -> Int -> IO Int+foreign import "wrapper" f :: Fun -> IO (FunPtr Fun)++-- Haskell-visible constructor, which is generated from the above:+-- SUP: No check for NULL from createAdjustor anymore???++f :: Fun -> IO (FunPtr Fun)+f cback =+ bindIO (newStablePtr cback)+ (\StablePtr sp# -> IO (\s1# ->+ case _ccall_ createAdjustor cconv sp# ``f_helper'' <arg info> s1# of+ (# s2#, a# #) -> (# s2#, A# a# #)))++foreign import "&f_helper" f_helper :: FunPtr (StablePtr Fun -> Fun)++-- and the helper in C: (approximately; see `mkFExportCBits` below)++f_helper(StablePtr s, HsBool b, HsInt i)+{+ Capability *cap;+ cap = rts_lock();+ rts_inCall(&cap,+ rts_apply(rts_apply(deRefStablePtr(s),+ rts_mkBool(b)), rts_mkInt(i)));+ rts_unlock(cap);+}+\end{verbatim}+-}+dsCFExportDynamic :: Id+ -> Coercion+ -> CCallConv+ -> DsM ([Binding], CHeader, CStub)+dsCFExportDynamic id co0 cconv = do+ mod <- getModule+ dflags <- getDynFlags+ let platform = targetPlatform dflags+ let fe_nm = mkFastString $ zEncodeString+ (moduleStableString mod ++ "$" ++ toCName id)+ -- Construct the label based on the passed id, don't use names+ -- depending on Unique. See #13807 and Note [Unique Determinism].+ cback <- newSysLocalDs arg_mult arg_ty+ newStablePtrId <- dsLookupGlobalId newStablePtrName+ stable_ptr_tycon <- dsLookupTyCon stablePtrTyConName+ let+ stable_ptr_ty = mkTyConApp stable_ptr_tycon [arg_ty]+ export_ty = mkVisFunTyMany stable_ptr_ty arg_ty+ bindIOId <- dsLookupGlobalId bindIOName+ stbl_value <- newSysLocalDs ManyTy stable_ptr_ty+ (h_code, c_code, typestring, args_size) <- dsCFExport id (mkRepReflCo export_ty) fe_nm cconv True+ let+ {-+ The arguments to the external function which will+ create a little bit of (template) code on the fly+ for allowing the (stable pointed) Haskell closure+ to be entered using an external calling convention+ (stdcall, ccall).+ -}+ adj_args = [ mkIntLit platform (fromIntegral (ccallConvToInt cconv))+ , Var stbl_value+ , Lit (LitLabel fe_nm mb_sz_args IsFunction)+ , Lit (mkLitString typestring)+ ]+ -- name of external entry point providing these services.+ -- (probably in the RTS.)+ adjustor = fsLit "createAdjustor"++ -- Determine the number of bytes of arguments to the stub function,+ -- so that we can attach the '@N' suffix to its label if it is a+ -- stdcall on Windows.+ mb_sz_args = case cconv of+ StdCallConv -> Just args_size+ _ -> Nothing++ ccall_adj <- dsCCall adjustor adj_args PlayRisky (mkTyConApp io_tc [res_ty])+ -- PlayRisky: the adjustor doesn't allocate in the Haskell heap or do a callback++ let io_app = mkLams tvs $+ Lam cback $+ mkApps (Var bindIOId)+ [ Type stable_ptr_ty+ , Type res_ty+ , mkApps (Var newStablePtrId) [ Type arg_ty, Var cback ]+ , Lam stbl_value ccall_adj+ ]++ fed = (id `setInlineActivation` NeverActive, Cast io_app co0)+ -- Never inline the f.e.d. function, because the litlit+ -- might not be in scope in other modules.++ return ([fed], h_code, c_code)++ where+ ty = coercionLKind co0+ (tvs,sans_foralls) = tcSplitForAllInvisTyVars ty+ ([Scaled arg_mult arg_ty], fn_res_ty) = tcSplitFunTys sans_foralls+ Just (io_tc, res_ty) = tcSplitIOType_maybe fn_res_ty+ -- Must have an IO type; hence Just+++-- | Foreign calls+dsFCall :: Id -> Coercion -> ForeignCall -> Maybe Header+ -> DsM ([(Id, Expr TyVar)], CHeader, CStub)+dsFCall fn_id co fcall mDeclHeader = do+ let+ (ty,ty1) = (coercionLKind co, coercionRKind co)+ (tv_bndrs, rho) = tcSplitForAllTyVarBinders ty+ (arg_tys, io_res_ty) = tcSplitFunTys rho++ let constQual -- provide 'const' qualifier (#22043)+ | (_, res_ty1) <- tcSplitFunTys ty1+ , newty <- maybe res_ty1 snd (tcSplitIOType_maybe res_ty1)+ , Just (ptr, _) <- splitTyConApp_maybe newty+ , tyConName ptr == constPtrConName+ = text "const"+ | otherwise = empty++ args <- newSysLocalsDs arg_tys -- no FFI representation polymorphism+ (val_args, arg_wrappers) <- mapAndUnzipM unboxArg (map Var args)++ let+ work_arg_ids = [v | Var v <- val_args] -- All guaranteed to be vars++ (ccall_result_ty, res_wrapper) <- boxResult io_res_ty++ ccall_uniq <- newUnique+ work_uniq <- newUnique++ (fcall', cDoc) <-+ case fcall of+ CCall (CCallSpec (StaticTarget _ cName mUnitId isFun)+ CApiConv safety) ->+ do nextWrapperNum <- ds_next_wrapper_num <$> getGblEnv+ wrapperName <- mkWrapperName nextWrapperNum "ghc_wrapper" (unpackFS cName)+ let fcall' = CCall (CCallSpec+ (StaticTarget NoSourceText+ wrapperName mUnitId+ True)+ CApiConv safety)+ c = includes+ $$ fun_proto <+> braces (cRet <> semi)+ includes = vcat [ text "#include \"" <> ftext h+ <> text "\""+ | Header _ h <- nub headers ]+ fun_proto = constQual <+> cResType <+> pprCconv <+> ppr wrapperName <> parens argTypes+ cRet+ | isVoidRes = cCall+ | otherwise = text "return" <+> cCall+ cCall+ | isFun = ppr cName <> parens argVals+ | null arg_tys = ppr cName+ | otherwise = panic "dsFCall: Unexpected arguments to FFI value import"+ raw_res_ty = case tcSplitIOType_maybe io_res_ty of+ Just (_ioTyCon, res_ty) -> res_ty+ Nothing -> io_res_ty+ isVoidRes = raw_res_ty `eqType` unitTy+ (mHeader, cResType)+ | isVoidRes = (Nothing, text "void")+ | otherwise = toCType raw_res_ty+ pprCconv = ccallConvAttribute CApiConv+ mHeadersArgTypeList+ = [ (header, cType <+> char 'a' <> int n)+ | (t, n) <- zip arg_tys [1..]+ , let (header, cType) = toCType (scaledThing t) ]+ (mHeaders, argTypeList) = unzip mHeadersArgTypeList+ argTypes = if null argTypeList+ then text "void"+ else hsep $ punctuate comma argTypeList+ mHeaders' = mDeclHeader : mHeader : mHeaders+ headers = catMaybes mHeaders'+ argVals = hsep $ punctuate comma+ [ char 'a' <> int n+ | (_, n) <- zip arg_tys [1..] ]+ return (fcall', c)+ _ ->+ return (fcall, empty)+ dflags <- getDynFlags+ let+ -- Build the worker+ worker_ty = mkForAllTys tv_bndrs (mkVisFunTysMany (map idType work_arg_ids) ccall_result_ty)+ tvs = map binderVar tv_bndrs+ the_ccall_app = mkFCall ccall_uniq fcall' val_args ccall_result_ty+ work_rhs = mkLams tvs (mkLams work_arg_ids the_ccall_app)+ work_id = mkSysLocal (fsLit "$wccall") work_uniq ManyTy worker_ty++ -- Build the wrapper+ work_app = mkApps (mkVarApps (Var work_id) tvs) val_args+ wrapper_body = foldr ($) (res_wrapper work_app) arg_wrappers+ wrap_rhs = mkLams (tvs ++ args) wrapper_body+ wrap_rhs' = Cast wrap_rhs co+ simpl_opts = initSimpleOpts dflags+ fn_id_w_inl = fn_id `setIdUnfolding` mkInlineUnfoldingWithArity simpl_opts+ StableSystemSrc (length args)+ wrap_rhs'++ return ([(work_id, work_rhs), (fn_id_w_inl, wrap_rhs')], mempty, CStub cDoc [] [])+++toCName :: Id -> String+toCName i = showSDocOneLine defaultSDocContext (pprCode (ppr (idName i)))++toCType :: Type -> (Maybe Header, SDoc)+toCType = f False+ where f voidOK t+ -- First, if we have (Ptr t) of (FunPtr t), then we need to+ -- convert t to a C type and put a * after it. If we don't+ -- know a type for t, then "void" is fine, though.+ | Just (ptr, [t']) <- splitTyConApp_maybe t+ , tyConName ptr `elem` [ptrTyConName, funPtrTyConName]+ = case f True t' of+ (mh, cType') ->+ (mh, cType' <> char '*')+ -- Otherwise, if we have a type constructor application, then+ -- see if there is a C type associated with that constructor.+ -- Note that we aren't looking through type synonyms or+ -- anything, as it may be the synonym that is annotated.+ | Just tycon <- tyConAppTyConPicky_maybe t+ , Just (CType _ mHeader (_,cType)) <- tyConCType_maybe tycon+ = (mHeader, ftext cType)+ -- If we don't know a C type for this type, then try looking+ -- through one layer of type synonym etc.+ | Just t' <- coreView t+ = f voidOK t'+ -- Handle 'UnliftedFFITypes' argument+ | Just tyCon <- tyConAppTyConPicky_maybe t+ , isPrimTyCon tyCon+ , Just cType <- ppPrimTyConStgType tyCon+ = (Nothing, text cType)++ -- Otherwise we don't know the C type. If we are allowing+ -- void then return that; otherwise something has gone wrong.+ | voidOK = (Nothing, text "void")+ | otherwise+ = pprPanic "toCType" (ppr t)++{-+*++\subsection{Generating @foreign export@ stubs}++*++For each @foreign export@ function, a C stub function is generated.+The C stub constructs the application of the exported Haskell function+using the hugs/ghc rts invocation API.+-}++mkFExportCBits :: DynFlags+ -> FastString+ -> Maybe Id -- Just==static, Nothing==dynamic+ -> [Type]+ -> Type+ -> Bool -- True <=> returns an IO type+ -> CCallConv+ -> (CHeader,+ CStub,+ String, -- the argument reps+ Int -- total size of arguments+ )+mkFExportCBits dflags c_nm maybe_target arg_htys res_hty is_IO_res_ty cc+ = ( header_bits+ , CStub body [] []+ , type_string,+ sum [ widthInBytes (typeWidth rep) | (_,_,_,rep) <- aug_arg_info] -- all the args+ -- NB. the calculation here isn't strictly speaking correct.+ -- We have a primitive Haskell type (eg. Int#, Double#), and+ -- we want to know the size, when passed on the C stack, of+ -- the associated C type (eg. HsInt, HsDouble). We don't have+ -- this information to hand, but we know what GHC's conventions+ -- are for passing around the primitive Haskell types, so we+ -- use that instead. I hope the two coincide --SDM+ )+ where+ platform = targetPlatform dflags++ -- list the arguments to the C function+ arg_info :: [(SDoc, -- arg name+ SDoc, -- C type+ Type, -- Haskell type+ CmmType)] -- the CmmType+ arg_info = [ let stg_type = showStgType ty in+ (arg_cname n stg_type,+ stg_type,+ ty,+ typeCmmType platform (getPrimTyOf ty))+ | (ty,n) <- zip arg_htys [1::Int ..] ]++ arg_cname n stg_ty+ | libffi = char '*' <> parens (stg_ty <> char '*') <>+ text "args" <> brackets (int (n-1))+ | otherwise = char 'a' <> int n++ -- generate a libffi-style stub if this is a "wrapper" and libffi is enabled+ libffi = platformMisc_libFFI (platformMisc dflags) && isNothing maybe_target++ type_string+ -- libffi needs to know the result type too:+ | libffi = primTyDescChar platform res_hty : arg_type_string+ | otherwise = arg_type_string++ arg_type_string = [primTyDescChar platform ty | (_,_,ty,_) <- arg_info]+ -- just the real args++ -- add some auxiliary args; the stable ptr in the wrapper case, and+ -- a slot for the dummy return address in the wrapper + ccall case+ aug_arg_info+ | isNothing maybe_target = stable_ptr_arg : insertRetAddr platform cc arg_info+ | otherwise = arg_info++ stable_ptr_arg =+ (text "the_stableptr", text "StgStablePtr", undefined,+ typeCmmType platform (mkStablePtrPrimTy alphaTy))++ -- stuff to do with the return type of the C function+ res_hty_is_unit = res_hty `eqType` unitTy -- Look through any newtypes++ cResType | res_hty_is_unit = text "void"+ | otherwise = showStgType res_hty++ -- when the return type is integral and word-sized or smaller, it+ -- must be assigned as type ffi_arg (#3516). To see what type+ -- libffi is expecting here, take a look in its own testsuite, e.g.+ -- libffi/testsuite/libffi.call/cls_align_ulonglong.c+ ffi_cResType+ | is_ffi_arg_type = text "ffi_arg"+ | otherwise = cResType+ where+ res_ty_key = getUnique (getName (typeTyCon res_hty))+ is_ffi_arg_type = res_ty_key `notElem`+ [floatTyConKey, doubleTyConKey,+ int64TyConKey, word64TyConKey]++ -- Now we can cook up the prototype for the exported function.+ pprCconv = ccallConvAttribute cc++ header_bits = CHeader (text "extern" <+> fun_proto <> semi)++ fun_args+ | null aug_arg_info = text "void"+ | otherwise = hsep $ punctuate comma+ $ map (\(nm,ty,_,_) -> ty <+> nm) aug_arg_info++ fun_proto+ | libffi+ = text "void" <+> ftext c_nm <>+ parens (text "void *cif STG_UNUSED, void* resp, void** args, void* the_stableptr")+ | otherwise+ = cResType <+> pprCconv <+> ftext c_nm <> parens fun_args++ -- the target which will form the root of what we ask rts_inCall to run+ the_cfun+ = case maybe_target of+ Nothing -> text "(StgClosure*)deRefStablePtr(the_stableptr)"+ Just hs_fn -> char '&' <> ppr hs_fn <> text "_closure"++ cap = text "cap" <> comma++ -- the expression we give to rts_inCall+ expr_to_run+ = foldl' appArg the_cfun arg_info -- NOT aug_arg_info+ where+ appArg acc (arg_cname, _, arg_hty, _)+ = text "rts_apply"+ <> parens (cap <> acc <> comma <> mkHObj arg_hty <> parens (cap <> arg_cname))++ -- various other bits for inside the fn+ declareResult = text "HaskellObj ret;"+ declareCResult | res_hty_is_unit = empty+ | otherwise = cResType <+> text "cret;"++ assignCResult | res_hty_is_unit = empty+ | otherwise =+ text "cret=" <> unpackHObj res_hty <> parens (text "ret") <> semi++ -- an extern decl for the fn being called+ extern_decl+ = case maybe_target of+ Nothing -> empty+ Just hs_fn -> text "extern StgClosure " <> ppr hs_fn <> text "_closure" <> semi+++ -- finally, the whole darn thing+ body =+ space $$+ extern_decl $$+ fun_proto $$+ vcat+ [ lbrace+ , text "Capability *cap;"+ , declareResult+ , declareCResult+ , text "cap = rts_lock();"+ -- create the application + perform it.+ , text "rts_inCall" <> parens (+ char '&' <> cap <>+ text "rts_apply" <> parens (+ cap <>+ text "(HaskellObj)"+ <> (if is_IO_res_ty+ then text "runIO_closure"+ else text "runNonIO_closure")+ <> comma+ <> expr_to_run+ ) <+> comma+ <> text "&ret"+ ) <> semi+ , text "rts_checkSchedStatus" <> parens (doubleQuotes (ftext c_nm)+ <> comma <> text "cap") <> semi+ , assignCResult+ , text "rts_unlock(cap);"+ , ppUnless res_hty_is_unit $+ if libffi+ then char '*' <> parens (ffi_cResType <> char '*') <>+ text "resp = cret;"+ else text "return cret;"+ , rbrace+ ]++mkHObj :: Type -> SDoc+mkHObj t = text "rts_mk" <> showFFIType t++unpackHObj :: Type -> SDoc+unpackHObj t = text "rts_get" <> showFFIType t++showStgType :: Type -> SDoc+showStgType t = text "Hs" <> showFFIType t++showFFIType :: Type -> SDoc+showFFIType t = ftext (occNameFS (getOccName (typeTyCon t)))++typeTyCon :: Type -> TyCon+typeTyCon ty+ | Just (tc, _) <- tcSplitTyConApp_maybe (unwrapType ty)+ = tc+ | otherwise+ = pprPanic "GHC.HsToCore.Foreign.C.typeTyCon" (ppr ty)+++insertRetAddr :: Platform -> CCallConv+ -> [(SDoc, SDoc, Type, CmmType)]+ -> [(SDoc, SDoc, Type, CmmType)]+insertRetAddr platform CCallConv args+ = case platformArch platform of+ ArchX86_64+ | platformOS platform == OSMinGW32 ->+ -- On other Windows x86_64 we insert the return address+ -- after the 4th argument, because this is the point+ -- at which we need to flush a register argument to the stack+ -- (See rts/Adjustor.c for details).+ let go :: Int -> [(SDoc, SDoc, Type, CmmType)]+ -> [(SDoc, SDoc, Type, CmmType)]+ go 4 args = ret_addr_arg platform : args+ go n (arg:args) = arg : go (n+1) args+ go _ [] = []+ in go 0 args+ | otherwise ->+ -- On other x86_64 platforms we insert the return address+ -- after the 6th integer argument, because this is the point+ -- at which we need to flush a register argument to the stack+ -- (See rts/Adjustor.c for details).+ let go :: Int -> [(SDoc, SDoc, Type, CmmType)]+ -> [(SDoc, SDoc, Type, CmmType)]+ go 6 args = ret_addr_arg platform : args+ go n (arg@(_,_,_,rep):args)+ | cmmEqType_ignoring_ptrhood rep b64 = arg : go (n+1) args+ | otherwise = arg : go n args+ go _ [] = []+ in go 0 args+ _ ->+ ret_addr_arg platform : args+insertRetAddr _ _ args = args++ret_addr_arg :: Platform -> (SDoc, SDoc, Type, CmmType)+ret_addr_arg platform = (text "original_return_addr", text "void*", undefined,+ typeCmmType platform addrPrimTy)++-- For stdcall labels, if the type was a FunPtr or newtype thereof,+-- then we need to calculate the size of the arguments in order to add+-- the @n suffix to the label.+fun_type_arg_stdcall_info :: Platform -> CCallConv -> Type -> Maybe Int+fun_type_arg_stdcall_info platform StdCallConv ty+ | Just (tc,[arg_ty]) <- splitTyConApp_maybe ty,+ tyConUnique tc == funPtrTyConKey+ = let+ (bndrs, _) = tcSplitPiTys arg_ty+ fe_arg_tys = mapMaybe anonPiTyBinderType_maybe bndrs+ in Just $ sum (map (widthInBytes . typeWidth . typeCmmType platform . getPrimTyOf) fe_arg_tys)+fun_type_arg_stdcall_info _ _other_conv _+ = Nothing
compiler/GHC/HsToCore/Foreign/Call.hs view
@@ -150,7 +150,7 @@ tc `hasKey` boolTyConKey = do dflags <- getDynFlags let platform = targetPlatform dflags- prim_arg <- newSysLocalDs Many intPrimTy+ prim_arg <- newSysLocalDs ManyTy intPrimTy return (Var prim_arg, \ body -> Case (mkIfThenElse arg (mkIntLit platform 1) (mkIntLit platform 0)) prim_arg@@ -162,8 +162,8 @@ | is_product_type && data_con_arity == 1 = assertPpr (isUnliftedType data_con_arg_ty1) (pprType arg_ty) $ -- Typechecker ensures this- do case_bndr <- newSysLocalDs Many arg_ty- prim_arg <- newSysLocalDs Many data_con_arg_ty1+ do case_bndr <- newSysLocalDs ManyTy arg_ty+ prim_arg <- newSysLocalDs ManyTy data_con_arg_ty1 return (Var prim_arg, \ body -> Case arg case_bndr (exprType body) [Alt (DataAlt data_con) [prim_arg] body] )@@ -177,7 +177,7 @@ isJust maybe_arg3_tycon && (arg3_tycon == byteArrayPrimTyCon || arg3_tycon == mutableByteArrayPrimTyCon)- = do case_bndr <- newSysLocalDs Many arg_ty+ = do case_bndr <- newSysLocalDs ManyTy arg_ty vars@[_l_var, _r_var, arr_cts_var] <- newSysLocalsDs (map unrestricted data_con_arg_tys) return (Var arr_cts_var, \ body -> Case arg case_bndr (exprType body) [Alt (DataAlt data_con) vars body]@@ -223,14 +223,11 @@ -- another case, and a coercion.) -- The result is IO t, so wrap the result in an IO constructor = do { res <- resultWrapper io_res_ty- ; let return_result state anss- = mkCoreUbxTup- [realWorldStatePrimTy, io_res_ty]- [state, anss]+ ; let return_result state anss = mkCoreUnboxedTuple [state, anss] ; (ccall_res_ty, the_alt) <- mk_alt return_result res - ; state_id <- newSysLocalDs Many realWorldStatePrimTy+ ; state_id <- newSysLocalDs ManyTy realWorldStatePrimTy ; let io_data_con = head (tyConDataCons io_tycon) toIOCon = dataConWrapId io_data_con @@ -266,7 +263,7 @@ -> DsM (Type, CoreAlt) mk_alt return_result (Nothing, wrap_result) = do -- The ccall returns ()- state_id <- newSysLocalDs Many realWorldStatePrimTy+ state_id <- newSysLocalDs ManyTy realWorldStatePrimTy let the_rhs = return_result (Var state_id) (wrap_result (panic "boxResult"))@@ -280,8 +277,8 @@ = -- The ccall returns a non-() value assertPpr (isPrimitiveType prim_res_ty) (ppr prim_res_ty) $ -- True because resultWrapper ensures it is so- do { result_id <- newSysLocalDs Many prim_res_ty- ; state_id <- newSysLocalDs Many realWorldStatePrimTy+ do { result_id <- newSysLocalDs ManyTy prim_res_ty+ ; state_id <- newSysLocalDs ManyTy realWorldStatePrimTy ; let the_rhs = return_result (Var state_id) (wrap_result (Var result_id)) ccall_res_ty = mkTupleTy Unboxed [realWorldStatePrimTy, prim_res_ty]
compiler/GHC/HsToCore/Foreign/Decl.hs view
@@ -2,68 +2,43 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-} -{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}- {- (c) The University of Glasgow 2006 (c) The AQUA Project, Glasgow University, 1998---Desugaring foreign declarations (see also GHC.HsToCore.Foreign.Call). -} -module GHC.HsToCore.Foreign.Decl ( dsForeigns ) where+-- | Desugaring foreign declarations+module GHC.HsToCore.Foreign.Decl+ ( dsForeigns+ )+where import GHC.Prelude+import GHC.Data.FastString import GHC.Tc.Utils.Monad -- temp -import GHC.Core--import GHC.HsToCore.Foreign.Call+import GHC.HsToCore.Foreign.C+import GHC.HsToCore.Foreign.JavaScript+import GHC.HsToCore.Foreign.Utils import GHC.HsToCore.Monad-import GHC.HsToCore.Types (ds_next_wrapper_num) import GHC.Hs-import GHC.Core.DataCon-import GHC.Core.Unfold.Make import GHC.Types.Id-import GHC.Types.Literal import GHC.Types.ForeignStubs-import GHC.Types.SourceText import GHC.Unit.Module-import GHC.Types.Name-import GHC.Core.Type-import GHC.Types.RepType-import GHC.Core.TyCon import GHC.Core.Coercion-import GHC.Core.Multiplicity-import GHC.Tc.Utils.Env-import GHC.Tc.Utils.TcType -import GHC.Cmm.Expr-import GHC.Cmm.Utils import GHC.Cmm.CLabel-import GHC.Driver.Ppr import GHC.Types.ForeignCall-import GHC.Builtin.Types-import GHC.Builtin.Types.Prim-import GHC.Builtin.Names-import GHC.Types.Basic import GHC.Types.SrcLoc import GHC.Utils.Outputable-import GHC.Data.FastString import GHC.Driver.Session-import GHC.Driver.Config import GHC.Platform import GHC.Data.OrdList-import GHC.Utils.Panic-import GHC.Utils.Panic.Plain import GHC.Driver.Hooks-import GHC.Utils.Encoding -import Data.Maybe-import Data.List (unzip4, nub)+import Data.List (unzip4) {- Desugaring of @foreign@ declarations is naturally split up into@@ -80,9 +55,6 @@ so we reuse the desugaring code in @GHC.HsToCore.Foreign.Call@ to deal with these. -} -type Binding = (Id, CoreExpr) -- No rec/nonrec structure;- -- the occurrence analyser will sort it all out- dsForeigns :: [LForeignDecl GhcTc] -> DsM (ForeignStubs, OrdList Binding) dsForeigns fos = do hooks <- getHooks@@ -120,8 +92,8 @@ do_decl (ForeignExport { fd_name = L _ id , fd_e_ext = co- , fd_fe = CExport- (L _ (CExportStatic _ ext_nm cconv)) _ }) = do+ , fd_fe = CExport _+ (L _ (CExportStatic _ ext_nm cconv)) }) = do (h, c, _, _) <- dsFExport id co ext_nm cconv False return (h, c, [id], []) @@ -153,186 +125,13 @@ dsFImport :: Id -> Coercion- -> ForeignImport- -> DsM ([Binding], CHeader, CStub)-dsFImport id co (CImport cconv safety mHeader spec _) =- dsCImport id co spec (unLoc cconv) (unLoc safety) mHeader--dsCImport :: Id- -> Coercion- -> CImportSpec- -> CCallConv- -> Safety- -> Maybe Header+ -> ForeignImport (GhcPass p) -> DsM ([Binding], CHeader, CStub)-dsCImport id co (CLabel cid) cconv _ _ = do- dflags <- getDynFlags- let ty = coercionLKind co- platform = targetPlatform dflags- fod = case tyConAppTyCon_maybe (dropForAlls ty) of- Just tycon- | tyConUnique tycon == funPtrTyConKey ->- IsFunction- _ -> IsData- (resTy, foRhs) <- resultWrapper ty- assert (fromJust resTy `eqType` addrPrimTy) $ -- typechecker ensures this- let- rhs = foRhs (Lit (LitLabel cid stdcall_info fod))- rhs' = Cast rhs co- stdcall_info = fun_type_arg_stdcall_info platform cconv ty- in- return ([(id, rhs')], mempty, mempty)--dsCImport id co (CFunction target) cconv@PrimCallConv safety _- = dsPrimCall id co (CCall (CCallSpec target cconv safety))-dsCImport id co (CFunction target) cconv safety mHeader- = dsFCall id co (CCall (CCallSpec target cconv safety)) mHeader-dsCImport id co CWrapper cconv _ _- = dsFExportDynamic id co cconv---- For stdcall labels, if the type was a FunPtr or newtype thereof,--- then we need to calculate the size of the arguments in order to add--- the @n suffix to the label.-fun_type_arg_stdcall_info :: Platform -> CCallConv -> Type -> Maybe Int-fun_type_arg_stdcall_info platform StdCallConv ty- | Just (tc,[arg_ty]) <- splitTyConApp_maybe ty,- tyConUnique tc == funPtrTyConKey- = let- (bndrs, _) = tcSplitPiTys arg_ty- fe_arg_tys = mapMaybe binderRelevantType_maybe bndrs- in Just $ sum (map (widthInBytes . typeWidth . typeCmmType platform . getPrimTyOf) fe_arg_tys)-fun_type_arg_stdcall_info _ _other_conv _- = Nothing--{--************************************************************************-* *-\subsection{Foreign calls}-* *-************************************************************************--}--dsFCall :: Id -> Coercion -> ForeignCall -> Maybe Header- -> DsM ([(Id, Expr TyVar)], CHeader, CStub)-dsFCall fn_id co fcall mDeclHeader = do- let- ty = coercionLKind co- (tv_bndrs, rho) = tcSplitForAllTyVarBinders ty- (arg_tys, io_res_ty) = tcSplitFunTys rho-- args <- newSysLocalsDs arg_tys -- no FFI representation polymorphism- (val_args, arg_wrappers) <- mapAndUnzipM unboxArg (map Var args)-- let- work_arg_ids = [v | Var v <- val_args] -- All guaranteed to be vars-- (ccall_result_ty, res_wrapper) <- boxResult io_res_ty-- ccall_uniq <- newUnique- work_uniq <- newUnique-- (fcall', cDoc) <-- case fcall of- CCall (CCallSpec (StaticTarget _ cName mUnitId isFun)- CApiConv safety) ->- do nextWrapperNum <- ds_next_wrapper_num <$> getGblEnv- wrapperName <- mkWrapperName nextWrapperNum "ghc_wrapper" (unpackFS cName)- let fcall' = CCall (CCallSpec- (StaticTarget NoSourceText- wrapperName mUnitId- True)- CApiConv safety)- c = includes- $$ fun_proto <+> braces (cRet <> semi)- includes = vcat [ text "#include \"" <> ftext h- <> text "\""- | Header _ h <- nub headers ]- fun_proto = cResType <+> pprCconv <+> ppr wrapperName <> parens argTypes- cRet- | isVoidRes = cCall- | otherwise = text "return" <+> cCall- cCall = if isFun- then ppr cName <> parens argVals- else if null arg_tys- then ppr cName- else panic "dsFCall: Unexpected arguments to FFI value import"- raw_res_ty = case tcSplitIOType_maybe io_res_ty of- Just (_ioTyCon, res_ty) -> res_ty- Nothing -> io_res_ty- isVoidRes = raw_res_ty `eqType` unitTy- (mHeader, cResType)- | isVoidRes = (Nothing, text "void")- | otherwise = toCType raw_res_ty- pprCconv = ccallConvAttribute CApiConv- mHeadersArgTypeList- = [ (header, cType <+> char 'a' <> int n)- | (t, n) <- zip arg_tys [1..]- , let (header, cType) = toCType (scaledThing t) ]- (mHeaders, argTypeList) = unzip mHeadersArgTypeList- argTypes = if null argTypeList- then text "void"- else hsep $ punctuate comma argTypeList- mHeaders' = mDeclHeader : mHeader : mHeaders- headers = catMaybes mHeaders'- argVals = hsep $ punctuate comma- [ char 'a' <> int n- | (_, n) <- zip arg_tys [1..] ]- return (fcall', c)- _ ->- return (fcall, empty)- dflags <- getDynFlags- let- -- Build the worker- worker_ty = mkForAllTys tv_bndrs (mkVisFunTysMany (map idType work_arg_ids) ccall_result_ty)- tvs = map binderVar tv_bndrs- the_ccall_app = mkFCall ccall_uniq fcall' val_args ccall_result_ty- work_rhs = mkLams tvs (mkLams work_arg_ids the_ccall_app)- work_id = mkSysLocal (fsLit "$wccall") work_uniq Many worker_ty-- -- Build the wrapper- work_app = mkApps (mkVarApps (Var work_id) tvs) val_args- wrapper_body = foldr ($) (res_wrapper work_app) arg_wrappers- wrap_rhs = mkLams (tvs ++ args) wrapper_body- wrap_rhs' = Cast wrap_rhs co- simpl_opts = initSimpleOpts dflags- fn_id_w_inl = fn_id `setIdUnfolding` mkInlineUnfoldingWithArity- (length args)- simpl_opts- wrap_rhs'-- return ([(work_id, work_rhs), (fn_id_w_inl, wrap_rhs')], mempty, CStub cDoc [] [])--{--************************************************************************-* *-\subsection{Primitive calls}-* *-************************************************************************--This is for `@foreign import prim@' declarations.--Currently, at the core level we pretend that these primitive calls are-foreign calls. It may make more sense in future to have them as a distinct-kind of Id, or perhaps to bundle them with PrimOps since semantically and-for calling convention they are really prim ops.--}--dsPrimCall :: Id -> Coercion -> ForeignCall- -> DsM ([(Id, Expr TyVar)], CHeader, CStub)-dsPrimCall fn_id co fcall = do- let- ty = coercionLKind co- (tvs, fun_ty) = tcSplitForAllInvisTyVars ty- (arg_tys, io_res_ty) = tcSplitFunTys fun_ty-- args <- newSysLocalsDs arg_tys -- no FFI representation polymorphism-- ccall_uniq <- newUnique- let- call_app = mkFCall ccall_uniq fcall (map Var args) io_res_ty- rhs = mkLams tvs (mkLams args call_app)- rhs' = Cast rhs co- return ([(fn_id, rhs')], mempty, mempty)+dsFImport id co (CImport _ cconv safety mHeader spec) = do+ platform <- getPlatform+ case platformArch platform of+ ArchJavaScript -> dsJsImport id co spec (unLoc cconv) (unLoc safety) mHeader+ _ -> dsCImport id co spec (unLoc cconv) (unLoc safety) mHeader {- ************************************************************************@@ -367,325 +166,13 @@ , String -- string describing type to pass to createAdj. , Int -- size of args to stub function )--dsFExport fn_id co ext_name cconv isDyn = do- let- ty = coercionRKind co- (bndrs, orig_res_ty) = tcSplitPiTys ty- fe_arg_tys' = mapMaybe binderRelevantType_maybe bndrs- -- We must use tcSplits here, because we want to see- -- the (IO t) in the corner of the type!- fe_arg_tys | isDyn = tail fe_arg_tys'- | otherwise = fe_arg_tys'-- -- Look at the result type of the exported function, orig_res_ty- -- If it's IO t, return (t, True)- -- If it's plain t, return (t, False)- (res_ty, is_IO_res_ty) = case tcSplitIOType_maybe orig_res_ty of- -- The function already returns IO t- Just (_ioTyCon, res_ty) -> (res_ty, True)- -- The function returns t- Nothing -> (orig_res_ty, False)-- dflags <- getDynFlags- return $- mkFExportCBits dflags ext_name- (if isDyn then Nothing else Just fn_id)- fe_arg_tys res_ty is_IO_res_ty cconv--{--@foreign import "wrapper"@ (previously "foreign export dynamic") lets-you dress up Haskell IO actions of some fixed type behind an-externally callable interface (i.e., as a C function pointer). Useful-for callbacks and stuff.--\begin{verbatim}-type Fun = Bool -> Int -> IO Int-foreign import "wrapper" f :: Fun -> IO (FunPtr Fun)---- Haskell-visible constructor, which is generated from the above:--- SUP: No check for NULL from createAdjustor anymore???--f :: Fun -> IO (FunPtr Fun)-f cback =- bindIO (newStablePtr cback)- (\StablePtr sp# -> IO (\s1# ->- case _ccall_ createAdjustor cconv sp# ``f_helper'' <arg info> s1# of- (# s2#, a# #) -> (# s2#, A# a# #)))--foreign import "&f_helper" f_helper :: FunPtr (StablePtr Fun -> Fun)---- and the helper in C: (approximately; see `mkFExportCBits` below)--f_helper(StablePtr s, HsBool b, HsInt i)-{- Capability *cap;- cap = rts_lock();- rts_inCall(&cap,- rts_apply(rts_apply(deRefStablePtr(s),- rts_mkBool(b)), rts_mkInt(i)));- rts_unlock(cap);-}-\end{verbatim}--}--dsFExportDynamic :: Id- -> Coercion- -> CCallConv- -> DsM ([Binding], CHeader, CStub)-dsFExportDynamic id co0 cconv = do- mod <- getModule- dflags <- getDynFlags- let platform = targetPlatform dflags- let fe_nm = mkFastString $ zEncodeString- (moduleStableString mod ++ "$" ++ toCName dflags id)- -- Construct the label based on the passed id, don't use names- -- depending on Unique. See #13807 and Note [Unique Determinism].- cback <- newSysLocalDs arg_mult arg_ty- newStablePtrId <- dsLookupGlobalId newStablePtrName- stable_ptr_tycon <- dsLookupTyCon stablePtrTyConName- let- stable_ptr_ty = mkTyConApp stable_ptr_tycon [arg_ty]- export_ty = mkVisFunTyMany stable_ptr_ty arg_ty- bindIOId <- dsLookupGlobalId bindIOName- stbl_value <- newSysLocalDs Many stable_ptr_ty- (h_code, c_code, typestring, args_size) <- dsFExport id (mkRepReflCo export_ty) fe_nm cconv True- let- {-- The arguments to the external function which will- create a little bit of (template) code on the fly- for allowing the (stable pointed) Haskell closure- to be entered using an external calling convention- (stdcall, ccall).- -}- adj_args = [ mkIntLit platform (fromIntegral (ccallConvToInt cconv))- , Var stbl_value- , Lit (LitLabel fe_nm mb_sz_args IsFunction)- , Lit (mkLitString typestring)- ]- -- name of external entry point providing these services.- -- (probably in the RTS.)- adjustor = fsLit "createAdjustor"-- -- Determine the number of bytes of arguments to the stub function,- -- so that we can attach the '@N' suffix to its label if it is a- -- stdcall on Windows.- mb_sz_args = case cconv of- StdCallConv -> Just args_size- _ -> Nothing-- ccall_adj <- dsCCall adjustor adj_args PlayRisky (mkTyConApp io_tc [res_ty])- -- PlayRisky: the adjustor doesn't allocate in the Haskell heap or do a callback-- let io_app = mkLams tvs $- Lam cback $- mkApps (Var bindIOId)- [ Type stable_ptr_ty- , Type res_ty- , mkApps (Var newStablePtrId) [ Type arg_ty, Var cback ]- , Lam stbl_value ccall_adj- ]-- fed = (id `setInlineActivation` NeverActive, Cast io_app co0)- -- Never inline the f.e.d. function, because the litlit- -- might not be in scope in other modules.-- return ([fed], h_code, c_code)-- where- ty = coercionLKind co0- (tvs,sans_foralls) = tcSplitForAllInvisTyVars ty- ([Scaled arg_mult arg_ty], fn_res_ty) = tcSplitFunTys sans_foralls- Just (io_tc, res_ty) = tcSplitIOType_maybe fn_res_ty- -- Must have an IO type; hence Just---toCName :: DynFlags -> Id -> String-toCName dflags i = showSDoc dflags (pprCode CStyle (ppr (idName i)))--{--*--\subsection{Generating @foreign export@ stubs}--*--For each @foreign export@ function, a C stub function is generated.-The C stub constructs the application of the exported Haskell function-using the hugs/ghc rts invocation API.--}--mkFExportCBits :: DynFlags- -> FastString- -> Maybe Id -- Just==static, Nothing==dynamic- -> [Type]- -> Type- -> Bool -- True <=> returns an IO type- -> CCallConv- -> (CHeader,- CStub,- String, -- the argument reps- Int -- total size of arguments- )-mkFExportCBits dflags c_nm maybe_target arg_htys res_hty is_IO_res_ty cc- = ( header_bits- , CStub body [] []- , type_string,- sum [ widthInBytes (typeWidth rep) | (_,_,_,rep) <- aug_arg_info] -- all the args- -- NB. the calculation here isn't strictly speaking correct.- -- We have a primitive Haskell type (eg. Int#, Double#), and- -- we want to know the size, when passed on the C stack, of- -- the associated C type (eg. HsInt, HsDouble). We don't have- -- this information to hand, but we know what GHC's conventions- -- are for passing around the primitive Haskell types, so we- -- use that instead. I hope the two coincide --SDM- )- where- platform = targetPlatform dflags-- -- list the arguments to the C function- arg_info :: [(SDoc, -- arg name- SDoc, -- C type- Type, -- Haskell type- CmmType)] -- the CmmType- arg_info = [ let stg_type = showStgType ty in- (arg_cname n stg_type,- stg_type,- ty,- typeCmmType platform (getPrimTyOf ty))- | (ty,n) <- zip arg_htys [1::Int ..] ]-- arg_cname n stg_ty- | libffi = char '*' <> parens (stg_ty <> char '*') <>- text "args" <> brackets (int (n-1))- | otherwise = text ('a':show n)-- -- generate a libffi-style stub if this is a "wrapper" and libffi is enabled- libffi = platformMisc_libFFI (platformMisc dflags) && isNothing maybe_target-- type_string- -- libffi needs to know the result type too:- | libffi = primTyDescChar platform res_hty : arg_type_string- | otherwise = arg_type_string-- arg_type_string = [primTyDescChar platform ty | (_,_,ty,_) <- arg_info]- -- just the real args-- -- add some auxiliary args; the stable ptr in the wrapper case, and- -- a slot for the dummy return address in the wrapper + ccall case- aug_arg_info- | isNothing maybe_target = stable_ptr_arg : insertRetAddr platform cc arg_info- | otherwise = arg_info-- stable_ptr_arg =- (text "the_stableptr", text "StgStablePtr", undefined,- typeCmmType platform (mkStablePtrPrimTy alphaTy))-- -- stuff to do with the return type of the C function- res_hty_is_unit = res_hty `eqType` unitTy -- Look through any newtypes-- cResType | res_hty_is_unit = text "void"- | otherwise = showStgType res_hty-- -- when the return type is integral and word-sized or smaller, it- -- must be assigned as type ffi_arg (#3516). To see what type- -- libffi is expecting here, take a look in its own testsuite, e.g.- -- libffi/testsuite/libffi.call/cls_align_ulonglong.c- ffi_cResType- | is_ffi_arg_type = text "ffi_arg"- | otherwise = cResType- where- res_ty_key = getUnique (getName (typeTyCon res_hty))- is_ffi_arg_type = res_ty_key `notElem`- [floatTyConKey, doubleTyConKey,- int64TyConKey, word64TyConKey]-- -- Now we can cook up the prototype for the exported function.- pprCconv = ccallConvAttribute cc-- header_bits = CHeader (text "extern" <+> fun_proto <> semi)-- fun_args- | null aug_arg_info = text "void"- | otherwise = hsep $ punctuate comma- $ map (\(nm,ty,_,_) -> ty <+> nm) aug_arg_info-- fun_proto- | libffi- = text "void" <+> ftext c_nm <>- parens (text "void *cif STG_UNUSED, void* resp, void** args, void* the_stableptr")- | otherwise- = cResType <+> pprCconv <+> ftext c_nm <> parens fun_args-- -- the target which will form the root of what we ask rts_inCall to run- the_cfun- = case maybe_target of- Nothing -> text "(StgClosure*)deRefStablePtr(the_stableptr)"- Just hs_fn -> char '&' <> ppr hs_fn <> text "_closure"-- cap = text "cap" <> comma-- -- the expression we give to rts_inCall- expr_to_run- = foldl' appArg the_cfun arg_info -- NOT aug_arg_info- where- appArg acc (arg_cname, _, arg_hty, _)- = text "rts_apply"- <> parens (cap <> acc <> comma <> mkHObj arg_hty <> parens (cap <> arg_cname))-- -- various other bits for inside the fn- declareResult = text "HaskellObj ret;"- declareCResult | res_hty_is_unit = empty- | otherwise = cResType <+> text "cret;"-- assignCResult | res_hty_is_unit = empty- | otherwise =- text "cret=" <> unpackHObj res_hty <> parens (text "ret") <> semi-- -- an extern decl for the fn being called- extern_decl- = case maybe_target of- Nothing -> empty- Just hs_fn -> text "extern StgClosure " <> ppr hs_fn <> text "_closure" <> semi+dsFExport fn_id co ext_name cconv is_dyn = do+ platform <- getPlatform+ case platformArch platform of+ ArchJavaScript -> dsJsFExport fn_id co ext_name cconv is_dyn+ _ -> dsCFExport fn_id co ext_name cconv is_dyn - -- finally, the whole darn thing- body =- space $$- extern_decl $$- fun_proto $$- vcat- [ lbrace- , text "Capability *cap;"- , declareResult- , declareCResult- , text "cap = rts_lock();"- -- create the application + perform it.- , text "rts_inCall" <> parens (- char '&' <> cap <>- text "rts_apply" <> parens (- cap <>- text "(HaskellObj)"- <> (if is_IO_res_ty- then text "runIO_closure"- else text "runNonIO_closure")- <> comma- <> expr_to_run- ) <+> comma- <> text "&ret"- ) <> semi- , text "rts_checkSchedStatus" <> parens (doubleQuotes (ftext c_nm)- <> comma <> text "cap") <> semi- , assignCResult- , text "rts_unlock(cap);"- , ppUnless res_hty_is_unit $- if libffi- then char '*' <> parens (ffi_cResType <> char '*') <>- text "resp = cret;"- else text "return cret;"- , rbrace- ]- foreignExportsInitialiser :: Platform -> Module -> [Id] -> CStub foreignExportsInitialiser _ _ [] = mempty foreignExportsInitialiser platform mod hs_fns =@@ -701,7 +188,7 @@ -- See Note [Tracking foreign exports] in rts/ForeignExports.c initializerCStub platform fn_nm list_decl fn_body where- fn_nm = mkInitializerStubLabel mod "fexports"+ fn_nm = mkInitializerStubLabel mod (fsLit "fexports") mod_str = pprModuleName (moduleName mod) fn_body = text "registerForeignExports" <> parens (char '&' <> list_symbol) <> semi list_symbol = text "stg_exports_" <> mod_str@@ -716,140 +203,3 @@ closure_ptr :: Id -> SDoc closure_ptr fn = text "(StgPtr) &" <> ppr fn <> text "_closure" --mkHObj :: Type -> SDoc-mkHObj t = text "rts_mk" <> text (showFFIType t)--unpackHObj :: Type -> SDoc-unpackHObj t = text "rts_get" <> text (showFFIType t)--showStgType :: Type -> SDoc-showStgType t = text "Hs" <> text (showFFIType t)--showFFIType :: Type -> String-showFFIType t = getOccString (getName (typeTyCon t))--toCType :: Type -> (Maybe Header, SDoc)-toCType = f False- where f voidOK t- -- First, if we have (Ptr t) of (FunPtr t), then we need to- -- convert t to a C type and put a * after it. If we don't- -- know a type for t, then "void" is fine, though.- | Just (ptr, [t']) <- splitTyConApp_maybe t- , tyConName ptr `elem` [ptrTyConName, funPtrTyConName]- = case f True t' of- (mh, cType') ->- (mh, cType' <> char '*')- -- Otherwise, if we have a type constructor application, then- -- see if there is a C type associated with that constructor.- -- Note that we aren't looking through type synonyms or- -- anything, as it may be the synonym that is annotated.- | Just tycon <- tyConAppTyConPicky_maybe t- , Just (CType _ mHeader (_,cType)) <- tyConCType_maybe tycon- = (mHeader, ftext cType)- -- If we don't know a C type for this type, then try looking- -- through one layer of type synonym etc.- | Just t' <- coreView t- = f voidOK t'- -- This may be an 'UnliftedFFITypes'-style ByteArray# argument- -- (which is marshalled like a Ptr)- | Just byteArrayPrimTyCon == tyConAppTyConPicky_maybe t- = (Nothing, text "const void*")- | Just mutableByteArrayPrimTyCon == tyConAppTyConPicky_maybe t- = (Nothing, text "void*")- -- Otherwise we don't know the C type. If we are allowing- -- void then return that; otherwise something has gone wrong.- | voidOK = (Nothing, text "void")- | otherwise- = pprPanic "toCType" (ppr t)--typeTyCon :: Type -> TyCon-typeTyCon ty- | Just (tc, _) <- tcSplitTyConApp_maybe (unwrapType ty)- = tc- | otherwise- = pprPanic "GHC.HsToCore.Foreign.Decl.typeTyCon" (ppr ty)--insertRetAddr :: Platform -> CCallConv- -> [(SDoc, SDoc, Type, CmmType)]- -> [(SDoc, SDoc, Type, CmmType)]-insertRetAddr platform CCallConv args- = case platformArch platform of- ArchX86_64- | platformOS platform == OSMinGW32 ->- -- On other Windows x86_64 we insert the return address- -- after the 4th argument, because this is the point- -- at which we need to flush a register argument to the stack- -- (See rts/Adjustor.c for details).- let go :: Int -> [(SDoc, SDoc, Type, CmmType)]- -> [(SDoc, SDoc, Type, CmmType)]- go 4 args = ret_addr_arg platform : args- go n (arg:args) = arg : go (n+1) args- go _ [] = []- in go 0 args- | otherwise ->- -- On other x86_64 platforms we insert the return address- -- after the 6th integer argument, because this is the point- -- at which we need to flush a register argument to the stack- -- (See rts/Adjustor.c for details).- let go :: Int -> [(SDoc, SDoc, Type, CmmType)]- -> [(SDoc, SDoc, Type, CmmType)]- go 6 args = ret_addr_arg platform : args- go n (arg@(_,_,_,rep):args)- | cmmEqType_ignoring_ptrhood rep b64 = arg : go (n+1) args- | otherwise = arg : go n args- go _ [] = []- in go 0 args- _ ->- ret_addr_arg platform : args-insertRetAddr _ _ args = args--ret_addr_arg :: Platform -> (SDoc, SDoc, Type, CmmType)-ret_addr_arg platform = (text "original_return_addr", text "void*", undefined,- typeCmmType platform addrPrimTy)---- This function returns the primitive type associated with the boxed--- type argument to a foreign export (eg. Int ==> Int#).-getPrimTyOf :: Type -> UnaryType-getPrimTyOf ty- | isBoolTy rep_ty = intPrimTy- -- Except for Bool, the types we are interested in have a single constructor- -- with a single primitive-typed argument (see TcType.legalFEArgTyCon).- | otherwise =- case splitDataProductType_maybe rep_ty of- Just (_, _, data_con, [Scaled _ prim_ty]) ->- assert (dataConSourceArity data_con == 1) $- assertPpr (isUnliftedType prim_ty) (ppr prim_ty)- -- NB: it's OK to call isUnliftedType here, as we don't allow- -- representation-polymorphic types in foreign import/export declarations- prim_ty- _other -> pprPanic "GHC.HsToCore.Foreign.Decl.getPrimTyOf" (ppr ty)- where- rep_ty = unwrapType ty---- represent a primitive type as a Char, for building a string that--- described the foreign function type. The types are size-dependent,--- e.g. 'W' is a signed 32-bit integer.-primTyDescChar :: Platform -> Type -> Char-primTyDescChar platform ty- | ty `eqType` unitTy = 'v'- | otherwise- = case typePrimRep1 (getPrimTyOf ty) of- IntRep -> signed_word- WordRep -> unsigned_word- Int8Rep -> 'B'- Word8Rep -> 'b'- Int16Rep -> 'S'- Word16Rep -> 's'- Int32Rep -> 'W'- Word32Rep -> 'w'- Int64Rep -> 'L'- Word64Rep -> 'l'- AddrRep -> 'p'- FloatRep -> 'f'- DoubleRep -> 'd'- _ -> pprPanic "primTyDescChar" (ppr ty)- where- (signed_word, unsigned_word) = case platformWordSize platform of- PW4 -> ('W','w')- PW8 -> ('L','l')
+ compiler/GHC/HsToCore/Foreign/JavaScript.hs view
@@ -0,0 +1,683 @@+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}++-- | Handling of JavaScript foreign imports/exports+module GHC.HsToCore.Foreign.JavaScript+ ( dsJsImport+ , dsJsFExport+ , dsJsFExportDynamic+ )+where++import GHC.Prelude++import GHC.Platform++import GHC.Hs++import GHC.HsToCore.Monad+import GHC.HsToCore.Foreign.Call+import GHC.HsToCore.Foreign.Prim+import GHC.HsToCore.Foreign.Utils+import GHC.HsToCore.Utils++import GHC.Core+import GHC.Core.Make+import GHC.Core.Utils+import GHC.Core.DataCon+import GHC.Core.Unfold.Make+import GHC.Core.Type+import GHC.Core.TyCon+import GHC.Core.Coercion+import GHC.Core.Multiplicity++import GHC.Types.Id+import GHC.Types.Id.Make+import GHC.Types.Literal+import GHC.Types.ForeignStubs+import GHC.Types.SourceText+import GHC.Types.Name+import GHC.Types.RepType+import GHC.Types.ForeignCall+import GHC.Types.Basic+import GHC.Types.Unique++import GHC.Unit.Module++import GHC.Tc.Utils.TcType++import GHC.Cmm.Expr+import GHC.Cmm.Utils++import GHC.JS.Ppr++import GHC.Driver.Session+import GHC.Driver.Config++import GHC.Builtin.Types+import GHC.Builtin.Types.Prim+import GHC.Builtin.Names++import GHC.Data.FastString+import GHC.Data.Pair+import GHC.Data.Maybe++import GHC.Utils.Outputable+import GHC.Utils.Panic+import GHC.Utils.Encoding++dsJsFExport+ :: Id -- Either the exported Id,+ -- or the foreign-export-dynamic constructor+ -> Coercion -- Coercion between the Haskell type callable+ -- from C, and its representation type+ -> CLabelString -- The name to export to C land+ -> CCallConv+ -> Bool -- True => foreign export dynamic+ -- so invoke IO action that's hanging off+ -- the first argument's stable pointer+ -> DsM ( CHeader -- contents of Module_stub.h+ , CStub -- contents of Module_stub.c+ , String -- string describing type to pass to createAdj.+ , Int -- size of args to stub function+ )++dsJsFExport fn_id co ext_name cconv isDyn = do+ let+ ty = pSnd $ coercionKind co+ (_tvs,sans_foralls) = tcSplitForAllTyVars ty+ (fe_arg_tys', orig_res_ty) = tcSplitFunTys sans_foralls+ -- We must use tcSplits here, because we want to see+ -- the (IO t) in the corner of the type!+ fe_arg_tys | isDyn = tail fe_arg_tys'+ | otherwise = fe_arg_tys'++ -- Look at the result type of the exported function, orig_res_ty+ -- If it's IO t, return (t, True)+ -- If it's plain t, return (t, False)+ (res_ty, is_IO_res_ty) = case tcSplitIOType_maybe orig_res_ty of+ -- The function already returns IO t+ Just (_ioTyCon, res_ty) -> (res_ty, True)+ -- The function returns t+ Nothing -> (orig_res_ty, False)+ platform <- targetPlatform <$> getDynFlags+ return $+ mkFExportJSBits platform ext_name+ (if isDyn then Nothing else Just fn_id)+ (map scaledThing fe_arg_tys) res_ty is_IO_res_ty cconv++mkFExportJSBits+ :: Platform+ -> FastString+ -> Maybe Id -- Just==static, Nothing==dynamic+ -> [Type]+ -> Type+ -> Bool -- True <=> returns an IO type+ -> CCallConv+ -> (CHeader,+ CStub,+ String, -- the argument reps+ Int -- total size of arguments+ )+mkFExportJSBits platform c_nm maybe_target arg_htys res_hty is_IO_res_ty _cconv+ = (header_bits, js_bits, type_string,+ sum [ widthInBytes (typeWidth rep) | (_,_,_,rep) <- arg_info] -- all the args+ -- NB. the calculation here isn't strictly speaking correct.+ -- We have a primitive Haskell type (eg. Int#, Double#), and+ -- we want to know the size, when passed on the C stack, of+ -- the associated C type (eg. HsInt, HsDouble). We don't have+ -- this information to hand, but we know what GHC's conventions+ -- are for passing around the primitive Haskell types, so we+ -- use that instead. I hope the two coincide --SDM+ )+ where+ -- list the arguments to the JS function+ arg_info :: [(SDoc, -- arg name+ SDoc, -- C type+ Type, -- Haskell type+ CmmType)] -- the CmmType+ arg_info = [ let stg_type = showStgType ty in+ (arg_cname n stg_type,+ stg_type,+ ty,+ typeCmmType platform (getPrimTyOf ty))+ | (ty,n) <- zip arg_htys [1::Int ..] ]++ arg_cname n _stg_ty = text ('a':show n)++ type_string = primTyDescChar platform res_hty : arg_type_string++ arg_type_string = [primTyDescChar platform ty | (_,_,ty,_) <- arg_info]++ -- stuff to do with the return type of the JS function+ res_hty_is_unit = res_hty `eqType` unitTy -- Look through any newtypes++ unboxResType | res_hty_is_unit = text "h$rts_getUnit"+ | otherwise = unpackHObj res_hty++ header_bits = maybe mempty idTag maybe_target+ idTag i = let (tag, u) = unpkUnique (getUnique i)+ in CHeader (char tag <> int u)++ fun_args+ | null arg_info = empty -- text "void"+ | otherwise = hsep $ punctuate comma+ $ map (\(nm,_ty,_,_) -> nm) arg_info++ fun_proto+ = text "async" <+>+ text "function" <+>+ (if isNothing maybe_target+ then text "h$" <> ftext c_nm+ else ftext c_nm) <>+ parens fun_args++ fun_export+ = case maybe_target of+ Just hs_fn | Just m <- nameModule_maybe (getName hs_fn) ->+ text "h$foreignExport" <>+ parens (+ ftext c_nm <> comma <>+ strlit (unitIdString (moduleUnitId m)) <> comma <>+ strlit (moduleNameString (moduleName m)) <> comma <>+ strlit (unpackFS c_nm) <> comma <>+ strlit type_string+ ) <> semi+ _ -> empty++ strlit xs = docToSDoc (pprStringLit (mkFastString xs))++ -- the target which will form the root of what we ask rts_evalIO to run+ the_cfun+ = case maybe_target of+ Nothing -> text "h$deRefStablePtr(the_stableptr)"+ Just hs_fn -> idClosureText hs_fn++ -- the expression we give to rts_eval+ expr_to_run :: SDoc+ expr_to_run+ = foldl appArg the_cfun arg_info+ where+ appArg acc (arg_cname, _, arg_hty, _)+ = text "h$rts_apply"+ <> parens (acc <> comma <> mkHObj arg_hty <> parens arg_cname)++ -- finally, the whole darn thing+ js_bits = CStub { getCStub = js_sdoc+ , getInitializers = mempty+ , getFinalizers = mempty+ }+ where js_sdoc = space+ $$ fun_proto+ $$ vcat+ [ lbrace+ , text "return"+ <+> text "await"+ <+> text "h$rts_eval"+ <> parens ((if is_IO_res_ty+ then expr_to_run+ else text "h$rts_toIO" <> parens expr_to_run)+ <> comma <+> unboxResType)+ <> semi+ , rbrace+ ]+ $$ fun_export++idClosureText :: Id -> SDoc+idClosureText i+ | isExportedId i+ , name <- getName i+ , Just m <- nameModule_maybe name+ = let str = renderWithContext defaultSDocContext (pprFullName m (localiseName name))+ in text "h$" <> text (zEncodeString str)+ | otherwise+ = panic "idClosureText: unknown module"++-- | Desugaring of JavaScript foreign imports+dsJsImport+ :: Id+ -> Coercion+ -> CImportSpec+ -> CCallConv+ -> Safety+ -> Maybe Header+ -> DsM ([Binding], CHeader, CStub)+dsJsImport id co (CLabel cid) cconv _ _ = do+ let ty = pFst $ coercionKind co+ fod = case tyConAppTyCon_maybe (dropForAlls ty) of+ Just tycon+ | tyConUnique tycon == funPtrTyConKey ->+ IsFunction+ _ -> IsData+ (_resTy, foRhs) <- jsResultWrapper ty+-- ASSERT(fromJust resTy `eqType` addrPrimTy) -- typechecker ensures this+ let rhs = foRhs (Lit (LitLabel cid stdcall_info fod))+ rhs' = Cast rhs co+ stdcall_info = fun_type_arg_stdcall_info cconv ty++ return ([(id, rhs')], mempty, mempty)++dsJsImport id co (CFunction target) cconv@PrimCallConv safety _+ = dsPrimCall id co (CCall (CCallSpec target cconv safety))+dsJsImport id co (CFunction target) cconv safety mHeader+ = dsJsCall id co (CCall (CCallSpec target cconv safety)) mHeader+dsJsImport id co CWrapper cconv _ _+ = dsJsFExportDynamic id co cconv++-- fixme work in progress+-- FIXME (Sylvain 2022-03): possibility of code sharing with dsFExportDynamic?+-- Lot of duplication+dsJsFExportDynamic :: Id+ -> Coercion+ -> CCallConv+ -> DsM ([Binding], CHeader, CStub)+dsJsFExportDynamic id co0 cconv = do+ let+ ty = pFst (coercionKind co0)+ (tvs,sans_foralls) = tcSplitForAllTyVars ty+ ([Scaled arg_mult arg_ty], fn_res_ty) = tcSplitFunTys sans_foralls+ (io_tc, res_ty) = expectJust "dsJsFExportDynamic: IO type expected"+ -- Must have an IO type; hence Just+ $ tcSplitIOType_maybe fn_res_ty+ mod <- getModule+ platform <- targetPlatform <$> getDynFlags+ let fe_nm = mkFastString $ zEncodeString+ ("h$" ++ moduleStableString mod ++ "$" ++ toJsName id)+ -- Construct the label based on the passed id, don't use names+ -- depending on Unique. See #13807 and Note [Unique Determinism].+ cback <- newSysLocalDs arg_mult arg_ty+ newStablePtrId <- dsLookupGlobalId newStablePtrName+ stable_ptr_tycon <- dsLookupTyCon stablePtrTyConName+ let+ stable_ptr_ty = mkTyConApp stable_ptr_tycon [arg_ty]+ export_ty = mkVisFunTyMany stable_ptr_ty arg_ty+ bindIOId <- dsLookupGlobalId bindIOName+ stbl_value <- newSysLocalDs ManyTy stable_ptr_ty+ (h_code, c_code, typestring, args_size) <- dsJsFExport id (mkRepReflCo export_ty) fe_nm cconv True+ let+ {-+ The arguments to the external function which will+ create a little bit of (template) code on the fly+ for allowing the (stable pointed) Haskell closure+ to be entered using an external calling convention+ (stdcall, ccall).+ -}+ adj_args = [ mkIntLit platform (toInteger (ccallConvToInt cconv))+ , Var stbl_value+ , Lit (LitLabel fe_nm mb_sz_args IsFunction)+ , Lit (mkLitString typestring)+ ]+ -- name of external entry point providing these services.+ -- (probably in the RTS.)+ adjustor = fsLit "createAdjustor"++ -- Determine the number of bytes of arguments to the stub function,+ -- so that we can attach the '@N' suffix to its label if it is a+ -- stdcall on Windows.+ mb_sz_args = case cconv of+ StdCallConv -> Just args_size+ _ -> Nothing++ ccall_adj <- dsCCall adjustor adj_args PlayRisky (mkTyConApp io_tc [res_ty])+ -- PlayRisky: the adjustor doesn't allocate in the Haskell heap or do a callback++ let io_app = mkLams tvs $+ Lam cback $+ mkApps (Var bindIOId)+ [ Type stable_ptr_ty+ , Type res_ty+ , mkApps (Var newStablePtrId) [ Type arg_ty, Var cback ]+ , Lam stbl_value ccall_adj+ ]++ fed = (id `setInlineActivation` NeverActive, Cast io_app co0)+ -- Never inline the f.e.d. function, because the litlit+ -- might not be in scope in other modules.++ return ([fed], h_code, c_code)++toJsName :: Id -> String+toJsName i = renderWithContext defaultSDocContext (pprCode (ppr (idName i)))++dsJsCall :: Id -> Coercion -> ForeignCall -> Maybe Header+ -> DsM ([(Id, Expr TyVar)], CHeader, CStub)+dsJsCall fn_id co (CCall (CCallSpec target cconv safety)) _mDeclHeader = do+ let+ ty = pFst $ coercionKind co+ (tv_bndrs, rho) = tcSplitForAllTyVarBinders ty+ (arg_tys, io_res_ty) = tcSplitFunTys rho++ args <- newSysLocalsDs arg_tys -- no FFI levity-polymorphism+ (val_args, arg_wrappers) <- mapAndUnzipM unboxJsArg (map Var args)++ let+ work_arg_ids = [v | Var v <- val_args] -- All guaranteed to be vars++ (ccall_result_ty, res_wrapper) <- boxJsResult io_res_ty++ ccall_uniq <- newUnique+ work_uniq <- newUnique++ simpl_opts <- initSimpleOpts <$> getDynFlags++ let+ -- Build the worker+ fcall = CCall (CCallSpec target cconv safety)+ worker_ty = mkForAllTys tv_bndrs (mkVisFunTysMany (map idType work_arg_ids) ccall_result_ty)+ tvs = map binderVar tv_bndrs+ the_ccall_app = mkFCall ccall_uniq fcall val_args ccall_result_ty+ work_rhs = mkLams tvs (mkLams work_arg_ids the_ccall_app)+ work_id = mkSysLocal (fsLit "$wccall") work_uniq ManyTy worker_ty++ -- Build the wrapper+ work_app = mkApps (mkVarApps (Var work_id) tvs) val_args+ wrapper_body = foldr ($) (res_wrapper work_app) arg_wrappers+ wrap_rhs = mkLams (tvs ++ args) wrapper_body+ wrap_rhs' = Cast wrap_rhs co+ fn_id_w_inl = fn_id+ `setIdUnfolding`+ mkInlineUnfoldingWithArity simpl_opts VanillaSrc+ (length args) wrap_rhs'++ return ([(work_id, work_rhs), (fn_id_w_inl, wrap_rhs')], mempty, mempty)+++mkHObj :: Type -> SDoc+mkHObj t = text "h$rts_mk" <> text (showFFIType t)++unpackHObj :: Type -> SDoc+unpackHObj t = text "h$rts_get" <> text (showFFIType t)++showStgType :: Type -> SDoc+showStgType t = text "Hs" <> text (showFFIType t)++showFFIType :: Type -> String+showFFIType t = getOccString (getName (typeTyCon t))++typeTyCon :: Type -> TyCon+typeTyCon ty+ -- UnaryRep rep_ty <- repType ty+ | Just (tc, _) <- tcSplitTyConApp_maybe (unwrapType ty) -- rep_ty+ = tc+ | otherwise+ = pprPanic "typeTyCon" (ppr ty)+++{-+ We unbox arguments for JS calls a bit different from native code:+ - Bool is marshalled to true/false, not 0/1+ - All int types are narrowed, since JS floats have a greater range than Int32+ -}++unboxJsArg :: CoreExpr -- The supplied argument+ -> DsM (CoreExpr, -- To pass as the actual argument+ CoreExpr -> CoreExpr -- Wrapper to unbox the arg+ )+unboxJsArg arg+ -- Primtive types: nothing to unbox+ | isPrimitiveType arg_ty+ = return (arg, \body -> body)++ -- Recursive newtypes+ | Just (co, _rep_ty) <- topNormaliseNewType_maybe arg_ty+ = unboxJsArg (mkCast arg co)++ -- Booleans, do not convert to 0/1, only force them+ | Just tc <- tyConAppTyCon_maybe arg_ty,+ tc `hasKey` boolTyConKey+ = return (arg,+ \ body -> mkWildCase arg (unrestricted boolTy) (exprType body) [Alt DEFAULT [] body])++ | Just tc <- tyConAppTyCon_maybe arg_ty,+ tc `hasKey` anyTyConKey+ = return (arg,+ \ body -> mkWildCase arg (unrestricted arg_ty) (exprType body) [Alt DEFAULT [] body])+ -- Data types with a single constructor, which has a single, primitive-typed arg+ -- This deals with Int, Float etc; also Ptr, ForeignPtr+ | is_product_type && data_con_arity == 1+ = do case_bndr <- newSysLocalDs ManyTy arg_ty+ prim_arg <- newSysLocalDs ManyTy (scaledThing data_con_arg_ty1)+ return (Var prim_arg,+ \ body -> Case arg case_bndr (exprType body) [Alt (DataAlt data_con) [prim_arg] body]+ )++ -- Byte-arrays, both mutable and otherwise; hack warning+ -- We're looking for values of type ByteArray, MutableByteArray+ -- data ByteArray ix = ByteArray ix ix ByteArray#+ -- data MutableByteArray s ix = MutableByteArray ix ix (MutableByteArray# s)+ | is_product_type &&+ data_con_arity == 3 &&+ isJust maybe_arg3_tycon &&+ (arg3_tycon == byteArrayPrimTyCon ||+ arg3_tycon == mutableByteArrayPrimTyCon)+ = do case_bndr <- newSysLocalDs ManyTy arg_ty+ vars@[_l_var, _r_var, arr_cts_var] <- newSysLocalsDs data_con_arg_tys+ return (Var arr_cts_var,+ \ body -> Case arg case_bndr (exprType body) [Alt (DataAlt data_con) vars body]+ )++ | otherwise+ = do l <- getSrcSpanDs+ pprPanic "unboxJsArg: " (ppr l <+> ppr arg_ty)+ where+ arg_ty = exprType arg+ maybe_product_type = splitDataProductType_maybe arg_ty+ is_product_type = isJust maybe_product_type+ Just (_, _, data_con, data_con_arg_tys) = maybe_product_type+ data_con_arity = dataConSourceArity data_con+ (data_con_arg_ty1 : _) = data_con_arg_tys++ (_ : _ : data_con_arg_ty3 : _) = data_con_arg_tys+ maybe_arg3_tycon = tyConAppTyCon_maybe (scaledThing data_con_arg_ty3)+ Just arg3_tycon = maybe_arg3_tycon+++boxJsResult :: Type+ -> DsM (Type, CoreExpr -> CoreExpr)+boxJsResult result_ty+ | isRuntimeRepKindedTy result_ty = panic "boxJsResult: runtime rep ty" -- fixme+-- Takes the result of the user-level ccall:+-- either (IO t),+-- or maybe just t for an side-effect-free call+-- Returns a wrapper for the primitive ccall itself, along with the+-- type of the result of the primitive ccall. This result type+-- will be of the form+-- State# RealWorld -> (# State# RealWorld, t' #)+-- where t' is the unwrapped form of t. If t is simply (), then+-- the result type will be+-- State# RealWorld -> (# State# RealWorld #)++boxJsResult result_ty+ | Just (io_tycon, io_res_ty) <- tcSplitIOType_maybe result_ty+ -- isIOType_maybe handles the case where the type is a+ -- simple wrapping of IO. E.g.+ -- newtype Wrap a = W (IO a)+ -- No coercion necessary because its a non-recursive newtype+ -- (If we wanted to handle a *recursive* newtype too, we'd need+ -- another case, and a coercion.)+ -- The result is IO t, so wrap the result in an IO constructor+ = do { res <- jsResultWrapper io_res_ty+ ; let return_result state ans+ = mkCoreUnboxedTuple [state, ans]++ ; (ccall_res_ty, the_alt) <- mk_alt return_result res++ ; state_id <- newSysLocalDs ManyTy realWorldStatePrimTy+ ; let io_data_con = head (tyConDataCons io_tycon)+ toIOCon = dataConWrapId io_data_con++ wrap the_call =+ mkApps (Var toIOCon)+ [ Type io_res_ty,+ Lam state_id $+ mkWildCase (App the_call (Var state_id))+ (unrestricted ccall_res_ty)+ (coreAltType the_alt)+ [the_alt]+ ]++ ; return (realWorldStatePrimTy `mkVisFunTyMany` ccall_res_ty, wrap) }++boxJsResult result_ty+ = do -- It isn't IO, so do unsafePerformIO+ -- It's not conveniently available, so we inline it+ res <- jsResultWrapper result_ty+ (ccall_res_ty, the_alt) <- mk_alt return_result res+ let+ wrap = \ the_call -> mkWildCase (App the_call (Var realWorldPrimId))+ (unrestricted ccall_res_ty)+ (coreAltType the_alt)+ [the_alt]+ return (realWorldStatePrimTy `mkVisFunTyMany` ccall_res_ty, wrap)+ where+ return_result _ ans = ans++mk_alt :: (Expr Var -> Expr Var -> Expr Var)+ -> (Maybe Type, Expr Var -> Expr Var)+ -> DsM (Type, CoreAlt)+mk_alt return_result (Nothing, wrap_result)+ = do -- The ccall returns ()+ state_id <- newSysLocalDs ManyTy realWorldStatePrimTy+ let+ the_rhs = return_result (Var state_id)+ (wrap_result $ panic "jsBoxResult")+ ccall_res_ty = mkTupleTy Unboxed [realWorldStatePrimTy]+ the_alt = Alt (DataAlt (tupleDataCon Unboxed 1)) [state_id] the_rhs+ return (ccall_res_ty, the_alt)++mk_alt return_result (Just prim_res_ty, wrap_result)+ -- The ccall returns a non-() value+ | isUnboxedTupleType prim_res_ty = do+ let+ Just ls = fmap dropRuntimeRepArgs (tyConAppArgs_maybe prim_res_ty)+ arity = 1 + length ls+ args_ids <- mapM (newSysLocalDs ManyTy) ls+ state_id <- newSysLocalDs ManyTy realWorldStatePrimTy+ let+ result_tup = mkCoreUnboxedTuple (map Var args_ids)+ the_rhs = return_result (Var state_id)+ (wrap_result result_tup)+ ccall_res_ty = mkTupleTy Unboxed (realWorldStatePrimTy : ls)+ the_alt = Alt (DataAlt (tupleDataCon Unboxed arity))+ (state_id : args_ids)+ the_rhs+ return (ccall_res_ty, the_alt)++ | otherwise = do+ result_id <- newSysLocalDs ManyTy prim_res_ty+ state_id <- newSysLocalDs ManyTy realWorldStatePrimTy+ let+ the_rhs = return_result (Var state_id)+ (wrap_result (Var result_id))+ ccall_res_ty = mkTupleTy Unboxed [realWorldStatePrimTy, prim_res_ty]+ the_alt = Alt (DataAlt (tupleDataCon Unboxed 2)) [state_id, result_id] the_rhs+ return (ccall_res_ty, the_alt)++fun_type_arg_stdcall_info :: CCallConv -> Type -> Maybe Int+fun_type_arg_stdcall_info _other_conv _ = Nothing+++jsResultWrapper+ :: Type+ -> DsM ( Maybe Type -- Type of the expected result, if any+ , CoreExpr -> CoreExpr -- Wrapper for the result+ )+-- resultWrapper deals with the result *value*+-- E.g. foreign import foo :: Int -> IO T+-- Then resultWrapper deals with marshalling the 'T' part+jsResultWrapper result_ty+ | isRuntimeRepKindedTy result_ty = return (Nothing, id) -- fixme this seems like a hack+ -- Base case 1a: unboxed tuples+ | Just (tc, args) <- splitTyConApp_maybe result_ty+ , isUnboxedTupleTyCon tc {- && False -} = do+ let args' = dropRuntimeRepArgs args+ (tys, wrappers) <- unzip <$> mapM jsResultWrapper args'+ matched <- mapM (mapM (newSysLocalDs ManyTy)) tys+ let tys' = catMaybes tys+ -- arity = length args'+ -- resCon = tupleDataCon Unboxed (length args)+ err = panic "jsResultWrapper: used Id with result type Nothing"+ resWrap :: CoreExpr+ resWrap = mkCoreUnboxedTuple (zipWith (\w -> w . Var . fromMaybe err) wrappers matched)+ return $+ if null tys'+ then (Nothing, \_ -> resWrap)+ else let innerArity = length tys'+ innerTy = mkTupleTy Unboxed tys'+ innerCon = tupleDataCon Unboxed innerArity+ inner :: CoreExpr -> CoreExpr+ inner e = mkWildCase e (unrestricted innerTy) result_ty+ [Alt (DataAlt innerCon)+ (catMaybes matched)+ resWrap+ ]+ in (Just innerTy, inner)++ -- Base case 1b: primitive types+ | isPrimitiveType result_ty+ = return (Just result_ty, \e -> e)+ -- Base case 1c: boxed tuples+ -- fixme: levity args?+ | Just (tc, args) <- splitTyConApp_maybe result_ty+ , isBoxedTupleTyCon tc = do+ let args' = dropRuntimeRepArgs args+ innerTy = mkTupleTy Unboxed args'+ (inner_res, w) <- jsResultWrapper innerTy+ matched <- mapM (newSysLocalDs ManyTy) args'+ let inner e = mkWildCase (w e) (unrestricted innerTy) result_ty+ [ Alt (DataAlt (tupleDataCon Unboxed (length args')))+ matched+ (mkCoreTup (map Var matched))+ -- mkCoreConApps (tupleDataCon Boxed (length args)) (map Type args ++ map Var matched)+ ]+ return (inner_res, inner)++ -- Base case 2: the unit type ()+ | Just (tc,_) <- maybe_tc_app, tc `hasKey` unitTyConKey+ = return (Nothing, \_ -> Var unitDataConId)++ -- Base case 3: the boolean type+ | Just (tc,_) <- maybe_tc_app, tc `hasKey` boolTyConKey = do+-- result_id <- newSysLocalDs boolTy+ ccall_uniq <- newUnique+ let forceBool e = mkJsCall ccall_uniq "$r = !(!$1)" [e] boolTy+ return+ (Just intPrimTy, \e -> forceBool e)++ -- Base case 4: the any type+ | Just (tc,_) <- maybe_tc_app, tc `hasKey` anyTyConKey+ = return (Just result_ty, \e -> e)++ -- Newtypes+ | Just (co, rep_ty) <- topNormaliseNewType_maybe result_ty+ = do (maybe_ty, wrapper) <- jsResultWrapper rep_ty+ return (maybe_ty, \e -> mkCastDs (wrapper e) (mkSymCo co))++ -- The type might contain foralls (eg. for dummy type arguments,+ -- referring to 'Ptr a' is legal).+ | Just (tyvar, rest) <- splitForAllTyCoVar_maybe result_ty+ = do (maybe_ty, wrapper) <- jsResultWrapper rest+ return (maybe_ty, \e -> Lam tyvar (wrapper e))++ -- Data types with a single constructor, which has a single arg+ -- This includes types like Ptr and ForeignPtr+ | Just (_tycon, tycon_arg_tys, data_con, data_con_arg_tys) <- splitDataProductType_maybe result_ty,+ dataConSourceArity data_con == 1+ = do let (unwrapped_res_ty : _) = data_con_arg_tys+ (maybe_ty, wrapper) <- jsResultWrapper (scaledThing unwrapped_res_ty)+ return+ (maybe_ty, \e -> mkApps (Var (dataConWrapId data_con))+ (map Type tycon_arg_tys ++ [wrapper e]))++ | otherwise+ = pprPanic "jsResultWrapper" (ppr result_ty)+ where+ maybe_tc_app = splitTyConApp_maybe result_ty++-- low-level primitive JavaScript call:+mkJsCall :: Unique -> String -> [CoreExpr] -> Type -> CoreExpr+mkJsCall u tgt args t = mkFCall u ccall args t+ where+ ccall = CCall $ CCallSpec+ (StaticTarget NoSourceText (mkFastString tgt) (Just primUnit) True)+ JavaScriptCallConv+ PlayRisky
+ compiler/GHC/HsToCore/Foreign/Prim.hs view
@@ -0,0 +1,45 @@+-- | Foreign primitive calls+--+-- This is for `@foreign import prim@' declarations.+--+-- Currently, at the core level we pretend that these primitive calls are+-- foreign calls. It may make more sense in future to have them as a distinct+-- kind of Id, or perhaps to bundle them with PrimOps since semantically and for+-- calling convention they are really prim ops.+module GHC.HsToCore.Foreign.Prim+ ( dsPrimCall+ )+where++import GHC.Prelude++import GHC.Tc.Utils.Monad -- temp+import GHC.Tc.Utils.TcType++import GHC.Core+import GHC.Core.Type+import GHC.Core.Coercion++import GHC.HsToCore.Monad+import GHC.HsToCore.Foreign.Call++import GHC.Types.Id+import GHC.Types.ForeignStubs+import GHC.Types.ForeignCall++dsPrimCall :: Id -> Coercion -> ForeignCall+ -> DsM ([(Id, Expr TyVar)], CHeader, CStub)+dsPrimCall fn_id co fcall = do+ let+ ty = coercionLKind co+ (tvs, fun_ty) = tcSplitForAllInvisTyVars ty+ (arg_tys, io_res_ty) = tcSplitFunTys fun_ty++ args <- newSysLocalsDs arg_tys -- no FFI representation polymorphism++ ccall_uniq <- newUnique+ let+ call_app = mkFCall ccall_uniq fcall (map Var args) io_res_ty+ rhs = mkLams tvs (mkLams args call_app)+ rhs' = Cast rhs co+ return ([(fn_id, rhs')], mempty, mempty)
+ compiler/GHC/HsToCore/Foreign/Utils.hs view
@@ -0,0 +1,105 @@+{-# LANGUAGE MultiWayIf #-}++module GHC.HsToCore.Foreign.Utils+ ( Binding+ , getPrimTyOf+ , primTyDescChar+ , ppPrimTyConStgType+ )+where++import GHC.Prelude++import GHC.Platform++import GHC.Tc.Utils.TcType++import GHC.Core (CoreExpr)+import GHC.Core.DataCon+import GHC.Core.TyCon+import GHC.Core.TyCo.Rep++import GHC.Types.Id+import GHC.Types.RepType++import GHC.Builtin.Types+import GHC.Builtin.Types.Prim++import GHC.Utils.Outputable+import GHC.Utils.Panic+import GHC.Utils.Panic.Plain++type Binding = (Id, CoreExpr) -- No rec/nonrec structure;+ -- the occurrence analyser will sort it all out++-- This function returns the primitive type associated with the boxed+-- type argument to a foreign export (eg. Int ==> Int#).+getPrimTyOf :: Type -> UnaryType+getPrimTyOf ty+ | isBoolTy rep_ty = intPrimTy+ -- Except for Bool, the types we are interested in have a single constructor+ -- with a single primitive-typed argument (see TcType.legalFEArgTyCon).+ | otherwise =+ case splitDataProductType_maybe rep_ty of+ Just (_, _, data_con, [Scaled _ prim_ty]) ->+ assert (dataConSourceArity data_con == 1) $+ assertPpr (isUnliftedType prim_ty) (ppr prim_ty)+ -- NB: it's OK to call isUnliftedType here, as we don't allow+ -- representation-polymorphic types in foreign import/export declarations+ prim_ty+ _other -> pprPanic "getPrimTyOf" (ppr ty)+ where+ rep_ty = unwrapType ty++-- represent a primitive type as a Char, for building a string that+-- described the foreign function type. The types are size-dependent,+-- e.g. 'W' is a signed 32-bit integer.+primTyDescChar :: Platform -> Type -> Char+primTyDescChar !platform ty+ | ty `eqType` unitTy = 'v'+ | otherwise+ = case typePrimRep1 (getPrimTyOf ty) of+ IntRep -> signed_word+ WordRep -> unsigned_word+ Int8Rep -> 'B'+ Word8Rep -> 'b'+ Int16Rep -> 'S'+ Word16Rep -> 's'+ Int32Rep -> 'W'+ Word32Rep -> 'w'+ Int64Rep -> 'L'+ Word64Rep -> 'l'+ AddrRep -> 'p'+ FloatRep -> 'f'+ DoubleRep -> 'd'+ _ -> pprPanic "primTyDescChar" (ppr ty)+ where+ (signed_word, unsigned_word) = case platformWordSize platform of+ PW4 -> ('W','w')+ PW8 -> ('L','l')++-- | Printed C Type to be used with CAPI calling convention+ppPrimTyConStgType :: TyCon -> Maybe String+ppPrimTyConStgType tc =+ if | tc == charPrimTyCon -> Just "StgChar"+ | tc == intPrimTyCon -> Just "StgInt"+ | tc == int8PrimTyCon -> Just "StgInt8"+ | tc == int16PrimTyCon -> Just "StgInt16"+ | tc == int32PrimTyCon -> Just "StgInt32"+ | tc == int64PrimTyCon -> Just "StgInt64"+ | tc == wordPrimTyCon -> Just "StgWord"+ | tc == word8PrimTyCon -> Just "StgWord8"+ | tc == word16PrimTyCon -> Just "StgWord16"+ | tc == word32PrimTyCon -> Just "StgWord32"+ | tc == word64PrimTyCon -> Just "StgWord64"+ | tc == floatPrimTyCon -> Just "StgFloat"+ | tc == doublePrimTyCon -> Just "StgDouble"+ | tc == addrPrimTyCon -> Just "StgAddr"+ | tc == stablePtrPrimTyCon -> Just "StgStablePtr"+ | tc == arrayPrimTyCon -> Just "const StgAddr"+ | tc == mutableArrayPrimTyCon -> Just "StgAddr"+ | tc == byteArrayPrimTyCon -> Just "const StgAddr"+ | tc == mutableByteArrayPrimTyCon -> Just "StgAddr"+ | tc == smallArrayPrimTyCon -> Just "const StgAddr"+ | tc == smallMutableArrayPrimTyCon -> Just "StgAddr"+ | otherwise -> Nothing
compiler/GHC/HsToCore/GuardedRHSs.hs view
@@ -129,7 +129,7 @@ matchGuards (BindStmt _ pat bind_rhs : stmts) ctx nablas rhs rhs_ty = do let upat = unLoc pat- match_var <- selectMatchVar Many upat+ match_var <- selectMatchVar ManyTy upat -- We only allow unrestricted patterns in guard, hence the `Many` -- above. It isn't clear what linear patterns would mean, maybe we will -- figure it out in the future.
compiler/GHC/HsToCore/ListComp.hs view
@@ -366,8 +366,8 @@ let b_ty = idType n_id -- create some new local id's- b <- newSysLocalDs Many b_ty- x <- newSysLocalDs Many x_ty+ b <- newSysLocalDs ManyTy b_ty+ x <- newSysLocalDs ManyTy x_ty -- build rest of the comprehension core_rest <- dfListComp c_id b quals@@ -397,11 +397,11 @@ -- (a2:as'2) -> (a1, a2) : zip as'1 as'2)] mkZipBind elt_tys = do- ass <- mapM (newSysLocalDs Many) elt_list_tys- as' <- mapM (newSysLocalDs Many) elt_tys- as's <- mapM (newSysLocalDs Many) elt_list_tys+ ass <- mapM (newSysLocalDs ManyTy) elt_list_tys+ as' <- mapM (newSysLocalDs ManyTy) elt_tys+ as's <- mapM (newSysLocalDs ManyTy) elt_list_tys - zip_fn <- newSysLocalDs Many zip_fn_ty+ zip_fn <- newSysLocalDs ManyTy zip_fn_ty let inner_rhs = mkConsExpr elt_tuple_ty (mkBigCoreVarTup as')@@ -436,13 +436,13 @@ mkUnzipBind ThenForm _ = return Nothing -- No unzipping for ThenForm mkUnzipBind _ elt_tys- = do { ax <- newSysLocalDs Many elt_tuple_ty- ; axs <- newSysLocalDs Many elt_list_tuple_ty- ; ys <- newSysLocalDs Many elt_tuple_list_ty- ; xs <- mapM (newSysLocalDs Many) elt_tys- ; xss <- mapM (newSysLocalDs Many) elt_list_tys+ = do { ax <- newSysLocalDs ManyTy elt_tuple_ty+ ; axs <- newSysLocalDs ManyTy elt_list_tuple_ty+ ; ys <- newSysLocalDs ManyTy elt_tuple_list_ty+ ; xs <- mapM (newSysLocalDs ManyTy) elt_tys+ ; xss <- mapM (newSysLocalDs ManyTy) elt_list_tys - ; unzip_fn <- newSysLocalDs Many unzip_fn_ty+ ; unzip_fn <- newSysLocalDs ManyTy unzip_fn_ty ; [us1, us2] <- sequence [newUniqueSupply, newUniqueSupply] @@ -450,8 +450,8 @@ concat_expressions = map mkConcatExpression (zip3 elt_tys (map Var xs) (map Var xss)) tupled_concat_expression = mkBigCoreTup concat_expressions - folder_body_inner_case = mkTupleCase us1 xss tupled_concat_expression axs (Var axs)- folder_body_outer_case = mkTupleCase us2 xs folder_body_inner_case ax (Var ax)+ folder_body_inner_case = mkBigTupleCase us1 xss tupled_concat_expression (Var axs)+ folder_body_outer_case = mkBigTupleCase us2 xs folder_body_inner_case (Var ax) folder_body = mkLams [ax, axs] folder_body_outer_case ; unzip_body <- mkFoldrExpr elt_tuple_ty elt_list_tuple_ty folder_body nil_tuple (Var ys)@@ -543,15 +543,12 @@ -- Generate the expressions to build the grouped list -- Build a pattern that ensures the consumer binds into the NEW binders, -- which hold monads rather than single values- ; let tup_n_ty' = mkBigCoreVarTupTy to_bndrs- ; body <- dsMcStmts stmts_rest- ; n_tup_var' <- newSysLocalDs Many n_tup_ty'- ; tup_n_var' <- newSysLocalDs Many tup_n_ty'+ ; n_tup_var' <- newSysLocalDs ManyTy n_tup_ty' ; tup_n_expr' <- mkMcUnzipM form fmap_op n_tup_var' from_bndr_tys ; us <- newUniqueSupply ; let rhs' = mkApps usingExpr' usingArgs'- body' = mkTupleCase us to_bndrs body tup_n_var' tup_n_expr'+ body' = mkBigTupleCase us to_bndrs body tup_n_expr' ; dsSyntaxExpr bind_op [rhs', Lam n_tup_var' body'] } @@ -596,8 +593,8 @@ -- \x. case x of (a,b,c) -> body matchTuple ids body = do { us <- newUniqueSupply- ; tup_id <- newSysLocalDs Many (mkBigCoreVarTupTy ids)- ; return (Lam tup_id $ mkTupleCase us ids body tup_id (Var tup_id)) }+ ; tup_id <- newSysLocalDs ManyTy (mkBigCoreVarTupTy ids)+ ; return (Lam tup_id $ mkBigTupleCase us ids body (Var tup_id)) } -- general `rhs' >>= \pat -> stmts` desugaring where `rhs'` is already a -- desugared `CoreExpr`@@ -610,7 +607,7 @@ -> DsM CoreExpr dsMcBindStmt pat rhs' bind_op fail_op res1_ty stmts = do { body <- dsMcStmts stmts- ; var <- selectSimpleMatchVarL Many pat+ ; var <- selectSimpleMatchVarL ManyTy pat ; match <- matchSinglePatVar var Nothing (StmtCtxt (HsDoStmt (DoExpr Nothing))) pat res1_ty (cantFailMatchResult body) ; match_code <- dsHandleMonadicFailure MonadComp pat match fail_op@@ -651,15 +648,15 @@ mkMcUnzipM _ fmap_op ys elt_tys = do { fmap_op' <- dsExpr fmap_op- ; xs <- mapM (newSysLocalDs Many) elt_tys+ ; xs <- mapM (newSysLocalDs ManyTy) elt_tys ; let tup_ty = mkBigCoreTupTy elt_tys- ; tup_xs <- newSysLocalDs Many tup_ty+ ; tup_xs <- newSysLocalDs ManyTy tup_ty ; let mk_elt i = mkApps fmap_op' -- fmap :: forall a b. (a -> b) -> n a -> n b [ Type tup_ty, Type (getNth elt_tys i) , mk_sel i, Var ys] mk_sel n = Lam tup_xs $- mkTupleSelector xs (getNth xs n) tup_xs (Var tup_xs)+ mkBigTupleSelector xs (getNth xs n) tup_xs (Var tup_xs) ; return (mkBigCoreTup (map mk_elt [0..length elt_tys - 1])) }
compiler/GHC/HsToCore/Match.hs view
@@ -6,7 +6,6 @@ {-# LANGUAGE ViewPatterns #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}-{-# OPTIONS_GHC -Wno-incomplete-record-updates #-} {- (c) The University of Glasgow 2006@@ -25,9 +24,11 @@ import GHC.Prelude import GHC.Platform +import Language.Haskell.Syntax.Basic (Boxity(..))+ import {-#SOURCE#-} GHC.HsToCore.Expr (dsExpr) -import GHC.Types.Basic ( Origin(..), isGenerated, Boxity(..) )+import GHC.Types.Basic ( Origin(..), isGenerated ) import GHC.Types.SourceText import GHC.Driver.Session import GHC.Hs@@ -36,27 +37,31 @@ import GHC.Tc.Utils.Monad import GHC.HsToCore.Pmc import GHC.HsToCore.Pmc.Types ( Nablas, initNablas )-import GHC.Core-import GHC.Types.Literal-import GHC.Core.Utils-import GHC.Core.Make import GHC.HsToCore.Monad import GHC.HsToCore.Binds import GHC.HsToCore.GuardedRHSs import GHC.HsToCore.Utils-import GHC.Types.Id-import GHC.Core.ConLike-import GHC.Core.DataCon-import GHC.Core.PatSyn import GHC.HsToCore.Errors.Types import GHC.HsToCore.Match.Constructor import GHC.HsToCore.Match.Literal++import GHC.Core+import GHC.Core.Utils+import GHC.Core.Make+import GHC.Core.ConLike+import GHC.Core.DataCon+import GHC.Core.PatSyn import GHC.Core.Type+import GHC.Core.TyCo.Compare( eqType, eqTypes ) import GHC.Core.Coercion ( eqCoercion ) import GHC.Core.TyCon ( isNewTyCon ) import GHC.Core.Multiplicity import GHC.Builtin.Types++import GHC.Types.Id+import GHC.Types.Literal import GHC.Types.SrcLoc+ import GHC.Data.Maybe import GHC.Utils.Misc import GHC.Types.Name@@ -430,7 +435,7 @@ -- case v of { x@p -> mr[] } -- = case v of { p -> let x=v in mr[] }-tidy1 v o (AsPat _ (L _ var) pat)+tidy1 v o (AsPat _ (L _ var) _ pat) = do { (wrap, pat') <- tidy1 v o (unLoc pat) ; return (wrapBind var v . wrap, pat') } @@ -517,8 +522,8 @@ -- Push the bang-pattern inwards, in the hope that -- it may disappear next time-tidy_bang_pat v o l (AsPat x v' p)- = tidy1 v o (AsPat x v' (L l (BangPat noExtField p)))+tidy_bang_pat v o l (AsPat x v' at p)+ = tidy1 v o (AsPat x v' at (L l (BangPat noExtField p))) tidy_bang_pat v o l (XPat (CoPat w p t)) = tidy1 v o (XPat $ CoPat w (BangPat noExtField (L l p)) t) @@ -751,7 +756,7 @@ it creates another equation if the match can fail (see @GHC.HsToCore.Expr.doDo@ function) \item @let@ patterns, are treated by @matchSimply@- List Comprension Patterns, are treated by @matchSimply@ also+ List Comprehension Patterns, are treated by @matchSimply@ also \end{itemize} We can't call @matchSimply@ with Lambda patterns,@@ -762,8 +767,8 @@ -} matchWrapper ctxt scrs (MG { mg_alts = L _ matches- , mg_ext = MatchGroupTc arg_tys rhs_ty- , mg_origin = origin })+ , mg_ext = MatchGroupTc arg_tys rhs_ty origin+ }) = do { dflags <- getDynFlags ; locn <- getSrcSpanDs @@ -869,7 +874,7 @@ = matchSinglePatVar var Nothing ctx pat ty match_result matchSinglePat scrut hs_ctx pat ty match_result- = do { var <- selectSimpleMatchVarL Many pat+ = do { var <- selectSimpleMatchVarL ManyTy pat -- matchSinglePat is only used in matchSimply, which -- is used in list comprehension, arrow notation, -- and to create field selectors. All of which only
compiler/GHC/HsToCore/Match/Constructor.hs view
@@ -36,8 +36,8 @@ import GHC.Utils.Panic import GHC.Utils.Panic.Plain import Control.Monad(liftM)-import Data.List (groupBy) import Data.List.NonEmpty (NonEmpty(..))+import qualified Data.List.NonEmpty as NE {- We are confronted with the first column of patterns in a set of@@ -143,13 +143,13 @@ -- and returns the types of the *value* args, which is what we want match_group :: [Id]- -> [(ConArgPats, EquationInfo)] -> DsM (MatchResult CoreExpr)+ -> NonEmpty (ConArgPats, EquationInfo)+ -> DsM (MatchResult CoreExpr) -- All members of the group have compatible ConArgPats match_group arg_vars arg_eqn_prs- = assert (notNull arg_eqn_prs) $- do { (wraps, eqns') <- liftM unzip (mapM shift arg_eqn_prs)+ = do { (wraps, eqns') <- liftM NE.unzip (mapM shift arg_eqn_prs) ; let group_arg_vars = select_arg_vars arg_vars arg_eqn_prs- ; match_result <- match (group_arg_vars ++ vars) ty eqns'+ ; match_result <- match (group_arg_vars ++ vars) ty (NE.toList eqns') ; return $ foldr1 (.) wraps <$> match_result } @@ -184,9 +184,9 @@ -- suggestions for the new variables -- Divide into sub-groups; see Note [Record patterns]- ; let groups :: [[(ConArgPats, EquationInfo)]]- groups = groupBy compatible_pats [ (pat_args (firstPat eqn), eqn)- | eqn <- eqn1:eqns ]+ ; let groups :: NonEmpty (NonEmpty (ConArgPats, EquationInfo))+ groups = NE.groupBy1 compatible_pats+ $ fmap (\eqn -> (pat_args (firstPat eqn), eqn)) (eqn1 :| eqns) ; match_results <- mapM (match_group arg_vars) groups @@ -210,8 +210,8 @@ -- Choose the right arg_vars in the right order for this group -- Note [Record patterns]- select_arg_vars :: [Id] -> [(ConArgPats, EquationInfo)] -> [Id]- select_arg_vars arg_vars ((arg_pats, _) : _)+ select_arg_vars :: [Id] -> NonEmpty (ConArgPats, EquationInfo) -> [Id]+ select_arg_vars arg_vars ((arg_pats, _) :| _) | RecCon flds <- arg_pats , let rpats = rec_flds flds , not (null rpats) -- Treated specially; cf conArgPats@@ -224,7 +224,6 @@ fld_var_env = mkNameEnv $ zipEqual "get_arg_vars" fields1 arg_vars lookup_fld (L _ rpat) = lookupNameEnv_NF fld_var_env (idName (hsRecFieldId rpat))- select_arg_vars _ [] = panic "matchOneCon/select_arg_vars []" ----------------- compatible_pats :: (ConArgPats,a) -> (ConArgPats,a) -> Bool
compiler/GHC/HsToCore/Match/Literal.hs view
@@ -36,29 +36,36 @@ import GHC.Hs -import GHC.Types.Id-import GHC.Types.SourceText+import GHC.Tc.Utils.Zonk ( shortCutLit )+import GHC.Tc.Utils.TcType+ import GHC.Core import GHC.Core.Make import GHC.Core.TyCon import GHC.Core.Reduction ( Reduction(..) ) import GHC.Core.DataCon-import GHC.Tc.Utils.Zonk ( shortCutLit )-import GHC.Tc.Utils.TcType-import GHC.Types.Name import GHC.Core.Type+import GHC.Core.FamInstEnv ( FamInstEnvs, normaliseType )++import GHC.Types.Name+import GHC.Types.Literal+import GHC.Types.SrcLoc+ import GHC.Builtin.Names import GHC.Builtin.Types import GHC.Builtin.Types.Prim-import GHC.Types.Literal-import GHC.Types.SrcLoc-import GHC.Utils.Outputable as Outputable++import GHC.Types.Id+import GHC.Types.SourceText+ import GHC.Driver.Session++import GHC.Utils.Outputable as Outputable import GHC.Utils.Misc import GHC.Utils.Panic import GHC.Utils.Panic.Plain+ import GHC.Data.FastString-import GHC.Core.FamInstEnv ( FamInstEnvs, normaliseType ) import Control.Monad import Data.Int@@ -118,7 +125,7 @@ There is a fun wrinkle to this, we used to simply compute the value for these literals and store it as `Rational`. While this might seem reasonable it meant typechecking literals of extremely large numbers-wasn't possible. This happend for example in #15646.+wasn't possible. This happened for example in #15646. There a user would write in GHCi e.g. `:t 1e1234111111111111111111111` which would trip up the compiler. The reason being we would parse it as@@ -265,7 +272,7 @@ warnAboutIdentities dflags conv_fn type_of_conv | wopt Opt_WarnIdentities dflags , idName conv_fn `elem` conversionNames- , Just (_, arg_ty, res_ty) <- splitFunTy_maybe type_of_conv+ , Just (_, _, arg_ty, res_ty) <- splitFunTy_maybe type_of_conv , arg_ty `eqType` res_ty -- So we are converting ty -> ty = diagnosticDs (DsIdentitiesFound conv_fn type_of_conv) warnAboutIdentities _ _ _ = return ()
compiler/GHC/HsToCore/Monad.hs view
@@ -23,7 +23,7 @@ newSysLocalsDs, newUniqueId, newFailLocalDs, newPredVarDs, getSrcSpanDs, putSrcSpanDs, putSrcSpanDsA,- mkPrintUnqualifiedDs,+ mkNamePprCtxDs, newUnique, UniqSupply, newUniqueSupply, getGhcModeDs, dsGetFamInstEnvs,@@ -167,11 +167,11 @@ | MR_Fallible (CoreExpr -> DsM a) deriving (Functor) --- | Product is an "or" on falliblity---the combined match result is infallible+-- | Product is an "or" on fallibility---the combined match result is infallible -- only if the left and right argument match results both were. -- -- This is useful for combining a bunch of alternatives together and then--- getting the overall falliblity of the entire group. See 'mkDataConCase' for+-- getting the overall fallibility of the entire group. See 'mkDataConCase' for -- an example. instance Applicative MatchResult where pure v = MR_Infallible (pure v)@@ -199,7 +199,7 @@ type DsWarning = (SrcSpan, SDoc) -- Not quite the same as a WarnMsg, we have an SDoc here- -- and we'll do the print_unqual stuff later on to turn it+ -- and we'll do the name_ppr_ctx stuff later on to turn it -- into a Doc. -- | Run a 'DsM' action inside the 'TcM' monad.@@ -237,12 +237,13 @@ type_env = tcg_type_env tcg_env rdr_env = tcg_rdr_env tcg_env fam_inst_env = tcg_fam_inst_env tcg_env+ ptc = initPromotionTickContext (hsc_dflags hsc_env) complete_matches = hptCompleteSigs hsc_env -- from the home package ++ tcg_complete_matches tcg_env -- from the current module ++ eps_complete_matches eps -- from imports -- re-use existing next_wrapper_num to ensure uniqueness next_wrapper_num_var = tcg_next_wrapper_num tcg_env- ; return $ mkDsEnvs unit_env this_mod rdr_env type_env fam_inst_env+ ; return $ mkDsEnvs unit_env this_mod rdr_env type_env fam_inst_env ptc msg_var cc_st_var next_wrapper_num_var complete_matches } @@ -272,6 +273,7 @@ ; eps <- liftIO $ hscEPS hsc_env ; let unit_env = hsc_unit_env hsc_env type_env = typeEnvFromEntities ids tycons patsyns fam_insts+ ptc = initPromotionTickContext (hsc_dflags hsc_env) complete_matches = hptCompleteSigs hsc_env -- from the home package ++ local_complete_matches -- from the current module ++ eps_complete_matches eps -- from imports@@ -281,7 +283,7 @@ ids = concatMap bindsToIds binds envs = mkDsEnvs unit_env this_mod rdr_env type_env- fam_inst_env msg_var cc_st_var+ fam_inst_env ptc msg_var cc_st_var next_wrapper_num complete_matches ; runDs hsc_env envs thing_inside }@@ -317,13 +319,14 @@ thing_inside ; case mb_ret of Just ret -> pure ret- Nothing -> pprPanic "initTcDsForSolver" (vcat $ pprMsgEnvelopeBagWithLoc (getErrorMessages msgs)) }+ Nothing -> pprPanic "initTcDsForSolver" (vcat $ pprMsgEnvelopeBagWithLocDefault (getErrorMessages msgs)) } mkDsEnvs :: UnitEnv -> Module -> GlobalRdrEnv -> TypeEnv -> FamInstEnv+ -> PromotionTickContext -> IORef (Messages DsMessage) -> IORef CostCentreState -> IORef (ModuleEnv Int) -> CompleteMatches -> (DsGblEnv, DsLclEnv)-mkDsEnvs unit_env mod rdr_env type_env fam_inst_env msg_var cc_st_var+mkDsEnvs unit_env mod rdr_env type_env fam_inst_env ptc msg_var cc_st_var next_wrapper_num complete_matches = let if_genv = IfGblEnv { if_doc = text "mkDsEnvs" -- Failing tests here are `ghci` and `T11985` if you get this wrong.@@ -340,7 +343,7 @@ , ds_fam_inst_env = fam_inst_env , ds_gbl_rdr_env = rdr_env , ds_if_env = (if_genv, if_lenv)- , ds_unqual = mkPrintUnqualified unit_env rdr_env+ , ds_name_ppr_ctx = mkNamePprCtx ptc unit_env rdr_env , ds_msgs = msg_var , ds_complete_matches = complete_matches , ds_cc_st = cc_st_var@@ -378,7 +381,7 @@ newPredVarDs :: PredType -> DsM Var newPredVarDs- = mkSysLocalOrCoVarM (fsLit "ds") Many -- like newSysLocalDs, but we allow covars+ = mkSysLocalOrCoVarM (fsLit "ds") ManyTy -- like newSysLocalDs, but we allow covars newSysLocalDs, newFailLocalDs :: Mult -> Type -> DsM Id newSysLocalDs = mkSysLocalM (fsLit "ds")@@ -425,7 +428,7 @@ = do { env <- getGblEnv ; loc <- getSrcSpanDs ; !diag_opts <- initDiagOpts <$> getDynFlags- ; let msg = mkMsgEnvelope diag_opts loc (ds_unqual env) dsMessage+ ; let msg = mkMsgEnvelope diag_opts loc (ds_name_ppr_ctx env) dsMessage ; updMutVar (ds_msgs env) (\ msgs -> msg `addMessage` msgs) } -- | Issue an error, but return the expression for (), so that we can continue@@ -443,8 +446,8 @@ failDs :: DsM a failDs = failM -mkPrintUnqualifiedDs :: DsM PrintUnqualified-mkPrintUnqualifiedDs = ds_unqual <$> getGblEnv+mkNamePprCtxDs :: DsM NamePprCtx+mkNamePprCtxDs = ds_name_ppr_ctx <$> getGblEnv instance MonadThings (IOEnv (Env DsGblEnv DsLclEnv)) where lookupThing = dsLookupGlobal
compiler/GHC/HsToCore/Pmc.hs view
@@ -318,7 +318,7 @@ -- * Formatting and reporting warnings -- --- | A datatype to accomodate the different call sites of+-- | A datatype to accommodate the different call sites of -- 'formatReportWarnings'. Used for extracting 'CIRB's from a concrete 'ann' -- through 'collectInMode'. Since this is only possible for a couple of -- well-known 'ann's, this is a GADT.
compiler/GHC/HsToCore/Pmc/Desugar.hs view
@@ -38,10 +38,11 @@ import GHC.Tc.Types.Evidence (HsWrapper(..), isIdHsWrapper) import {-# SOURCE #-} GHC.HsToCore.Expr (dsExpr, dsLExpr, dsSyntaxExpr) import {-# SOURCE #-} GHC.HsToCore.Binds (dsHsWrapper)-import GHC.HsToCore.Utils (isTrueLHsExpr, selectMatchVar)+import GHC.HsToCore.Utils (isTrueLHsExpr, selectMatchVar, decideBangHood) import GHC.HsToCore.Match.Literal (dsLit, dsOverLit) import GHC.HsToCore.Monad import GHC.Core.TyCo.Rep+import GHC.Core.TyCo.Compare( eqType ) import GHC.Core.Type import GHC.Data.Maybe import qualified GHC.LanguageExtensions as LangExt@@ -119,7 +120,7 @@ -- (x@pat) ==> Desugar pat with x as match var and handle impedance -- mismatch with incoming match var- AsPat _ (L _ y) p -> (mkPmLetVar y x ++) <$> desugarLPat y p+ AsPat _ (L _ y) _ p -> (mkPmLetVar y x ++) <$> desugarLPat y p SigPat _ p _ty -> desugarLPat x p @@ -139,7 +140,8 @@ ListPat {} | ViewPat arg_ty _lexpr pat <- expansion , not (xopt LangExt.RebindableSyntax dflags)- , Just _ <- splitListTyConApp_maybe arg_ty+ , Just tc <- tyConAppTyCon_maybe arg_ty+ , tc == listTyCon -> desugarLPat x pat _ -> desugarPat x expansion@@ -247,7 +249,7 @@ -- | 'desugarPat', but also select and return a new match var. desugarPatV :: Pat GhcTc -> DsM (Id, [PmGrd]) desugarPatV pat = do- x <- selectMatchVar Many pat+ x <- selectMatchVar ManyTy pat grds <- desugarPat x pat pure (x, grds) @@ -332,7 +334,10 @@ -- Desugar a single match desugarMatch :: [Id] -> LMatch GhcTc (LHsExpr GhcTc) -> DsM (PmMatch Pre) desugarMatch vars (L match_loc (Match { m_pats = pats, m_grhss = grhss })) = do- pats' <- concat <$> zipWithM desugarLPat vars pats+ dflags <- getDynFlags+ -- decideBangHood: See Note [Desugaring -XStrict matches in Pmc]+ let banged_pats = map (decideBangHood dflags) pats+ pats' <- concat <$> zipWithM desugarLPat vars banged_pats grhss' <- desugarGRHSs (locA match_loc) (sep (map ppr pats)) grhss -- tracePm "desugarMatch" (vcat [ppr pats, ppr pats', ppr grhss']) return PmMatch { pm_pats = GrdVec pats', pm_grhss = grhss' }@@ -487,7 +492,7 @@ instances such as @Sing (a :: Bool)@ are a good example of this: If we would just drop the coercion, we'd get a type error when matching @pat@ against its value abstraction, with the result being that pmIsSatisfiable decides that every-possible data constructor fitting @pat@ is rejected as uninhabitated, leading to+possible data constructor fitting @pat@ is rejected as uninhabited, leading to a lot of false warnings. But we can check whether the coercion is a hole or if it is just refl, in@@ -531,4 +536,30 @@ The place to store the 'PmLet' guards for @where@ clauses (which are per 'GRHSs') is as a field of 'PmGRHSs'. For plain @let@ guards as in the guards of @x@, we can simply add them to the 'pg_grds' field of 'PmGRHS'.++Note [Desugaring -XStrict matches in Pmc]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider (#21761)++ {-# LANGUAGE Strict #-}+ idV :: Void -> Void+ idV v = v++Without -XStrict, we would not warn here. But with -XStrict, there is an+implicit bang on `v` and we should give an inaccessible warning for the RHS.+The way we account for that is by calling `decideBangHood` on patterns+in a `Match`, which inserts the implicit bang.++Making the call here actually seems redundant with the call to `decideBangHood`+in `GHC.HsToCore.Match.matchWrapper`, which does it *after* it calls the+pattern-match checker on the Match's patterns. It would be great if we could expect+`matchWrapper` to pass the bang-adorned `Match` to the pattern-match checker,+but sadly then we get worse warning messages which would print `idV` as if the+user *had* written a bang:++ Pattern match has inaccessible right hand side+- In an equation for ‘idV’: idV v = ...++ In an equation for ‘idV’: idV !v = ...++So we live with the duplication. -}
compiler/GHC/HsToCore/Pmc/Solver.hs view
@@ -1,4 +1,3 @@- {-# LANGUAGE LambdaCase #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-}@@ -46,6 +45,7 @@ import GHC.Utils.Panic import GHC.Utils.Panic.Plain import GHC.Data.Bag+ import GHC.Types.Basic (Levity(..)) import GHC.Types.CompleteMatch import GHC.Types.Unique.Set@@ -56,13 +56,17 @@ import GHC.Types.Var (EvVar) import GHC.Types.Var.Env import GHC.Types.Var.Set+import GHC.Types.Unique.Supply+ import GHC.Core-import GHC.Core.FVs (exprFreeVars)+import GHC.Core.FVs (exprFreeVars)+import GHC.Core.TyCo.Compare( eqType ) import GHC.Core.Map.Expr+import GHC.Core.Predicate (typeDeterminesValue) import GHC.Core.SimpleOpt (simpleOptExpr, exprIsConApp_maybe) import GHC.Core.Utils (exprType)-import GHC.Core.Make (mkListExpr, mkCharExpr)-import GHC.Types.Unique.Supply+import GHC.Core.Make (mkListExpr, mkCharExpr, mkImpossibleExpr)+ import GHC.Data.FastString import GHC.Types.SrcLoc import GHC.Data.Maybe@@ -73,9 +77,8 @@ import GHC.Core.TyCon.RecWalk import GHC.Builtin.Names import GHC.Builtin.Types-import GHC.Builtin.Types.Prim (tYPETyCon) import GHC.Core.TyCo.Rep-import GHC.Core.TyCo.Subst (elemTCvSubst)+import GHC.Core.TyCo.Subst (elemSubst) import GHC.Core.Type import GHC.Tc.Solver (tcNormalise, tcCheckGivens, tcCheckWanteds) import GHC.Core.Unify (tcMatchTy)@@ -97,9 +100,6 @@ import qualified Data.List.NonEmpty as NE import Data.Ord (comparing) -import GHC.Utils.Trace-_ = pprTrace -- to silence unused import warnings- -- -- * Main exports --@@ -147,11 +147,16 @@ -- Ex.: @vanillaCompleteMatchTC 'Maybe' ==> Just ("Maybe", {'Just','Nothing'})@ vanillaCompleteMatchTC :: TyCon -> Maybe CompleteMatch vanillaCompleteMatchTC tc =- let -- TYPE acts like an empty data type on the term-level (#14086), but- -- it is a PrimTyCon, so tyConDataCons_maybe returns Nothing. Hence a- -- special case.- mb_dcs | tc == tYPETyCon = Just []- | otherwise = tyConDataCons_maybe tc+ let mb_dcs | -- TYPE acts like an empty data type on the term level (#14086),+ -- but it is a PrimTyCon, so tyConDataCons_maybe returns Nothing.+ -- Hence a special case.+ tc == tYPETyCon = Just []+ | -- Similarly, treat `type data` declarations as empty data types on+ -- the term level, as `type data` data constructors only exist at+ -- the type level (#22964).+ -- See Note [Type data declarations] in GHC.Rename.Module.+ isTypeDataTyCon tc = Just []+ | otherwise = tyConDataCons_maybe tc in vanillaCompleteMatch . mkUniqDSet . map RealDataCon <$> mb_dcs -- | Initialise from 'dsGetCompleteMatches' (containing all COMPLETE pragmas)@@ -409,7 +414,7 @@ -- (See "Type#type_classification" for what an algebraic type is.) -- -- This is qualified with \"like\" because of a particular special- -- case: TYPE (the underlyind kind behind Type, among others). TYPE+ -- case: TYPE (the underlying kind behind Type, among others). TYPE -- is conceptually a datatype (and thus algebraic), but in practice it is -- a primitive builtin type, so we must check for it specially. --@@ -642,7 +647,7 @@ unique <- getUniqueM let occname = mkVarOccFS (fsLit ("pm_"++show unique)) idname = mkInternalName unique occname noSrcSpan- return (mkLocalIdOrCoVar idname Many pred_ty)+ return (mkLocalIdOrCoVar idname ManyTy pred_ty) ----------------------------- -- ** Adding term constraints@@ -881,7 +886,7 @@ where expr_ty = exprType e expr_in_scope = mkInScopeSet (exprFreeVars e)- in_scope_env = (expr_in_scope, const NoUnfolding)+ in_scope_env = ISE expr_in_scope noUnfoldingFun -- It's inconvenient to get hold of a global in-scope set -- here, but it'll only be needed if exprIsConApp_maybe ends -- up substituting inside a forall or lambda (i.e. seldom)@@ -918,7 +923,7 @@ ex_tys = map exprToType ex_ty_args vis_args = reverse $ take arty $ reverse val_args uniq_supply <- lift $ lift $ getUniqueSupplyM- let (_, ex_tvs) = cloneTyVarBndrs (mkEmptyTCvSubst in_scope) dc_ex_tvs uniq_supply+ let (_, ex_tvs) = cloneTyVarBndrs (mkEmptySubst in_scope) dc_ex_tvs uniq_supply ty_cts = equateTys (map mkTyVarTy ex_tvs) ex_tys -- 1. @x ≁ ⊥@ if 'K' is not a Newtype constructor (#18341) when (not (isNewDataCon dc)) $@@ -942,23 +947,122 @@ pm_alt_con_app :: Id -> PmAltCon -> [TyVar] -> [Id] -> StateT Nabla (MaybeT DsM) () pm_alt_con_app x con tvs args = modifyT $ \nabla -> addConCt nabla x con tvs args +-- | Like 'modify', but with an effectful modifier action+modifyT :: Monad m => (s -> m s) -> StateT s m ()+modifyT f = StateT $ fmap ((,) ()) . f+ -- | Finds a representant of the semantic equality class of the given @e@. -- Which is the @x@ of a @let x = e'@ constraint (with @e@ semantically -- equivalent to @e'@) we encountered earlier, or a fresh identifier if -- there weren't any such constraints. representCoreExpr :: Nabla -> CoreExpr -> DsM (Id, Nabla) representCoreExpr nabla@MkNabla{ nabla_tm_st = ts@TmSt{ ts_reps = reps } } e- | Just rep <- lookupCoreMap reps e = pure (rep, nabla)+ | Just rep <- lookupCoreMap reps key = pure (rep, nabla) | otherwise = do rep <- mkPmId (exprType e)- let reps' = extendCoreMap reps e rep+ let reps' = extendCoreMap reps key rep let nabla' = nabla{ nabla_tm_st = ts{ ts_reps = reps' } } pure (rep, nabla')+ where+ key = makeDictsCoherent e+ -- Use a key in which dictionaries for the same type become equal.+ -- See Note [Unique dictionaries in the TmOracle CoreMap] --- | Like 'modify', but with an effectful modifier action-modifyT :: Monad m => (s -> m s) -> StateT s m ()-modifyT f = StateT $ fmap ((,) ()) . f+-- | Change out 'Id's which are uniquely determined by their type to a+-- common value, so that different names for dictionaries of the same type+-- are considered equal when building a 'CoreMap'.+--+-- See Note [Unique dictionaries in the TmOracle CoreMap]+makeDictsCoherent :: CoreExpr -> CoreExpr+makeDictsCoherent var@(Var v)+ | let ty = idType v+ , typeDeterminesValue ty+ = mkImpossibleExpr ty "Solver.makeDictsCoherent"+ | otherwise+ = var+makeDictsCoherent lit@(Lit {})+ = lit+makeDictsCoherent (App f a)+ = App (makeDictsCoherent f) (makeDictsCoherent a)+makeDictsCoherent (Lam f body)+ = Lam f (makeDictsCoherent body)+makeDictsCoherent (Let bndr body)+ = Let+ (go_bndr bndr)+ (makeDictsCoherent body)+ where+ go_bndr (NonRec bndr expr) = NonRec bndr (makeDictsCoherent expr)+ go_bndr (Rec bndrs) = Rec (map ( \(b, expr) -> (b, makeDictsCoherent expr) ) bndrs)+makeDictsCoherent (Case scrut bndr ty alts)+ = Case scrut bndr ty+ [ Alt con bndr expr'+ | Alt con bndr expr <- alts+ , let expr' = makeDictsCoherent expr ]+makeDictsCoherent (Cast expr co)+ = Cast (makeDictsCoherent expr) co+makeDictsCoherent (Tick tick expr)+ = Tick tick (makeDictsCoherent expr)+makeDictsCoherent ty@(Type {})+ = ty+makeDictsCoherent co@(Coercion {})+ = co +{- Note [Unique dictionaries in the TmOracle CoreMap]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Any two dictionaries for a coherent typeclass should be considered equal+in the TmOracle CoreMap, as this allows us to report better pattern-match+warnings.++Consider for example T21662:++ view_fn :: forall (n :: Nat). KnownNat n => Int -> Bool++ foo :: Int -> Int+ foo (view_fn @12 -> True ) = 0+ foo (view_fn @12 -> False) = 1++In this example, the pattern match is exhaustive because we have covered+the range of the view pattern function. However, we may fail to recognise+the fact that the two cases use the same view function if the KnownNat+dictionaries aren't syntactically equal:++ eqn 1: [let ds_d1p0 = view_fn @12 $dKnownNat_a1ny ds_d1oR, True <- ds_d1p0]+ eqn 2: [let ds_d1p6 = view_fn @12 $dKnownNat_a1nC ds_d1oR, False <- ds_d1p6]++Note that the uniques of the KnownNat 12 dictionary differ. If we fail to utilise+the coherence of the KnownNat constraint, then we have to pessimistically assume+that we have two function calls with different arguments:++ foo (fn arg1 -> True ) = ...+ foo (fn arg2 -> False) = ...++In this case we can't determine whether the pattern matches are complete, so we+emit a pattern match warning.++Solution: replace all 'Id's whose type uniquely determines its value with+a common value, e.g. in the above example we would replace both+$dKnownNat_a1ny and $dKnownNat_a1nC with error @(KnownNat 12).++Why did we choose this solution? Here are some alternatives that were considered:++ 1. Perform CSE first. This would common up the dictionaries before we compare+ using the CoreMap.+ However, this is architecturally difficult as it would require threading+ a CSEnv through to desugarPat.+ 2. Directly modify CoreMap so that any two dictionaries of the same type are+ considered equal.+ The problem is that this affects all users of CoreMap. For example, CSE+ would now assume that any two dictionaries of the same type are equal,+ but this isn't necessarily true in the presence of magicDict, which+ violates coherence by design. It seems more prudent to limit the changes+ to the pattern-match checker only, to avoid undesirable consequences.++In the end, replacing dictionaries with an error value in the pattern-match+checker was the most self-contained, although we might want to revisit once+we implement a more robust approach to computing equality in the pattern-match+checker (see #19272).+-}+ {- Note [The Pos/Neg invariant] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Invariant applying to each VarInfo: Whenever we have @C @tvs args@ in 'vi_pos',@@ -968,7 +1072,7 @@ * @x ~ Just y@, @x ≁ [Just]@. 'eqPmAltCon' returns @Equal@, so refute. * @x ~ Nothing@, @x ≁ [Just]@. 'eqPmAltCon' returns @Disjoint@, so negative info is redundant and should be discarded.-* @x ~ I# y@, @x ≁ [4,2]@. 'eqPmAltCon' returns @PossiblyOverlap@, so orthogal.+* @x ~ I# y@, @x ≁ [4,2]@. 'eqPmAltCon' returns @PossiblyOverlap@, so orthogonal. We keep this info in order to be able to refute a redundant match on i.e. 4 later on. @@ -1245,7 +1349,7 @@ -- Internally uses and updates the CompleteMatchs in vi_rcm. -- -- NB: Does /not/ filter each CompleteMatch with the oracle; members may--- remain that do not statisfy it. This lazy approach just+-- remain that do not satisfy it. This lazy approach just -- avoids doing unnecessary work. instantiate :: Int -> Nabla -> VarInfo -> MaybeT DsM VarInfo instantiate fuel nabla vi = {-# SCC "instantiate" #-}@@ -1382,7 +1486,7 @@ compareConLikeTestability :: ConLike -> ConLike -> Ordering -- We should instantiate DataCons first, because they are likely to occur in -- multiple COMPLETE sets at once and we might find that multiple COMPLETE sets--- are inhabitated by instantiating only a single DataCon.+-- are inhabited by instantiating only a single DataCon. compareConLikeTestability PatSynCon{} _ = GT compareConLikeTestability _ PatSynCon{} = GT compareConLikeTestability (RealDataCon a) (RealDataCon b) = mconcat@@ -1477,7 +1581,7 @@ -- Make sure that @ty@ is normalised before. -- -- See Note [Matching against a ConLike result type].-matchConLikeResTy :: FamInstEnvs -> TyState -> Type -> ConLike -> DsM (Maybe TCvSubst)+matchConLikeResTy :: FamInstEnvs -> TyState -> Type -> ConLike -> DsM (Maybe Subst) matchConLikeResTy env _ ty (RealDataCon dc) = pure $ do (rep_tc, tc_args, _co) <- splitReprTyConApp_maybe env ty if rep_tc == dataConTyCon dc@@ -1486,7 +1590,7 @@ matchConLikeResTy _ (TySt _ inert) ty (PatSynCon ps) = {-# SCC "matchConLikeResTy" #-} runMaybeT $ do let (univ_tvs,req_theta,_,_,_,con_res_ty) = patSynSig ps subst <- MaybeT $ pure $ tcMatchTy con_res_ty ty- guard $ all (`elemTCvSubst` subst) univ_tvs -- See the Note about T11336b+ guard $ all (`elemSubst` subst) univ_tvs -- See the Note about T11336b if null req_theta then pure subst else do@@ -1521,7 +1625,7 @@ definition be flagged as inexhaustive (no false positives). Via the LYG algorithm, we reduce both these properties to a property on-the inhabitation test of refinementment types:+the inhabitation test of refinement types: *Soundness*: If the inhabitation test says "no" for a given refinement type Nabla, then it provably has no inhabitant. *Completeness*: If the inhabitation test says "yes" for a given refinement type@@ -1637,7 +1741,7 @@ check if `MkT` is inhabitable in each of those three fields, which in turn will require us to check if `MkT` is inhabitable again... As you can see, the branching factor adds up quickly, and if the initial fuel is, say,-100, then the inhabiation test will effectively take forever.+100, then the inhabitation test will effectively take forever. To mitigate this, we check the branching factor every time we are about to do inhabitation testing in 'instCon'. If the branching factor exceeds 1@@ -1738,7 +1842,7 @@ Note [Instantiating a ConLike] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-`instCon` implements the the \(Inst\) function from Figure 8 of the LYG paper.+`instCon` implements the \(Inst\) function from Figure 8 of the LYG paper. Given the following type of ConLike `K`
compiler/GHC/HsToCore/Pmc/Utils.hs view
@@ -35,8 +35,8 @@ tracePm :: String -> SDoc -> DsM () tracePm herald doc = do logger <- getLogger- printer <- mkPrintUnqualifiedDs- liftIO $ putDumpFileMaybe' logger printer+ name_ppr_ctx <- mkNamePprCtxDs+ liftIO $ putDumpFileMaybe' logger name_ppr_ctx Opt_D_dump_ec_trace "" FormatText (text herald $$ (nest 2 doc)) {-# INLINE tracePm #-} -- see Note [INLINE conditional tracing utilities] @@ -52,7 +52,7 @@ mkPmId ty = getUniqueM >>= \unique -> let occname = mkVarOccFS $ fsLit "pm" name = mkInternalName unique occname noSrcSpan- in return (mkLocalIdOrCoVar name Many ty)+ in return (mkLocalIdOrCoVar name ManyTy ty) {-# NOINLINE mkPmId #-} -- We'll CPR deeply, that should be enough -- | All warning flags that need to run the pattern match checker.
compiler/GHC/HsToCore/Quote.hs view
@@ -51,8 +51,8 @@ import GHC.Core.Class import GHC.Core.DataCon import GHC.Core.TyCon-import GHC.Core.Multiplicity ( pattern Many ) import GHC.Core+import GHC.Core.Type( pattern ManyTy, mkFunTy ) import GHC.Core.Make import GHC.Core.Utils @@ -89,6 +89,8 @@ import qualified GHC.LanguageExtensions as LangExt +import Language.Haskell.Syntax.Basic (FieldLabelString(..))+ import Data.ByteString ( unpack ) import Control.Monad import Data.List (sort, sortBy)@@ -96,6 +98,8 @@ import Data.Function import Control.Monad.Trans.Reader import Control.Monad.Trans.Class+import Data.Foldable ( toList )+import GHC.Types.Name.Reader (RdrName(..)) data MetaWrappers = MetaWrappers { -- Applies its argument to a type argument `m` and dictionary `Quote m`@@ -126,8 +130,9 @@ -- the expected type tyvars = dataConUserTyVarBinders (classDataCon cls) expected_ty = mkInvisForAllTys tyvars $- mkInvisFunTyMany (mkClassPred cls (mkTyVarTys (binderVars tyvars)))- (mkClassPred monad_cls (mkTyVarTys (binderVars tyvars)))+ mkFunTy invisArgConstraintLike ManyTy+ (mkClassPred cls (mkTyVarTys (binderVars tyvars)))+ (mkClassPred monad_cls (mkTyVarTys (binderVars tyvars))) massertPpr (idType monad_sel `eqType` expected_ty) (ppr monad_sel $$ ppr expected_ty) @@ -157,36 +162,32 @@ getPlatform = targetPlatform <$> getDynFlags ------------------------------------------------------------------------------dsBracket :: Maybe QuoteWrapper -- ^ This is Nothing only when we are dealing with a VarBr- -> HsQuote GhcRn -- See Note [The life cycle of a TH quotation]- -> [PendingTcSplice]- -> DsM CoreExpr+dsBracket :: HsBracketTc -> DsM CoreExpr -- See Note [Desugaring Brackets] -- Returns a CoreExpr of type (M TH.Exp) -- The quoted thing is parameterised over Name, even though it has -- been type checked. We don't want all those type decorations! -dsBracket wrap brack splices- = do_brack brack+dsBracket (HsBracketTc { hsb_wrap = mb_wrap, hsb_splices = splices, hsb_quote = quote })+ = case quote of+ VarBr _ _ n -> do { MkC e1 <- lookupOccDsM (unLoc n) ; return e1 }+ ExpBr _ e -> runOverloaded $ do { MkC e1 <- repLE e ; return e1 }+ PatBr _ p -> runOverloaded $ do { MkC p1 <- repTopP p ; return p1 }+ TypBr _ t -> runOverloaded $ do { MkC t1 <- repLTy t ; return t1 }+ DecBrG _ gp -> runOverloaded $ do { MkC ds1 <- repTopDs gp ; return ds1 }+ DecBrL {} -> panic "dsUntypedBracket: unexpected DecBrL" where- runOverloaded act = do+ Just wrap = mb_wrap -- Not used in VarBr case -- In the overloaded case we have to get given a wrapper, it is just- -- for variable quotations that there is no wrapper, because they+ -- the VarBr case that there is no wrapper, because they -- have a simple type.- mw <- mkMetaWrappers (expectJust "runOverloaded" wrap)- runReaderT (mapReaderT (dsExtendMetaEnv new_bit) act) mw + runOverloaded act = do { mw <- mkMetaWrappers wrap+ ; runReaderT (mapReaderT (dsExtendMetaEnv new_bit) act) mw }+ new_bit = mkNameEnv [(n, DsSplice (unLoc e)) | PendingTcSplice n e <- splices] - do_brack (VarBr _ _ n) = do { MkC e1 <- lookupOccDsM (unLoc n) ; return e1 }- do_brack (ExpBr _ e) = runOverloaded $ do { MkC e1 <- repLE e ; return e1 }- do_brack (PatBr _ p) = runOverloaded $ do { MkC p1 <- repTopP p ; return p1 }- do_brack (TypBr _ t) = runOverloaded $ do { MkC t1 <- repLTy t ; return t1 }- do_brack (DecBrG _ gp) = runOverloaded $ do { MkC ds1 <- repTopDs gp ; return ds1 }- do_brack (DecBrL {}) = panic "dsUntypedBracket: unexpected DecBrL"-- {- Note [Desugaring Brackets] ~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -461,7 +462,7 @@ repTyClD (L loc (SynDecl { tcdLName = tc, tcdTyVars = tvs, tcdRhs = rhs })) = do { tc1 <- lookupLOcc tc -- See Note [Binders and occurrences]- ; dec <- addTyClTyVarBinds tvs $ \bndrs ->+ ; dec <- addQTyVarBinds ReuseBoundNames tvs $ \bndrs -> repSynDecl tc1 bndrs rhs ; return (Just (locA loc, dec)) } @@ -469,7 +470,7 @@ , tcdTyVars = tvs , tcdDataDefn = defn })) = do { tc1 <- lookupLOcc tc -- See Note [Binders and occurrences]- ; dec <- addTyClTyVarBinds tvs $ \bndrs ->+ ; dec <- addQTyVarBinds ReuseBoundNames tvs $ \bndrs -> repDataDefn tc1 (Left bndrs) defn ; return (Just (locA loc, dec)) } @@ -478,7 +479,7 @@ tcdSigs = sigs, tcdMeths = meth_binds, tcdATs = ats, tcdATDefs = atds })) = do { cls1 <- lookupLOcc cls -- See Note [Binders and occurrences]- ; dec <- addQTyVarBinds tvs $ \bndrs ->+ ; dec <- addQTyVarBinds FreshNamesOnly tvs $ \bndrs -> do { cxt1 <- repLContext cxt -- See Note [Scoped type variables in quotes] ; (ss, sigs_binds) <- rep_meth_sigs_binds sigs meth_binds@@ -519,20 +520,19 @@ -> HsDataDefn GhcRn -> MetaM (Core (M TH.Dec)) repDataDefn tc opts- (HsDataDefn { dd_ND = new_or_data, dd_ctxt = cxt, dd_kindSig = ksig+ (HsDataDefn { dd_ctxt = cxt, dd_kindSig = ksig , dd_cons = cons, dd_derivs = mb_derivs }) = do { cxt1 <- repLContext cxt ; derivs1 <- repDerivs mb_derivs- ; case (new_or_data, cons) of- (NewType, [con]) -> do { con' <- repC con+ ; case cons of+ NewTypeCon con -> do { con' <- repC con ; ksig' <- repMaybeLTy ksig ; repNewtype cxt1 tc opts ksig' con' derivs1 }- (NewType, _) -> lift $ failWithDs (DsMultipleConForNewtype (getConNames $ unLoc $ head cons))- (DataType, _) -> do { ksig' <- repMaybeLTy ksig+ DataTypeCons type_data cons -> do { ksig' <- repMaybeLTy ksig ; consL <- mapM repC cons ; cons1 <- coreListM conTyConName consL- ; repData cxt1 tc opts ksig' cons1+ ; repData type_data cxt1 tc opts ksig' cons1 derivs1 } } @@ -550,14 +550,11 @@ , fdResultSig = L _ resultSig , fdInjectivityAnn = injectivity })) = do { tc1 <- lookupLOcc tc -- See Note [Binders and occurrences]- ; let mkHsQTvs :: [LHsTyVarBndr () GhcRn] -> LHsQTyVars GhcRn- mkHsQTvs tvs = HsQTvs { hsq_ext = []- , hsq_explicit = tvs }- resTyVar = case resultSig of- TyVarSig _ bndr -> mkHsQTvs [bndr]- _ -> mkHsQTvs []- ; dec <- addTyClTyVarBinds tvs $ \bndrs ->- addTyClTyVarBinds resTyVar $ \_ ->+ ; let resTyVar = case resultSig of+ TyVarSig _ bndr -> [hsLTyVarName bndr]+ _ -> []+ ; dec <- addQTyVarBinds ReuseBoundNames tvs $ \bndrs ->+ addSimpleTyVarBinds ReuseBoundNames resTyVar $ case info of ClosedTypeFamily Nothing -> notHandled (ThAbstractClosedTypeFamily decl)@@ -646,7 +643,7 @@ , cid_datafam_insts = adts , cid_overlap_mode = overlap })- = addSimpleTyVarBinds tvs $+ = addSimpleTyVarBinds FreshNamesOnly tvs $ -- We must bring the type variables into scope, so their -- occurrences don't fail, even though the binders don't -- appear in the resulting data structure@@ -673,7 +670,7 @@ repStandaloneDerivD (L loc (DerivDecl { deriv_strategy = strat , deriv_type = ty })) = do { dec <- repDerivStrategy strat $ \strat' ->- addSimpleTyVarBinds tvs $+ addSimpleTyVarBinds FreshNamesOnly tvs $ do { cxt' <- repLContext cxt ; inst_ty' <- repLTy inst_ty ; repDeriv strat' cxt' inst_ty' }@@ -739,14 +736,14 @@ repForD :: LForeignDecl GhcRn -> MetaM (SrcSpan, Core (M TH.Dec)) repForD (L loc (ForeignImport { fd_name = name, fd_sig_ty = typ- , fd_fi = CImport (L _ cc)- (L _ s) mch cis _ }))+ , fd_fi = CImport _ (L _ cc)+ (L _ s) mch cis })) = do MkC name' <- lookupLOcc name MkC typ' <- repHsSigType typ MkC cc' <- repCCallConv cc MkC s' <- repSafety s cis' <- conv_cimportspec cis- MkC str <- coreStringLit (static ++ chStr ++ cis')+ MkC str <- coreStringLit (mkFastString (static ++ chStr ++ cis')) dec <- rep2 forImpDName [cc', s', str, name', typ'] return (locA loc, dec) where@@ -805,22 +802,23 @@ repRuleD :: LRuleDecl GhcRn -> MetaM (SrcSpan, Core (M TH.Dec)) repRuleD (L loc (HsRule { rd_name = n , rd_act = act- , rd_tyvs = ty_bndrs+ , rd_tyvs = m_ty_bndrs , rd_tmvs = tm_bndrs , rd_lhs = lhs , rd_rhs = rhs }))- = do { rule <- addHsTyVarBinds (fromMaybe [] ty_bndrs) $ \ ex_bndrs ->+ = do { let ty_bndrs = fromMaybe [] m_ty_bndrs+ ; rule <- addHsTyVarBinds FreshNamesOnly ty_bndrs $ \ ex_bndrs -> do { let tm_bndr_names = concatMap ruleBndrNames tm_bndrs ; ss <- mkGenSyms tm_bndr_names ; rule <- addBinds ss $ do { elt_ty <- wrapName tyVarBndrUnitTyConName- ; ty_bndrs' <- return $ case ty_bndrs of+ ; ty_bndrs' <- return $ case m_ty_bndrs of Nothing -> coreNothing' (mkListTy elt_ty) Just _ -> coreJust' (mkListTy elt_ty) ex_bndrs ; tm_bndrs' <- repListM ruleBndrTyConName repRuleBndr tm_bndrs- ; n' <- coreStringLit $ unpackFS $ snd $ unLoc n+ ; n' <- coreStringLit $ unLoc n ; act' <- repPhases act ; lhs' <- repLE lhs ; rhs' <- repLE rhs@@ -844,7 +842,7 @@ ; rep2 typedRuleVarName [n', ty'] } repAnnD :: LAnnDecl GhcRn -> MetaM (SrcSpan, Core (M TH.Dec))-repAnnD (L loc (HsAnnotation _ _ ann_prov (L _ exp)))+repAnnD (L loc (HsAnnotation _ ann_prov (L _ exp))) = do { target <- repAnnProv ann_prov ; exp' <- repE exp ; dec <- repPragAnn target exp'@@ -879,7 +877,7 @@ , con_ex_tvs = con_tvs , con_mb_cxt = mcxt , con_args = args }))- = addHsTyVarBinds con_tvs $ \ ex_bndrs ->+ = addHsTyVarBinds FreshNamesOnly con_tvs $ \ ex_bndrs -> do { c' <- repH98DataCon con args ; ctxt' <- repMbContext mcxt ; if not is_existential && isNothing mcxt@@ -993,16 +991,16 @@ rep_sig (L loc (ClassOpSig _ is_deflt nms ty)) | is_deflt = mapM (rep_ty_sig defaultSigDName (locA loc) ty) nms | otherwise = mapM (rep_ty_sig sigDName (locA loc) ty) nms-rep_sig d@(L _ (IdSig {})) = pprPanic "rep_sig IdSig" (ppr d) rep_sig (L loc (FixSig _ fix_sig)) = rep_fix_d (locA loc) fix_sig rep_sig (L loc (InlineSig _ nm ispec))= rep_inline nm ispec (locA loc) rep_sig (L loc (SpecSig _ nm tys ispec)) = concatMapM (\t -> rep_specialise nm t ispec (locA loc)) tys-rep_sig (L loc (SpecInstSig _ _ ty)) = rep_specialiseInst ty (locA loc)+rep_sig (L loc (SpecInstSig _ ty)) = rep_specialiseInst ty (locA loc) rep_sig (L _ (MinimalSig {})) = notHandled ThMinimalPragmas rep_sig (L _ (SCCFunSig {})) = notHandled ThSCCPragmas-rep_sig (L loc (CompleteMatchSig _ _st cls mty))+rep_sig (L loc (CompleteMatchSig _ cls mty)) = rep_complete_sig cls mty (locA loc)+rep_sig d@(L _ (XSig {})) = pprPanic "rep_sig IdSig" (ppr d) -- Desugar the explicit type variable binders in an 'LHsSigType', making -- sure not to gensym them.@@ -1189,14 +1187,11 @@ elt_ty <- wrapName tyVarBndrUnitTyConName case outer_bndrs of HsOuterImplicit{hso_ximplicit = imp_tvs} ->- addTyClTyVarBinds (mk_qtvs imp_tvs []) $ \_th_exp_bndrs ->+ addSimpleTyVarBinds ReuseBoundNames imp_tvs $ thing_inside $ coreNothingList elt_ty HsOuterExplicit{hso_bndrs = exp_bndrs} ->- addTyClTyVarBinds (mk_qtvs [] exp_bndrs) $ \th_exp_bndrs ->+ addHsTyVarBinds FreshNamesOnly exp_bndrs $ \th_exp_bndrs -> thing_inside $ coreJustList elt_ty th_exp_bndrs- where- mk_qtvs imp_tvs exp_tvs = HsQTvs { hsq_ext = imp_tvs- , hsq_explicit = exp_tvs } addHsOuterSigTyVarBinds :: HsOuterSigTyVarBndrs GhcRn@@ -1205,9 +1200,9 @@ addHsOuterSigTyVarBinds outer_bndrs thing_inside = case outer_bndrs of HsOuterImplicit{hso_ximplicit = imp_tvs} -> do th_nil <- coreListM tyVarBndrSpecTyConName []- addSimpleTyVarBinds imp_tvs $ thing_inside th_nil+ addSimpleTyVarBinds FreshNamesOnly imp_tvs $ thing_inside th_nil HsOuterExplicit{hso_bndrs = exp_bndrs} ->- addHsTyVarBinds exp_bndrs thing_inside+ addHsTyVarBinds FreshNamesOnly exp_bndrs thing_inside -- | If a type implicitly quantifies its outermost type variables, return -- 'True' if the list of implicitly bound type variables is empty. If a type@@ -1231,69 +1226,86 @@ nullOuterExplicit (HsOuterImplicit{}) = True -- Vacuously true, as there is no outermost explicit quantification -addSimpleTyVarBinds :: [Name] -- the binders to be added+-- Do we want to generate fresh names for type variables+-- or reuse the ones that are already in scope?+data FreshOrReuse+ = FreshNamesOnly+ -- Generate fresh names for all type variables, regardless of existing+ -- variables in the MetaEnv.+ --+ -- This is the default strategy.++ | ReuseBoundNames+ -- Generate fresh names for type variables not in the MetaEnv.+ -- Where a name is already bound in the MetaEnv, use that existing binding;+ -- do not create a new one with a fresh name.+ --+ -- This is the strategy used for data/newtype declarations and type family+ -- instances, so that the nested type variables work right:+ --+ -- class C a where+ -- type W a b+ -- instance C (T a) where+ -- type W (T a) b = blah+ --+ -- The 'a' in the type instance is the one bound by the instance decl+ --+ -- Test cases: TH_reifyExplicitForAllFams T9081 T9199 T10811++mkGenSyms' :: FreshOrReuse -> [Name] -> MetaM [GenSymBind]+mkGenSyms' FreshNamesOnly names = mkGenSyms names+mkGenSyms' ReuseBoundNames names =+ -- Make fresh names for the ones that are not already in scope+ -- This makes things work for associated types+ do { env <- lift dsGetMetaEnv+ ; mkGenSyms (filterOut (`elemNameEnv` env) names) }++addSimpleTyVarBinds :: FreshOrReuse+ -> [Name] -- the binders to be added -> MetaM (Core (M a)) -- action in the ext env -> MetaM (Core (M a))-addSimpleTyVarBinds names thing_inside- = do { fresh_names <- mkGenSyms names+addSimpleTyVarBinds fresh_or_reuse names thing_inside+ = do { fresh_names <- mkGenSyms' fresh_or_reuse names ; term <- addBinds fresh_names thing_inside ; wrapGenSyms fresh_names term } addHsTyVarBinds :: forall flag flag' a. RepTV flag flag'- => [LHsTyVarBndr flag GhcRn] -- the binders to be added+ => FreshOrReuse+ -> [LHsTyVarBndr flag GhcRn] -- the binders to be added -> (Core [(M (TH.TyVarBndr flag'))] -> MetaM (Core (M a))) -- action in the ext env -> MetaM (Core (M a))-addHsTyVarBinds exp_tvs thing_inside- = do { fresh_exp_names <- mkGenSyms (hsLTyVarNames exp_tvs)+addHsTyVarBinds fresh_or_reuse exp_tvs thing_inside+ = do { fresh_exp_names <- mkGenSyms' fresh_or_reuse (hsLTyVarNames exp_tvs) ; term <- addBinds fresh_exp_names $ do { kbs <- repListM (tyVarBndrName @flag @flag') repTyVarBndr exp_tvs ; thing_inside kbs } ; wrapGenSyms fresh_exp_names term } -addQTyVarBinds :: LHsQTyVars GhcRn -- the binders to be added+addQTyVarBinds :: FreshOrReuse+ -> LHsQTyVars GhcRn -- the binders to be added -> (Core [(M (TH.TyVarBndr ()))] -> MetaM (Core (M a))) -- action in the ext env -> MetaM (Core (M a))-addQTyVarBinds (HsQTvs { hsq_ext = imp_tvs- , hsq_explicit = exp_tvs })- thing_inside- = addTyVarBinds exp_tvs imp_tvs thing_inside+addQTyVarBinds fresh_or_reuse qtvs thing_inside =+ let HsQTvs { hsq_ext = imp_tvs+ , hsq_explicit = exp_tvs }+ = qtvs+ in addTyVarBinds fresh_or_reuse exp_tvs imp_tvs thing_inside addTyVarBinds :: RepTV flag flag'- => [LHsTyVarBndr flag GhcRn] -- the binders to be added+ => FreshOrReuse+ -> [LHsTyVarBndr flag GhcRn] -- the binders to be added -> [Name] -> (Core [(M (TH.TyVarBndr flag'))] -> MetaM (Core (M a))) -- action in the ext env -> MetaM (Core (M a)) -- gensym a list of type variables and enter them into the meta environment; -- the computations passed as the second argument is executed in that extended -- meta environment and gets the *new* names on Core-level as an argument-addTyVarBinds exp_tvs imp_tvs thing_inside- = addSimpleTyVarBinds imp_tvs $- addHsTyVarBinds exp_tvs $+addTyVarBinds fresh_or_reuse exp_tvs imp_tvs thing_inside+ = addSimpleTyVarBinds fresh_or_reuse imp_tvs $+ addHsTyVarBinds fresh_or_reuse exp_tvs $ thing_inside -addTyClTyVarBinds :: LHsQTyVars GhcRn- -> (Core [(M (TH.TyVarBndr ()))] -> MetaM (Core (M a)))- -> MetaM (Core (M a))--- Used for data/newtype declarations, and family instances,--- so that the nested type variables work right--- instance C (T a) where--- type W (T a) = blah--- The 'a' in the type instance is the one bound by the instance decl-addTyClTyVarBinds tvs m- = do { let tv_names = hsAllLTyVarNames tvs- ; env <- lift $ dsGetMetaEnv- ; freshNames <- mkGenSyms (filterOut (`elemNameEnv` env) tv_names)- -- Make fresh names for the ones that are not already in scope- -- This makes things work for family declarations-- ; term <- addBinds freshNames $- do { kbs <- repListM tyVarBndrUnitTyConName repTyVarBndr- (hsQTvExplicit tvs)- ; m kbs }-- ; wrapGenSyms freshNames term }- -- | Represent a type variable binder repTyVarBndr :: RepTV flag flag' => LHsTyVarBndr flag GhcRn -> MetaM (Core (M (TH.TyVarBndr flag')))@@ -1342,7 +1354,7 @@ repForallT :: HsType GhcRn -> MetaM (Core (M TH.Type)) repForallT ty | (tvs, ctxt, tau) <- splitLHsSigmaTyInvis (noLocA ty)- = addHsTyVarBinds tvs $ \bndrs ->+ = addHsTyVarBinds FreshNamesOnly tvs $ \bndrs -> do { ctxt1 <- repLContext ctxt ; tau1 <- repLTy tau ; repTForall bndrs ctxt1 tau1 -- forall a. C a => {...}@@ -1353,16 +1365,16 @@ case tele of HsForAllInvis{} -> repForallT ty HsForAllVis { hsf_vis_bndrs = tvs } ->- addHsTyVarBinds tvs $ \bndrs ->+ addHsTyVarBinds FreshNamesOnly tvs $ \bndrs -> do body1 <- repLTy body repTForallVis bndrs body1 repTy ty@(HsQualTy {}) = repForallT ty repTy (HsTyVar _ _ (L _ n))- | isLiftedTypeKindTyConName n = repTStar+ | n `hasKey` liftedTypeKindTyConKey = repTStar | n `hasKey` constraintKindTyConKey = repTConstraint | n `hasKey` unrestrictedFunTyConKey = repArrowTyCon- | n `hasKey` funTyConKey = repMulArrowTyCon+ | n `hasKey` fUNTyConKey = repMulArrowTyCon | isTvOcc occ = do tv1 <- lookupOcc n repTvar tv1 | isDataOcc occ = do tc1 <- lookupOcc n@@ -1413,7 +1425,8 @@ t1 <- repLTy t k1 <- repLTy k repTSig t1 k1-repTy (HsSpliceTy _ splice) = repSplice splice+repTy (HsSpliceTy (HsUntypedSpliceNested n) _) = rep_splice n+repTy t@(HsSpliceTy (HsUntypedSpliceTop _ _) _) = pprPanic "repTy: top level splice" (ppr t) repTy (HsExplicitListTy _ _ tys) = do tys1 <- repLTys tys repTPromotedList tys1@@ -1432,7 +1445,7 @@ repTy ty = notHandled (ThExoticFormOfType ty) -repTyLit :: HsTyLit -> MetaM (Core (M TH.TyLit))+repTyLit :: HsTyLit (GhcPass p) -> MetaM (Core (M TH.TyLit)) repTyLit (HsNumTy _ i) = do platform <- getPlatform rep2 numTyLitName [mkIntegerExpr platform i]@@ -1460,13 +1473,8 @@ -- Splices ----------------------------------------------------------------------------- -repSplice :: HsSplice GhcRn -> MetaM (Core a) -- See Note [How brackets and nested splices are handled] in GHC.Tc.Gen.Splice -- We return a CoreExpr of any old type; the context should know-repSplice (HsTypedSplice _ _ n _) = rep_splice n-repSplice (HsUntypedSplice _ _ n _) = rep_splice n-repSplice (HsQuasiQuote _ n _ _ _) = rep_splice n-repSplice e@(HsSpliced {}) = pprPanic "repSplice" (ppr e) rep_splice :: Name -> MetaM (Core a) rep_splice splice_name@@ -1500,7 +1508,7 @@ Just (DsSplice e) -> do { e' <- lift $ dsExpr e ; return (MkC e') } } repE (HsIPVar _ n) = rep_implicit_param_name n >>= repImplicitParamVar-repE (HsOverLabel _ s) = repOverLabel s+repE (HsOverLabel _ _ s) = repOverLabel s repE (HsRecSel _ (FieldOcc x _)) = repE (HsVar noExtField (noLocA x)) @@ -1519,7 +1527,8 @@ ; core_ms <- coreListM matchTyConName ms' ; repLamCases core_ms } repE (HsApp _ x y) = do {a <- repLE x; b <- repLE y; repApp a b}-repE (HsAppType _ e t) = do { a <- repLE e+repE (HsAppType _ e _ t)+ = do { a <- repLE e ; s <- repLTy (hswc_body t) ; repAppType a s } @@ -1610,7 +1619,7 @@ panic "The impossible has happened!" repE (ExprWithTySig _ e wc_ty)- = addSimpleTyVarBinds (get_scoped_tvs_from_sig sig_ty) $+ = addSimpleTyVarBinds FreshNamesOnly (get_scoped_tvs_from_sig sig_ty) $ do { e1 <- repLE e ; t1 <- rep_ty_sig' sig_ty ; repSigExp e1 t1 }@@ -1634,16 +1643,17 @@ ds3 <- repLE e3 repFromThenTo ds1 ds2 ds3 -repE (HsSpliceE _ splice) = repSplice splice+repE (HsTypedSplice n _) = rep_splice n+repE (HsUntypedSplice (HsUntypedSpliceNested n) _) = rep_splice n+repE e@(HsUntypedSplice (HsUntypedSpliceTop _ _) _) = pprPanic "repE: top level splice" (ppr e) repE (HsStatic _ e) = repLE e >>= rep2 staticEName . (:[]) . unC repE (HsUnboundVar _ uv) = do- occ <- occNameLit uv- sname <- repNameS occ- repUnboundVar sname-repE (HsGetField _ e (L _ (DotFieldOcc _ (L _ f)))) = do+ name <- repRdrName uv+ repUnboundVar name+repE (HsGetField _ e (L _ (DotFieldOcc _ (L _ (FieldLabelString f))))) = do e1 <- repLE e repGetField e1 f-repE (HsProjection _ xs) = repProjection (fmap (unLoc . dfoLabel . unLoc) xs)+repE (HsProjection _ xs) = repProjection (fmap (field_label . unLoc . dfoLabel . unLoc) xs) repE (XExpr (HsExpanded orig_expr ds_expr)) = do { rebindable_on <- lift $ xoptM LangExt.RebindableSyntax ; if rebindable_on -- See Note [Quotation and rebindable syntax]@@ -1864,7 +1874,7 @@ ; return (locA loc, ipb) } rep_implicit_param_name :: HsIPName -> MetaM (Core String)-rep_implicit_param_name (HsIPName name) = coreStringLit (unpackFS name)+rep_implicit_param_name (HsIPName name) = coreStringLit name rep_val_binds :: HsValBinds GhcRn -> MetaM [(SrcSpan, Core (M TH.Dec))] -- Assumes: all the binders of the binding are already in the meta-env@@ -2061,7 +2071,7 @@ repP (VarPat _ x) = do { x' <- lookupBinder (unLoc x); repPvar x' } repP (LazyPat _ p) = do { p1 <- repLP p; repPtilde p1 } repP (BangPat _ p) = do { p1 <- repLP p; repPbang p1 }-repP (AsPat _ x p) = do { x' <- lookupNBinder x; p1 <- repLP p+repP (AsPat _ x _ p) = do { x' <- lookupNBinder x; p1 <- repLP p ; repPaspat x' p1 } repP (ParPat _ _ p _) = repLP p repP (ListPat _ ps) = do { qs <- repLPs ps; repPlist qs }@@ -2074,7 +2084,8 @@ = do { con_str <- lookupLOcc dc ; case details of PrefixCon tyargs ps -> do { qs <- repLPs ps- ; ts <- repListM typeTyConName (repTy . unLoc . hsps_body) tyargs+ ; let unwrapTyArg (HsConPatTyArg _ t) = unLoc (hsps_body t)+ ; ts <- repListM typeTyConName (repTy . unwrapTyArg) tyargs ; repPcon con_str ts qs } RecCon rec -> do { fps <- repListM fieldPatTyConName rep_fld (rec_flds rec) ; repPrec con_str fps }@@ -2101,7 +2112,8 @@ repP (SigPat _ p t) = do { p' <- repLP p ; t' <- repLTy (hsPatSigType t) ; repPsig p' t' }-repP (SplicePat _ splice) = repSplice splice+repP (SplicePat (HsUntypedSpliceNested n) _) = rep_splice n+repP p@(SplicePat (HsUntypedSpliceTop _ _) _) = pprPanic "repP: top level splice" (ppr p) repP other = notHandled (ThExoticPattern other) ----------------------------------------------------------@@ -2135,7 +2147,8 @@ -- -- Nevertheless, it's monadic because we have to generate nameTy mkGenSyms ns = do { var_ty <- lookupType nameTyConName- ; return [(nm, mkLocalId (localiseName nm) Many var_ty) | nm <- ns] }+ ; return [ (nm, mkLocalId (localiseName nm) ManyTy var_ty)+ | nm <- ns] } addBinds :: [GenSymBind] -> MetaM a -> MetaM a@@ -2178,32 +2191,41 @@ Just (DsSplice _) -> pprPanic "repE:lookupOcc" (ppr n) } -globalVar :: Name -> DsM (Core TH.Name)+ -- Not bound by the meta-env -- Could be top-level; or could be local -- f x = $(g [| x |]) -- Here the x will be local-globalVar name- | isExternalName name- = do { MkC mod <- coreStringLit name_mod- ; MkC pkg <- coreStringLit name_pkg- ; MkC occ <- nameLit name- ; rep2_nwDsM mk_varg [pkg,mod,occ] }- | otherwise- = do { MkC occ <- nameLit name+globalVar :: Name -> DsM (Core TH.Name)+globalVar n =+ case nameModule_maybe n of+ Just m -> globalVarExternal m (getOccName n)+ Nothing -> globalVarLocal (getUnique n) (getOccName n)++globalVarLocal :: Unique -> OccName -> DsM (Core TH.Name)+globalVarLocal unique name+ = do { MkC occ <- occNameLit name ; platform <- targetPlatform <$> getDynFlags- ; let uni = mkIntegerExpr platform (toInteger $ getKey (getUnique name))+ ; let uni = mkIntegerExpr platform (toInteger $ getKey unique) ; rep2_nwDsM mkNameLName [occ,uni] }++globalVarExternal :: Module -> OccName -> DsM (Core TH.Name)+globalVarExternal mod name_occ+ = do {++ ; MkC mod <- coreStringLit name_mod+ ; MkC pkg <- coreStringLit name_pkg+ ; MkC occ <- occNameLit name_occ+ ; rep2_nwDsM mk_varg [pkg,mod,occ] } where- mod = assert (isExternalName name) nameModule name- name_mod = moduleNameString (moduleName mod)- name_pkg = unitString (moduleUnit mod)- name_occ = nameOccName name- mk_varg | isDataOcc name_occ = mkNameG_dName- | isVarOcc name_occ = mkNameG_vName- | isTcOcc name_occ = mkNameG_tcName- | otherwise = pprPanic "GHC.HsToCore.Quote.globalVar" (ppr name)+ name_mod = moduleNameFS (moduleName mod)+ name_pkg = unitFS (moduleUnit mod)+ mk_varg | isDataOcc name_occ = mkNameG_dName+ | isVarOcc name_occ = mkNameG_vName+ | isTcOcc name_occ = mkNameG_tcName+ | otherwise = pprPanic "GHC.HsToCore.Quote.globalVar" (ppr name_occ) + lookupType :: Name -- Name of type constructor (e.g. (M TH.Exp)) -> MetaM Type -- The type lookupType tc_name = do { tc <- lift $ dsLookupTyCon tc_name ;@@ -2230,16 +2252,13 @@ go _ [] = return body go var_ty ((name,id) : binds) = do { MkC body' <- go var_ty binds- ; lit_str <- lift $ nameLit name+ ; lit_str <- occNameLit (occName name) ; gensym_app <- repGensym lit_str ; repBindM var_ty elt_ty gensym_app (MkC (Lam id body')) } -nameLit :: Name -> DsM (Core String)-nameLit n = coreStringLit (occNameString (nameOccName n))--occNameLit :: OccName -> MetaM (Core String)-occNameLit name = coreStringLit (occNameString name)+occNameLit :: MonadThings m => OccName -> m (Core String)+occNameLit name = coreStringLit (occNameFS name) -- %*********************************************************************@@ -2417,7 +2436,7 @@ coreModNameM :: MetaM (Core (Maybe TH.ModName)) coreModNameM = case maybeModName of Just m -> do- MkC s <- coreStringLit (moduleNameString m)+ MkC s <- coreStringLit (moduleNameFS m) mName <- rep2_nw mkModNameName [s] coreJust modNameTyConName mName _ -> coreNothing modNameTyConName@@ -2515,14 +2534,17 @@ repFun :: Core TH.Name -> Core [(M TH.Clause)] -> MetaM (Core (M TH.Dec)) repFun (MkC nm) (MkC b) = rep2 funDName [nm, b] -repData :: Core (M TH.Cxt) -> Core TH.Name+repData :: Bool -- ^ @True@ for a @type data@ declaration.+ -- See Note [Type data declarations] in GHC.Rename.Module+ -> Core (M TH.Cxt) -> Core TH.Name -> Either (Core [(M (TH.TyVarBndr ()))]) (Core (Maybe [(M (TH.TyVarBndr ()))]), Core (M TH.Type)) -> Core (Maybe (M TH.Kind)) -> Core [(M TH.Con)] -> Core [M TH.DerivClause] -> MetaM (Core (M TH.Dec))-repData (MkC cxt) (MkC nm) (Left (MkC tvs)) (MkC ksig) (MkC cons) (MkC derivs)- = rep2 dataDName [cxt, nm, tvs, ksig, cons, derivs]-repData (MkC cxt) (MkC _) (Right (MkC mb_bndrs, MkC ty)) (MkC ksig) (MkC cons)+repData type_data (MkC cxt) (MkC nm) (Left (MkC tvs)) (MkC ksig) (MkC cons) (MkC derivs)+ | type_data = rep2 typeDataDName [nm, tvs, ksig, cons]+ | otherwise = rep2 dataDName [cxt, nm, tvs, ksig, cons, derivs]+repData _ (MkC cxt) (MkC _) (Right (MkC mb_bndrs, MkC ty)) (MkC ksig) (MkC cons) (MkC derivs) = rep2 dataInstDName [cxt, mb_bndrs, ty, ksig, cons, derivs] @@ -2559,7 +2581,7 @@ StockStrategy _ -> thing_inside =<< just =<< repStockStrategy AnyclassStrategy _ -> thing_inside =<< just =<< repAnyclassStrategy NewtypeStrategy _ -> thing_inside =<< just =<< repNewtypeStrategy- ViaStrategy ty -> addSimpleTyVarBinds (get_scoped_tvs_from_sig ty) $+ ViaStrategy ty -> addSimpleTyVarBinds FreshNamesOnly (get_scoped_tvs_from_sig ty) $ do ty' <- rep_ty_sig' ty via_strat <- repViaStrategy ty' m_via_strat <- just via_strat@@ -2705,7 +2727,7 @@ arg_vtys <- repRecConArgs ips rep2 recCName [unC con', unC arg_vtys] -repGadtDataCons :: [LocatedN Name]+repGadtDataCons :: NonEmpty (LocatedN Name) -> HsConDeclGADTDetails GhcRn -> LHsType GhcRn -> MetaM (Core (M TH.Con))@@ -2715,11 +2737,11 @@ PrefixConGADT ps -> do arg_tys <- repPrefixConArgs ps res_ty' <- repLTy res_ty- rep2 gadtCName [ unC (nonEmptyCoreList cons'), unC arg_tys, unC res_ty']+ rep2 gadtCName [ unC (nonEmptyCoreList' cons'), unC arg_tys, unC res_ty'] RecConGADT ips _ -> do arg_vtys <- repRecConArgs ips res_ty' <- repLTy res_ty- rep2 recGadtCName [unC (nonEmptyCoreList cons'), unC arg_vtys,+ rep2 recGadtCName [unC (nonEmptyCoreList' cons'), unC arg_vtys, unC res_ty'] -- TH currently only supports linear constructors.@@ -2929,9 +2951,25 @@ mk_lit (HsFractional f) = mk_rational f mk_lit (HsIsString _ s) = mk_string s +repRdrName :: RdrName -> MetaM (Core TH.Name)+repRdrName rdr_name = do+ case rdr_name of+ Unqual occ ->+ repNameS =<< occNameLit occ+ Qual mn occ -> do+ let name_mod = moduleNameFS mn+ mod <- coreStringLit name_mod+ occ <- occNameLit occ+ repNameQ mod occ+ Orig m n -> lift $ globalVarExternal m n+ Exact n -> lift $ globalVar n+ repNameS :: Core String -> MetaM (Core TH.Name) repNameS (MkC name) = rep2_nw mkNameSName [name] +repNameQ :: Core String -> Core String -> MetaM (Core TH.Name)+repNameQ (MkC mn) (MkC name) = rep2_nw mkNameQName [mn, name]+ --------------- Miscellaneous ------------------- repGensym :: Core String -> MetaM (Core (M TH.Name))@@ -2951,17 +2989,19 @@ repOverLabel :: FastString -> MetaM (Core (M TH.Exp)) repOverLabel fs = do- (MkC s) <- coreStringLit $ unpackFS fs+ MkC s <- coreStringLit fs rep2 labelEName [s] repGetField :: Core (M TH.Exp) -> FastString -> MetaM (Core (M TH.Exp)) repGetField (MkC exp) fs = do- MkC s <- coreStringLit $ unpackFS fs+ MkC s <- coreStringLit fs rep2 getFieldEName [exp,s] repProjection :: NonEmpty FastString -> MetaM (Core (M TH.Exp)) repProjection fs = do- MkC xs <- coreListNonEmpty stringTy <$> mapM (coreStringLit . unpackFS) fs+ ne_tycon <- lift $ dsLookupTyCon nonEmptyTyConName+ MkC xs <- coreListNonEmpty ne_tycon stringTy <$>+ mapM coreStringLit fs rep2 projectionEName [xs] ------------ Lists -------------------@@ -2993,8 +3033,13 @@ -> [Core a] -> Core [a] coreList' elt_ty es = MkC (mkListExpr elt_ty (map unC es )) -coreListNonEmpty :: Type -> NonEmpty (Core a) -> Core (NonEmpty a)-coreListNonEmpty ty (MkC x :| xs) = MkC $ mkNonEmptyListExpr ty x (map unC xs)+coreListNonEmpty :: TyCon -- TyCon for NonEmpty+ -> Type -- Element type+ -> NonEmpty (Core a)+ -> Core (NonEmpty a)+coreListNonEmpty ne_tc ty (MkC x :| xs)+ = MkC $ mkCoreConApps (tyConSingleDataCon ne_tc)+ [Type ty, x, mkListExpr ty (map unC xs)] nonEmptyCoreList :: [Core a] -> Core [a] -- The list must be non-empty so we can get the element type@@ -3002,9 +3047,11 @@ nonEmptyCoreList [] = panic "coreList: empty argument" nonEmptyCoreList xs@(MkC x:_) = MkC (mkListExpr (exprType x) (map unC xs)) +nonEmptyCoreList' :: NonEmpty (Core a) -> Core [a]+nonEmptyCoreList' xs@(MkC x:|_) = MkC (mkListExpr (exprType x) (toList $ fmap unC xs)) -coreStringLit :: MonadThings m => String -> m (Core String)-coreStringLit s = do { z <- mkStringExpr s; return(MkC z) }+coreStringLit :: MonadThings m => FastString -> m (Core String)+coreStringLit s = do { z <- mkStringExprFS s; return (MkC z) } ------------------- Maybe ------------------
+ compiler/GHC/HsToCore/Ticks.hs view
@@ -0,0 +1,1249 @@+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE NondecreasingIndentation #-}+{-# LANGUAGE TypeFamilies #-}++{-# OPTIONS_GHC -Wno-incomplete-record-updates #-}++{-+(c) Galois, 2006+(c) University of Glasgow, 2007+-}++module GHC.HsToCore.Ticks+ ( TicksConfig (..)+ , Tick (..)+ , TickishType (..)+ , addTicksToBinds+ , isGoodSrcSpan'+ , stripTicksTopHsExpr+ ) where++import GHC.Prelude as Prelude++import GHC.Hs+import GHC.Unit++import GHC.Core.Type+import GHC.Core.TyCon++import GHC.Data.Maybe+import GHC.Data.FastString+import GHC.Data.Bag+import GHC.Data.SizedSeq++import GHC.Driver.Flags (DumpFlag(..))++import GHC.Utils.Outputable as Outputable+import GHC.Utils.Panic+import GHC.Utils.Logger+import GHC.Types.SrcLoc+import GHC.Types.Basic+import GHC.Types.Id+import GHC.Types.Var.Set+import GHC.Types.Name.Set hiding (FreeVars)+import GHC.Types.Name+import GHC.Types.CostCentre+import GHC.Types.CostCentre.State+import GHC.Types.Tickish+import GHC.Types.ProfAuto++import Control.Monad+import Data.List (isSuffixOf, intersperse)++import Trace.Hpc.Mix++import Data.Bifunctor (second)+import Data.Set (Set)+import qualified Data.Set as Set++{-+************************************************************************+* *+* The main function: addTicksToBinds+* *+************************************************************************+-}++-- | Configuration for compilation pass to add tick for instrumentation+-- to binding sites.+data TicksConfig = TicksConfig+ { ticks_passes :: ![TickishType]+ -- ^ What purposes do we need ticks for++ , ticks_profAuto :: !ProfAuto+ -- ^ What kind of {-# SCC #-} to add automatically++ , ticks_countEntries :: !Bool+ -- ^ Whether to count the entries to functions+ --+ -- Requires extra synchronization which can vastly degrade+ -- performance.+ }++data Tick = Tick+ { tick_loc :: SrcSpan -- ^ Tick source span+ , tick_path :: [String] -- ^ Path to the declaration+ , tick_ids :: [OccName] -- ^ Identifiers being bound+ , tick_label :: BoxLabel -- ^ Label for the tick counter+ }+++addTicksToBinds+ :: Logger+ -> TicksConfig+ -> Module+ -> ModLocation -- ^ location of the current module+ -> NameSet -- ^ Exported Ids. When we call addTicksToBinds,+ -- isExportedId doesn't work yet (the desugarer+ -- hasn't set it), so we have to work from this set.+ -> [TyCon] -- ^ Type constructors in this module+ -> LHsBinds GhcTc+ -> IO (LHsBinds GhcTc, Maybe (FilePath, SizedSeq Tick))++addTicksToBinds logger cfg+ mod mod_loc exports tyCons binds+ | let passes = ticks_passes cfg+ , not (null passes)+ , Just orig_file <- ml_hs_file mod_loc = do++ let orig_file2 = guessSourceFile binds orig_file++ tickPass tickish (binds,st) =+ let env = TTE+ { fileName = mkFastString orig_file2+ , declPath = []+ , tte_countEntries = ticks_countEntries cfg+ , exports = exports+ , inlines = emptyVarSet+ , inScope = emptyVarSet+ , blackList = Set.fromList $+ mapMaybe (\tyCon -> case getSrcSpan (tyConName tyCon) of+ RealSrcSpan l _ -> Just l+ UnhelpfulSpan _ -> Nothing)+ tyCons+ , density = mkDensity tickish $ ticks_profAuto cfg+ , this_mod = mod+ , tickishType = tickish+ }+ (binds',_,st') = unTM (addTickLHsBinds binds) env st+ in (binds', st')++ (binds1,st) = foldr tickPass (binds, initTTState) passes++ extendedMixEntries = ticks st++ putDumpFileMaybe logger Opt_D_dump_ticked "HPC" FormatHaskell+ (pprLHsBinds binds1)++ return (binds1, Just (orig_file2, extendedMixEntries))++ | otherwise = return (binds, Nothing)++guessSourceFile :: LHsBinds GhcTc -> FilePath -> FilePath+guessSourceFile binds orig_file =+ -- Try look for a file generated from a .hsc file to a+ -- .hs file, by peeking ahead.+ let top_pos = catMaybes $ foldr (\ (L pos _) rest ->+ srcSpanFileName_maybe (locA pos) : rest) [] binds+ in+ case top_pos of+ (file_name:_) | ".hsc" `isSuffixOf` unpackFS file_name+ -> unpackFS file_name+ _ -> orig_file+++-- -----------------------------------------------------------------------------+-- TickDensity++-- | Where to insert ticks+data TickDensity+ = TickForCoverage -- ^ for Hpc+ | TickForBreakPoints -- ^ for GHCi+ | TickAllFunctions -- ^ for -prof-auto-all+ | TickTopFunctions -- ^ for -prof-auto-top+ | TickExportedFunctions -- ^ for -prof-auto-exported+ | TickCallSites -- ^ for stack tracing+ deriving Eq++mkDensity :: TickishType -> ProfAuto -> TickDensity+mkDensity tickish pa = case tickish of+ HpcTicks -> TickForCoverage+ SourceNotes -> TickForCoverage+ Breakpoints -> TickForBreakPoints+ ProfNotes ->+ case pa of+ ProfAutoAll -> TickAllFunctions+ ProfAutoTop -> TickTopFunctions+ ProfAutoExports -> TickExportedFunctions+ ProfAutoCalls -> TickCallSites+ _other -> panic "mkDensity"++-- | Decide whether to add a tick to a binding or not.+shouldTickBind :: TickDensity+ -> Bool -- ^ top level?+ -> Bool -- ^ exported?+ -> Bool -- ^ simple pat bind?+ -> Bool -- ^ INLINE pragma?+ -> Bool++shouldTickBind density top_lev exported _simple_pat inline+ = case density of+ TickForBreakPoints -> False+ -- we never add breakpoints to simple pattern bindings+ -- (there's always a tick on the rhs anyway).+ TickAllFunctions -> not inline+ TickTopFunctions -> top_lev && not inline+ TickExportedFunctions -> exported && not inline+ TickForCoverage -> True+ TickCallSites -> False++shouldTickPatBind :: TickDensity -> Bool -> Bool+shouldTickPatBind density top_lev+ = case density of+ TickForBreakPoints -> False+ TickAllFunctions -> True+ TickTopFunctions -> top_lev+ TickExportedFunctions -> False+ TickForCoverage -> False+ TickCallSites -> False++-- Strip ticks HsExpr++-- | Strip CoreTicks from an HsExpr+stripTicksTopHsExpr :: HsExpr GhcTc -> ([CoreTickish], HsExpr GhcTc)+stripTicksTopHsExpr (XExpr (HsTick t e)) = let (ts, body) = stripTicksTopHsExpr (unLoc e)+ in (t:ts, body)+stripTicksTopHsExpr e = ([], e)++-- -----------------------------------------------------------------------------+-- Adding ticks to bindings++addTickLHsBinds :: LHsBinds GhcTc -> TM (LHsBinds GhcTc)+addTickLHsBinds = mapBagM addTickLHsBind++addTickLHsBind :: LHsBind GhcTc -> TM (LHsBind GhcTc)+addTickLHsBind (L pos (XHsBindsLR bind@(AbsBinds { abs_binds = binds+ , abs_exports = abs_exports+ }))) =+ withEnv add_exports $+ withEnv add_inlines $ do+ binds' <- addTickLHsBinds binds+ return $ L pos $ XHsBindsLR $ bind { abs_binds = binds' }+ where+ -- in AbsBinds, the Id on each binding is not the actual top-level+ -- Id that we are defining, they are related by the abs_exports+ -- field of AbsBinds. So if we're doing TickExportedFunctions we need+ -- to add the local Ids to the set of exported Names so that we know to+ -- tick the right bindings.+ add_exports env =+ env{ exports = exports env `extendNameSetList`+ [ idName mid+ | ABE{ abe_poly = pid, abe_mono = mid } <- abs_exports+ , idName pid `elemNameSet` (exports env) ] }++ -- See Note [inline sccs]+ add_inlines env =+ env{ inlines = inlines env `extendVarSetList`+ [ mid+ | ABE{ abe_poly = pid, abe_mono = mid } <- abs_exports+ , isInlinePragma (idInlinePragma pid) ] }++addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id }))) = do+ let name = getOccString id+ decl_path <- getPathEntry+ density <- getDensity++ inline_ids <- liftM inlines getEnv+ -- See Note [inline sccs]+ let inline = isInlinePragma (idInlinePragma id)+ || id `elemVarSet` inline_ids++ -- See Note [inline sccs]+ tickish <- tickishType `liftM` getEnv+ case tickish of { ProfNotes | inline -> return (L pos funBind); _ -> do++ (fvs, mg) <-+ getFreeVars $+ addPathEntry name $+ addTickMatchGroup False (fun_matches funBind)++ blackListed <- isBlackListed (locA pos)+ exported_names <- liftM exports getEnv++ -- We don't want to generate code for blacklisted positions+ -- We don't want redundant ticks on simple pattern bindings+ -- We don't want to tick non-exported bindings in TickExportedFunctions+ let simple = isSimplePatBind funBind+ toplev = null decl_path+ exported = idName id `elemNameSet` exported_names++ tick <- if not blackListed &&+ shouldTickBind density toplev exported simple inline+ then+ bindTick density name (locA pos) fvs+ else+ return Nothing++ let mbCons = maybe Prelude.id (:)+ return $ L pos $ funBind { fun_matches = mg+ , fun_ext = second (tick `mbCons`) (fun_ext funBind) }+ }++ where+ -- a binding is a simple pattern binding if it is a funbind with+ -- zero patterns+ isSimplePatBind :: HsBind GhcTc -> Bool+ isSimplePatBind funBind = matchGroupArity (fun_matches funBind) == 0++-- TODO: Revisit this+addTickLHsBind (L pos (pat@(PatBind { pat_lhs = lhs+ , pat_rhs = rhs }))) = do++ let simplePatId = isSimplePat lhs++ -- TODO: better name for rhs's for non-simple patterns?+ let name = maybe "(...)" getOccString simplePatId++ (fvs, rhs') <- getFreeVars $ addPathEntry name $ addTickGRHSs False False rhs+ let pat' = pat { pat_rhs = rhs'}++ -- Should create ticks here?+ density <- getDensity+ decl_path <- getPathEntry+ let top_lev = null decl_path+ if not (shouldTickPatBind density top_lev)+ then return (L pos pat')+ else do++ let mbCons = maybe id (:)++ let (initial_rhs_ticks, initial_patvar_tickss) = snd $ pat_ext pat'++ -- Allocate the ticks++ rhs_tick <- bindTick density name (locA pos) fvs+ let rhs_ticks = rhs_tick `mbCons` initial_rhs_ticks++ patvar_tickss <- case simplePatId of+ Just{} -> return initial_patvar_tickss+ Nothing -> do+ let patvars = map getOccString (collectPatBinders CollNoDictBinders lhs)+ patvar_ticks <- mapM (\v -> bindTick density v (locA pos) fvs) patvars+ return+ (zipWith mbCons patvar_ticks+ (initial_patvar_tickss ++ repeat []))++ return $ L pos $ pat' { pat_ext = second (const (rhs_ticks, patvar_tickss)) (pat_ext pat') }++-- Only internal stuff, not from source, uses VarBind, so we ignore it.+addTickLHsBind var_bind@(L _ (VarBind {})) = return var_bind+addTickLHsBind patsyn_bind@(L _ (PatSynBind {})) = return patsyn_bind++bindTick+ :: TickDensity -> String -> SrcSpan -> FreeVars -> TM (Maybe CoreTickish)+bindTick density name pos fvs = do+ decl_path <- getPathEntry+ let+ toplev = null decl_path+ count_entries = toplev || density == TickAllFunctions+ top_only = density /= TickAllFunctions+ box_label = if toplev then TopLevelBox [name]+ else LocalBox (decl_path ++ [name])+ --+ allocATickBox box_label count_entries top_only pos fvs+++-- Note [inline sccs]+-- ~~~~~~~~~~~~~~~~~~+-- The reason not to add ticks to INLINE functions is that this is+-- sometimes handy for avoiding adding a tick to a particular function+-- (see #6131)+--+-- So for now we do not add any ticks to INLINE functions at all.+--+-- We used to use isAnyInlinePragma to figure out whether to avoid adding+-- ticks for this purpose. However, #12962 indicates that this contradicts+-- the documentation on profiling (which only mentions INLINE pragmas).+-- So now we're more careful about what we avoid adding ticks to.++-- -----------------------------------------------------------------------------+-- Decorate an LHsExpr with ticks++-- selectively add ticks to interesting expressions+addTickLHsExpr :: LHsExpr GhcTc -> TM (LHsExpr GhcTc)+addTickLHsExpr e@(L pos e0) = do+ d <- getDensity+ case d of+ TickForBreakPoints | isGoodBreakExpr e0 -> tick_it+ TickForCoverage -> tick_it+ TickCallSites | isCallSite e0 -> tick_it+ _other -> dont_tick_it+ where+ tick_it = allocTickBox (ExpBox False) False False (locA pos)+ $ addTickHsExpr e0+ dont_tick_it = addTickLHsExprNever e++-- Add a tick to an expression which is the RHS of an equation or a binding.+-- We always consider these to be breakpoints, unless the expression is a 'let'+-- (because the body will definitely have a tick somewhere). ToDo: perhaps+-- we should treat 'case' and 'if' the same way?+addTickLHsExprRHS :: LHsExpr GhcTc -> TM (LHsExpr GhcTc)+addTickLHsExprRHS e@(L pos e0) = do+ d <- getDensity+ case d of+ TickForBreakPoints | HsLet{} <- e0 -> dont_tick_it+ | otherwise -> tick_it+ TickForCoverage -> tick_it+ TickCallSites | isCallSite e0 -> tick_it+ _other -> dont_tick_it+ where+ tick_it = allocTickBox (ExpBox False) False False (locA pos)+ $ addTickHsExpr e0+ dont_tick_it = addTickLHsExprNever e++-- The inner expression of an evaluation context:+-- let binds in [], ( [] )+-- we never tick these if we're doing HPC, but otherwise+-- we treat it like an ordinary expression.+addTickLHsExprEvalInner :: LHsExpr GhcTc -> TM (LHsExpr GhcTc)+addTickLHsExprEvalInner e = do+ d <- getDensity+ case d of+ TickForCoverage -> addTickLHsExprNever e+ _otherwise -> addTickLHsExpr e++-- | A let body is treated differently from addTickLHsExprEvalInner+-- above with TickForBreakPoints, because for breakpoints we always+-- want to tick the body, even if it is not a redex. See test+-- break012. This gives the user the opportunity to inspect the+-- values of the let-bound variables.+addTickLHsExprLetBody :: LHsExpr GhcTc -> TM (LHsExpr GhcTc)+addTickLHsExprLetBody e@(L pos e0) = do+ d <- getDensity+ case d of+ TickForBreakPoints | HsLet{} <- e0 -> dont_tick_it+ | otherwise -> tick_it+ _other -> addTickLHsExprEvalInner e+ where+ tick_it = allocTickBox (ExpBox False) False False (locA pos)+ $ addTickHsExpr e0+ dont_tick_it = addTickLHsExprNever e++-- version of addTick that does not actually add a tick,+-- because the scope of this tick is completely subsumed by+-- another.+addTickLHsExprNever :: LHsExpr GhcTc -> TM (LHsExpr GhcTc)+addTickLHsExprNever (L pos e0) = do+ e1 <- addTickHsExpr e0+ return $ L pos e1++-- General heuristic: expressions which are calls (do not denote+-- values) are good break points.+isGoodBreakExpr :: HsExpr GhcTc -> Bool+isGoodBreakExpr e = isCallSite e++isCallSite :: HsExpr GhcTc -> Bool+isCallSite HsApp{} = True+isCallSite HsAppType{} = True+isCallSite (XExpr (ExpansionExpr (HsExpanded _ e)))+ = isCallSite e+-- NB: OpApp, SectionL, SectionR are all expanded out+isCallSite _ = False++addTickLHsExprOptAlt :: Bool -> LHsExpr GhcTc -> TM (LHsExpr GhcTc)+addTickLHsExprOptAlt oneOfMany (L pos e0)+ = ifDensity TickForCoverage+ (allocTickBox (ExpBox oneOfMany) False False (locA pos)+ $ addTickHsExpr e0)+ (addTickLHsExpr (L pos e0))++addBinTickLHsExpr :: (Bool -> BoxLabel) -> LHsExpr GhcTc -> TM (LHsExpr GhcTc)+addBinTickLHsExpr boxLabel (L pos e0)+ = ifDensity TickForCoverage+ (allocBinTickBox boxLabel (locA pos) $ addTickHsExpr e0)+ (addTickLHsExpr (L pos e0))+++-- -----------------------------------------------------------------------------+-- Decorate the body of an HsExpr with ticks.+-- (Whether to put a tick around the whole expression was already decided,+-- in the addTickLHsExpr family of functions.)++addTickHsExpr :: HsExpr GhcTc -> TM (HsExpr GhcTc)+addTickHsExpr e@(HsVar _ (L _ id)) = do freeVar id; return e+addTickHsExpr e@(HsUnboundVar {}) = return e+addTickHsExpr e@(HsRecSel _ (FieldOcc id _)) = do freeVar id; return e++addTickHsExpr e@(HsIPVar {}) = return e+addTickHsExpr e@(HsOverLit {}) = return e+addTickHsExpr e@(HsOverLabel{}) = return e+addTickHsExpr e@(HsLit {}) = return e+addTickHsExpr (HsLam x mg) = liftM (HsLam x)+ (addTickMatchGroup True mg)+addTickHsExpr (HsLamCase x lc_variant mgs) = liftM (HsLamCase x lc_variant)+ (addTickMatchGroup True mgs)+addTickHsExpr (HsApp x e1 e2) = liftM2 (HsApp x) (addTickLHsExprNever e1)+ (addTickLHsExpr e2)+addTickHsExpr (HsAppType x e at ty) = do+ e' <- addTickLHsExprNever e+ return (HsAppType x e' at ty)+addTickHsExpr (OpApp fix e1 e2 e3) =+ liftM4 OpApp+ (return fix)+ (addTickLHsExpr e1)+ (addTickLHsExprNever e2)+ (addTickLHsExpr e3)+addTickHsExpr (NegApp x e neg) =+ liftM2 (NegApp x)+ (addTickLHsExpr e)+ (addTickSyntaxExpr hpcSrcSpan neg)+addTickHsExpr (HsPar x lpar e rpar) = do+ e' <- addTickLHsExprEvalInner e+ return (HsPar x lpar e' rpar)+addTickHsExpr (SectionL x e1 e2) =+ liftM2 (SectionL x)+ (addTickLHsExpr e1)+ (addTickLHsExprNever e2)+addTickHsExpr (SectionR x e1 e2) =+ liftM2 (SectionR x)+ (addTickLHsExprNever e1)+ (addTickLHsExpr e2)+addTickHsExpr (ExplicitTuple x es boxity) =+ liftM2 (ExplicitTuple x)+ (mapM addTickTupArg es)+ (return boxity)+addTickHsExpr (ExplicitSum ty tag arity e) = do+ e' <- addTickLHsExpr e+ return (ExplicitSum ty tag arity e')+addTickHsExpr (HsCase x e mgs) =+ liftM2 (HsCase x)+ (addTickLHsExpr e) -- not an EvalInner; e might not necessarily+ -- be evaluated.+ (addTickMatchGroup False mgs)+addTickHsExpr (HsIf x e1 e2 e3) =+ liftM3 (HsIf x)+ (addBinTickLHsExpr (BinBox CondBinBox) e1)+ (addTickLHsExprOptAlt True e2)+ (addTickLHsExprOptAlt True e3)+addTickHsExpr (HsMultiIf ty alts)+ = do { let isOneOfMany = case alts of [_] -> False; _ -> True+ ; alts' <- mapM (traverse $ addTickGRHS isOneOfMany False) alts+ ; return $ HsMultiIf ty alts' }+addTickHsExpr (HsLet x tkLet binds tkIn e) =+ bindLocals (collectLocalBinders CollNoDictBinders binds) $ do+ binds' <- addTickHsLocalBinds binds -- to think about: !patterns.+ e' <- addTickLHsExprLetBody e+ return (HsLet x tkLet binds' tkIn e')+addTickHsExpr (HsDo srcloc cxt (L l stmts))+ = do { (stmts', _) <- addTickLStmts' forQual stmts (return ())+ ; return (HsDo srcloc cxt (L l stmts')) }+ where+ forQual = case cxt of+ ListComp -> Just $ BinBox QualBinBox+ _ -> Nothing+addTickHsExpr (ExplicitList ty es)+ = liftM2 ExplicitList (return ty) (mapM (addTickLHsExpr) es)++addTickHsExpr (HsStatic fvs e) = HsStatic fvs <$> addTickLHsExpr e++addTickHsExpr expr@(RecordCon { rcon_flds = rec_binds })+ = do { rec_binds' <- addTickHsRecordBinds rec_binds+ ; return (expr { rcon_flds = rec_binds' }) }++addTickHsExpr expr@(RecordUpd { rupd_expr = e, rupd_flds = Left flds })+ = do { e' <- addTickLHsExpr e+ ; flds' <- mapM addTickHsRecField flds+ ; return (expr { rupd_expr = e', rupd_flds = Left flds' }) }+addTickHsExpr expr@(RecordUpd { rupd_expr = e, rupd_flds = Right flds })+ = do { e' <- addTickLHsExpr e+ ; flds' <- mapM addTickHsRecField flds+ ; return (expr { rupd_expr = e', rupd_flds = Right flds' }) }++addTickHsExpr (ExprWithTySig x e ty) =+ liftM3 ExprWithTySig+ (return x)+ (addTickLHsExprNever e) -- No need to tick the inner expression+ -- for expressions with signatures+ (return ty)+addTickHsExpr (ArithSeq ty wit arith_seq) =+ liftM3 ArithSeq+ (return ty)+ (addTickWit wit)+ (addTickArithSeqInfo arith_seq)+ where addTickWit Nothing = return Nothing+ addTickWit (Just fl) = do fl' <- addTickSyntaxExpr hpcSrcSpan fl+ return (Just fl')++addTickHsExpr (HsPragE x p e) =+ liftM (HsPragE x p) (addTickLHsExpr e)+addTickHsExpr e@(HsTypedBracket {}) = return e+addTickHsExpr e@(HsUntypedBracket{}) = return e+addTickHsExpr e@(HsTypedSplice{}) = return e+addTickHsExpr e@(HsUntypedSplice{}) = return e+addTickHsExpr e@(HsGetField {}) = return e+addTickHsExpr e@(HsProjection {}) = return e+addTickHsExpr (HsProc x pat cmdtop) =+ liftM2 (HsProc x)+ (addTickLPat pat)+ (traverse (addTickHsCmdTop) cmdtop)+addTickHsExpr (XExpr (WrapExpr (HsWrap w e))) =+ liftM (XExpr . WrapExpr . HsWrap w) $+ (addTickHsExpr e) -- Explicitly no tick on inside+addTickHsExpr (XExpr (ExpansionExpr (HsExpanded a b))) =+ liftM (XExpr . ExpansionExpr . HsExpanded a) $+ (addTickHsExpr b)++addTickHsExpr e@(XExpr (ConLikeTc {})) = return e+ -- We used to do a freeVar on a pat-syn builder, but actually+ -- such builders are never in the inScope env, which+ -- doesn't include top level bindings++-- We might encounter existing ticks (multiple Coverage passes)+addTickHsExpr (XExpr (HsTick t e)) =+ liftM (XExpr . HsTick t) (addTickLHsExprNever e)+addTickHsExpr (XExpr (HsBinTick t0 t1 e)) =+ liftM (XExpr . HsBinTick t0 t1) (addTickLHsExprNever e)++addTickTupArg :: HsTupArg GhcTc -> TM (HsTupArg GhcTc)+addTickTupArg (Present x e) = do { e' <- addTickLHsExpr e+ ; return (Present x e') }+addTickTupArg (Missing ty) = return (Missing ty)+++addTickMatchGroup :: Bool{-is lambda-} -> MatchGroup GhcTc (LHsExpr GhcTc)+ -> TM (MatchGroup GhcTc (LHsExpr GhcTc))+addTickMatchGroup is_lam mg@(MG { mg_alts = L l matches }) = do+ let isOneOfMany = matchesOneOfMany matches+ matches' <- mapM (traverse (addTickMatch isOneOfMany is_lam)) matches+ return $ mg { mg_alts = L l matches' }++addTickMatch :: Bool -> Bool -> Match GhcTc (LHsExpr GhcTc)+ -> TM (Match GhcTc (LHsExpr GhcTc))+addTickMatch isOneOfMany isLambda match@(Match { m_pats = pats+ , m_grhss = gRHSs }) =+ bindLocals (collectPatsBinders CollNoDictBinders pats) $ do+ gRHSs' <- addTickGRHSs isOneOfMany isLambda gRHSs+ return $ match { m_grhss = gRHSs' }++addTickGRHSs :: Bool -> Bool -> GRHSs GhcTc (LHsExpr GhcTc)+ -> TM (GRHSs GhcTc (LHsExpr GhcTc))+addTickGRHSs isOneOfMany isLambda (GRHSs x guarded local_binds) =+ bindLocals binders $ do+ local_binds' <- addTickHsLocalBinds local_binds+ guarded' <- mapM (traverse (addTickGRHS isOneOfMany isLambda)) guarded+ return $ GRHSs x guarded' local_binds'+ where+ binders = collectLocalBinders CollNoDictBinders local_binds++addTickGRHS :: Bool -> Bool -> GRHS GhcTc (LHsExpr GhcTc)+ -> TM (GRHS GhcTc (LHsExpr GhcTc))+addTickGRHS isOneOfMany isLambda (GRHS x stmts expr) = do+ (stmts',expr') <- addTickLStmts' (Just $ BinBox $ GuardBinBox) stmts+ (addTickGRHSBody isOneOfMany isLambda expr)+ return $ GRHS x stmts' expr'++addTickGRHSBody :: Bool -> Bool -> LHsExpr GhcTc -> TM (LHsExpr GhcTc)+addTickGRHSBody isOneOfMany isLambda expr@(L pos e0) = do+ d <- getDensity+ case d of+ TickForCoverage -> addTickLHsExprOptAlt isOneOfMany expr+ TickAllFunctions | isLambda ->+ addPathEntry "\\" $+ allocTickBox (ExpBox False) True{-count-} False{-not top-} (locA pos) $+ addTickHsExpr e0+ _otherwise ->+ addTickLHsExprRHS expr++addTickLStmts :: (Maybe (Bool -> BoxLabel)) -> [ExprLStmt GhcTc]+ -> TM [ExprLStmt GhcTc]+addTickLStmts isGuard stmts = do+ (stmts, _) <- addTickLStmts' isGuard stmts (return ())+ return stmts++addTickLStmts' :: (Maybe (Bool -> BoxLabel)) -> [ExprLStmt GhcTc] -> TM a+ -> TM ([ExprLStmt GhcTc], a)+addTickLStmts' isGuard lstmts res+ = bindLocals (collectLStmtsBinders CollNoDictBinders lstmts) $+ do { lstmts' <- mapM (traverse (addTickStmt isGuard)) lstmts+ ; a <- res+ ; return (lstmts', a) }++addTickStmt :: (Maybe (Bool -> BoxLabel)) -> Stmt GhcTc (LHsExpr GhcTc)+ -> TM (Stmt GhcTc (LHsExpr GhcTc))+addTickStmt _isGuard (LastStmt x e noret ret) =+ liftM3 (LastStmt x)+ (addTickLHsExpr e)+ (pure noret)+ (addTickSyntaxExpr hpcSrcSpan ret)+addTickStmt _isGuard (BindStmt xbs pat e) =+ liftM4 (\b f -> BindStmt $ XBindStmtTc+ { xbstc_bindOp = b+ , xbstc_boundResultType = xbstc_boundResultType xbs+ , xbstc_boundResultMult = xbstc_boundResultMult xbs+ , xbstc_failOp = f+ })+ (addTickSyntaxExpr hpcSrcSpan (xbstc_bindOp xbs))+ (mapM (addTickSyntaxExpr hpcSrcSpan) (xbstc_failOp xbs))+ (addTickLPat pat)+ (addTickLHsExprRHS e)+addTickStmt isGuard (BodyStmt x e bind' guard') =+ liftM3 (BodyStmt x)+ (addTick isGuard e)+ (addTickSyntaxExpr hpcSrcSpan bind')+ (addTickSyntaxExpr hpcSrcSpan guard')+addTickStmt _isGuard (LetStmt x binds) =+ liftM (LetStmt x)+ (addTickHsLocalBinds binds)+addTickStmt isGuard (ParStmt x pairs mzipExpr bindExpr) =+ liftM3 (ParStmt x)+ (mapM (addTickStmtAndBinders isGuard) pairs)+ (unLoc <$> addTickLHsExpr (L (noAnnSrcSpan hpcSrcSpan) mzipExpr))+ (addTickSyntaxExpr hpcSrcSpan bindExpr)+addTickStmt isGuard (ApplicativeStmt body_ty args mb_join) = do+ args' <- mapM (addTickApplicativeArg isGuard) args+ return (ApplicativeStmt body_ty args' mb_join)++addTickStmt isGuard stmt@(TransStmt { trS_stmts = stmts+ , trS_by = by, trS_using = using+ , trS_ret = returnExpr, trS_bind = bindExpr+ , trS_fmap = liftMExpr }) = do+ t_s <- addTickLStmts isGuard stmts+ t_y <- traverse addTickLHsExprRHS by+ t_u <- addTickLHsExprRHS using+ t_f <- addTickSyntaxExpr hpcSrcSpan returnExpr+ t_b <- addTickSyntaxExpr hpcSrcSpan bindExpr+ t_m <- fmap unLoc (addTickLHsExpr (L (noAnnSrcSpan hpcSrcSpan) liftMExpr))+ return $ stmt { trS_stmts = t_s, trS_by = t_y, trS_using = t_u+ , trS_ret = t_f, trS_bind = t_b, trS_fmap = t_m }++addTickStmt isGuard stmt@(RecStmt {})+ = do { stmts' <- addTickLStmts isGuard (unLoc $ recS_stmts stmt)+ ; ret' <- addTickSyntaxExpr hpcSrcSpan (recS_ret_fn stmt)+ ; mfix' <- addTickSyntaxExpr hpcSrcSpan (recS_mfix_fn stmt)+ ; bind' <- addTickSyntaxExpr hpcSrcSpan (recS_bind_fn stmt)+ ; return (stmt { recS_stmts = noLocA stmts', recS_ret_fn = ret'+ , recS_mfix_fn = mfix', recS_bind_fn = bind' }) }++addTick :: Maybe (Bool -> BoxLabel) -> LHsExpr GhcTc -> TM (LHsExpr GhcTc)+addTick isGuard e | Just fn <- isGuard = addBinTickLHsExpr fn e+ | otherwise = addTickLHsExprRHS e++addTickApplicativeArg+ :: Maybe (Bool -> BoxLabel) -> (SyntaxExpr GhcTc, ApplicativeArg GhcTc)+ -> TM (SyntaxExpr GhcTc, ApplicativeArg GhcTc)+addTickApplicativeArg isGuard (op, arg) =+ liftM2 (,) (addTickSyntaxExpr hpcSrcSpan op) (addTickArg arg)+ where+ addTickArg (ApplicativeArgOne m_fail pat expr isBody) =+ ApplicativeArgOne+ <$> mapM (addTickSyntaxExpr hpcSrcSpan) m_fail+ <*> addTickLPat pat+ <*> addTickLHsExpr expr+ <*> pure isBody+ addTickArg (ApplicativeArgMany x stmts ret pat ctxt) =+ (ApplicativeArgMany x)+ <$> addTickLStmts isGuard stmts+ <*> (unLoc <$> addTickLHsExpr (L (noAnnSrcSpan hpcSrcSpan) ret))+ <*> addTickLPat pat+ <*> pure ctxt++addTickStmtAndBinders :: Maybe (Bool -> BoxLabel) -> ParStmtBlock GhcTc GhcTc+ -> TM (ParStmtBlock GhcTc GhcTc)+addTickStmtAndBinders isGuard (ParStmtBlock x stmts ids returnExpr) =+ liftM3 (ParStmtBlock x)+ (addTickLStmts isGuard stmts)+ (return ids)+ (addTickSyntaxExpr hpcSrcSpan returnExpr)++addTickHsLocalBinds :: HsLocalBinds GhcTc -> TM (HsLocalBinds GhcTc)+addTickHsLocalBinds (HsValBinds x binds) =+ liftM (HsValBinds x)+ (addTickHsValBinds binds)+addTickHsLocalBinds (HsIPBinds x binds) =+ liftM (HsIPBinds x)+ (addTickHsIPBinds binds)+addTickHsLocalBinds (EmptyLocalBinds x) = return (EmptyLocalBinds x)++addTickHsValBinds :: HsValBindsLR GhcTc (GhcPass a)+ -> TM (HsValBindsLR GhcTc (GhcPass b))+addTickHsValBinds (XValBindsLR (NValBinds binds sigs)) = do+ b <- liftM2 NValBinds+ (mapM (\ (rec,binds') ->+ liftM2 (,)+ (return rec)+ (addTickLHsBinds binds'))+ binds)+ (return sigs)+ return $ XValBindsLR b+addTickHsValBinds _ = panic "addTickHsValBinds"++addTickHsIPBinds :: HsIPBinds GhcTc -> TM (HsIPBinds GhcTc)+addTickHsIPBinds (IPBinds dictbinds ipbinds) =+ liftM2 IPBinds+ (return dictbinds)+ (mapM (traverse (addTickIPBind)) ipbinds)++addTickIPBind :: IPBind GhcTc -> TM (IPBind GhcTc)+addTickIPBind (IPBind x nm e) =+ liftM (IPBind x nm)+ (addTickLHsExpr e)++-- There is no location here, so we might need to use a context location??+addTickSyntaxExpr :: SrcSpan -> SyntaxExpr GhcTc -> TM (SyntaxExpr GhcTc)+addTickSyntaxExpr pos syn@(SyntaxExprTc { syn_expr = x }) = do+ x' <- fmap unLoc (addTickLHsExpr (L (noAnnSrcSpan pos) x))+ return $ syn { syn_expr = x' }+addTickSyntaxExpr _ NoSyntaxExprTc = return NoSyntaxExprTc++-- we do not walk into patterns.+addTickLPat :: LPat GhcTc -> TM (LPat GhcTc)+addTickLPat pat = return pat++addTickHsCmdTop :: HsCmdTop GhcTc -> TM (HsCmdTop GhcTc)+addTickHsCmdTop (HsCmdTop x cmd) =+ liftM2 HsCmdTop+ (return x)+ (addTickLHsCmd cmd)++addTickLHsCmd :: LHsCmd GhcTc -> TM (LHsCmd GhcTc)+addTickLHsCmd (L pos c0) = do+ c1 <- addTickHsCmd c0+ return $ L pos c1++addTickHsCmd :: HsCmd GhcTc -> TM (HsCmd GhcTc)+addTickHsCmd (HsCmdLam x matchgroup) =+ liftM (HsCmdLam x) (addTickCmdMatchGroup matchgroup)+addTickHsCmd (HsCmdApp x c e) =+ liftM2 (HsCmdApp x) (addTickLHsCmd c) (addTickLHsExpr e)+{-+addTickHsCmd (OpApp e1 c2 fix c3) =+ liftM4 OpApp+ (addTickLHsExpr e1)+ (addTickLHsCmd c2)+ (return fix)+ (addTickLHsCmd c3)+-}+addTickHsCmd (HsCmdPar x lpar e rpar) = do+ e' <- addTickLHsCmd e+ return (HsCmdPar x lpar e' rpar)+addTickHsCmd (HsCmdCase x e mgs) =+ liftM2 (HsCmdCase x)+ (addTickLHsExpr e)+ (addTickCmdMatchGroup mgs)+addTickHsCmd (HsCmdLamCase x lc_variant mgs) =+ liftM (HsCmdLamCase x lc_variant) (addTickCmdMatchGroup mgs)+addTickHsCmd (HsCmdIf x cnd e1 c2 c3) =+ liftM3 (HsCmdIf x cnd)+ (addBinTickLHsExpr (BinBox CondBinBox) e1)+ (addTickLHsCmd c2)+ (addTickLHsCmd c3)+addTickHsCmd (HsCmdLet x tkLet binds tkIn c) =+ bindLocals (collectLocalBinders CollNoDictBinders binds) $ do+ binds' <- addTickHsLocalBinds binds -- to think about: !patterns.+ c' <- addTickLHsCmd c+ return (HsCmdLet x tkLet binds' tkIn c')+addTickHsCmd (HsCmdDo srcloc (L l stmts))+ = do { (stmts', _) <- addTickLCmdStmts' stmts (return ())+ ; return (HsCmdDo srcloc (L l stmts')) }++addTickHsCmd (HsCmdArrApp arr_ty e1 e2 ty1 lr) =+ liftM5 HsCmdArrApp+ (return arr_ty)+ (addTickLHsExpr e1)+ (addTickLHsExpr e2)+ (return ty1)+ (return lr)+addTickHsCmd (HsCmdArrForm x e f fix cmdtop) =+ liftM4 (HsCmdArrForm x)+ (addTickLHsExpr e)+ (return f)+ (return fix)+ (mapM (traverse (addTickHsCmdTop)) cmdtop)++addTickHsCmd (XCmd (HsWrap w cmd)) =+ liftM XCmd $+ liftM (HsWrap w) (addTickHsCmd cmd)++-- Others should never happen in a command context.+--addTickHsCmd e = pprPanic "addTickHsCmd" (ppr e)++addTickCmdMatchGroup :: MatchGroup GhcTc (LHsCmd GhcTc)+ -> TM (MatchGroup GhcTc (LHsCmd GhcTc))+addTickCmdMatchGroup mg@(MG { mg_alts = (L l matches) }) = do+ matches' <- mapM (traverse addTickCmdMatch) matches+ return $ mg { mg_alts = L l matches' }++addTickCmdMatch :: Match GhcTc (LHsCmd GhcTc) -> TM (Match GhcTc (LHsCmd GhcTc))+addTickCmdMatch match@(Match { m_pats = pats, m_grhss = gRHSs }) =+ bindLocals (collectPatsBinders CollNoDictBinders pats) $ do+ gRHSs' <- addTickCmdGRHSs gRHSs+ return $ match { m_grhss = gRHSs' }++addTickCmdGRHSs :: GRHSs GhcTc (LHsCmd GhcTc) -> TM (GRHSs GhcTc (LHsCmd GhcTc))+addTickCmdGRHSs (GRHSs x guarded local_binds) =+ bindLocals binders $ do+ local_binds' <- addTickHsLocalBinds local_binds+ guarded' <- mapM (traverse addTickCmdGRHS) guarded+ return $ GRHSs x guarded' local_binds'+ where+ binders = collectLocalBinders CollNoDictBinders local_binds++addTickCmdGRHS :: GRHS GhcTc (LHsCmd GhcTc) -> TM (GRHS GhcTc (LHsCmd GhcTc))+-- The *guards* are *not* Cmds, although the body is+-- C.f. addTickGRHS for the BinBox stuff+addTickCmdGRHS (GRHS x stmts cmd)+ = do { (stmts',expr') <- addTickLStmts' (Just $ BinBox $ GuardBinBox)+ stmts (addTickLHsCmd cmd)+ ; return $ GRHS x stmts' expr' }++addTickLCmdStmts :: [LStmt GhcTc (LHsCmd GhcTc)]+ -> TM [LStmt GhcTc (LHsCmd GhcTc)]+addTickLCmdStmts stmts = do+ (stmts, _) <- addTickLCmdStmts' stmts (return ())+ return stmts++addTickLCmdStmts' :: [LStmt GhcTc (LHsCmd GhcTc)] -> TM a+ -> TM ([LStmt GhcTc (LHsCmd GhcTc)], a)+addTickLCmdStmts' lstmts res+ = bindLocals binders $ do+ lstmts' <- mapM (traverse addTickCmdStmt) lstmts+ a <- res+ return (lstmts', a)+ where+ binders = collectLStmtsBinders CollNoDictBinders lstmts++addTickCmdStmt :: Stmt GhcTc (LHsCmd GhcTc) -> TM (Stmt GhcTc (LHsCmd GhcTc))+addTickCmdStmt (BindStmt x pat c) =+ liftM2 (BindStmt x)+ (addTickLPat pat)+ (addTickLHsCmd c)+addTickCmdStmt (LastStmt x c noret ret) =+ liftM3 (LastStmt x)+ (addTickLHsCmd c)+ (pure noret)+ (addTickSyntaxExpr hpcSrcSpan ret)+addTickCmdStmt (BodyStmt x c bind' guard') =+ liftM3 (BodyStmt x)+ (addTickLHsCmd c)+ (addTickSyntaxExpr hpcSrcSpan bind')+ (addTickSyntaxExpr hpcSrcSpan guard')+addTickCmdStmt (LetStmt x binds) =+ liftM (LetStmt x)+ (addTickHsLocalBinds binds)+addTickCmdStmt stmt@(RecStmt {})+ = do { stmts' <- addTickLCmdStmts (unLoc $ recS_stmts stmt)+ ; ret' <- addTickSyntaxExpr hpcSrcSpan (recS_ret_fn stmt)+ ; mfix' <- addTickSyntaxExpr hpcSrcSpan (recS_mfix_fn stmt)+ ; bind' <- addTickSyntaxExpr hpcSrcSpan (recS_bind_fn stmt)+ ; return (stmt { recS_stmts = noLocA stmts', recS_ret_fn = ret'+ , recS_mfix_fn = mfix', recS_bind_fn = bind' }) }+addTickCmdStmt ApplicativeStmt{} =+ panic "ToDo: addTickCmdStmt ApplicativeLastStmt"++-- Others should never happen in a command context.+addTickCmdStmt stmt = pprPanic "addTickHsCmd" (ppr stmt)++addTickHsRecordBinds :: HsRecordBinds GhcTc -> TM (HsRecordBinds GhcTc)+addTickHsRecordBinds (HsRecFields fields dd)+ = do { fields' <- mapM addTickHsRecField fields+ ; return (HsRecFields fields' dd) }++addTickHsRecField :: LHsFieldBind GhcTc id (LHsExpr GhcTc)+ -> TM (LHsFieldBind GhcTc id (LHsExpr GhcTc))+addTickHsRecField (L l (HsFieldBind x id expr pun))+ = do { expr' <- addTickLHsExpr expr+ ; return (L l (HsFieldBind x id expr' pun)) }++addTickArithSeqInfo :: ArithSeqInfo GhcTc -> TM (ArithSeqInfo GhcTc)+addTickArithSeqInfo (From e1) =+ liftM From+ (addTickLHsExpr e1)+addTickArithSeqInfo (FromThen e1 e2) =+ liftM2 FromThen+ (addTickLHsExpr e1)+ (addTickLHsExpr e2)+addTickArithSeqInfo (FromTo e1 e2) =+ liftM2 FromTo+ (addTickLHsExpr e1)+ (addTickLHsExpr e2)+addTickArithSeqInfo (FromThenTo e1 e2 e3) =+ liftM3 FromThenTo+ (addTickLHsExpr e1)+ (addTickLHsExpr e2)+ (addTickLHsExpr e3)++data TickTransState = TT { ticks :: !(SizedSeq Tick)+ , ccIndices :: !CostCentreState+ }++initTTState :: TickTransState+initTTState = TT { ticks = emptySS+ , ccIndices = newCostCentreState+ }++addMixEntry :: Tick -> TM Int+addMixEntry ent = do+ c <- fromIntegral . sizeSS . ticks <$> getState+ setState $ \st ->+ st { ticks = addToSS (ticks st) ent+ }+ return c++data TickTransEnv = TTE { fileName :: FastString+ , density :: TickDensity+ , tte_countEntries :: !Bool+ -- ^ Whether the number of times functions are+ -- entered should be counted.+ , exports :: NameSet+ , inlines :: VarSet+ , declPath :: [String]+ , inScope :: VarSet+ , blackList :: Set RealSrcSpan+ , this_mod :: Module+ , tickishType :: TickishType+ }++-- deriving Show++-- | Reasons why we need ticks,+data TickishType+ -- | For profiling+ = ProfNotes+ -- | For Haskell Program Coverage+ | HpcTicks+ -- | For ByteCode interpreter break points+ | Breakpoints+ -- | For source notes+ | SourceNotes+ deriving (Eq)++-- | Tickishs that only make sense when their source code location+-- refers to the current file. This might not always be true due to+-- LINE pragmas in the code - which would confuse at least HPC.+tickSameFileOnly :: TickishType -> Bool+tickSameFileOnly HpcTicks = True+tickSameFileOnly _other = False++type FreeVars = OccEnv Id+noFVs :: FreeVars+noFVs = emptyOccEnv++-- Note [freevars]+-- ~~~~~~~~~~~~~~~+-- For breakpoints we want to collect the free variables of an+-- expression for pinning on the HsTick. We don't want to collect+-- *all* free variables though: in particular there's no point pinning+-- on free variables that are will otherwise be in scope at the GHCi+-- prompt, which means all top-level bindings. Unfortunately detecting+-- top-level bindings isn't easy (collectHsBindsBinders on the top-level+-- bindings doesn't do it), so we keep track of a set of "in-scope"+-- variables in addition to the free variables, and the former is used+-- to filter additions to the latter. This gives us complete control+-- over what free variables we track.++newtype TM a = TM { unTM :: TickTransEnv -> TickTransState -> (a,FreeVars,TickTransState) }+ deriving (Functor)+ -- a combination of a state monad (TickTransState) and a writer+ -- monad (FreeVars).++instance Applicative TM where+ pure a = TM $ \ _env st -> (a,noFVs,st)+ (<*>) = ap++instance Monad TM where+ (TM m) >>= k = TM $ \ env st ->+ case m env st of+ (r1,fv1,st1) ->+ case unTM (k r1) env st1 of+ (r2,fv2,st2) ->+ (r2, fv1 `plusOccEnv` fv2, st2)++-- | Get the next HPC cost centre index for a given centre name+getCCIndexM :: FastString -> TM CostCentreIndex+getCCIndexM n = TM $ \_ st -> let (idx, is') = getCCIndex n $+ ccIndices st+ in (idx, noFVs, st { ccIndices = is' })++getState :: TM TickTransState+getState = TM $ \ _ st -> (st, noFVs, st)++setState :: (TickTransState -> TickTransState) -> TM ()+setState f = TM $ \ _ st -> ((), noFVs, f st)++getEnv :: TM TickTransEnv+getEnv = TM $ \ env st -> (env, noFVs, st)++withEnv :: (TickTransEnv -> TickTransEnv) -> TM a -> TM a+withEnv f (TM m) = TM $ \ env st ->+ case m (f env) st of+ (a, fvs, st') -> (a, fvs, st')++getDensity :: TM TickDensity+getDensity = TM $ \env st -> (density env, noFVs, st)++ifDensity :: TickDensity -> TM a -> TM a -> TM a+ifDensity d th el = do d0 <- getDensity; if d == d0 then th else el++getFreeVars :: TM a -> TM (FreeVars, a)+getFreeVars (TM m)+ = TM $ \ env st -> case m env st of (a, fv, st') -> ((fv,a), fv, st')++freeVar :: Id -> TM ()+freeVar id = TM $ \ env st ->+ if id `elemVarSet` inScope env+ then ((), unitOccEnv (nameOccName (idName id)) id, st)+ else ((), noFVs, st)++addPathEntry :: String -> TM a -> TM a+addPathEntry nm = withEnv (\ env -> env { declPath = declPath env ++ [nm] })++getPathEntry :: TM [String]+getPathEntry = declPath `liftM` getEnv++getFileName :: TM FastString+getFileName = fileName `liftM` getEnv++isGoodSrcSpan' :: SrcSpan -> Bool+isGoodSrcSpan' pos@(RealSrcSpan _ _) = srcSpanStart pos /= srcSpanEnd pos+isGoodSrcSpan' (UnhelpfulSpan _) = False++isGoodTickSrcSpan :: SrcSpan -> TM Bool+isGoodTickSrcSpan pos = do+ file_name <- getFileName+ tickish <- tickishType `liftM` getEnv+ let need_same_file = tickSameFileOnly tickish+ same_file = Just file_name == srcSpanFileName_maybe pos+ return (isGoodSrcSpan' pos && (not need_same_file || same_file))++ifGoodTickSrcSpan :: SrcSpan -> TM a -> TM a -> TM a+ifGoodTickSrcSpan pos then_code else_code = do+ good <- isGoodTickSrcSpan pos+ if good then then_code else else_code++bindLocals :: [Id] -> TM a -> TM a+bindLocals new_ids (TM m)+ = TM $ \ env st ->+ case m env{ inScope = inScope env `extendVarSetList` new_ids } st of+ (r, fv, st') -> (r, fv `delListFromOccEnv` occs, st')+ where occs = [ nameOccName (idName id) | id <- new_ids ]++isBlackListed :: SrcSpan -> TM Bool+isBlackListed (RealSrcSpan pos _) = TM $ \ env st -> (Set.member pos (blackList env), noFVs, st)+isBlackListed (UnhelpfulSpan _) = return False++-- the tick application inherits the source position of its+-- expression argument to support nested box allocations+allocTickBox :: BoxLabel -> Bool -> Bool -> SrcSpan -> TM (HsExpr GhcTc)+ -> TM (LHsExpr GhcTc)+allocTickBox boxLabel countEntries topOnly pos m =+ ifGoodTickSrcSpan pos (do+ (fvs, e) <- getFreeVars m+ env <- getEnv+ tickish <- mkTickish boxLabel countEntries topOnly pos fvs (declPath env)+ return (L (noAnnSrcSpan pos) (XExpr $ HsTick tickish $ L (noAnnSrcSpan pos) e))+ ) (do+ e <- m+ return (L (noAnnSrcSpan pos) e)+ )++-- the tick application inherits the source position of its+-- expression argument to support nested box allocations+allocATickBox :: BoxLabel -> Bool -> Bool -> SrcSpan -> FreeVars+ -> TM (Maybe CoreTickish)+allocATickBox boxLabel countEntries topOnly pos fvs =+ ifGoodTickSrcSpan pos (do+ let+ mydecl_path = case boxLabel of+ TopLevelBox x -> x+ LocalBox xs -> xs+ _ -> panic "allocATickBox"+ tickish <- mkTickish boxLabel countEntries topOnly pos fvs mydecl_path+ return (Just tickish)+ ) (return Nothing)+++mkTickish :: BoxLabel -> Bool -> Bool -> SrcSpan -> OccEnv Id -> [String]+ -> TM CoreTickish+mkTickish boxLabel countEntries topOnly pos fvs decl_path = do++ let ids = filter (not . mightBeUnliftedType . idType) $ nonDetOccEnvElts fvs+ -- unlifted types cause two problems here:+ -- * we can't bind them at the GHCi prompt+ -- (bindLocalsAtBreakpoint already filters them out),+ -- * the simplifier might try to substitute a literal for+ -- the Id, and we can't handle that.++ me = Tick+ { tick_loc = pos+ , tick_path = decl_path+ , tick_ids = map (nameOccName.idName) ids+ , tick_label = boxLabel+ }++ cc_name | topOnly = head decl_path+ | otherwise = concat (intersperse "." decl_path)++ env <- getEnv+ case tickishType env of+ HpcTicks -> HpcTick (this_mod env) <$> addMixEntry me++ ProfNotes -> do+ let nm = mkFastString cc_name+ flavour <- mkHpcCCFlavour <$> getCCIndexM nm+ let cc = mkUserCC nm (this_mod env) pos flavour+ count = countEntries && tte_countEntries env+ return $ ProfNote cc count True{-scopes-}++ Breakpoints -> Breakpoint noExtField <$> addMixEntry me <*> pure ids++ SourceNotes | RealSrcSpan pos' _ <- pos ->+ return $ SourceNote pos' cc_name++ _otherwise -> panic "mkTickish: bad source span!"+++allocBinTickBox :: (Bool -> BoxLabel) -> SrcSpan -> TM (HsExpr GhcTc)+ -> TM (LHsExpr GhcTc)+allocBinTickBox boxLabel pos m = do+ env <- getEnv+ case tickishType env of+ HpcTicks -> do e <- liftM (L (noAnnSrcSpan pos)) m+ ifGoodTickSrcSpan pos+ (mkBinTickBoxHpc boxLabel pos e)+ (return e)+ _other -> allocTickBox (ExpBox False) False False pos m++mkBinTickBoxHpc :: (Bool -> BoxLabel) -> SrcSpan -> LHsExpr GhcTc+ -> TM (LHsExpr GhcTc)+mkBinTickBoxHpc boxLabel pos e = do+ env <- getEnv+ binTick <- HsBinTick+ <$> addMixEntry (Tick { tick_loc = pos+ , tick_path = declPath env+ , tick_ids = []+ , tick_label = boxLabel True+ })+ <*> addMixEntry (Tick { tick_loc = pos+ , tick_path = declPath env+ , tick_ids = []+ , tick_label = boxLabel False+ })+ <*> pure e+ tick <- HpcTick (this_mod env)+ <$> addMixEntry (Tick { tick_loc = pos+ , tick_path = declPath env+ , tick_ids = []+ , tick_label = ExpBox False+ })+ let pos' = noAnnSrcSpan pos+ return $ L pos' $ XExpr $ HsTick tick (L pos' (XExpr binTick))++hpcSrcSpan :: SrcSpan+hpcSrcSpan = mkGeneralSrcSpan (fsLit "Haskell Program Coverage internals")++matchesOneOfMany :: [LMatch GhcTc body] -> Bool+matchesOneOfMany lmatches = sum (map matchCount lmatches) > 1+ where+ matchCount :: LMatch GhcTc body -> Int+ matchCount (L _ (Match { m_grhss = GRHSs _ grhss _ }))+ = length grhss
compiler/GHC/HsToCore/Types.hs view
@@ -49,7 +49,7 @@ , ds_gbl_rdr_env :: GlobalRdrEnv -- needed *only* to know what newtype -- constructors are in scope during -- pattern-match satisfiability checking- , ds_unqual :: PrintUnqualified+ , ds_name_ppr_ctx :: NamePprCtx , ds_msgs :: IORef (Messages DsMessage) -- Diagnostic messages , ds_if_env :: (IfGblEnv, IfLclEnv) -- Used for looking up global, -- possibly-imported things
compiler/GHC/HsToCore/Usage.hs view
@@ -5,26 +5,30 @@ module GHC.HsToCore.Usage ( -- * Dependency/fingerprinting code (used by GHC.Iface.Make) mkUsageInfo, mkUsedNames,++ UsageConfig(..), ) where import GHC.Prelude import GHC.Driver.Env-import GHC.Driver.Session - import GHC.Tc.Types +import GHC.Iface.Load+ import GHC.Utils.Outputable import GHC.Utils.Misc import GHC.Utils.Fingerprint import GHC.Utils.Panic+import GHC.Utils.Monad import GHC.Types.Name import GHC.Types.Name.Set ( NameSet, allUses ) import GHC.Types.Unique.Set import GHC.Unit+import GHC.Unit.Env import GHC.Unit.External import GHC.Unit.Module.Imported import GHC.Unit.Module.ModIface@@ -32,6 +36,7 @@ import GHC.Data.Maybe +import Data.IORef import Data.List (sortBy) import Data.Map (Map) import qualified Data.Map as Map@@ -64,17 +69,24 @@ mkUsedNames :: TcGblEnv -> NameSet mkUsedNames TcGblEnv{ tcg_dus = dus } = allUses dus -mkUsageInfo :: HscEnv -> Module -> ImportedMods -> NameSet -> [FilePath]- -> [(Module, Fingerprint)] -> [Linkable] -> PkgsLoaded -> IO [Usage]-mkUsageInfo hsc_env this_mod dir_imp_mods used_names dependent_files merged needed_links needed_pkgs+data UsageConfig = UsageConfig+ { uc_safe_implicit_imps_req :: !Bool -- ^ Are all implicit imports required to be safe for this Safe Haskell mode?+ }++mkUsageInfo :: UsageConfig -> Plugins -> FinderCache -> UnitEnv -> Module -> ImportedMods -> NameSet -> [FilePath]+ -> [(Module, Fingerprint)] -> [Linkable] -> PkgsLoaded -> IfG [Usage]+mkUsageInfo uc plugins fc unit_env this_mod dir_imp_mods used_names dependent_files merged needed_links needed_pkgs = do- eps <- hscEPS hsc_env- hashes <- mapM getFileHash dependent_files+ eps <- liftIO $ readIORef (euc_eps (ue_eps unit_env))+ hashes <- liftIO $ mapM getFileHash dependent_files+ let hu = unsafeGetHomeUnit unit_env+ hug = ue_home_unit_graph unit_env -- Dependencies on object files due to TH and plugins- object_usages <- mkObjectUsage (eps_PIT eps) hsc_env needed_links needed_pkgs- let mod_usages = mk_mod_usage_info (eps_PIT eps) hsc_env this_mod+ object_usages <- liftIO $ mkObjectUsage (eps_PIT eps) plugins fc hug needed_links needed_pkgs+ let all_home_ids = ue_all_home_unit_ids unit_env+ mod_usages <- mk_mod_usage_info uc hu all_home_ids this_mod dir_imp_mods used_names- usages = mod_usages ++ [ UsageFile { usg_file_path = f+ let usages = mod_usages ++ [ UsageFile { usg_file_path = f , usg_file_hash = hash , usg_file_label = Nothing } | (f, hash) <- zip dependent_files hashes ]@@ -151,18 +163,18 @@ -- | Find object files corresponding to the transitive closure of given home -- modules and direct object files for pkg dependencies-mkObjectUsage :: PackageIfaceTable -> HscEnv -> [Linkable] -> PkgsLoaded -> IO [Usage]-mkObjectUsage pit hsc_env th_links_needed th_pkgs_needed = do+mkObjectUsage :: PackageIfaceTable -> Plugins -> FinderCache -> HomeUnitGraph-> [Linkable] -> PkgsLoaded -> IO [Usage]+mkObjectUsage pit plugins fc hug th_links_needed th_pkgs_needed = do let ls = ordNubOn linkableModule (th_links_needed ++ plugins_links_needed) ds = concatMap loaded_pkg_hs_objs $ eltsUDFM (plusUDFM th_pkgs_needed plugin_pkgs_needed) -- TODO possibly record loaded_pkg_non_hs_objs as well- (plugins_links_needed, plugin_pkgs_needed) = loadedPluginDeps $ hsc_plugins hsc_env+ (plugins_links_needed, plugin_pkgs_needed) = loadedPluginDeps plugins concat <$> sequence (map linkableToUsage ls ++ map librarySpecToUsage ds) where linkableToUsage (LM _ m uls) = mapM (unlinkedToUsage m) uls msg m = moduleNameString (moduleName m) ++ "[TH] changed" - fing mmsg fn = UsageFile fn <$> lookupFileCache (hsc_FC hsc_env) fn <*> pure mmsg+ fing mmsg fn = UsageFile fn <$> lookupFileCache fc fn <*> pure mmsg unlinkedToUsage m ul = case nameOfObject_maybe ul of@@ -170,7 +182,7 @@ Nothing -> do -- This should only happen for home package things but oneshot puts -- home package ifaces in the PIT.- let miface = lookupIfaceByModule (hsc_HUG hsc_env) pit m+ let miface = lookupIfaceByModule hug pit m case miface of Nothing -> pprPanic "mkObjectUsage" (ppr m) Just iface ->@@ -182,19 +194,17 @@ librarySpecToUsage (DLLPath fn) = traverse (fing Nothing) [fn] librarySpecToUsage _ = return [] -mk_mod_usage_info :: PackageIfaceTable- -> HscEnv+mk_mod_usage_info :: UsageConfig+ -> HomeUnit+ -> Set.Set UnitId -> Module -> ImportedMods -> NameSet- -> [Usage]-mk_mod_usage_info pit hsc_env this_mod direct_imports used_names- = mapMaybe mkUsage usage_mods+ -> IfG [Usage]+mk_mod_usage_info uc home_unit home_unit_ids this_mod direct_imports used_names+ = mapMaybeM mkUsageM usage_mods where- hpt = hsc_HUG hsc_env- dflags = hsc_dflags hsc_env- home_unit = hsc_home_unit hsc_env- home_unit_ids = hsc_all_home_unit_ids hsc_env+ safe_implicit_imps_req = uc_safe_implicit_imps_req uc used_mods = moduleEnvKeys ent_map dir_imp_mods = moduleEnvKeys direct_imports@@ -228,22 +238,27 @@ in extendModuleEnvWith (\_ xs -> occ:xs) mv_map mod' [occ] where occ = nameOccName name + mkUsageM :: Module -> IfG (Maybe Usage)+ mkUsageM mod | mod == this_mod -- We don't care about usages of things in *this* module+ || moduleUnit mod == interactiveUnit -- ... or in GHCi+ = return Nothing+ mkUsageM mod = do+ iface <- loadSysInterface (text "mk_mod_usage") mod+ -- Make sure the interface is loaded even if we don't directly use+ -- any symbols from it, to ensure determinism. See #22217.+ return $ mkUsage mod iface++ -- We want to create a Usage for a home module if -- a) we used something from it; has something in used_names -- b) we imported it, even if we used nothing from it -- (need to recompile if its export list changes: export_fprint)- mkUsage :: Module -> Maybe Usage- mkUsage mod- | isNothing maybe_iface -- We can't depend on it if we didn't- -- load its interface.- || mod == this_mod -- We don't care about usages of- -- things in *this* module- = Nothing-+ mkUsage :: Module -> ModIface -> Maybe Usage+ mkUsage mod iface | toUnitId (moduleUnit mod) `Set.notMember` home_unit_ids- = Just UsagePackageModule{ usg_mod = mod,- usg_mod_hash = mod_hash,- usg_safe = imp_safe }+ = Just $ UsagePackageModule{ usg_mod = mod,+ usg_mod_hash = mod_hash,+ usg_safe = imp_safe } -- for package modules, we record the module hash only | (null used_occs@@ -264,11 +279,6 @@ usg_entities = Map.toList ent_hashs, usg_safe = imp_safe } where- maybe_iface = lookupIfaceByModule hpt pit mod- -- In one-shot mode, the interfaces for home-package- -- modules accumulate in the PIT not HPT. Sigh.-- Just iface = maybe_iface finsts_mod = mi_finsts (mi_final_exts iface) hash_env = mi_hash_fn (mi_final_exts iface) mod_hash = mi_mod_hash (mi_final_exts iface)@@ -284,7 +294,7 @@ -- across all imports, why did the old code only look -- at the first import? Just bys -> (True, any by_is_safe bys)- Nothing -> (False, safeImplicitImpsReq dflags)+ Nothing -> (False, safe_implicit_imps_req) -- Nothing case is for references to entities which were -- not directly imported (NB: the "implicit" Prelude import -- counts as directly imported! An entity is not directly
compiler/GHC/HsToCore/Utils.hs view
@@ -46,12 +46,13 @@ import GHC.Prelude +import Language.Haskell.Syntax.Basic (Boxity(..))+ import {-# SOURCE #-} GHC.HsToCore.Match ( matchSimply ) import {-# SOURCE #-} GHC.HsToCore.Expr ( dsLExpr, dsSyntaxExpr ) import GHC.Hs import GHC.Hs.Syn.Type-import GHC.Tc.Utils.TcType( tcSplitTyConApp ) import GHC.Core import GHC.HsToCore.Monad @@ -66,7 +67,6 @@ import GHC.Core.Type import GHC.Core.Coercion import GHC.Builtin.Types-import GHC.Types.Basic import GHC.Core.ConLike import GHC.Types.Unique.Set import GHC.Types.Unique.Supply@@ -142,7 +142,7 @@ -- multiplicity stored within the variable -- itself. It's easier to pull it from the -- variable, so we ignore the multiplicity.-selectMatchVar _w (AsPat _ var _) = assert (isManyDataConTy _w ) (return (unLoc var))+selectMatchVar _w (AsPat _ var _ _) = assert (isManyTy _w ) (return (unLoc var)) selectMatchVar w other_pat = newSysLocalDs w (hsPatType other_pat) {- Note [Localise pattern binders]@@ -421,7 +421,7 @@ Adding 'f x False = error "Non-exhaustive pattern..."' would violate the linearity of x.-Instead, we use 'f x False = case error "Non-exhausive pattern..." :: () of {}'.+Instead, we use 'f x False = case error "Non-exhaustive pattern..." :: () of {}'. This case expression accounts for linear variables by assigning bottom usage (See Note [Bottom as a usage] in GHC.Core.Multiplicity). This is done in mkErrorAppDs, called from mkFailExpr.@@ -471,14 +471,12 @@ Consider, f x y = x `seq` (y `seq` (# x,y #)) - The [Core let/app invariant] means that, other things being equal, because- the argument to the outer 'seq' has an unlifted type, we'll use call-by-value thus:+ Because the argument to the outer 'seq' has an unlifted type, we'll use+ call-by-value, and compile it as if we had f x y = case (y `seq` (# x,y #)) of v -> x `seq` v - But that is bad for two reasons:- (a) we now evaluate y before x, and- (b) we can't bind v to an unboxed pair+ But that is bad, because we now evaluate y before x! Seq is very, very special! So we recognise it right here, and desugar to case x of _ -> case y of _ -> (# x,y #)@@ -495,7 +493,7 @@ let chp = case b of { True -> fst x; False -> 0 } case chp of _ { I# -> ...chp... } - But since chp is cheap, and the case is an alluring contet, we'll+ But since chp is cheap, and the case is an alluring context, we'll inline chp into the case scrutinee. Now there is only one use of chp, so we'll inline a second copy. Alas, we've now ruined the purpose of the seq, by re-introducing the space leak:@@ -541,7 +539,7 @@ case_bndr = case arg1 of Var v1 | isInternalName (idName v1) -> v1 -- Note [Desugaring seq], points (2) and (3)- _ -> mkWildValBinder Many ty1+ _ -> mkWildValBinder ManyTy ty1 mkCoreAppDs _ (Var f `App` Type _r) arg | f `hasKey` noinlineIdKey -- See Note [noinlineId magic] in GHC.Types.Id.Make@@ -631,7 +629,7 @@ Note that we return 't' as the variable to force if the pattern is strict (i.e. with -XStrict or an outermost-bang-pattern) - Note that (A) /includes/ the situation where+ Note that (C) /includes/ the situation where * The pattern binds exactly one variable let !(Just (Just x) = e in body@@ -641,7 +639,8 @@ in t `seq` body The 'Solo' is a one-tuple; see Note [One-tuples] in GHC.Builtin.Types Note that forcing 't' makes the pattern match happen,- but does not force 'v'.+ but does not force 'v'. That's why we call `mkBigCoreVarTupSolo`+ in `mkSeletcorBinds` * The pattern binds no variables let !(True,False) = e in body@@ -739,7 +738,7 @@ | is_flat_prod_lpat pat' -- Special case (B) = do { let pat_ty = hsLPatType pat'- ; val_var <- newSysLocalDs Many pat_ty+ ; val_var <- newSysLocalDs ManyTy pat_ty ; let mk_bind tick bndr_var -- (mk_bind sv bv) generates bv = case sv of { pat -> bv }@@ -757,13 +756,13 @@ ; return ( val_var, (val_var, val_expr) : binds) } | otherwise -- General case (C)- = do { tuple_var <- newSysLocalDs Many tuple_ty+ = do { tuple_var <- newSysLocalDs ManyTy tuple_ty ; error_expr <- mkErrorAppDs pAT_ERROR_ID tuple_ty (ppr pat') ; tuple_expr <- matchSimply val_expr PatBindRhs pat local_tuple error_expr ; let mk_tup_bind tick binder = (binder, mkOptTickBox tick $- mkTupleSelector1 local_binders binder+ mkBigTupleSelectorSolo local_binders binder tuple_var (Var tuple_var)) tup_binds = zipWith mk_tup_bind ticks' binders ; return (tuple_var, (tuple_var, tuple_expr) : tup_binds) }@@ -776,7 +775,7 @@ ticks' = ticks ++ repeat [] local_binders = map localiseId binders -- See Note [Localise pattern binders]- local_tuple = mkBigCoreVarTup1 binders+ local_tuple = mkBigCoreVarTupSolo binders tuple_ty = exprType local_tuple strip_bangs :: LPat (GhcPass p) -> LPat (GhcPass p)@@ -822,7 +821,7 @@ mkLHsPatTup :: [LPat GhcTc] -> LPat GhcTc mkLHsPatTup [] = noLocA $ mkVanillaTuplePat [] Boxed mkLHsPatTup [lpat] = lpat-mkLHsPatTup lpats = L (getLoc (head lpats)) $+mkLHsPatTup lpats@(L l _:_) = L l $ mkVanillaTuplePat lpats Boxed mkVanillaTuplePat :: [LPat GhcTc] -> Boxity -> Pat GhcTc@@ -914,11 +913,11 @@ CoreExpr) -- Fail variable applied to realWorld# -- See Note [Failure thunks and CPR] mkFailurePair expr- = do { fail_fun_var <- newFailLocalDs Many (unboxedUnitTy `mkVisFunTyMany` ty)- ; fail_fun_arg <- newSysLocalDs Many unboxedUnitTy+ = do { fail_fun_var <- newFailLocalDs ManyTy (unboxedUnitTy `mkVisFunTyMany` ty)+ ; fail_fun_arg <- newSysLocalDs ManyTy unboxedUnitTy ; let real_arg = setOneShotLambda fail_fun_arg ; return (NonRec fail_fun_var (Lam real_arg expr),- App (Var fail_fun_var) (Var voidPrimId)) }+ App (Var fail_fun_var) unboxedUnitExpr) } where ty = exprType expr @@ -939,8 +938,8 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (This note predates join points as formal entities (hence the quotation marks). We can't use actual join points here (see above); if we did, this would also-solve the CPR problem, since join points don't get CPR'd. See Note [Don't CPR-join points] in GHC.Core.Opt.WorkWrap.)+solve the CPR problem, since join points don't get CPR'd. See Note [Don't w/w+join points for CPR] in GHC.Core.Opt.WorkWrap.) When we make a failure point we ensure that it does not look like a thunk. Example:@@ -998,7 +997,7 @@ mkBinaryTickBox ixT ixF e = do uq <- newUnique this_mod <- getModule- let bndr1 = mkSysLocal (fsLit "t1") uq One boolTy+ let bndr1 = mkSysLocal (fsLit "t1") uq OneTy boolTy -- It's always sufficient to pattern-match on a boolean with -- multiplicity 'One'. let@@ -1067,7 +1066,7 @@ -- Returns Just {..} if we're sure that the expression is True -- I.e. * 'True' datacon -- * 'otherwise' Id--- * Trivial wappings of these+-- * Trivial wrappings of these -- The arguments to Just are any HsTicks that we have found, -- because we still want to tick then, even it they are always evaluated. isTrueLHsExpr (L _ (HsVar _ (L _ v)))
compiler/GHC/Iface/Binary.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE BinaryLiterals, ScopedTypeVariables, BangPatterns #-}+{-# LANGUAGE BinaryLiterals, ScopedTypeVariables #-} -- -- (c) The University of Glasgow 2002-2006@@ -15,7 +15,6 @@ readBinIface, readBinIfaceHeader, getSymtabName,- getDictFastString, CheckHiWay(..), TraceBinIFace(..), getWithUserData,@@ -24,11 +23,8 @@ -- * Internal serialisation functions getSymbolTable, putName,- putDictionary,- putFastString, putSymbolTable, BinSymbolTable(..),- BinDictionary(..) ) where import GHC.Prelude@@ -48,7 +44,6 @@ import GHC.Types.Name.Cache import GHC.Types.SrcLoc import GHC.Platform-import GHC.Data.FastString import GHC.Settings.Constants import GHC.Utils.Fingerprint @@ -118,7 +113,7 @@ check_tag <- get bh let tag = profileBuildTag profile- wantedGot "Way" tag check_tag ppr+ wantedGot "Way" tag check_tag text when (checkHiWay == CheckHiWay) $ errorOnMismatch "mismatched interface file profile tag" tag check_tag @@ -153,31 +148,28 @@ -- Names or FastStrings. getWithUserData :: Binary a => NameCache -> BinHandle -> IO a getWithUserData name_cache bh = do+ bh <- getTables name_cache bh+ get bh++-- | Setup a BinHandle to read something written using putWithTables+--+-- Reading names has the side effect of adding them into the given NameCache.+getTables :: NameCache -> BinHandle -> IO BinHandle+getTables name_cache bh = do -- Read the dictionary -- The next word in the file is a pointer to where the dictionary is -- (probably at the end of the file)- dict_p <- Binary.get bh- data_p <- tellBin bh -- Remember where we are now- seekBin bh dict_p- dict <- getDictionary bh- seekBin bh data_p -- Back to where we were before+ dict <- Binary.forwardGet bh (getDictionary bh) -- Initialise the user-data field of bh- bh <- do- bh <- return $ setUserData bh $ newReadState (error "getSymtabName")- (getDictFastString dict)- symtab_p <- Binary.get bh -- Get the symtab ptr- data_p <- tellBin bh -- Remember where we are now- seekBin bh symtab_p- symtab <- getSymbolTable bh name_cache- seekBin bh data_p -- Back to where we were before+ let bh_fs = setUserData bh $ newReadState (error "getSymtabName")+ (getDictFastString dict) - -- It is only now that we know how to get a Name- return $ setUserData bh $ newReadState (getSymtabName name_cache dict symtab)- (getDictFastString dict)+ symtab <- Binary.forwardGet bh_fs (getSymbolTable bh_fs name_cache) - -- Read the interface file- get bh+ -- It is only now that we know how to get a Name+ return $ setUserData bh $ newReadState (getSymtabName name_cache dict symtab)+ (getDictFastString dict) -- | Write an interface file writeBinIface :: Profile -> TraceBinIFace -> FilePath -> ModIface -> IO ()@@ -211,67 +203,66 @@ -- This segment should be read using `getWithUserData`. putWithUserData :: Binary a => TraceBinIFace -> BinHandle -> a -> IO () putWithUserData traceBinIface bh payload = do- -- Remember where the dictionary pointer will go- dict_p_p <- tellBin bh- -- Placeholder for ptr to dictionary- put_ bh dict_p_p+ (name_count, fs_count, _b) <- putWithTables bh (\bh' -> put bh' payload) - -- Remember where the symbol table pointer will go- symtab_p_p <- tellBin bh- put_ bh symtab_p_p- -- Make some initial state+ case traceBinIface of+ QuietBinIFace -> return ()+ TraceBinIFace printer -> do+ printer (text "writeBinIface:" <+> int name_count+ <+> text "Names")+ printer (text "writeBinIface:" <+> int fs_count+ <+> text "dict entries")++-- | Write name/symbol tables+--+-- 1. setup the given BinHandle with Name/FastString table handling+-- 2. write the following+-- - FastString table pointer+-- - Name table pointer+-- - payload+-- - Name table+-- - FastString table+--+-- It returns (number of names, number of FastStrings, payload write result)+--+putWithTables :: BinHandle -> (BinHandle -> IO b) -> IO (Int,Int,b)+putWithTables bh put_payload = do+ -- initialize state for the name table and the FastString table. symtab_next <- newFastMutInt 0 symtab_map <- newIORef emptyUFM- let bin_symtab = BinSymbolTable {- bin_symtab_next = symtab_next,- bin_symtab_map = symtab_map }- dict_next_ref <- newFastMutInt 0- dict_map_ref <- newIORef emptyUFM- let bin_dict = BinDictionary {- bin_dict_next = dict_next_ref,- bin_dict_map = dict_map_ref }+ let bin_symtab = BinSymbolTable+ { bin_symtab_next = symtab_next+ , bin_symtab_map = symtab_map+ } - -- Put the main thing,- bh <- return $ setUserData bh $ newWriteState (putName bin_dict bin_symtab)- (putName bin_dict bin_symtab)- (putFastString bin_dict)- put_ bh payload+ (bh_fs, bin_dict, put_dict) <- initFSTable bh - -- Write the symtab pointer at the front of the file- symtab_p <- tellBin bh -- This is where the symtab will start- putAt bh symtab_p_p symtab_p -- Fill in the placeholder- seekBin bh symtab_p -- Seek back to the end of the file+ (fs_count,(name_count,r)) <- forwardPut bh (const put_dict) $ do - -- Write the symbol table itself- symtab_next <- readFastMutInt symtab_next- symtab_map <- readIORef symtab_map- putSymbolTable bh symtab_next symtab_map- case traceBinIface of- QuietBinIFace -> return ()- TraceBinIFace printer ->- printer (text "writeBinIface:" <+> int symtab_next- <+> text "Names")+ -- NB. write the dictionary after the symbol table, because+ -- writing the symbol table may create more dictionary entries.+ let put_symtab = do+ name_count <- readFastMutInt symtab_next+ symtab_map <- readIORef symtab_map+ putSymbolTable bh_fs name_count symtab_map+ pure name_count - -- NB. write the dictionary after the symbol table, because- -- writing the symbol table may create more dictionary entries.+ forwardPut bh_fs (const put_symtab) $ do - -- Write the dictionary pointer at the front of the file- dict_p <- tellBin bh -- This is where the dictionary will start- putAt bh dict_p_p dict_p -- Fill in the placeholder- seekBin bh dict_p -- Seek back to the end of the file+ -- BinHandle with FastString and Name writing support+ let ud_fs = getUserData bh_fs+ let ud_name = ud_fs+ { ud_put_nonbinding_name = putName bin_dict bin_symtab+ , ud_put_binding_name = putName bin_dict bin_symtab+ }+ let bh_name = setUserData bh ud_name - -- Write the dictionary itself- dict_next <- readFastMutInt dict_next_ref- dict_map <- readIORef dict_map_ref- putDictionary bh dict_next dict_map- case traceBinIface of- QuietBinIFace -> return ()- TraceBinIFace printer ->- printer (text "writeBinIface:" <+> int dict_next- <+> text "dict entries")+ put_payload bh_name + return (name_count, fs_count, r) + -- | Initial ram buffer to allocate for writing interface files initBinMemSize :: Int initBinMemSize = 1024 * 1024@@ -287,9 +278,9 @@ -- putSymbolTable :: BinHandle -> Int -> UniqFM Name (Int,Name) -> IO ()-putSymbolTable bh next_off symtab = do- put_ bh next_off- let names = elems (array (0,next_off-1) (nonDetEltsUFM symtab))+putSymbolTable bh name_count symtab = do+ put_ bh name_count+ let names = elems (array (0,name_count-1) (nonDetEltsUFM symtab)) -- It's OK to use nonDetEltsUFM here because the elements have -- indices that array uses to create order mapM_ (\n -> serialiseName bh n symtab) names@@ -340,7 +331,7 @@ -- See Note [Symbol table representation of names]-putName :: BinDictionary -> BinSymbolTable -> BinHandle -> Name -> IO ()+putName :: FSTable -> BinSymbolTable -> BinHandle -> Name -> IO () putName _dict BinSymbolTable{ bin_symtab_map = symtab_map_ref, bin_symtab_next = symtab_next }@@ -381,7 +372,7 @@ in return $! case lookupKnownKeyName u of Nothing -> pprPanic "getSymtabName:unknown known-key unique"- (ppr i $$ ppr (unpkUnique u))+ (ppr i $$ ppr u $$ char tag $$ ppr ix) Just n -> n _ -> pprPanic "getSymtabName:unknown name tag" (ppr i)@@ -390,32 +381,5 @@ bin_symtab_next :: !FastMutInt, -- The next index to use bin_symtab_map :: !(IORef (UniqFM Name (Int,Name))) -- indexed by Name- }--putFastString :: BinDictionary -> BinHandle -> FastString -> IO ()-putFastString dict bh fs = allocateFastString dict fs >>= put_ bh--allocateFastString :: BinDictionary -> FastString -> IO Word32-allocateFastString BinDictionary { bin_dict_next = j_r,- bin_dict_map = out_r} f = do- out <- readIORef out_r- let !uniq = getUnique f- case lookupUFM_Directly out uniq of- Just (j, _) -> return (fromIntegral j :: Word32)- Nothing -> do- j <- readFastMutInt j_r- writeFastMutInt j_r (j + 1)- writeIORef out_r $! addToUFM_Directly out uniq (j, f)- return (fromIntegral j :: Word32)--getDictFastString :: Dictionary -> BinHandle -> IO FastString-getDictFastString dict bh = do- j <- get bh- return $! (dict ! fromIntegral (j :: Word32))--data BinDictionary = BinDictionary {- bin_dict_next :: !FastMutInt, -- The next index to use- bin_dict_map :: !(IORef (UniqFM FastString (Int,FastString)))- -- indexed by FastString }
compiler/GHC/Iface/Errors.hs view
@@ -46,7 +46,7 @@ -- are the same withPprStyle (mkUserStyle alwaysQualify AllTheWay) $ -- we want the Modules below to be qualified with package names,- -- so reset the PrintUnqualified setting.+ -- so reset the NamePprCtx setting. hsep [ text "Something is amiss; requested module " , ppr requested_mod , text "differs from name found in the interface file"
compiler/GHC/Iface/Ext/Ast.hs view
@@ -23,7 +23,7 @@ import GHC.Utils.Outputable(ppr) -import GHC.Prelude+import GHC.Prelude hiding ( head, init, last, tail ) import GHC.Types.Avail ( Avails ) import GHC.Data.Bag ( Bag, bagToList )@@ -61,7 +61,7 @@ import GHC.Iface.Ext.Types import GHC.Iface.Ext.Utils -import GHC.Unit.Module ( ModuleName, ml_hs_file )+import GHC.Unit.Module ( ml_hs_file ) import GHC.Unit.Module.ModSummary import qualified Data.Array as A@@ -69,7 +69,10 @@ import qualified Data.Map as M import qualified Data.Set as S import Data.Data ( Data, Typeable )+import Data.Foldable ( toList ) import Data.Functor.Identity ( Identity(..) )+import Data.List.NonEmpty ( NonEmpty(..), nonEmpty )+import qualified Data.List.NonEmpty as NE import Data.Void ( Void, absurd ) import Control.Monad ( forM_ ) import Control.Monad.Trans.State.Strict@@ -320,7 +323,7 @@ runIdentity $ flip evalStateT initState $ flip runReaderT SourceInfo $ do tasts <- toHie $ fmap (BC RegularBind ModuleScope) ts rasts <- processGrp hsGrp- imps <- toHie $ filter (not . ideclImplicit . unLoc) imports+ imps <- toHie $ filter (not . ideclImplicit . ideclExt . unLoc) imports exps <- toHie $ fmap (map $ IEC Export . fst) exports docs <- toHie docs -- Add Instance bindings@@ -332,10 +335,11 @@ Nothing -> pure () Just c -> forM_ (classSCSelIds c) $ \v -> addUnlocatedEvBind v (EvidenceVarBind (EvInstBind True (className c)) ModuleScope Nothing)- let spanFile file children = case children of- [] -> realSrcLocSpan (mkRealSrcLoc file 1 1)- _ -> mkRealSrcSpan (realSrcSpanStart $ nodeSpan $ head children)- (realSrcSpanEnd $ nodeSpan $ last children)+ let spanFile file children = case nonEmpty children of+ Nothing -> realSrcLocSpan (mkRealSrcLoc file 1 1)+ Just children -> mkRealSrcSpan+ (realSrcSpanStart $ nodeSpan (NE.head children))+ (realSrcSpanEnd $ nodeSpan (NE.last children)) flat_asts = concat [ tasts@@ -377,8 +381,9 @@ let asts = HieASTs $ resolveTyVarScopes asts' return asts- where- processGrp grp = concatM++processGrp :: HsGroup GhcRn -> HieM [HieAST Type]+processGrp grp = concatM [ toHie $ fmap (RS ModuleScope ) hs_valds grp , toHie $ hs_splcds grp , toHie $ hs_tyclds grp@@ -495,15 +500,15 @@ map (\(RS sc a) -> PS rsp useScope sc a) $ listScopes patScope xs --- | 'listScopes' specialised to 'HsPatSigType'-tScopes+-- | 'listScopes' specialised to 'HsConPatTyArg'+taScopes :: Scope -> Scope- -> [HsPatSigType (GhcPass a)]+ -> [HsConPatTyArg (GhcPass a)] -> [TScoped (HsPatSigType (GhcPass a))]-tScopes scope rhsScope xs =+taScopes scope rhsScope xs = map (\(RS sc a) -> TS (ResolvedScopes [scope, sc]) (unLoc a)) $- listScopes rhsScope (map (\hsps -> L (getLoc $ hsps_body hsps) hsps) xs)+ listScopes rhsScope (map (\(HsConPatTyArg _ hsps) -> L (getLoc $ hsps_body hsps) hsps) xs) -- We make the HsPatSigType into a Located one by using the location of the underlying LHsType. -- We then strip off the redundant location information afterward, and take the union of the given scope and those to the right when forming the TS. @@ -555,6 +560,9 @@ loc [] = noSrcSpan loc xs = foldl1' combineSrcSpans $ map loc xs +instance HasLoc a => HasLoc (DataDefnCons a) where+ loc = loc . toList+ instance (HasLoc a, HiePass p) => HasLoc (FamEqn (GhcPass p) a) where loc (FamEqn _ a outer_bndrs b _ c) = case outer_bndrs of HsOuterImplicit{} ->@@ -588,6 +596,12 @@ instance (ToHie a) => ToHie [a] where toHie = concatMapM toHie +instance (ToHie a) => ToHie (NonEmpty a) where+ toHie = concatMapM toHie++instance (ToHie a) => ToHie (DataDefnCons a) where+ toHie = concatMapM toHie+ instance (ToHie a) => ToHie (Bag a) where toHie = toHie . bagToList @@ -744,9 +758,6 @@ RecordCon con_expr _ _ -> computeType con_expr ExprWithTySig _ e _ -> computeLType e HsPragE _ _ e -> computeLType e- -- By this point all splices are lifted into splice environments so- -- the remaining HsSpliceE in the syntax tree contain bogus information.- HsSpliceE {} -> Nothing XExpr (ExpansionExpr (HsExpanded (HsGetField _ _ _) e)) -> Just (hsExprType e) -- for record-dot-syntax XExpr (ExpansionExpr (HsExpanded _ e)) -> computeType e XExpr (HsTick _ e) -> computeLType e@@ -802,7 +813,7 @@ , Data (AmbiguousFieldOcc (GhcPass p)) , Data (HsCmdTop (GhcPass p)) , Data (GRHS (GhcPass p) (LocatedA (HsCmd (GhcPass p))))- , Data (HsSplice (GhcPass p))+ , Data (HsUntypedSplice (GhcPass p)) , Data (HsLocalBinds (GhcPass p)) , Data (FieldOcc (GhcPass p)) , Data (HsTupArg (GhcPass p))@@ -839,11 +850,11 @@ instance HiePass p => ToHie (BindContext (LocatedA (HsBind (GhcPass p)))) where toHie (BC context scope b@(L span bind)) = concatM $ getTypeNode b : case bind of- FunBind{fun_id = name, fun_matches = matches, fun_ext = wrap} ->+ FunBind{fun_id = name, fun_matches = matches, fun_ext = ext} -> [ toHie $ C (ValBind context scope $ getRealSpanA span) name , toHie matches , case hiePass @p of- HieTc -> toHie $ L span wrap+ HieTc | (wrap, _) <- ext -> toHie $ L span wrap _ -> pure [] ] PatBind{pat_lhs = lhs, pat_rhs = rhs} ->@@ -883,11 +894,14 @@ , ToHie (LocatedA (body (GhcPass p))) ) => ToHie (MatchGroup (GhcPass p) (LocatedA (body (GhcPass p)))) where toHie mg = case mg of- MG{ mg_alts = (L span alts) , mg_origin = origin} ->+ MG{ mg_alts = (L span alts) } -> local (setOrigin origin) $ concatM [ locOnly (locA span) , toHie alts ]+ where origin = case hiePass @p of+ HieRn -> mg_ext mg+ HieTc -> mg_origin $ mg_ext mg setOrigin :: Origin -> NodeOrigin -> NodeOrigin setOrigin FromSource _ = SourceInfo@@ -964,7 +978,7 @@ LazyPat _ p -> [ toHie $ PS rsp scope pscope p ]- AsPat _ lname pat ->+ AsPat _ lname _ pat -> [ toHie $ C (PatternBind scope (combineScopes (mkLScopeA pat) pscope) rsp)@@ -1039,9 +1053,11 @@ ] ExpansionPat _ p -> [ toHie $ PS rsp scope pscope (L ospan p) ] where- contextify :: a ~ LPat (GhcPass p) => HsConDetails (HsPatSigType GhcRn) a (HsRecFields (GhcPass p) a)+ contextify :: a ~ LPat (GhcPass p) => HsConDetails (HsConPatTyArg GhcRn) a (HsRecFields (GhcPass p) a) -> HsConDetails (TScoped (HsPatSigType GhcRn)) (PScoped a) (RContext (HsRecFields (GhcPass p) (PScoped a)))- contextify (PrefixCon tyargs args) = PrefixCon (tScopes scope argscope tyargs) (patScopes rsp scope pscope args)+ contextify (PrefixCon tyargs args) =+ PrefixCon (taScopes scope argscope tyargs)+ (patScopes rsp scope pscope args) where argscope = foldr combineScopes NoScope $ map mkLScopeA args contextify (InfixCon a b) = InfixCon a' b' where [a', b'] = patScopes rsp scope pscope [a,b]@@ -1105,7 +1121,7 @@ [ toHie a , toHie b ]- HsAppType _ expr sig ->+ HsAppType _ expr _ sig -> [ toHie expr , toHie $ TS (ResolvedScopes []) sig ]@@ -1203,11 +1219,14 @@ [ toHie b , toHie xbracket ]- HieTc | HsBracketTc _ _ _ p <- xbracket ->- [ toHie b+ HieTc | HsBracketTc q _ _ p <- xbracket ->+ [ toHie q , toHie p ]- HsSpliceE _ x ->+ HsTypedSplice _ x ->+ [ toHie x+ ]+ HsUntypedSplice _ x -> [ toHie $ L mspan x ] HsGetField {} -> []@@ -1468,8 +1487,8 @@ quant_scope = mkLScopeA $ fromMaybe (noLocA []) $ dd_ctxt defn rhs_scope = sig_sc `combineScopes` con_sc `combineScopes` deriv_sc sig_sc = maybe NoScope mkLScopeA $ dd_kindSig defn- con_sc = foldr combineScopes NoScope $ map mkLScopeA $ dd_cons defn- deriv_sc = foldr combineScopes NoScope $ map mkLScopeA $ dd_derivs defn+ con_sc = foldr combineScopes NoScope $ mkLScopeA <$> dd_cons defn+ deriv_sc = foldr combineScopes NoScope $ mkLScopeA <$> dd_derivs defn ClassDecl { tcdCtxt = context , tcdLName = name , tcdTyVars = vars@@ -1562,7 +1581,7 @@ ] instance ToHie (HsDataDefn GhcRn) where- toHie (HsDataDefn _ _ ctx _ mkind cons derivs) = concatM+ toHie (HsDataDefn _ ctx _ mkind cons derivs) = concatM [ toHie ctx , toHie mkind , toHie cons@@ -1605,7 +1624,7 @@ ConDeclGADT { con_names = names, con_bndrs = L outer_bndrs_loc outer_bndrs , con_mb_cxt = ctx, con_g_args = args, con_res_ty = typ , con_doc = doc} ->- [ toHie $ map (C (Decl ConDec $ getRealSpanA span)) names+ [ toHie $ C (Decl ConDec $ getRealSpanA span) <$> names , case outer_bndrs of HsOuterImplicit{hso_ximplicit = imp_vars} -> bindingsOnly $ map (C $ TyVarBind (mkScopeA outer_bndrs_loc) resScope)@@ -1693,7 +1712,6 @@ _ -> toHie $ map (C $ TyDecl) names , toHie $ TS (UnresolvedScope (map unLoc names) msp) typ ]- IdSig _ _ -> [] FixSig _ fsig -> [ toHie $ L sp fsig ]@@ -1704,21 +1722,22 @@ [ toHie $ (C Use) name , toHie $ map (TS (ResolvedScopes [])) typs ]- SpecInstSig _ _ typ ->+ SpecInstSig _ typ -> [ toHie $ TS (ResolvedScopes []) typ ]- MinimalSig _ _ form ->+ MinimalSig _ form -> [ toHie form ]- SCCFunSig _ _ name mtxt ->+ SCCFunSig _ name mtxt -> [ toHie $ (C Use) name , maybe (pure []) (locOnly . getLocA) mtxt ]- CompleteMatchSig _ _ (L ispan names) typ ->+ CompleteMatchSig _ (L ispan names) typ -> [ locOnly ispan , toHie $ map (C Use) names , toHie $ fmap (C Use) typ ]+ XSig _ -> [] instance ToHie (TScoped (LocatedA (HsSigType GhcRn))) where toHie (TS tsc (L span t@HsSig{sig_bndrs=bndrs,sig_body=body})) = concatM $ makeNodeA t span :@@ -1872,8 +1891,13 @@ [ toHie splice ] -instance ToHie (HsQuote a) where- toHie _ = pure []+instance ToHie (HsQuote GhcRn) where+ toHie (ExpBr _ e) = toHie e+ toHie (PatBr _ b) = toHie (PS Nothing NoScope NoScope b)+ toHie (DecBrL {} ) = pure []+ toHie (DecBrG _ decls) = processGrp decls+ toHie (TypBr _ ty) = toHie ty+ toHie (VarBr {} ) = pure [] instance ToHie PendingRnSplice where toHie (PendingRnSplice _ _ e) = toHie e@@ -1899,25 +1923,14 @@ instance ToHie (LocatedAn NoEpAnns HsIPName) where toHie (L span e) = makeNodeA e span -instance HiePass p => ToHie (LocatedA (HsSplice (GhcPass p))) where+instance HiePass p => ToHie (LocatedA (HsUntypedSplice (GhcPass p))) where toHie (L span sp) = concatM $ makeNodeA sp span : case sp of- HsTypedSplice _ _ _ expr ->- [ toHie expr- ]- HsUntypedSplice _ _ _ expr ->+ HsUntypedSpliceExpr _ expr -> [ toHie expr ]- HsQuasiQuote _ _ _ ispan _ ->- [ locOnly ispan+ HsQuasiQuote _ _ ispanFs ->+ [ locOnly (getLocA ispanFs) ]- HsSpliced _ _ _ ->- []- XSplice x -> case hiePass @p of-#if __GLASGOW_HASKELL__ < 811- HieRn -> dataConCantHappen x-#endif- HieTc -> case x of- HsSplicedT _ -> [] instance ToHie (LocatedA (RoleAnnotDecl GhcRn)) where toHie (L span annot) = concatM $ makeNodeA annot span : case annot of@@ -2000,22 +2013,22 @@ , toHie fe ] -instance ToHie ForeignImport where- toHie (CImport (L a _) (L b _) _ _ (L c _)) = concatM $+instance ToHie (ForeignImport GhcRn) where+ toHie (CImport (L c _) (L a _) (L b _) _ _) = concatM $ [ locOnly a , locOnly b , locOnly c ] -instance ToHie ForeignExport where- toHie (CExport (L a _) (L b _)) = concatM $+instance ToHie (ForeignExport GhcRn) where+ toHie (CExport (L b _) (L a _)) = concatM $ [ locOnly a , locOnly b ] instance ToHie (LocatedA (WarnDecls GhcRn)) where toHie (L span decl) = concatM $ makeNodeA decl span : case decl of- Warnings _ _ warnings ->+ Warnings _ warnings -> [ toHie warnings ] @@ -2027,7 +2040,7 @@ instance ToHie (LocatedA (AnnDecl GhcRn)) where toHie (L span decl) = concatM $ makeNodeA decl span : case decl of- HsAnnotation _ _ prov expr ->+ HsAnnotation _ prov expr -> [ toHie prov , toHie expr ]@@ -2039,7 +2052,7 @@ instance ToHie (LocatedA (RuleDecls GhcRn)) where toHie (L span decl) = concatM $ makeNodeA decl span : case decl of- HsRules _ _ rules ->+ HsRules _ rules -> [ toHie rules ] @@ -2069,7 +2082,7 @@ instance ToHie (LocatedA (ImportDecl GhcRn)) where toHie (L span decl) = concatM $ makeNode decl (locA span) : case decl of- ImportDecl { ideclName = name, ideclAs = as, ideclHiding = hidden } ->+ ImportDecl { ideclName = name, ideclAs = as, ideclImportList = hidden } -> [ toHie $ IEC Import name , toHie $ fmap (IEC ImportAs) as , maybe (pure []) goIE hidden@@ -2080,8 +2093,13 @@ , toHie $ map (IEC c) liens ] where- c = if hiding then ImportHiding else Import+ -- ROMES:TODO: I notice some overlap here with Iface types, eventually+ -- we could join these+ c = case hiding of+ Exactly -> Import+ EverythingBut -> ImportHiding + instance ToHie (IEContext (LocatedA (IE GhcRn))) where toHie (IEC c (L span ie)) = concatM $ makeNode ie (locA span) : case ie of IEVar _ n ->@@ -2105,16 +2123,16 @@ IEDoc _ d -> [toHie d] IEDocNamed _ _ -> [] -instance ToHie (IEContext (LIEWrappedName Name)) where+instance ToHie (IEContext (LocatedA (IEWrappedName GhcRn))) where toHie (IEC c (L span iewn)) = concatM $ makeNodeA iewn span : case iewn of- IEName n ->- [ toHie $ C (IEThing c) n+ IEName _ (L l n) ->+ [ toHie $ C (IEThing c) (L l n) ]- IEPattern _ p ->- [ toHie $ C (IEThing c) p+ IEPattern _ (L l p) ->+ [ toHie $ C (IEThing c) (L l p) ]- IEType _ n ->- [ toHie $ C (IEThing c) n+ IEType _ (L l n) ->+ [ toHie $ C (IEThing c) (L l n) ] instance ToHie (IEContext (Located FieldLabel)) where
compiler/GHC/Iface/Ext/Binary.hs view
@@ -21,7 +21,6 @@ import GHC.Settings.Config ( cProjectVersion ) import GHC.Prelude import GHC.Utils.Binary-import GHC.Iface.Binary ( getDictFastString ) import GHC.Data.FastMutInt import GHC.Data.FastString ( FastString ) import GHC.Types.Name@@ -331,7 +330,7 @@ KnownKeyName u -> case lookupKnownKeyName u of Nothing -> pprPanic "fromHieName:unknown known-key unique"- (ppr (unpkUnique u))+ (ppr u) Just n -> pure n -- ** Reading and writing `HieName`'s@@ -339,10 +338,10 @@ putHieName :: BinHandle -> HieName -> IO () putHieName bh (ExternalName mod occ span) = do putByte bh 0- put_ bh (mod, occ, span)+ put_ bh (mod, occ, BinSrcSpan span) putHieName bh (LocalName occName span) = do putByte bh 1- put_ bh (occName, span)+ put_ bh (occName, BinSrcSpan span) putHieName bh (KnownKeyName uniq) = do putByte bh 2 put_ bh $ unpkUnique uniq@@ -353,10 +352,10 @@ case t of 0 -> do (modu, occ, span) <- get bh- return $ ExternalName modu occ span+ return $ ExternalName modu occ $ unBinSrcSpan span 1 -> do (occ, span) <- get bh- return $ LocalName occ span+ return $ LocalName occ $ unBinSrcSpan span 2 -> do (c,i) <- get bh return $ KnownKeyName $ mkUnique c i
compiler/GHC/Iface/Ext/Types.hs view
@@ -28,7 +28,6 @@ import GHC.Types.Avail import GHC.Types.Unique import qualified GHC.Utils.Outputable as O ( (<>) )-import GHC.Utils.Misc import GHC.Utils.Panic import qualified Data.Array as A@@ -41,6 +40,7 @@ import Control.Applicative ( (<|>) ) import Data.Coerce ( coerce ) import Data.Function ( on )+import qualified Data.Semigroup as S type Span = RealSrcSpan @@ -143,7 +143,7 @@ = HTyVarTy Name | HAppTy a (HieArgs a) | HTyConApp IfaceTyCon (HieArgs a)- | HForAllTy ((Name, a),ArgFlag) a+ | HForAllTy ((Name, a),ForAllTyFlag) a | HFunTy a a a | HQualTy a a -- ^ type with constraint: @t1 => t2@ (see 'IfaceDFunTy') | HLitTy IfaceTyLit@@ -206,7 +206,7 @@ -- | A list of type arguments along with their respective visibilities (ie. is--- this an argument that would return 'True' for 'isVisibleArgFlag'?).+-- this an argument that would return 'True' for 'isVisibleForAllTyFlag'?). newtype HieArgs a = HieArgs [(Bool,a)] deriving (Functor, Foldable, Traversable, Eq) @@ -251,12 +251,12 @@ instance Binary (HieAST TypeIndex) where put_ bh ast = do put_ bh $ sourcedNodeInfo ast- put_ bh $ nodeSpan ast+ put_ bh $ BinSpan $ nodeSpan ast put_ bh $ nodeChildren ast get bh = Node <$> get bh- <*> get bh+ <*> (unBinSpan <$> get bh) <*> get bh instance Outputable a => Outputable (HieAST a) where@@ -486,19 +486,19 @@ putByte bh 3 put_ bh bt put_ bh sc- put_ bh msp+ put_ bh $ BinSpan <$> msp put_ bh (PatternBind a b c) = do putByte bh 4 put_ bh a put_ bh b- put_ bh c+ put_ bh $ BinSpan <$> c put_ bh (ClassTyDecl sp) = do putByte bh 5- put_ bh sp+ put_ bh $ BinSpan <$> sp put_ bh (Decl a b) = do putByte bh 6 put_ bh a- put_ bh b+ put_ bh $ BinSpan <$> b put_ bh (TyVarBind a b) = do putByte bh 7 put_ bh a@@ -506,13 +506,13 @@ put_ bh (RecField a b) = do putByte bh 8 put_ bh a- put_ bh b+ put_ bh $ BinSpan <$> b put_ bh MatchBind = putByte bh 9 put_ bh (EvidenceVarBind a b c) = do putByte bh 10 put_ bh a put_ bh b- put_ bh c+ put_ bh $ BinSpan <$> c put_ bh EvidenceVarUse = putByte bh 11 get bh = do@@ -521,14 +521,14 @@ 0 -> return Use 1 -> IEThing <$> get bh 2 -> return TyDecl- 3 -> ValBind <$> get bh <*> get bh <*> get bh- 4 -> PatternBind <$> get bh <*> get bh <*> get bh- 5 -> ClassTyDecl <$> get bh- 6 -> Decl <$> get bh <*> get bh+ 3 -> ValBind <$> get bh <*> get bh <*> (fmap unBinSpan <$> get bh)+ 4 -> PatternBind <$> get bh <*> get bh <*> (fmap unBinSpan <$> get bh)+ 5 -> ClassTyDecl <$> (fmap unBinSpan <$> get bh)+ 6 -> Decl <$> get bh <*> (fmap unBinSpan <$> get bh) 7 -> TyVarBind <$> get bh <*> get bh- 8 -> RecField <$> get bh <*> get bh+ 8 -> RecField <$> get bh <*> (fmap unBinSpan <$> get bh) 9 -> return MatchBind- 10 -> EvidenceVarBind <$> get bh <*> get bh <*> get bh+ 10 -> EvidenceVarBind <$> get bh <*> get bh <*> (fmap unBinSpan <$> get bh) 11 -> return EvidenceVarUse _ -> panic "Binary ContextInfo: invalid tag" @@ -621,7 +621,7 @@ ppr RecFieldDecl = text "declaration" ppr RecFieldAssign = text "assignment" ppr RecFieldMatch = text "pattern match"- ppr RecFieldOcc = text "occurence"+ ppr RecFieldOcc = text "occurrence" instance Binary RecFieldContext where put_ bh b = putByte bh (fromIntegral (fromEnum b))@@ -679,14 +679,14 @@ put_ bh NoScope = putByte bh 0 put_ bh (LocalScope span) = do putByte bh 1- put_ bh span+ put_ bh $ BinSpan span put_ bh ModuleScope = putByte bh 2 get bh = do (t :: Word8) <- get bh case t of 0 -> return NoScope- 1 -> LocalScope <$> get bh+ 1 -> LocalScope . unBinSpan <$> get bh 2 -> return ModuleScope _ -> panic "Binary Scope: invalid tag" @@ -732,13 +732,13 @@ put_ bh (UnresolvedScope ns span) = do putByte bh 1 put_ bh ns- put_ bh span+ put_ bh (BinSpan <$> span) get bh = do (t :: Word8) <- get bh case t of 0 -> ResolvedScopes <$> get bh- 1 -> UnresolvedScope <$> get bh <*> get bh+ 1 -> UnresolvedScope <$> get bh <*> (fmap unBinSpan <$> get bh) _ -> panic "Binary TyVarScope: invalid tag" -- | `Name`'s get converted into `HieName`'s before being written into @.hie@@@ -751,9 +751,9 @@ deriving (Eq) instance Ord HieName where- compare (ExternalName a b c) (ExternalName d e f) = compare (a,b) (d,e) `thenCmp` leftmost_smallest c f+ compare (ExternalName a b c) (ExternalName d e f) = compare (a,b) (d,e) S.<> leftmost_smallest c f -- TODO (int-index): Perhaps use RealSrcSpan in HieName?- compare (LocalName a b) (LocalName c d) = compare a c `thenCmp` leftmost_smallest b d+ compare (LocalName a b) (LocalName c d) = compare a c S.<> leftmost_smallest b d -- TODO (int-index): Perhaps use RealSrcSpan in HieName? compare (KnownKeyName a) (KnownKeyName b) = nonDetCmpUnique a b -- Not actually non deterministic as it is a KnownKey@@ -774,7 +774,7 @@ case lookupKnownKeyName u of Just n -> nameOccName n Nothing -> pprPanic "hieNameOcc:unknown known-key unique"- (ppr (unpkUnique u))+ (ppr u) toHieName :: Name -> HieName toHieName name
compiler/GHC/Iface/Ext/Utils.hs view
@@ -13,7 +13,6 @@ import GHC.Driver.Ppr import GHC.Data.FastString ( FastString, mkFastString ) import GHC.Iface.Type-import GHC.Core.Multiplicity import GHC.Types.Name hiding (varName) import GHC.Types.Name.Set import GHC.Utils.Outputable hiding ( (<>) )@@ -22,6 +21,7 @@ import GHC.CoreToIface import GHC.Core.TyCon import GHC.Core.TyCo.Rep+import GHC.Core.TyCo.Compare( nonDetCmpType ) import GHC.Core.Type import GHC.Types.Var import GHC.Types.Var.Env@@ -41,6 +41,7 @@ import Data.Traversable ( for ) import Data.Coerce import GHC.Utils.Monad.State.Strict hiding (get)+import GHC.Utils.Panic.Plain( assert ) import Control.Monad.Trans.Reader import qualified Data.Tree as Tree @@ -61,7 +62,7 @@ resolveVisibility :: Type -> [Type] -> [(Bool,Type)] resolveVisibility kind ty_args- = go (mkEmptyTCvSubst in_scope) kind ty_args+ = go (mkEmptySubst in_scope) kind ty_args where in_scope = mkInScopeSet (tyCoVarsOfTypes ty_args) @@ -70,8 +71,8 @@ | Just ty' <- coreView ty = go env ty' ts go env (ForAllTy (Bndr tv vis) res) (t:ts)- | isVisibleArgFlag vis = (True , t) : ts'- | otherwise = (False, t) : ts'+ | isVisibleForAllTyFlag vis = (True , t) : ts'+ | otherwise = (False, t) : ts' where ts' = go (extendTvSubst env tv t) res ts @@ -160,8 +161,8 @@ go (HLitTy l) = IfaceLitTy l go (HForAllTy ((n,k),af) t) = let b = (occNameFS $ getOccName n, k) in IfaceForAllTy (Bndr (IfaceTvBndr b) af) t- go (HFunTy w a b) = IfaceFunTy VisArg w a b- go (HQualTy pred b) = IfaceFunTy InvisArg many_ty pred b+ go (HFunTy w a b) = IfaceFunTy visArgTypeLike w a b+ go (HQualTy pred b) = IfaceFunTy invisArgTypeLike many_ty pred b go (HCastTy a) = a go HCoercionTy = IfaceTyVar "<coercion type>" go (HTyConApp a xs) = IfaceTyConApp a (hieToIfaceArgs xs)@@ -240,9 +241,9 @@ ai <- getTypeIndex a bi <- getTypeIndex b wi <- getTypeIndex w- return $ case af of- InvisArg -> case w of Many -> HQualTy ai bi; _ -> error "Unexpected non-unrestricted predicate"- VisArg -> HFunTy wi ai bi+ return $ if isInvisibleFunArg af+ then assert (isManyTy w) $ HQualTy ai bi+ else HFunTy wi ai bi go (LitTy a) = return $ HLitTy $ toIfaceTyLit a go (CastTy t _) = do i <- getTypeIndex t
compiler/GHC/Iface/Load.hs view
@@ -68,7 +68,6 @@ import GHC.Utils.Panic.Plain import GHC.Utils.Constants (debugIsOn) import GHC.Utils.Logger-import GHC.Utils.Trace import GHC.Settings.Constants @@ -300,7 +299,7 @@ loadSrcInterface doc mod want_boot maybe_pkg = do { res <- loadSrcInterface_maybe doc mod want_boot maybe_pkg ; case res of- Failed err -> failWithTc (TcRnUnknownMessage $ mkPlainError noHints err)+ Failed err -> failWithTc (mkTcRnUnknownMessage $ mkPlainError noHints err) Succeeded iface -> return iface } -- | Like 'loadSrcInterface', but returns a 'MaybeErr'.@@ -603,8 +602,11 @@ This is a mess. -Note [HPT space leak] (#15111)-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Note [Home Unit Graph space leak]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++Ticket: #15111+ In IfL, we defer some work until it is demanded using forkM, such as building TyThings from IfaceDecls. These thunks are stored in the ExternalPackageState, and they might never be poked. If we're@@ -615,14 +617,15 @@ Therefore, when loading a package interface file , we use a "clean" version of the HscEnv with all the data about the currently loaded program stripped out. Most of the fields can be panics because-we'll never read them, but hsc_HPT needs to be empty because this+we'll never read them, but hsc_HUG needs to be empty because this interface will cause other interfaces to be loaded recursively, and-when looking up those interfaces we use the HPT in loadInterface.+when looking up those interfaces we use the HUG in loadInterface. We know that none of the interfaces below here can refer to-home-package modules however, so it's safe for the HPT to be empty.+home-package modules however, so it's safe for the HUG to be empty. -} -- Note [GHC Heap Invariants]+-- Note [Home Unit Graph space leak] dontLeakTheHUG :: IfL a -> IfL a dontLeakTheHUG thing_inside = do env <- getTopEnv@@ -1081,11 +1084,12 @@ qualifyImportedNames mod _ | mod == mi_module iface = NameUnqual | otherwise = NameNotInScope1- print_unqual = QueryQualify qualifyImportedNames+ name_ppr_ctx = QueryQualify qualifyImportedNames neverQualifyModules neverQualifyPackages+ alwaysPrintPromTick logMsg logger MCDump noSrcSpan- $ withPprStyle (mkDumpStyle print_unqual)+ $ withPprStyle (mkDumpStyle name_ppr_ctx) $ pprModIface unit_state iface -- | Show a ModIface but don't display details; suitable for ModIfaces stored in@@ -1126,6 +1130,10 @@ , vcat (map pprIfaceAnnotation (mi_anns iface)) , pprFixities (mi_fixities iface) , vcat [ppr ver $$ nest 2 (ppr decl) | (ver,decl) <- mi_decls iface]+ , case mi_extra_decls iface of+ Nothing -> empty+ Just eds -> text "extra decls:"+ $$ nest 2 (vcat ([ppr bs | bs <- eds])) , vcat (map ppr (mi_insts iface)) , vcat (map ppr (mi_fam_insts iface)) , vcat (map ppr (mi_rules iface))
compiler/GHC/Iface/Make.hs view
@@ -42,13 +42,15 @@ import GHC.Core.Coercion.Axiom import GHC.Core.ConLike import GHC.Core.DataCon+import GHC.Core.FVs ( orphNamesOfAxiomLHS ) import GHC.Core.Type import GHC.Core.Multiplicity import GHC.Core.InstEnv import GHC.Core.FamInstEnv import GHC.Core.Ppr-import GHC.Core.Unify( RoughMatchTc(..) )+import GHC.Core.RoughMap( RoughMatchTc(..) ) +import GHC.Driver.Config.HsToCore.Usage import GHC.Driver.Env import GHC.Driver.Backend import GHC.Driver.Session@@ -76,15 +78,14 @@ import GHC.Utils.Outputable import GHC.Utils.Panic import GHC.Utils.Panic.Plain-import GHC.Utils.Misc hiding ( eqListBy )+import GHC.Utils.Misc import GHC.Utils.Logger-import GHC.Utils.Trace import GHC.Data.FastString import GHC.Data.Maybe import GHC.HsToCore.Docs-import GHC.HsToCore.Usage ( mkUsageInfo, mkUsedNames )+import GHC.HsToCore.Usage import GHC.Unit import GHC.Unit.Module.Warnings@@ -110,11 +111,12 @@ -} mkPartialIface :: HscEnv+ -> CoreProgram -> ModDetails -> ModSummary -> ModGuts -> PartialModIface-mkPartialIface hsc_env mod_details mod_summary+mkPartialIface hsc_env core_prog mod_details mod_summary ModGuts{ mg_module = this_mod , mg_hsc_src = hsc_src , mg_usages = usages@@ -128,7 +130,7 @@ , mg_trust_pkg = self_trust , mg_docs = docs }- = mkIface_ hsc_env this_mod hsc_src used_th deps rdr_env fix_env warns hpc_info self_trust+ = mkIface_ hsc_env this_mod core_prog hsc_src used_th deps rdr_env fix_env warns hpc_info self_trust safe_mode usages docs mod_summary mod_details -- | Fully instantiate an interface. Adds fingerprints and potentially code@@ -193,9 +195,10 @@ -> SafeHaskellMode -- The safe haskell mode -> ModDetails -- gotten from mkBootModDetails, probably -> ModSummary+ -> Maybe CoreProgram -> TcGblEnv -- Usages, deprecations, etc -> IO ModIface-mkIfaceTc hsc_env safe_mode mod_details mod_summary+mkIfaceTc hsc_env safe_mode mod_details mod_summary mb_program tc_result@TcGblEnv{ tcg_mod = this_mod, tcg_src = hsc_src, tcg_imports = imports,@@ -219,6 +222,10 @@ used_th <- readIORef tc_splice_used dep_files <- (readIORef dependent_files) (needed_links, needed_pkgs) <- readIORef (tcg_th_needed_deps tc_result)+ let uc = initUsageConfig hsc_env+ plugins = hsc_plugins hsc_env+ fc = hsc_FC hsc_env+ unit_env = hsc_unit_env hsc_env -- Do NOT use semantic module here; this_mod in mkUsageInfo -- is used solely to decide if we should record a dependency -- or not. When we instantiate a signature, the semantic@@ -226,13 +233,13 @@ -- but if you pass that in here, we'll decide it's the local -- module and does not need to be recorded as a dependency. -- See Note [Identity versus semantic module]- usages <- mkUsageInfo hsc_env this_mod (imp_mods imports) used_names+ usages <- initIfaceLoad hsc_env $ mkUsageInfo uc plugins fc unit_env this_mod (imp_mods imports) used_names dep_files merged needed_links needed_pkgs docs <- extractDocs (ms_hspp_opts mod_summary) tc_result let partial_iface = mkIface_ hsc_env- this_mod hsc_src+ this_mod (fromMaybe [] mb_program) hsc_src used_th deps rdr_env fix_env warns hpc_info (imp_trust_own_pkg imports) safe_mode usages@@ -241,7 +248,7 @@ mkFullIface hsc_env partial_iface Nothing Nothing -mkIface_ :: HscEnv -> Module -> HscSource+mkIface_ :: HscEnv -> Module -> CoreProgram -> HscSource -> Bool -> Dependencies -> GlobalRdrEnv -> NameEnv FixItem -> Warnings GhcRn -> HpcInfo -> Bool@@ -252,7 +259,7 @@ -> ModDetails -> PartialModIface mkIface_ hsc_env- this_mod hsc_src used_th deps rdr_env fix_env src_warns+ this_mod core_prog hsc_src used_th deps rdr_env fix_env src_warns hpc_info pkg_trust_req safe_mode usages docs mod_summary ModDetails{ md_insts = insts,@@ -272,6 +279,9 @@ semantic_mod = homeModuleNameInstantiation home_unit (moduleName this_mod) entities = typeEnvElts type_env show_linear_types = xopt LangExt.LinearTypes (hsc_dflags hsc_env)++ extra_decls = if gopt Opt_WriteIfSimplifiedCore dflags then Just [ toIfaceTopBind b | b <- core_prog ]+ else Nothing decls = [ tyThingToIfaceDecl show_linear_types entity | entity <- entities, let name = getName entity,@@ -323,6 +333,7 @@ mi_globals = maybeGlobalRdrEnv rdr_env, mi_used_th = used_th, mi_decls = decls,+ mi_extra_decls = extra_decls, mi_hpc = isHpcUsed hpc_info, mi_trust = trust_info, mi_trust_pkg = pkg_trust_req,@@ -349,7 +360,7 @@ -- scope available. (#5534) maybeGlobalRdrEnv :: GlobalRdrEnv -> Maybe GlobalRdrEnv maybeGlobalRdrEnv rdr_env- | backendRetainsAllBindings (backend dflags) = Just rdr_env+ | backendWantsGlobalBindings (backend dflags) = Just rdr_env | otherwise = Nothing ifFamInstTcName = ifFamInstFam@@ -522,7 +533,7 @@ , IfaceData { ifName = getName tycon, ifBinders = if_binders, ifResKind = if_res_kind,- ifCType = tyConCType tycon,+ ifCType = tyConCType_maybe tycon, ifRoles = tyConRoles tycon, ifCtxt = tidyToIfaceContext tc_env1 (tyConStupidTheta tycon), ifCons = ifaceConDecls (algTyConRhs tycon),@@ -540,7 +551,7 @@ ifCType = Nothing, ifRoles = tyConRoles tycon, ifCtxt = [],- ifCons = IfDataTyCon [],+ ifCons = IfDataTyCon False [], ifGadtSyntax = False, ifParent = IfNoParent }) where@@ -549,7 +560,7 @@ -- an error. (tc_env1, tc_binders) = tidyTyConBinders env (tyConBinders tycon) tc_tyvars = binderVars tc_binders- if_binders = toIfaceTyCoVarBinders tc_binders+ if_binders = toIfaceForAllBndrs tc_binders -- No tidying of the binders; they are already tidy if_res_kind = tidyToIfaceType tc_env1 (tyConResKind tycon) if_syn_type ty = tidyToIfaceType tc_env1 ty@@ -574,9 +585,10 @@ axn = coAxiomName ax ifaceConDecls (NewTyCon { data_con = con }) = IfNewTyCon (ifaceConDecl con)- ifaceConDecls (DataTyCon { data_cons = cons }) = IfDataTyCon (map ifaceConDecl cons)- ifaceConDecls (TupleTyCon { data_con = con }) = IfDataTyCon [ifaceConDecl con]- ifaceConDecls (SumTyCon { data_cons = cons }) = IfDataTyCon (map ifaceConDecl cons)+ ifaceConDecls (DataTyCon { data_cons = cons, is_type_data = type_data })+ = IfDataTyCon type_data (map ifaceConDecl cons)+ ifaceConDecls (TupleTyCon { data_con = con }) = IfDataTyCon False [ifaceConDecl con]+ ifaceConDecls (SumTyCon { data_cons = cons }) = IfDataTyCon False (map ifaceConDecl cons) ifaceConDecls AbstractTyCon = IfAbstractTyCon -- The AbstractTyCon case happens when a TyCon has been trimmed -- during tidying.@@ -590,7 +602,7 @@ ifConInfix = dataConIsInfix data_con, ifConWrapper = isJust (dataConWrapId_maybe data_con), ifConExTCvs = map toIfaceBndr ex_tvs',- ifConUserTvBinders = map toIfaceForAllBndr user_bndrs',+ ifConUserTvBinders = toIfaceForAllBndrs user_bndrs', ifConEqSpec = map (to_eq_spec . eqSpecPair) eq_spec, ifConCtxt = tidyToIfaceContext con_env2 theta, ifConArgTys =@@ -617,18 +629,18 @@ -- A bit grimy, perhaps, but it's simple! (con_env2, ex_tvs') = tidyVarBndrs con_env1 ex_tvs- user_bndrs' = map (tidyUserTyCoVarBinder con_env2) user_bndrs+ user_bndrs' = map (tidyUserForAllTyBinder con_env2) user_bndrs to_eq_spec (tv,ty) = (tidyTyVar con_env2 tv, tidyToIfaceType con_env2 ty) -- By this point, we have tidied every universal and existential- -- tyvar. Because of the dcUserTyCoVarBinders invariant+ -- tyvar. Because of the dcUserForAllTyBinders invariant -- (see Note [DataCon user type variable binders]), *every* -- user-written tyvar must be contained in the substitution that -- tidying produced. Therefore, tidying the user-written tyvars is a -- simple matter of looking up each variable in the substitution, -- which tidyTyCoVarOcc accomplishes.- tidyUserTyCoVarBinder :: TidyEnv -> InvisTVBinder -> InvisTVBinder- tidyUserTyCoVarBinder env (Bndr tv vis) =+ tidyUserForAllTyBinder :: TidyEnv -> InvisTVBinder -> InvisTVBinder+ tidyUserForAllTyBinder env (Bndr tv vis) = Bndr (tidyTyCoVarOcc env tv) vis classToIfaceDecl :: TidyEnv -> Class -> (TidyEnv, IfaceDecl)@@ -636,7 +648,7 @@ = ( env1 , IfaceClass { ifName = getName tycon, ifRoles = tyConRoles (classTyCon clas),- ifBinders = toIfaceTyCoVarBinders tc_binders,+ ifBinders = toIfaceForAllBndrs tc_binders, ifBody = body, ifFDs = map toIfaceFD clas_fds }) where@@ -691,7 +703,7 @@ tidyTyConBinder env@(_, subst) tvb@(Bndr tv vis) = case lookupVarEnv subst tv of Just tv' -> (env, Bndr tv' vis)- Nothing -> tidyTyCoVarBinder env tvb+ Nothing -> tidyForAllTyBinder env tvb tidyTyConBinders :: TidyEnv -> [TyConBinder] -> (TidyEnv, [TyConBinder]) tidyTyConBinders = mapAccumL tidyTyConBinder@@ -729,7 +741,7 @@ nameModule (coAxiomName axiom) is_local name = nameIsLocalOrFrom mod name - lhs_names = filterNameSet is_local (orphNamesOfCoCon axiom)+ lhs_names = filterNameSet is_local (orphNamesOfAxiomLHS axiom) orph | is_local fam_decl = NotOrphan (nameOccName fam_decl)
compiler/GHC/Iface/Recomp.hs view
@@ -19,6 +19,7 @@ where import GHC.Prelude+import GHC.Data.FastString import GHC.Driver.Backend import GHC.Driver.Config.Finder@@ -44,13 +45,12 @@ import GHC.Utils.Panic import GHC.Utils.Panic.Plain import GHC.Utils.Outputable as Outputable-import GHC.Utils.Misc as Utils hiding ( eqListBy )+import GHC.Utils.Misc as Utils import GHC.Utils.Binary import GHC.Utils.Fingerprint import GHC.Utils.Exception import GHC.Utils.Logger import GHC.Utils.Constants (debugIsOn)-import GHC.Utils.Trace import GHC.Types.Annotations import GHC.Types.Name@@ -70,7 +70,7 @@ import GHC.Unit.Module.Deps import Control.Monad-import Data.List (sortBy, sort)+import Data.List (sortBy, sort, sortOn) import qualified Data.Map as Map import qualified Data.Set as Set import Data.Word (Word64)@@ -137,6 +137,10 @@ -- ^ The old item, if it exists deriving (Functor) +instance Outputable a => Outputable (MaybeValidated a) where+ ppr (UpToDateItem a) = text "UpToDate" <+> ppr a+ ppr (OutOfDateItem r _) = text "OutOfDate: " <+> ppr r+ outOfDateItemBecause :: RecompReason -> Maybe a -> MaybeValidated a outOfDateItemBecause reason item = OutOfDateItem (RecompBecause reason) item @@ -165,7 +169,7 @@ data RecompReason = UnitDepRemoved UnitId- | ModulePackageChanged String+ | ModulePackageChanged FastString | SourceFileChanged | ThisUnitIdChanged | ImpurePlugin@@ -197,7 +201,7 @@ instance Outputable RecompReason where ppr = \case UnitDepRemoved uid -> ppr uid <+> text "removed"- ModulePackageChanged s -> text s <+> text "package changed"+ ModulePackageChanged s -> ftext s <+> text "package changed" SourceFileChanged -> text "Source file changed" ThisUnitIdChanged -> text "-this-unit-id changed" ImpurePlugin -> text "Impure plugin forced recompilation"@@ -323,7 +327,7 @@ -- If the source has changed and we're in interactive mode, -- avoid reading an interface; just return the one we might -- have been supplied with.- True | not (backendProducesObject $ backend dflags) ->+ True | not (backendWritesFiles $ backend dflags) -> return $ OutOfDateItem MustCompile maybe_iface -- Try and read the old interface for the current module@@ -585,7 +589,7 @@ liftIO $ check_mods (sort hs) prev_dep_mods `recompThen`- let allPkgDeps = sortBy (comparing snd) $ nubOrdOn snd (ps ++ implicit_deps ++ bkpk_units)+ let allPkgDeps = sortBy (comparing snd) $ nubOrdOn snd (ps ++ implicit_deps) in check_packages allPkgDeps prev_dep_pkgs where @@ -593,7 +597,7 @@ -> [(t, GenLocated l ModuleName)] -> IfG [Either- CompileReason (Either (UnitId, ModuleName) (String, UnitId))]+ CompileReason (Either (UnitId, ModuleName) (FastString, UnitId))] classify_import find_import imports = liftIO $ traverse (\(mb_pkg, L _ mod) -> let reason = ModuleChanged mod@@ -609,9 +613,8 @@ prev_dep_mods = map (second gwib_mod) $ Set.toAscList $ dep_direct_mods (mi_deps iface) prev_dep_pkgs = Set.toAscList (Set.union (dep_direct_pkgs (mi_deps iface)) (dep_plugin_pkgs (mi_deps iface)))- bkpk_units = map (("Signature",) . instUnitInstanceOf . moduleUnit) (requirementMerges units (moduleName (mi_module iface))) - implicit_deps = map ("Implicit",) (implicitPackageDeps dflags)+ implicit_deps = map (fsLit "Implicit",) (implicitPackageDeps dflags) -- GHC.Prim is very special and doesn't appear in ms_textual_imps but -- ghc-prim will appear in the package dependencies still. In order to not confuse@@ -620,12 +623,12 @@ Just home_unit | homeUnitId home_unit == primUnitId -> Left (primUnitId, mkModuleName "GHC.Prim")- _ -> Right ("GHC.Prim", primUnitId)+ _ -> Right (fsLit "GHC.Prim", primUnitId) classify _ (Found _ mod) | (toUnitId $ moduleUnit mod) `elem` all_home_units = Right (Left ((toUnitId $ moduleUnit mod), moduleName mod))- | otherwise = Right (Right (moduleNameString (moduleName mod), toUnitId $ moduleUnit mod))+ | otherwise = Right (Right (moduleNameFS (moduleName mod), toUnitId $ moduleUnit mod)) classify reason _ = Left (RecompBecause reason) check_mods :: [(UnitId, ModuleName)] -> [(UnitId, ModuleName)] -> IO RecompileRequired@@ -646,21 +649,21 @@ text " not among previous dependencies" return $ needsRecompileBecause $ ModuleAdded new - check_packages :: [(String, UnitId)] -> [UnitId] -> IO RecompileRequired+ check_packages :: [(FastString, UnitId)] -> [UnitId] -> IO RecompileRequired check_packages [] [] = return UpToDate check_packages [] (old:_) = do trace_hi_diffs logger $ text "package " <> quotes (ppr old) <> text "no longer in dependencies" return $ needsRecompileBecause $ UnitDepRemoved old- check_packages (new:news) olds+ check_packages ((new_name, new_unit):news) olds | Just (old, olds') <- uncons olds- , snd new == old = check_packages (dropWhile ((== (snd new)) . snd) news) olds'+ , new_unit == old = check_packages (dropWhile ((== new_unit) . snd) news) olds' | otherwise = do trace_hi_diffs logger $- text "imported package " <> quotes (ppr new) <>- text " not among previous dependencies"- return $ needsRecompileBecause $ ModulePackageChanged $ fst new+ text "imported package" <+> ftext new_name <+> ppr new_unit <+>+ text "not among previous dependencies"+ return $ needsRecompileBecause $ ModulePackageChanged new_name needInterface :: Module -> (ModIface -> IO RecompileRequired)@@ -929,7 +932,7 @@ group_fingerprint. Since we included the sequence number in step (1) programs identical up to- transposition of recursive occurrences are distinguisable, avoiding the+ transposition of recursive occurrences are distinguishable, avoiding the second issue mentioned above. 3. Produce the final environment by extending hash_env, mapping each@@ -1200,6 +1203,16 @@ sorted_decls = Map.elems $ Map.fromList $ [(getOccName d, e) | e@(_, d) <- decls_w_hashes] + -- This key is safe because mi_extra_decls contains tidied things.+ getOcc (IfGblTopBndr b) = getOccName b+ getOcc (IfLclTopBndr fs _ _ _) = mkVarOccFS fs++ binding_key (IfaceNonRec b _) = IfaceNonRec (getOcc b) ()+ binding_key (IfaceRec bs) = IfaceRec (map (\(b, _) -> (getOcc b, ())) bs)++ sorted_extra_decls :: Maybe [IfaceBindingX IfaceMaybeRhs IfaceTopBndrInfo]+ sorted_extra_decls = sortOn binding_key <$> mi_extra_decls iface0+ -- the flag hash depends on: -- - (some of) dflags -- it returns two hashes, one that shouldn't change@@ -1233,7 +1246,7 @@ iface_hash <- computeFingerprint putNameLiterally (mod_hash, mi_src_hash iface0,- ann_fn (mkVarOcc "module"), -- See mkIfaceAnnCache+ ann_fn (mkVarOccFS (fsLit "module")), -- See mkIfaceAnnCache mi_usages iface0, sorted_deps, mi_hpc iface0)@@ -1257,7 +1270,7 @@ , mi_fix_fn = fix_fn , mi_hash_fn = lookupOccEnv local_env }- final_iface = iface0 { mi_decls = sorted_decls, mi_final_exts = final_iface_exts }+ final_iface = iface0 { mi_decls = sorted_decls, mi_extra_decls = sorted_extra_decls, mi_final_exts = final_iface_exts } -- return final_iface @@ -1628,7 +1641,7 @@ pair (IfaceAnnotation target value) = (case target of NamedTarget occn -> occn- ModuleTarget _ -> mkVarOcc "module"+ ModuleTarget _ -> mkVarOccFS (fsLit "module") , [value]) -- flipping (++), so the first argument is always short env = mkOccEnv_C (flip (++)) (map pair anns)
compiler/GHC/Iface/Rename.hs view
@@ -1,7 +1,3 @@---{-# OPTIONS_GHC -Wno-incomplete-record-updates #-}- -- | This module implements interface renaming, which is -- used to rewrite interface files on the fly when we -- are doing indefinite typechecking and need instantiations@@ -543,8 +539,8 @@ rnIfaceTyConParent IfNoParent = pure IfNoParent rnIfaceConDecls :: Rename IfaceConDecls-rnIfaceConDecls (IfDataTyCon ds)- = IfDataTyCon <$> mapM rnIfaceConDecl ds+rnIfaceConDecls (IfDataTyCon type_data ds)+ = IfDataTyCon type_data <$> mapM rnIfaceConDecl ds rnIfaceConDecls (IfNewTyCon d) = IfNewTyCon <$> rnIfaceConDecl d rnIfaceConDecls IfAbstractTyCon = pure IfAbstractTyCon @@ -600,12 +596,8 @@ = pure i rnIfaceUnfolding :: Rename IfaceUnfolding-rnIfaceUnfolding (IfCoreUnfold stable cache if_expr)- = IfCoreUnfold stable cache <$> rnIfaceExpr if_expr-rnIfaceUnfolding (IfCompulsory if_expr)- = IfCompulsory <$> rnIfaceExpr if_expr-rnIfaceUnfolding (IfInlineRule arity unsat_ok boring_ok if_expr)- = IfInlineRule arity unsat_ok boring_ok <$> rnIfaceExpr if_expr+rnIfaceUnfolding (IfCoreUnfold src cache guide if_expr)+ = IfCoreUnfold src cache guide <$> rnIfaceExpr if_expr rnIfaceUnfolding (IfDFunUnfold bs ops) = IfDFunUnfold <$> rnIfaceBndrs bs <*> mapM rnIfaceExpr ops @@ -636,7 +628,7 @@ rnIfaceExpr (IfaceCast expr co) = IfaceCast <$> rnIfaceExpr expr <*> rnIfaceCo co rnIfaceExpr (IfaceLit lit) = pure (IfaceLit lit)-rnIfaceExpr (IfaceLitRubbish rep) = IfaceLitRubbish <$> rnIfaceType rep+rnIfaceExpr (IfaceLitRubbish tc rep) = IfaceLitRubbish tc <$> rnIfaceType rep rnIfaceExpr (IfaceFCall cc ty) = IfaceFCall cc <$> rnIfaceType ty rnIfaceExpr (IfaceTick tickish expr) = IfaceTick tickish <$> rnIfaceExpr expr @@ -697,7 +689,7 @@ = IfaceTransCo <$> rnIfaceCo c1 <*> rnIfaceCo c2 rnIfaceCo (IfaceInstCo c1 c2) = IfaceInstCo <$> rnIfaceCo c1 <*> rnIfaceCo c2-rnIfaceCo (IfaceNthCo d c) = IfaceNthCo d <$> rnIfaceCo c+rnIfaceCo (IfaceSelCo d c) = IfaceSelCo d <$> rnIfaceCo c rnIfaceCo (IfaceLRCo lr c) = IfaceLRCo lr <$> rnIfaceCo c rnIfaceCo (IfaceSubCo c) = IfaceSubCo <$> rnIfaceCo c rnIfaceCo (IfaceAxiomRuleCo ax cos)
compiler/GHC/Iface/Tidy.hs view
@@ -24,15 +24,17 @@ import GHC.Core import GHC.Core.Unfold+-- import GHC.Core.Unfold.Make import GHC.Core.FVs import GHC.Core.Tidy-import GHC.Core.Seq (seqBinds)-import GHC.Core.Opt.Arity ( exprArity, exprBotStrictness_maybe, typeArity )+import GHC.Core.Seq ( seqBinds )+import GHC.Core.Opt.Arity ( exprArity, typeArity, exprBotStrictness_maybe ) import GHC.Core.InstEnv-import GHC.Core.Type ( tidyTopType, Type )+import GHC.Core.Type ( Type, tidyTopType ) import GHC.Core.DataCon import GHC.Core.TyCon import GHC.Core.Class+import GHC.Core.Opt.OccurAnal ( occurAnalyseExpr ) import GHC.Iface.Tidy.StaticPtrTable import GHC.Iface.Env@@ -40,7 +42,6 @@ import GHC.Utils.Outputable import GHC.Utils.Misc( filterOut ) import GHC.Utils.Panic-import GHC.Utils.Trace import GHC.Utils.Logger as Logger import qualified GHC.Utils.Error as Err @@ -51,8 +52,7 @@ import GHC.Types.Id import GHC.Types.Id.Make ( mkDictSelRhs ) import GHC.Types.Id.Info-import GHC.Types.Demand ( isDeadEndAppSig, isNopSig, isDeadEndSig )-import GHC.Types.Cpr ( mkCprSig, botCpr )+import GHC.Types.Demand ( isDeadEndAppSig, isNopSig, nopSig, isDeadEndSig ) import GHC.Types.Basic import GHC.Types.Name hiding (varName) import GHC.Types.Name.Set@@ -73,7 +73,6 @@ import Data.List ( sortBy, mapAccumL ) import qualified Data.Set as S import GHC.Types.CostCentre-import GHC.Core.Opt.OccurAnal (occurAnalyseExpr) {- Constructing the TypeEnv, Instances, Rules from which the@@ -380,9 +379,10 @@ }) = do let implicit_binds = concatMap getImplicitBinds tcs+ all_binds = implicit_binds ++ binds - (unfold_env, tidy_occ_env) <- chooseExternalIds opts mod binds implicit_binds imp_rules- let (trimmed_binds, trimmed_rules) = findExternalRules opts binds imp_rules unfold_env+ (unfold_env, tidy_occ_env) <- chooseExternalIds opts mod all_binds imp_rules+ let (trimmed_binds, trimmed_rules) = findExternalRules opts all_binds imp_rules unfold_env (tidy_env, tidy_binds) <- tidyTopBinds unfold_env boot_exports tidy_occ_env trimmed_binds @@ -419,7 +419,7 @@ tidy_rules = tidyRules tidy_env trimmed_rules -- See Note [Injecting implicit bindings]- all_tidy_binds = implicit_binds ++ tidy_binds'+ all_tidy_binds = tidy_binds' -- Get the TyCons to generate code for. Careful! We must use -- the untidied TyCons here, because we need@@ -646,12 +646,11 @@ chooseExternalIds :: TidyOpts -> Module -> [CoreBind]- -> [CoreBind] -> [CoreRule] -> IO (UnfoldEnv, TidyOccEnv) -- Step 1 from the notes above -chooseExternalIds opts mod binds implicit_binds imp_id_rules+chooseExternalIds opts mod binds imp_id_rules = do { (unfold_env1,occ_env1) <- search init_work_list emptyVarEnv init_occ_env ; let internal_ids = filter (not . (`elemVarEnv` unfold_env1)) binders ; tidy_internal internal_ids unfold_env1 occ_env1 }@@ -680,10 +679,9 @@ rule_rhs_vars = mapUnionVarSet ruleRhsFreeVars imp_id_rules binders = map fst $ flattenBinds binds- implicit_binders = bindersOfBinds implicit_binds binder_set = mkVarSet binders - avoids = [getOccName name | bndr <- binders ++ implicit_binders,+ avoids = [getOccName name | bndr <- binders, let name = idName bndr, isExternalName name ] -- In computing our "avoids" list, we must include@@ -875,9 +873,9 @@ dffvLetBndr :: Bool -> Id -> DFFV () -- Gather the free vars of the RULES and unfolding of a binder -- We always get the free vars of a *stable* unfolding, but--- for a *vanilla* one (InlineRhs), the flag controls what happens:+-- for a *vanilla* one (VanillaSrc), the flag controls what happens: -- True <=> get fvs of even a *vanilla* unfolding--- False <=> ignore an InlineRhs+-- False <=> ignore a VanillaSrc -- For nested bindings (call from dffvBind) we always say "False" because -- we are taking the fvs of the RHS anyway -- For top-level bindings (call from addExternal, via bndrFvsInOrder)@@ -889,10 +887,9 @@ idinfo = idInfo id go_unf (CoreUnfolding { uf_tmpl = rhs, uf_src = src })- = case src of- InlineRhs | vanilla_unfold -> dffvExpr rhs- | otherwise -> return ()- _ -> dffvExpr rhs+ | isStableSource src = dffvExpr rhs+ | vanilla_unfold = dffvExpr rhs+ | otherwise = return () go_unf (DFunUnfolding { df_bndrs = bndrs, df_args = args }) = extendScopeList bndrs $ mapM_ dffvExpr args@@ -964,6 +961,13 @@ called in the final code), we keep the rule too. This stuff is the only reason for the ru_auto field in a Rule.++NB: In #18532 we looked at keeping auto-rules and it turned out to just make+compiler performance worse while increasing code sizes at the same time. The impact+varied. Compiling Cabal got ~3% slower, allocated ~3% more and wrote 15% more code to disk.+Nofib only saw 0.7% more compiler allocations and executable file size growth. But given+there was no difference in runtime for these benchmarks it turned out to be flat out worse.+See the ticket for more details. -} findExternalRules :: TidyOpts@@ -975,7 +979,8 @@ findExternalRules opts binds imp_id_rules unfold_env = (trimmed_binds, filter keep_rule all_rules) where- imp_rules = filter expose_rule imp_id_rules+ imp_rules | (opt_expose_rules opts) = filter expose_rule imp_id_rules+ | otherwise = [] imp_user_rule_fvs = mapUnionVarSet user_rule_rhs_fvs imp_rules user_rule_rhs_fvs rule | isAutoRule rule = emptyVarSet@@ -996,16 +1001,14 @@ -- RHS: the auto rules that might mention a binder that has -- been discarded; see Note [Trimming auto-rules] - expose_rule rule- | not (opt_expose_rules opts) = False- | otherwise = all is_external_id (ruleLhsFreeIdsList rule)+ expose_rule rule = all is_external_id (ruleLhsFreeIdsList rule) -- Don't expose a rule whose LHS mentions a locally-defined -- Id that is completely internal (i.e. not visible to an -- importing module). NB: ruleLhsFreeIds only returns LocalIds. -- See Note [Which rules to expose] is_external_id id = case lookupVarEnv unfold_env id of- Just (name, _) -> isExternalName name+ Just (name, _) -> isExternalName name && not (isImplicitId id) Nothing -> False trim_binds :: [CoreBind]@@ -1020,7 +1023,7 @@ = ([], emptyVarSet, imp_user_rule_fvs, imp_rules) trim_binds (bind:binds)- | any needed bndrs -- Keep binding+ | any needed bndrs -- Keep this binding = ( bind : binds', bndr_set', needed_fvs', local_rules ++ rules ) | otherwise -- Discard binding altogether = stuff@@ -1041,7 +1044,8 @@ -- In needed_fvs', we don't bother to delete binders from the fv set local_rules = [ rule- | id <- bndrs+ | (opt_expose_rules opts)+ , id <- bndrs , is_external_id id -- Only collect rules for external Ids , rule <- idCoreRules id , expose_rule rule ] -- and ones that can fire in a client@@ -1093,12 +1097,12 @@ | otherwise = panic "tidyTopName" where- name = idName id+ !name = idName id external = isJust maybe_ref global = isExternalName name local = not global internal = not external- loc = nameSrcSpan name+ !loc = nameSrcSpan name old_occ = nameOccName name new_occ | Just ref <- maybe_ref@@ -1218,27 +1222,27 @@ -- Indeed, CorePrep must eta expand where necessary to make -- the manifest arity equal to the claimed arity. ---tidyTopIdInfo :: TidyEnv -> Name -> Type -> CoreExpr -> CoreExpr- -> IdInfo -> Bool -> IdInfo+tidyTopIdInfo :: TidyEnv -> Name -> Type+ -> CoreExpr -> CoreExpr -> IdInfo -> Bool -> IdInfo tidyTopIdInfo rhs_tidy_env name rhs_ty orig_rhs tidy_rhs idinfo show_unfold | not is_external -- For internal Ids (not externally visible) = vanillaIdInfo -- we only need enough info for code generation -- Arity and strictness info are enough; -- c.f. GHC.Core.Tidy.tidyLetBndr `setArityInfo` arity- `setDmdSigInfo` final_sig- `setCprSigInfo` final_cpr- `setUnfoldingInfo` minimal_unfold_info -- See Note [Preserve evaluatedness]+ `setDmdSigInfo` final_sig+ `setCprSigInfo` final_cpr+ `setUnfoldingInfo` minimal_unfold_info -- See note [Preserve evaluatedness] -- in GHC.Core.Tidy | otherwise -- Externally-visible Ids get the whole lot = vanillaIdInfo- `setArityInfo` arity- `setDmdSigInfo` final_sig- `setCprSigInfo` final_cpr- `setOccInfo` robust_occ_info- `setInlinePragInfo` (inlinePragInfo idinfo)- `setUnfoldingInfo` unfold_info+ `setArityInfo` arity+ `setDmdSigInfo` final_sig+ `setCprSigInfo` final_cpr+ `setOccInfo` robust_occ_info+ `setInlinePragInfo` inlinePragInfo idinfo+ `setUnfoldingInfo` unfold_info -- NB: we throw away the Rules -- They have already been extracted by findExternalRules where@@ -1253,23 +1257,32 @@ mb_bot_str = exprBotStrictness_maybe orig_rhs sig = dmdSigInfo idinfo- final_sig | not $ isNopSig sig+ final_sig | not (isNopSig sig) = warnPprTrace (_bottom_hidden sig) "tidyTopIdInfo" (ppr name) sig- -- try a cheap-and-cheerful bottom analyser- | Just (_, nsig) <- mb_bot_str = nsig- | otherwise = sig + -- No demand signature, so try a+ -- cheap-and-cheerful bottom analyser+ | Just (_, bot_str_sig, _) <- mb_bot_str+ = bot_str_sig++ -- No strictness info+ | otherwise = nopSig+ cpr = cprSigInfo idinfo- final_cpr | Just _ <- mb_bot_str- = mkCprSig arity botCpr+ final_cpr | Just (_, _, bot_cpr_sig) <- mb_bot_str+ = bot_cpr_sig | otherwise = cpr - _bottom_hidden id_sig = case mb_bot_str of- Nothing -> False- Just (arity, _) -> not (isDeadEndAppSig id_sig arity)+ _bottom_hidden id_sig+ = case mb_bot_str of+ Nothing -> False+ Just (arity, _, _) -> not (isDeadEndAppSig id_sig arity) --------- Unfolding ------------+ -- Force unfold_info (hence bangs), otherwise the old unfolding+ -- is retained during code generation. See #22071+ unf_info = realUnfoldingInfo idinfo !minimal_unfold_info = trimUnfolding unf_info @@ -1277,27 +1290,10 @@ = tidyTopUnfolding rhs_tidy_env tidy_rhs unf_info | otherwise = minimal_unfold_info- -- NB: use `orig_rhs` not `tidy_rhs` in this call to mkFinalUnfolding -- else you get a black hole (#22122). Reason: mkFinalUnfolding -- looks at IdInfo, and that is knot-tied in tidyTopBind (the Rec case) - -- NB: do *not* expose the worker if show_unfold is off,- -- because that means this thing is a loop breaker or- -- marked NOINLINE or something like that- -- This is important: if you expose the worker for a loop-breaker- -- then you can make the simplifier go into an infinite loop, because- -- in effect the unfolding is exposed. See #1709- --- -- You might think that if show_unfold is False, then the thing should- -- not be w/w'd in the first place. But a legitimate reason is this:- -- the function returns bottom- -- In this case, show_unfold will be false (we don't expose unfoldings- -- for bottoming functions), but we might still have a worker/wrapper- -- split (see Note [Worker/wrapper for bottoming functions] in- -- GHC.Core.Opt.WorkWrap)-- --------- Arity ------------ -- Usually the Id will have an accurate arity on it, because -- the simplifier has just run, but not always.@@ -1305,8 +1301,12 @@ -- did was to let-bind a non-atomic argument and then float -- it to the top level. So it seems more robust just to -- fix it here.- arity = exprArity orig_rhs `min` (length $ typeArity rhs_ty)-+ arity = exprArity orig_rhs `min` typeArity rhs_ty+ -- orig_rhs: using tidy_rhs would make a black hole, since+ -- exprArity uses the arities of Ids inside the rhs+ --+ -- typeArity: see Note [Arity invariants for bindings]+ -- in GHC.Core.Opt.Arity ------------ Unfolding -------------- tidyTopUnfolding :: TidyEnv -> CoreExpr -> Unfolding -> Unfolding@@ -1355,4 +1355,107 @@ And (unlike tidyNestedUnfolding) don't deep-seq the new unfolding, because that'll cause a black hole (I /think/ because occurAnalyseExpr looks in IdInfo).+++************************************************************************+* *+ Old, dead, type-trimming code+* *+************************************************************************++We used to try to "trim off" the constructors of data types that are+not exported, to reduce the size of interface files, at least without+-O. But that is not always possible: see the old Note [When we can't+trim types] below for exceptions.++Then (#7445) I realised that the TH problem arises for any data type+that we have deriving( Data ), because we can invoke+ Language.Haskell.TH.Quote.dataToExpQ+to get a TH Exp representation of a value built from that data type.+You don't even need {-# LANGUAGE TemplateHaskell #-}.++At this point I give up. The pain of trimming constructors just+doesn't seem worth the gain. So I've dumped all the code, and am just+leaving it here at the end of the module in case something like this+is ever resurrected.+++Note [When we can't trim types]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+The basic idea of type trimming is to export algebraic data types+abstractly (without their data constructors) when compiling without+-O, unless of course they are explicitly exported by the user.++We always export synonyms, because they can be mentioned in the type+of an exported Id. We could do a full dependency analysis starting+from the explicit exports, but that's quite painful, and not done for+now.++But there are some times we can't do that, indicated by the 'no_trim_types' flag.++First, Template Haskell. Consider (#2386) this+ module M(T, makeOne) where+ data T = Yay String+ makeOne = [| Yay "Yep" |]+Notice that T is exported abstractly, but makeOne effectively exports it too!+A module that splices in $(makeOne) will then look for a declaration of Yay,+so it'd better be there. Hence, brutally but simply, we switch off type+constructor trimming if TH is enabled in this module.++Second, data kinds. Consider (#5912)+ {-# LANGUAGE DataKinds #-}+ module M() where+ data UnaryTypeC a = UnaryDataC a+ type Bug = 'UnaryDataC+We always export synonyms, so Bug is exposed, and that means that+UnaryTypeC must be too, even though it's not explicitly exported. In+effect, DataKinds means that we'd need to do a full dependency analysis+to see what data constructors are mentioned. But we don't do that yet.++In these two cases we just switch off type trimming altogether.++mustExposeTyCon :: Bool -- Type-trimming flag+ -> NameSet -- Exports+ -> TyCon -- The tycon+ -> Bool -- Can its rep be hidden?+-- We are compiling without -O, and thus trying to write as little as+-- possible into the interface file. But we must expose the details of+-- any data types whose constructors or fields are exported+mustExposeTyCon no_trim_types exports tc+ | no_trim_types -- See Note [When we can't trim types]+ = True++ | not (isAlgTyCon tc) -- Always expose synonyms (otherwise we'd have to+ -- figure out whether it was mentioned in the type+ -- of any other exported thing)+ = True++ | isEnumerationTyCon tc -- For an enumeration, exposing the constructors+ = True -- won't lead to the need for further exposure++ | isFamilyTyCon tc -- Open type family+ = True++ -- Below here we just have data/newtype decls or family instances++ | null data_cons -- Ditto if there are no data constructors+ = True -- (NB: empty data types do not count as enumerations+ -- see Note [Enumeration types] in GHC.Core.TyCon++ | any exported_con data_cons -- Expose rep if any datacon or field is exported+ = True++ | isNewTyCon tc && isFFITy (snd (newTyConRhs tc))+ = True -- Expose the rep for newtypes if the rep is an FFI type.+ -- For a very annoying reason. 'Foreign import' is meant to+ -- be able to look through newtypes transparently, but it+ -- can only do that if it can "see" the newtype representation++ | otherwise+ = False+ where+ data_cons = tyConDataCons tc+ exported_con con = any (`elemNameSet` exports)+ (dataConName con : dataConFieldLabels con) -}+
compiler/GHC/Iface/Tidy/StaticPtrTable.hs view
@@ -144,6 +144,7 @@ import GHC.Types.Id import GHC.Types.ForeignStubs import GHC.Data.Maybe+import GHC.Data.FastString import Control.Monad.Trans.State.Strict import Data.List (intercalate)@@ -239,28 +240,33 @@ -- @fps@ is a list associating each binding corresponding to a static entry with -- its fingerprint. sptModuleInitCode :: Platform -> Module -> [SptEntry] -> CStub-sptModuleInitCode _ _ [] = mempty-sptModuleInitCode platform this_mod entries =+sptModuleInitCode platform this_mod entries+ -- no CStub if there is no entry+ | [] <- entries = mempty+ -- no CStub for the JS backend: it deals with it directly during JS code+ -- generation+ | ArchJavaScript <- platformArch platform = mempty+ | otherwise = initializerCStub platform init_fn_nm empty init_fn_body `mappend` finalizerCStub platform fini_fn_nm empty fini_fn_body where- init_fn_nm = mkInitializerStubLabel this_mod "spt"+ init_fn_nm = mkInitializerStubLabel this_mod (fsLit "spt") init_fn_body = vcat [ text "static StgWord64 k" <> int i <> text "[2] = " <> pprFingerprint fp <> semi $$ text "extern StgPtr "- <> (pdoc platform $ mkClosureLabel (idName n) (idCafInfo n)) <> semi+ <> (pprCLabel platform $ mkClosureLabel (idName n) (idCafInfo n)) <> semi $$ text "hs_spt_insert" <> parens (hcat $ punctuate comma [ char 'k' <> int i- , char '&' <> pdoc platform (mkClosureLabel (idName n) (idCafInfo n))+ , char '&' <> pprCLabel platform (mkClosureLabel (idName n) (idCafInfo n)) ] ) <> semi | (i, SptEntry n fp) <- zip [0..] entries ] - fini_fn_nm = mkFinalizerStubLabel this_mod "spt"+ fini_fn_nm = mkFinalizerStubLabel this_mod (fsLit "spt") fini_fn_body = vcat [ text "StgWord64 k" <> int i <> text "[2] = " <> pprFingerprint fp <> semi
compiler/GHC/IfaceToCore.hs view
@@ -11,12 +11,13 @@ {-# LANGUAGE FlexibleContexts #-} {-# OPTIONS_GHC -Wno-incomplete-record-updates #-}-{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TupleSections #-}+{-# LANGUAGE RecordWildCards #-} module GHC.IfaceToCore ( tcLookupImported_maybe, importDecl, checkWiredInTyCon, tcHiBootIface, typecheckIface,+ typecheckWholeCoreBindings, typecheckIfacesForMerging, typecheckIfaceForInstantiate, tcIfaceDecl, tcIfaceDecls,@@ -24,7 +25,7 @@ tcIfaceAnnotations, tcIfaceCompleteMatches, tcIfaceExpr, -- Desired by HERMIT (#7683) tcIfaceGlobal,- tcIfaceOneShot,+ tcIfaceOneShot, tcTopIfaceBindings, hydrateCgBreakInfo ) where @@ -36,6 +37,7 @@ import GHC.Driver.Env import GHC.Driver.Session+import GHC.Driver.Config.Core.Lint ( initLintConfig ) import GHC.Builtin.Types.Literals(typeNatCoAxiomRules) import GHC.Builtin.Types@@ -61,8 +63,9 @@ import GHC.Core.InstEnv import GHC.Core.FamInstEnv import GHC.Core-import GHC.Core.Unify( RoughMatchTc(..) )+import GHC.Core.RoughMap( RoughMatchTc(..) ) import GHC.Core.Utils+import GHC.Core.Unfold( calcUnfoldingGuidance ) import GHC.Core.Unfold.Make import GHC.Core.Lint import GHC.Core.Make@@ -101,7 +104,9 @@ import GHC.Types.TypeEnv import GHC.Types.Unique.FM import GHC.Types.Unique.DSet ( mkUniqDSet )+import GHC.Types.Unique.Set ( nonDetEltsUniqSet ) import GHC.Types.Unique.Supply+import GHC.Types.Demand( isDeadEndSig ) import GHC.Types.Literal import GHC.Types.Var as Var import GHC.Types.Var.Set@@ -121,6 +126,10 @@ import Control.Monad import GHC.Parser.Annotation import GHC.Driver.Env.KnotVars+import GHC.Unit.Module.WholeCoreBindings+import Data.IORef+import Data.Foldable+import GHC.Builtin.Names (ioTyConName, rOOT_MAIN) {- This module takes@@ -237,6 +246,12 @@ } } +typecheckWholeCoreBindings :: IORef TypeEnv -> WholeCoreBindings -> IfG [CoreBind]+typecheckWholeCoreBindings type_var (WholeCoreBindings tidy_bindings this_mod _) =+ initIfaceLcl this_mod (text "typecheckWholeCoreBindings") NotBoot $ do+ tcTopIfaceBindings type_var tidy_bindings++ {- ************************************************************************ * *@@ -330,7 +345,7 @@ mergeRoles roles1 roles2 = zipWithEqual "mergeRoles" max roles1 roles2 isRepInjectiveIfaceDecl :: IfaceDecl -> Bool-isRepInjectiveIfaceDecl IfaceData{ ifCons = IfDataTyCon _ } = True+isRepInjectiveIfaceDecl IfaceData{ ifCons = IfDataTyCon{} } = True isRepInjectiveIfaceDecl IfaceFamily{ ifFamFlav = IfaceDataFamilyTyCon } = True isRepInjectiveIfaceDecl _ = False @@ -581,9 +596,9 @@ Nothing -> return NoSelfBoot -- error cases Just (GWIB { gwib_isBoot = is_boot }) -> case is_boot of- IsBoot -> failWithTc (TcRnUnknownMessage $ mkPlainError noHints (elaborate err))+ IsBoot -> failWithTc (mkTcRnUnknownMessage $ mkPlainError noHints (elaborate err)) -- The hi-boot file has mysteriously disappeared.- NotBoot -> failWithTc (TcRnUnknownMessage $ mkPlainError noHints moduleLoop)+ NotBoot -> failWithTc (mkTcRnUnknownMessage $ mkPlainError noHints moduleLoop) -- Someone below us imported us! -- This is a loop with no hi-boot in the way }}}}@@ -610,7 +625,7 @@ return $ SelfBoot { sb_mds = mds , sb_tcs = mkNameSet tcs } where- -- Retuerns @True@ if, when you call 'tcIfaceDecl' on+ -- Returns @True@ if, when you call 'tcIfaceDecl' on -- this 'IfaceDecl', an ATyCon would be returned. -- NB: This code assumes that a TyCon cannot be implicit. isIfaceTyCon IfaceId{} = False@@ -876,7 +891,7 @@ , ifFieldLabels = field_labels }) = do { traceIf (text "tc_iface_decl" <+> ppr name) ; matcher <- tc_pr if_matcher- ; builder <- fmapMaybeM tc_pr if_builder+ ; builder <- traverse tc_pr if_builder ; bindIfaceForAllBndrs univ_bndrs $ \univ_tvs -> do { bindIfaceForAllBndrs ex_bndrs $ \ex_tvs -> do { patsyn <- forkM (mk_doc name) $@@ -895,6 +910,58 @@ tc_pr (nm, b) = do { id <- forkM (ppr nm) (tcIfaceExtId nm) ; return (nm, idType id, b) } +tcTopIfaceBindings :: IORef TypeEnv -> [IfaceBindingX IfaceMaybeRhs IfaceTopBndrInfo]+ -> IfL [CoreBind]+tcTopIfaceBindings ty_var ver_decls+ = do+ int <- mapM tcTopBinders ver_decls+ let all_ids :: [Id] = concatMap toList int+ liftIO $ modifyIORef ty_var (flip extendTypeEnvList (map AnId all_ids))++ extendIfaceIdEnv all_ids $ mapM (tc_iface_bindings) int++tcTopBinders :: IfaceBindingX a IfaceTopBndrInfo -> IfL (IfaceBindingX a Id)+tcTopBinders = traverse mk_top_id++tc_iface_bindings :: IfaceBindingX IfaceMaybeRhs Id -> IfL CoreBind+tc_iface_bindings (IfaceNonRec b rhs) = do+ rhs' <- tc_iface_binding b rhs+ return $ NonRec b rhs'+tc_iface_bindings (IfaceRec bs) = do+ rs <- mapM (\(b, rhs) -> (b,) <$> tc_iface_binding b rhs) bs+ return (Rec rs)++-- | See Note [Interface File with Core: Sharing RHSs]+tc_iface_binding :: Id -> IfaceMaybeRhs -> IfL CoreExpr+tc_iface_binding i IfUseUnfoldingRhs =+ case maybeUnfoldingTemplate $ realIdUnfolding i of+ Just e -> return e+ Nothing -> pprPanic "tc_iface_binding" (vcat [text "Binding" <+> quotes (ppr i) <+> text "had an unfolding when the interface file was created"+ , text "which has now gone missing, something has badly gone wrong."+ , text "Unfolding:" <+> ppr (realIdUnfolding i)])++tc_iface_binding _ (IfRhs rhs) = tcIfaceExpr rhs++mk_top_id :: IfaceTopBndrInfo -> IfL Id+mk_top_id (IfGblTopBndr gbl_name)+ -- See Note [Root-main Id]+ -- This special binding is actually defined in the current module+ -- (hence don't go looking for it externally) but the module name is rOOT_MAIN+ -- rather than the current module so we need this special case.+ -- See some similar logic in `GHC.Rename.Env`.+ | Just rOOT_MAIN == nameModule_maybe gbl_name+ = do+ ATyCon ioTyCon <- tcIfaceGlobal ioTyConName+ return $ mkExportedVanillaId gbl_name (mkTyConApp ioTyCon [unitTy])+ | otherwise = tcIfaceExtId gbl_name+mk_top_id (IfLclTopBndr raw_name iface_type info details) = do+ name <- newIfaceName (mkVarOccFS raw_name)+ ty <- tcIfaceType iface_type+ info' <- tcIdInfo False TopLevel name ty info+ details' <- tcIdDetails ty details+ let new_id = mkGlobalId details' name ty info'+ return new_id+ tcIfaceDecls :: Bool -> [(Fingerprint, IfaceDecl)] -> IfL [(Name,TyThing)]@@ -1039,11 +1106,12 @@ = case if_cons of IfAbstractTyCon -> return AbstractTyCon- IfDataTyCon cons+ IfDataTyCon type_data cons -> do { data_cons <- mapM tc_con_decl cons ; return $ mkLevPolyDataTyConRhs (isFixedRuntimeRepKind $ tyConResKind tycon)+ type_data data_cons } IfNewTyCon con -> do { data_con <- tc_con_decl con@@ -1129,7 +1197,7 @@ tc_strict :: IfaceBang -> IfL HsImplBang tc_strict IfNoBang = return (HsLazy)- tc_strict IfStrict = return (HsStrict)+ tc_strict IfStrict = return (HsStrict True) tc_strict IfUnpack = return (HsUnpack Nothing) tc_strict (IfUnpackCo if_co) = do { co <- tcIfaceCo if_co ; return (HsUnpack (Just co)) }@@ -1231,7 +1299,7 @@ (nonDetEltsUFM $ if_id_env lcl_env) ++ bndrs' ++ exprsFreeIdsList args')- ; case lintExpr dflags in_scope rhs' of+ ; case lintExpr (initLintConfig dflags in_scope) rhs' of Nothing -> return () Just errs -> do logger <- getLogger@@ -1409,11 +1477,10 @@ go (IfaceReflCo t) = Refl <$> tcIfaceType t go (IfaceGReflCo r t mco) = GRefl r <$> tcIfaceType t <*> go_mco mco- go (IfaceFunCo r w c1 c2) = mkFunCo r <$> go w <*> go c1 <*> go c2- go (IfaceTyConAppCo r tc cs)- = TyConAppCo r <$> tcIfaceTyCon tc <*> mapM go cs+ go (IfaceFunCo r w c1 c2) = mkFunCoNoFTF r <$> go w <*> go c1 <*> go c2+ go (IfaceTyConAppCo r tc cs) = TyConAppCo r <$> tcIfaceTyCon tc <*> mapM go cs go (IfaceAppCo c1 c2) = AppCo <$> go c1 <*> go c2- go (IfaceForAllCo tv k c) = do { k' <- go k+ go (IfaceForAllCo tv k c) = do { k' <- go k ; bindIfaceBndr tv $ \ tv' -> ForAllCo tv' k' <$> go c } go (IfaceCoVarCo n) = CoVarCo <$> go_var n@@ -1425,8 +1492,8 @@ <*> go c2 go (IfaceInstCo c1 t2) = InstCo <$> go c1 <*> go t2- go (IfaceNthCo d c) = do { c' <- go c- ; return $ mkNthCo (nthCoRole d c') d c' }+ go (IfaceSelCo d c) = do { c' <- go c+ ; return $ mkSelCo d c' } go (IfaceLRCo lr c) = LRCo lr <$> go c go (IfaceKindCo c) = KindCo <$> go c go (IfaceSubCo c) = SubCo <$> go c@@ -1468,9 +1535,9 @@ tcIfaceExpr (IfaceExt gbl) = Var <$> tcIfaceExtId gbl -tcIfaceExpr (IfaceLitRubbish rep)+tcIfaceExpr (IfaceLitRubbish tc rep) = do rep' <- tcIfaceType rep- return (Lit (LitRubbish rep'))+ return (Lit (LitRubbish tc rep')) tcIfaceExpr (IfaceLit lit) = do lit' <- tcIfaceLit lit@@ -1515,7 +1582,7 @@ case_bndr_name <- newIfaceName (mkVarOccFS case_bndr) let scrut_ty = exprType scrut'- case_mult = Many+ case_mult = ManyTy case_bndr' = mkLocalIdOrCoVar case_bndr_name case_mult scrut_ty -- "OrCoVar" since a coercion can be a scrutinee with -fdefer-type-errors -- (e.g. see test T15695). Ticket #17291 covers fixing this problem.@@ -1535,7 +1602,7 @@ ; ty' <- tcIfaceType ty ; id_info <- tcIdInfo False {- Don't ignore prags; we are inside one! -} NotTopLevel name ty' info- ; let id = mkLocalIdWithInfo name Many ty' id_info+ ; let id = mkLocalIdWithInfo name ManyTy ty' id_info `asJoinId_maybe` tcJoinInfo ji ; rhs' <- tcIfaceExpr rhs ; body' <- extendIfaceIdEnv [id] (tcIfaceExpr body)@@ -1551,7 +1618,7 @@ tc_rec_bndr (IfLetBndr fs ty _ ji) = do { name <- newIfaceName (mkVarOccFS fs) ; ty' <- tcIfaceType ty- ; return (mkLocalId name Many ty' `asJoinId_maybe` tcJoinInfo ji) }+ ; return (mkLocalId name ManyTy ty' `asJoinId_maybe` tcJoinInfo ji) } tc_pair (IfLetBndr _ _ info _, rhs) id = do { rhs' <- tcIfaceExpr rhs ; id_info <- tcIdInfo False {- Don't ignore prags; we are inside one! -}@@ -1660,8 +1727,8 @@ need_prag :: IfaceInfoItem -> Bool -- Always read in compulsory unfoldings -- See Note [Always expose compulsory unfoldings] in GHC.Iface.Tidy- need_prag (HsUnfold _ (IfCompulsory {})) = True- need_prag _ = False+ need_prag (HsUnfold _ (IfCoreUnfold src _ _ _)) = isCompulsorySource src+ need_prag _ = False tcPrag :: IdInfo -> IfaceInfoItem -> IfL IdInfo tcPrag info HsNoCafRefs = return (info `setCafInfo` NoCafRefs)@@ -1669,7 +1736,6 @@ tcPrag info (HsDmdSig str) = return (info `setDmdSigInfo` str) tcPrag info (HsCprSig cpr) = return (info `setCprSigInfo` cpr) tcPrag info (HsInline prag) = return (info `setInlinePragInfo` prag)- tcPrag info HsLevity = return (info `setNeverRepPoly` ty) tcPrag info (HsLFInfo lf_info) = do lf_info <- tcLFInfo lf_info return (info `setLFInfo` lf_info)@@ -1722,25 +1788,17 @@ tcUnfolding :: TopLevelFlag -> Name -> Type -> IdInfo -> IfaceUnfolding -> IfL Unfolding -- See Note [Lazily checking Unfoldings]-tcUnfolding toplvl name _ info (IfCoreUnfold stable cache if_expr)+tcUnfolding toplvl name _ info (IfCoreUnfold src cache if_guidance if_expr) = do { uf_opts <- unfoldingOpts <$> getDynFlags- ; expr <- tcUnfoldingRhs False toplvl name if_expr- ; let unf_src | stable = InlineStable- | otherwise = InlineRhs- ; return $ mkFinalUnfolding uf_opts unf_src strict_sig expr (Just cache) }+ ; expr <- tcUnfoldingRhs (isCompulsorySource src) toplvl name if_expr+ ; let guidance = case if_guidance of+ IfWhen arity unsat_ok boring_ok -> UnfWhen arity unsat_ok boring_ok+ IfNoGuidance -> calcUnfoldingGuidance uf_opts is_top_bottoming expr+ -- See Note [Tying the 'CoreUnfolding' knot]+ ; return $ mkCoreUnfolding src True expr (Just cache) guidance } where -- Strictness should occur before unfolding!- strict_sig = dmdSigInfo info--tcUnfolding toplvl name _ _ (IfCompulsory if_expr)- = do { expr <- tcUnfoldingRhs True toplvl name if_expr- ; return $ mkCompulsoryUnfolding' expr }--tcUnfolding toplvl name _ _ (IfInlineRule arity unsat_ok boring_ok if_expr)- = do { expr <- tcUnfoldingRhs False toplvl name if_expr- ; return $ mkCoreUnfolding InlineStable True expr Nothing guidance }- where- guidance = UnfWhen { ug_arity = arity, ug_unsat_ok = unsat_ok, ug_boring_ok = boring_ok }+ is_top_bottoming = isTopLevel toplvl && isDeadEndSig (dmdSigInfo info) tcUnfolding _toplvl name dfun_ty _ (IfDFunUnfold bs ops) = bindIfaceBndrs bs $ \ bs' ->@@ -1814,7 +1872,7 @@ Conclusion: `tcUnfolding` must return an `Unfolding` whose `uf_src` field is readable without forcing the `uf_tmpl` field. In particular, all the functions used at the end of-`tcUnfolding` (such as `mkFinalUnfolding`, `mkCompulsoryUnfolding'`, `mkCoreUnfolding`) must be+`tcUnfolding` (such as `mkFinalUnfolding`, `mkCoreUnfolding`) must be lazy in `expr`. Ticket #21139@@ -1831,10 +1889,10 @@ -- See Note [Linting Unfoldings from Interfaces] in GHC.Core.Lint when (isTopLevel toplvl) $ whenGOptM Opt_DoCoreLinting $ do- in_scope <- get_in_scope+ in_scope <- nonDetEltsUniqSet <$> get_in_scope dflags <- getDynFlags logger <- getLogger- case lintUnfolding is_compulsory dflags noSrcLoc in_scope core_expr' of+ case lintUnfolding is_compulsory (initLintConfig dflags in_scope) noSrcLoc core_expr' of Nothing -> return () Just errs -> liftIO $ displayLintResults logger False doc@@ -1903,7 +1961,7 @@ { mb_thing <- importDecl name -- It's imported; go get it ; case mb_thing of- Failed err -> failIfM err+ Failed err -> failIfM (ppr name <+> err) Succeeded thing -> return thing }}} @@ -1966,11 +2024,12 @@ -- this expression *after* typechecking T. tcIfaceTyCon :: IfaceTyCon -> IfL TyCon-tcIfaceTyCon (IfaceTyCon name info)+tcIfaceTyCon (IfaceTyCon name _info) = do { thing <- tcIfaceGlobal name- ; return $ case ifaceTyConIsPromoted info of- NotPromoted -> tyThingTyCon thing- IsPromoted -> promoteDataCon $ tyThingDataCon thing }+ ; case thing of+ ATyCon tc -> return tc+ AConLike (RealDataCon dc) -> return (promoteDataCon dc)+ _ -> pprPanic "tcIfaceTyCon" (ppr thing) } tcIfaceCoAxiom :: Name -> IfL (CoAxiom Branched) tcIfaceCoAxiom name = do { thing <- tcIfaceImplicit name
+ compiler/GHC/JS/Make.hs view
@@ -0,0 +1,715 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE GADTs #-}++{-# OPTIONS_GHC -fno-warn-orphans #-} -- only for Num, Fractional on JExpr++-----------------------------------------------------------------------------+-- |+-- Module : GHC.JS.Make+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Sylvain Henry <sylvain.henry@iohk.io>+-- Josh Meredith <josh.meredith@iohk.io>+-- Stability : experimental+--+--+-- * Domain and Purpose+--+-- GHC.JS.Make defines helper functions to ease the creation of JavaScript+-- ASTs as defined in 'GHC.JS.Syntax'. Its purpose is twofold: make the EDSL+-- more ergonomic to program in, and make errors in the EDSL /look/ obvious+-- because the EDSL is untyped. It is primarily concerned with injecting+-- terms into the domain of the EDSL to construct JS programs in Haskell.+--+-- * Strategy+--+-- The strategy for this module comes straight from gentzen; where we have+-- two types of helper functions. Functions which inject terms into the+-- EDSL, and combinator functions which operate on terms in the EDSL to+-- construct new terms in the EDSL. Crucially, missing from this module are+-- corresponding /elimination/ or /destructing/ functions which would+-- project information from the EDSL back to Haskell. See+-- 'GHC.StgToJS.UnitUtils' and 'GHC.StgToJS.CoreUtils' for such functions.+--+-- * /Introduction/ functions+--+-- We define various primitive helpers which /introduce/ terms in the+-- EDSL, for example 'jVar', 'jLam', and 'var' and 'jString'. Notice+-- that the type of each of these functions have the domain @isSat a+-- => a -> ...@; indicating that they each take something that /can/+-- be injected into the EDSL domain, and the range 'JExpr' or 'JStat';+-- indicating the corresponding value in the EDSL domain. Similarly+-- this module exports two typeclasses 'ToExpr' and 'ToSat', 'ToExpr'+-- injects values as a JS expression into the EDSL. 'ToSat' ensures+-- that terms introduced into the EDSL carry identifier information so+-- terms in the EDSL must have meaning.+--+-- * /Combinator/ functions+--+-- The rest of the module defines combinators which create terms in+-- the EDSL from terms in the EDSL. Notable examples are '|=' and+-- '||=', '|=' is sugar for 'AssignStat', it is a binding form that+-- declares @foo = bar@ /assuming/ foo has been already declared.+-- '||=' is more sugar on top of '|=', it is also a binding form that+-- declares the LHS of '|=' before calling '|=' to bind a value, bar,+-- to a variable foo. Other common examples are the 'if_' and 'math_'+-- helpers such as 'math_cos'.+--+-- * Consumers+--+-- The entire JS backend consumes this module, e.g., the modules in+-- GHC.StgToJS.\*.+--+-- * Notation+--+-- In this module we use @==>@ in docstrings to show the translation from+-- the JS EDSL domain to JS code. For example, @foo ||= bar ==> var foo; foo+-- = bar;@ should be read as @foo ||= bar@ is in the EDSL domain and results+-- in the JS code @var foo; foo = bar;@ when compiled.+-----------------------------------------------------------------------------+module GHC.JS.Make+ ( -- * Injection Type classes+ -- $classes+ ToJExpr(..)+ , ToStat(..)+ -- * Introduction functions+ -- $intro_funcs+ , var+ , jString+ , jLam, jVar, jFor, jForIn, jForEachIn, jTryCatchFinally+ -- * Combinators+ -- $combinators+ , (||=), (|=), (.==.), (.===.), (.!=.), (.!==.), (.!)+ , (.>.), (.>=.), (.<.), (.<=.)+ , (.<<.), (.>>.), (.>>>.)+ , (.|.), (.||.), (.&&.)+ , if_, if10, if01, ifS, ifBlockS+ , jwhenS+ , app, appS, returnS+ , loop, loopBlockS+ , preIncrS, postIncrS+ , preDecrS, postDecrS+ , off8, off16, off32, off64+ , mask8, mask16+ , signExtend8, signExtend16+ , typeof+ , returnStack, assignAllEqual, assignAll, assignAllReverseOrder+ , declAssignAll+ , nullStat, (.^)+ , trace+ -- ** Hash combinators+ , jhEmpty+ , jhSingle+ , jhAdd+ , jhFromList+ -- * Literals+ -- $literals+ , null_+ , undefined_+ , false_+ , true_+ , zero_+ , one_+ , two_+ , three_+ -- ** Math functions+ -- $math+ , math_log, math_sin, math_cos, math_tan, math_exp, math_acos, math_asin,+ math_atan, math_abs, math_pow, math_sqrt, math_asinh, math_acosh, math_atanh,+ math_cosh, math_sinh, math_tanh, math_expm1, math_log1p, math_fround+ -- * Statement helpers+ , decl+ -- * Miscellaneous+ -- $misc+ , allocData, allocClsA+ , dataFieldName, dataFieldNames+ )+where++import GHC.Prelude hiding ((.|.))++import GHC.JS.Syntax++import Control.Arrow ((***))++import Data.Array+import qualified Data.Map as M+import qualified Data.List as List++import GHC.Utils.Outputable (Outputable (..))+import GHC.Data.FastString+import GHC.Utils.Monad.State.Strict+import GHC.Utils.Panic+import GHC.Utils.Misc+import GHC.Types.Unique.Map++--------------------------------------------------------------------------------+-- Type Classes+--------------------------------------------------------------------------------+-- $classes+-- The 'ToJExpr' class handles injection of of things into the EDSL as a JS+-- expression++-- | Things that can be marshalled into javascript values.+-- Instantiate for any necessary data structures.+class ToJExpr a where+ toJExpr :: a -> JExpr+ toJExprFromList :: [a] -> JExpr+ toJExprFromList = ValExpr . JList . map toJExpr++instance ToJExpr a => ToJExpr [a] where+ toJExpr = toJExprFromList++instance ToJExpr JExpr where+ toJExpr = id++instance ToJExpr () where+ toJExpr _ = ValExpr $ JList []++instance ToJExpr Bool where+ toJExpr True = var "true"+ toJExpr False = var "false"++instance ToJExpr JVal where+ toJExpr = ValExpr++instance ToJExpr a => ToJExpr (UniqMap FastString a) where+ toJExpr = ValExpr . JHash . mapUniqMap toJExpr++instance ToJExpr a => ToJExpr (M.Map String a) where+ toJExpr = ValExpr . JHash . listToUniqMap . map (mkFastString *** toJExpr) . M.toList++instance ToJExpr Double where+ toJExpr = ValExpr . JDouble . SaneDouble++instance ToJExpr Int where+ toJExpr = ValExpr . JInt . fromIntegral++instance ToJExpr Integer where+ toJExpr = ValExpr . JInt++instance ToJExpr Char where+ toJExpr = ValExpr . JStr . mkFastString . (:[])+ toJExprFromList = ValExpr . JStr . mkFastString+-- where escQuotes = tailDef "" . initDef "" . show++instance ToJExpr Ident where+ toJExpr = ValExpr . JVar++instance ToJExpr FastString where+ toJExpr = ValExpr . JStr++instance (ToJExpr a, ToJExpr b) => ToJExpr (a,b) where+ toJExpr (a,b) = ValExpr . JList $ [toJExpr a, toJExpr b]++instance (ToJExpr a, ToJExpr b, ToJExpr c) => ToJExpr (a,b,c) where+ toJExpr (a,b,c) = ValExpr . JList $ [toJExpr a, toJExpr b, toJExpr c]++instance (ToJExpr a, ToJExpr b, ToJExpr c, ToJExpr d) => ToJExpr (a,b,c,d) where+ toJExpr (a,b,c,d) = ValExpr . JList $ [toJExpr a, toJExpr b, toJExpr c, toJExpr d]+instance (ToJExpr a, ToJExpr b, ToJExpr c, ToJExpr d, ToJExpr e) => ToJExpr (a,b,c,d,e) where+ toJExpr (a,b,c,d,e) = ValExpr . JList $ [toJExpr a, toJExpr b, toJExpr c, toJExpr d, toJExpr e]+instance (ToJExpr a, ToJExpr b, ToJExpr c, ToJExpr d, ToJExpr e, ToJExpr f) => ToJExpr (a,b,c,d,e,f) where+ toJExpr (a,b,c,d,e,f) = ValExpr . JList $ [toJExpr a, toJExpr b, toJExpr c, toJExpr d, toJExpr e, toJExpr f]+++-- | The 'ToStat' class handles injection of of things into the EDSL as a JS+-- statement. This ends up being polymorphic sugar for JS blocks, see helper+-- function 'GHC.JS.Make.expr2stat'. Instantiate for any necessary data+-- structures.+class ToStat a where+ toStat :: a -> JStat++instance ToStat JStat where+ toStat = id++instance ToStat [JStat] where+ toStat = BlockStat++instance ToStat JExpr where+ toStat = expr2stat++instance ToStat [JExpr] where+ toStat = BlockStat . map expr2stat++--------------------------------------------------------------------------------+-- Introduction Functions+--------------------------------------------------------------------------------+-- $intro_functions+-- Introduction functions are functions that map values or terms in the Haskell+-- domain to the JS EDSL domain++-- | Create a new anonymous function. The result is a 'GHC.JS.Syntax.JExpr'+-- expression.+-- Usage:+--+-- > jLam $ \x -> jVar x + one_+-- > jLam $ \f -> (jLam $ \x -> (f `app` (x `app` x))) `app` (jLam $ \x -> (f `app` (x `app` x)))+jLam :: ToSat a => a -> JExpr+jLam f = ValExpr . UnsatVal . IS $ do+ (block,is) <- runIdentSupply $ toSat_ f []+ return $ JFunc is block++-- | Introduce a new variable into scope for the duration+-- of the enclosed expression. The result is a block statement.+-- Usage:+--+-- @jVar $ \x y -> mconcat [jVar x ||= one_, jVar y ||= two_, jVar x + jVar y]@+jVar :: ToSat a => a -> JStat+jVar f = UnsatBlock . IS $ do+ (block, is) <- runIdentSupply $ toSat_ f []+ let addDecls (BlockStat ss) =+ BlockStat $ map decl is ++ ss+ addDecls x = x+ return $ addDecls block++-- | Create a 'for in' statement.+-- Usage:+--+-- @jForIn {expression} $ \x -> {block involving x}@+jForIn :: ToSat a => JExpr -> (JExpr -> a) -> JStat+jForIn e f = UnsatBlock . IS $ do+ (block, is) <- runIdentSupply $ toSat_ f []+ let i = List.head is+ return $ decl i `mappend` ForInStat False i e block++-- | As with "jForIn" but creating a \"for each in\" statement.+jForEachIn :: ToSat a => JExpr -> (JExpr -> a) -> JStat+jForEachIn e f = UnsatBlock . IS $ do+ (block, is) <- runIdentSupply $ toSat_ f []+ let i = List.head is+ return $ decl i `mappend` ForInStat True i e block++-- | As with "jForIn" but creating a \"for each in\" statement.+jTryCatchFinally :: (ToSat a) => JStat -> a -> JStat -> JStat+jTryCatchFinally s f s2 = UnsatBlock . IS $ do+ (block, is) <- runIdentSupply $ toSat_ f []+ let i = List.head is+ return $ TryStat s i block s2++-- | construct a JS variable reference+var :: FastString -> JExpr+var = ValExpr . JVar . TxtI++-- | Convert a ShortText to a Javascript String+jString :: FastString -> JExpr+jString = toJExpr++-- | Create a 'for' statement+jFor :: (ToJExpr a, ToStat b) => JStat -> a -> JStat -> b -> JStat+jFor before p after b = BlockStat [before, WhileStat False (toJExpr p) b']+ where b' = case toStat b of+ BlockStat xs -> BlockStat $ xs ++ [after]+ x -> BlockStat [x,after]++-- | construct a js declaration with the given identifier+decl :: Ident -> JStat+decl i = DeclStat i Nothing++-- | The empty JS HashMap+jhEmpty :: M.Map k JExpr+jhEmpty = M.empty++-- | A singleton JS HashMap+jhSingle :: (Ord k, ToJExpr a) => k -> a -> M.Map k JExpr+jhSingle k v = jhAdd k v jhEmpty++-- | insert a key-value pair into a JS HashMap+jhAdd :: (Ord k, ToJExpr a) => k -> a -> M.Map k JExpr -> M.Map k JExpr+jhAdd k v m = M.insert k (toJExpr v) m++-- | Construct a JS HashMap from a list of key-value pairs+jhFromList :: [(FastString, JExpr)] -> JVal+jhFromList = JHash . listToUniqMap++-- | The empty JS statement+nullStat :: JStat+nullStat = BlockStat []+++--------------------------------------------------------------------------------+-- Combinators+--------------------------------------------------------------------------------+-- $combinators+-- Combinators operate on terms in the JS EDSL domain to create new terms in the+-- EDSL domain.++-- | JS infix Equality operators+(.==.), (.===.), (.!=.), (.!==.) :: JExpr -> JExpr -> JExpr+(.==.) = InfixExpr EqOp+(.===.) = InfixExpr StrictEqOp+(.!=.) = InfixExpr NeqOp+(.!==.) = InfixExpr StrictNeqOp++infixl 6 .==., .===., .!=., .!==.++-- | JS infix Ord operators+(.>.), (.>=.), (.<.), (.<=.) :: JExpr -> JExpr -> JExpr+(.>.) = InfixExpr GtOp+(.>=.) = InfixExpr GeOp+(.<.) = InfixExpr LtOp+(.<=.) = InfixExpr LeOp++infixl 7 .>., .>=., .<., .<=.++-- | JS infix bit operators+(.|.), (.||.), (.&&.) :: JExpr -> JExpr -> JExpr+(.|.) = InfixExpr BOrOp+(.||.) = InfixExpr LOrOp+(.&&.) = InfixExpr LAndOp++infixl 8 .||., .&&.++-- | JS infix bit shift operators+(.<<.), (.>>.), (.>>>.) :: JExpr -> JExpr -> JExpr+(.<<.) = InfixExpr LeftShiftOp+(.>>.) = InfixExpr RightShiftOp+(.>>>.) = InfixExpr ZRightShiftOp++infixl 9 .<<., .>>., .>>>.++-- | Given a 'JExpr', return the its type.+typeof :: JExpr -> JExpr+typeof = UOpExpr TypeofOp++-- | JS if-expression+--+-- > if_ e1 e2 e3 ==> e1 ? e2 : e3+if_ :: JExpr -> JExpr -> JExpr -> JExpr+if_ e1 e2 e3 = IfExpr e1 e2 e3++-- | If-expression which returns statements, see related 'ifBlockS'+--+-- > if e s1 s2 ==> if(e) { s1 } else { s2 }+ifS :: JExpr -> JStat -> JStat -> JStat+ifS e s1 s2 = IfStat e s1 s2++-- | A when-statement as syntactic sugar via `ifS`+--+-- > jwhenS cond block ==> if(cond) { block } else { }+jwhenS :: JExpr -> JStat -> JStat+jwhenS cond block = ifS cond block mempty++-- | If-expression which returns blocks+--+-- > ifBlockS e s1 s2 ==> if(e) { s1 } else { s2 }+ifBlockS :: JExpr -> [JStat] -> [JStat] -> JStat+ifBlockS e s1 s2 = IfStat e (mconcat s1) (mconcat s2)++-- | if-expression that returns 1 if condition <=> true, 0 otherwise+--+-- > if10 e ==> e ? 1 : 0+if10 :: JExpr -> JExpr+if10 e = IfExpr e one_ zero_++-- | if-expression that returns 0 if condition <=> true, 1 otherwise+--+-- > if01 e ==> e ? 0 : 1+if01 :: JExpr -> JExpr+if01 e = IfExpr e zero_ one_++-- | an expression application, see related 'appS'+--+-- > app f xs ==> f(xs)+app :: FastString -> [JExpr] -> JExpr+app f xs = ApplExpr (var f) xs++-- | A statement application, see the expression form 'app'+appS :: FastString -> [JExpr] -> JStat+appS f xs = ApplStat (var f) xs++-- | Return a 'JExpr'+returnS :: JExpr -> JStat+returnS e = ReturnStat e++-- | "for" loop with increment at end of body+loop :: JExpr -> (JExpr -> JExpr) -> (JExpr -> JStat) -> JStat+loop initial test body = jVar $ \i ->+ mconcat [ i |= initial+ , WhileStat False (test i) (body i)+ ]++-- | "for" loop with increment at end of body+loopBlockS :: JExpr -> (JExpr -> JExpr) -> (JExpr -> [JStat]) -> JStat+loopBlockS initial test body = jVar $ \i ->+ mconcat [ i |= initial+ , WhileStat False (test i) (mconcat (body i))+ ]++-- | Prefix-increment a 'JExpr'+preIncrS :: JExpr -> JStat+preIncrS x = UOpStat PreIncOp x++-- | Postfix-increment a 'JExpr'+postIncrS :: JExpr -> JStat+postIncrS x = UOpStat PostIncOp x++-- | Prefix-decrement a 'JExpr'+preDecrS :: JExpr -> JStat+preDecrS x = UOpStat PreDecOp x++-- | Postfix-decrement a 'JExpr'+postDecrS :: JExpr -> JStat+postDecrS x = UOpStat PostDecOp x++-- | Byte indexing of o with a 64-bit offset+off64 :: JExpr -> JExpr -> JExpr+off64 o i = Add o (i .<<. three_)++-- | Byte indexing of o with a 32-bit offset+off32 :: JExpr -> JExpr -> JExpr+off32 o i = Add o (i .<<. two_)++-- | Byte indexing of o with a 16-bit offset+off16 :: JExpr -> JExpr -> JExpr+off16 o i = Add o (i .<<. one_)++-- | Byte indexing of o with a 8-bit offset+off8 :: JExpr -> JExpr -> JExpr+off8 o i = Add o i++-- | a bit mask to retrieve the lower 8-bits+mask8 :: JExpr -> JExpr+mask8 x = BAnd x (Int 0xFF)++-- | a bit mask to retrieve the lower 16-bits+mask16 :: JExpr -> JExpr+mask16 x = BAnd x (Int 0xFFFF)++-- | Sign-extend/narrow a 8-bit value+signExtend8 :: JExpr -> JExpr+signExtend8 x = (BAnd x (Int 0x7F )) `Sub` (BAnd x (Int 0x80))++-- | Sign-extend/narrow a 16-bit value+signExtend16 :: JExpr -> JExpr+signExtend16 x = (BAnd x (Int 0x7FFF)) `Sub` (BAnd x (Int 0x8000))++-- | Select a property 'prop', from and object 'obj'+--+-- > obj .^ prop ==> obj.prop+(.^) :: JExpr -> FastString -> JExpr+obj .^ prop = SelExpr obj (TxtI prop)+infixl 8 .^++-- | Assign a variable to an expression+--+-- > foo |= expr ==> var foo = expr;+(|=) :: JExpr -> JExpr -> JStat+(|=) = AssignStat++-- | Declare a variable and then Assign the variable to an expression+--+-- > foo |= expr ==> var foo; foo = expr;+(||=) :: Ident -> JExpr -> JStat+i ||= ex = DeclStat i (Just ex)++infixl 2 ||=, |=++-- | return the expression at idx of obj+--+-- > obj .! idx ==> obj[idx]+(.!) :: JExpr -> JExpr -> JExpr+(.!) = IdxExpr++infixl 8 .!++assignAllEqual :: HasDebugCallStack => [JExpr] -> [JExpr] -> JStat+assignAllEqual xs ys = mconcat (zipWithEqual "assignAllEqual" (|=) xs ys)++assignAll :: [JExpr] -> [JExpr] -> JStat+assignAll xs ys = mconcat (zipWith (|=) xs ys)++assignAllReverseOrder :: [JExpr] -> [JExpr] -> JStat+assignAllReverseOrder xs ys = mconcat (reverse (zipWith (|=) xs ys))++declAssignAll :: [Ident] -> [JExpr] -> JStat+declAssignAll xs ys = mconcat (zipWith (||=) xs ys)++trace :: ToJExpr a => a -> JStat+trace ex = appS "h$log" [toJExpr ex]+++--------------------------------------------------------------------------------+-- Literals+--------------------------------------------------------------------------------+-- $literals+-- Literals in the JS EDSL are constants in the Haskell domain. These are useful+-- helper values and never change++-- | The JS literal 'null'+null_ :: JExpr+null_ = var "null"++-- | The JS literal 0+zero_ :: JExpr+zero_ = Int 0++-- | The JS literal 1+one_ :: JExpr+one_ = Int 1++-- | The JS literal 2+two_ :: JExpr+two_ = Int 2++-- | The JS literal 3+three_ :: JExpr+three_ = Int 3++-- | The JS literal 'undefined'+undefined_ :: JExpr+undefined_ = var "undefined"++-- | The JS literal 'true'+true_ :: JExpr+true_ = var "true"++-- | The JS literal 'false'+false_ :: JExpr+false_ = var "false"++returnStack :: JStat+returnStack = ReturnStat (ApplExpr (var "h$rs") [])+++--------------------------------------------------------------------------------+-- Math functions+--------------------------------------------------------------------------------+-- $math+-- Math functions in the EDSL are literals, with the exception of 'math_' which+-- is the sole math introduction function.++math :: JExpr+math = var "Math"++math_ :: FastString -> [JExpr] -> JExpr+math_ op args = ApplExpr (math .^ op) args++math_log, math_sin, math_cos, math_tan, math_exp, math_acos, math_asin, math_atan,+ math_abs, math_pow, math_sqrt, math_asinh, math_acosh, math_atanh, math_sign,+ math_sinh, math_cosh, math_tanh, math_expm1, math_log1p, math_fround+ :: [JExpr] -> JExpr+math_log = math_ "log"+math_sin = math_ "sin"+math_cos = math_ "cos"+math_tan = math_ "tan"+math_exp = math_ "exp"+math_acos = math_ "acos"+math_asin = math_ "asin"+math_atan = math_ "atan"+math_abs = math_ "abs"+math_pow = math_ "pow"+math_sign = math_ "sign"+math_sqrt = math_ "sqrt"+math_asinh = math_ "asinh"+math_acosh = math_ "acosh"+math_atanh = math_ "atanh"+math_sinh = math_ "sinh"+math_cosh = math_ "cosh"+math_tanh = math_ "tanh"+math_expm1 = math_ "expm1"+math_log1p = math_ "log1p"+math_fround = math_ "fround"++instance Num JExpr where+ x + y = InfixExpr AddOp x y+ x - y = InfixExpr SubOp x y+ x * y = InfixExpr MulOp x y+ abs x = math_abs [x]+ negate x = UOpExpr NegOp x+ signum x = math_sign [x]+ fromInteger x = ValExpr (JInt x)++instance Fractional JExpr where+ x / y = InfixExpr DivOp x y+ fromRational x = ValExpr (JDouble (realToFrac x))+++--------------------------------------------------------------------------------+-- Miscellaneous+--------------------------------------------------------------------------------+-- $misc+-- Everything else,++-- | Cache "dXXX" field names+dataFieldCache :: Array Int FastString+dataFieldCache = listArray (0,nFieldCache) (map (mkFastString . ('d':) . show) [(0::Int)..nFieldCache])++nFieldCache :: Int+nFieldCache = 16384++dataFieldName :: Int -> FastString+dataFieldName i+ | i < 1 || i > nFieldCache = panic "dataFieldName" (ppr i)+ | otherwise = dataFieldCache ! i++dataFieldNames :: [FastString]+dataFieldNames = fmap dataFieldName [1..nFieldCache]+++-- | Cache "h$dXXX" names+dataCache :: Array Int FastString+dataCache = listArray (0,1024) (map (mkFastString . ("h$d"++) . show) [(0::Int)..1024])++allocData :: Int -> JExpr+allocData i = toJExpr (TxtI (dataCache ! i))++-- | Cache "h$cXXX" names+clsCache :: Array Int FastString+clsCache = listArray (0,1024) (map (mkFastString . ("h$c"++) . show) [(0::Int)..1024])++allocClsA :: Int -> JExpr+allocClsA i = toJExpr (TxtI (clsCache ! i))+++--------------------------------------------------------------------------------+-- New Identifiers+--------------------------------------------------------------------------------++-- | The 'ToSat' class is heavily used in the Introduction function. It ensures+-- that all identifiers in the EDSL are tracked and named with an 'IdentSupply'.+class ToSat a where+ toSat_ :: a -> [Ident] -> IdentSupply (JStat, [Ident])++instance ToSat [JStat] where+ toSat_ f vs = IS $ return $ (BlockStat f, reverse vs)++instance ToSat JStat where+ toSat_ f vs = IS $ return $ (f, reverse vs)++instance ToSat JExpr where+ toSat_ f vs = IS $ return $ (toStat f, reverse vs)++instance ToSat [JExpr] where+ toSat_ f vs = IS $ return $ (BlockStat $ map expr2stat f, reverse vs)++instance (ToSat a, b ~ JExpr) => ToSat (b -> a) where+ toSat_ f vs = IS $ do+ x <- takeOneIdent+ runIdentSupply $ toSat_ (f (ValExpr $ JVar x)) (x:vs)++-- | Convert A JS expression to a JS statement where applicable. This only+-- affects applications; 'ApplExpr', If-expressions; 'IfExpr', and Unary+-- expression; 'UOpExpr'.+expr2stat :: JExpr -> JStat+expr2stat (ApplExpr x y) = (ApplStat x y)+expr2stat (IfExpr x y z) = IfStat x (expr2stat y) (expr2stat z)+expr2stat (UOpExpr o x) = UOpStat o x+expr2stat _ = nullStat++takeOneIdent :: State [Ident] Ident+takeOneIdent = do+ xxs <- get+ case xxs of+ (x:xs) -> do+ put xs+ return x+ _ -> error "takeOneIdent: empty list"+
+ compiler/GHC/JS/Ppr.hs view
@@ -0,0 +1,294 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE BlockArguments #-}++-- For Outputable instances for JS syntax+{-# OPTIONS_GHC -Wno-orphans #-}++-- | Pretty-printing JavaScript+module GHC.JS.Ppr+ ( renderJs+ , renderJs'+ , renderPrefixJs+ , renderPrefixJs'+ , JsToDoc(..)+ , defaultRenderJs+ , RenderJs(..)+ , jsToDoc+ , pprStringLit+ , flattenBlocks+ , braceNest+ , hangBrace+ )+where++import GHC.Prelude++import GHC.JS.Syntax+import GHC.JS.Transform+++import Data.Char (isControl, ord)+import Data.List (sortOn)++import Numeric(showHex)++import GHC.Utils.Outputable (Outputable (..), docToSDoc)+import GHC.Utils.Ppr as PP+import GHC.Data.FastString+import GHC.Types.Unique.Map++instance Outputable JExpr where+ ppr = docToSDoc . renderJs++instance Outputable JVal where+ ppr = docToSDoc . renderJs+++($$$) :: Doc -> Doc -> Doc+x $$$ y = nest 2 $ x $+$ y++-- | Render a syntax tree as a pretty-printable document+-- (simply showing the resultant doc produces a nice,+-- well formatted String).+renderJs :: (JsToDoc a, JMacro a) => a -> Doc+renderJs = renderJs' defaultRenderJs++renderJs' :: (JsToDoc a, JMacro a) => RenderJs -> a -> Doc+renderJs' r = jsToDocR r . jsSaturate Nothing++data RenderJs = RenderJs+ { renderJsS :: !(RenderJs -> JStat -> Doc)+ , renderJsE :: !(RenderJs -> JExpr -> Doc)+ , renderJsV :: !(RenderJs -> JVal -> Doc)+ , renderJsI :: !(RenderJs -> Ident -> Doc)+ }++defaultRenderJs :: RenderJs+defaultRenderJs = RenderJs defRenderJsS defRenderJsE defRenderJsV defRenderJsI++jsToDoc :: JsToDoc a => a -> Doc+jsToDoc = jsToDocR defaultRenderJs++-- | Render a syntax tree as a pretty-printable document, using a given prefix+-- to all generated names. Use this with distinct prefixes to ensure distinct+-- generated names between independent calls to render(Prefix)Js.+renderPrefixJs :: (JsToDoc a, JMacro a) => FastString -> a -> Doc+renderPrefixJs pfx = renderPrefixJs' defaultRenderJs pfx++renderPrefixJs' :: (JsToDoc a, JMacro a) => RenderJs -> FastString -> a -> Doc+renderPrefixJs' r pfx = jsToDocR r . jsSaturate (Just $ "jmId_" `mappend` pfx)++braceNest :: Doc -> Doc+braceNest x = char '{' <+> nest 2 x $$ char '}'++-- | Hang with braces:+--+-- hdr {+-- body+-- }+hangBrace :: Doc -> Doc -> Doc+hangBrace hdr body = sep [ hdr <> char ' ' <> char '{', nest 2 body, char '}' ]++class JsToDoc a where jsToDocR :: RenderJs -> a -> Doc+instance JsToDoc JStat where jsToDocR r = renderJsS r r+instance JsToDoc JExpr where jsToDocR r = renderJsE r r+instance JsToDoc JVal where jsToDocR r = renderJsV r r+instance JsToDoc Ident where jsToDocR r = renderJsI r r+instance JsToDoc [JExpr] where+ jsToDocR r = vcat . map ((<> semi) . jsToDocR r)+instance JsToDoc [JStat] where+ jsToDocR r = vcat . map ((<> semi) . jsToDocR r)++defRenderJsS :: RenderJs -> JStat -> Doc+defRenderJsS r = \case+ IfStat cond x y -> hangBrace (text "if" <> parens (jsToDocR r cond))+ (jsToDocR r x)+ $$ mbElse+ where mbElse | y == BlockStat [] = PP.empty+ | otherwise = hangBrace (text "else") (jsToDocR r y)+ DeclStat x Nothing -> text "var" <+> jsToDocR r x+ DeclStat x (Just e) -> text "var" <+> jsToDocR r x <+> char '=' <+> jsToDocR r e+ WhileStat False p b -> hangBrace (text "while" <> parens (jsToDocR r p)) (jsToDocR r b)+ WhileStat True p b -> (hangBrace (text "do") (jsToDocR r b)) $+$ text "while" <+> parens (jsToDocR r p)+ UnsatBlock e -> jsToDocR r $ pseudoSaturate e+ BreakStat l -> maybe (text "break") (\(LexicalFastString s) -> (text "break" <+> ftext s)) l+ ContinueStat l -> maybe (text "continue") (\(LexicalFastString s) -> (text "continue" <+> ftext s)) l+ LabelStat (LexicalFastString l) s -> ftext l <> char ':' $$ printBS s+ where+ printBS (BlockStat ss) = vcat $ interSemi $ flattenBlocks ss+ printBS x = jsToDocR r x+ interSemi [x] = [jsToDocR r x]+ interSemi [] = []+ interSemi (x:xs) = (jsToDocR r x <> semi) : interSemi xs++ ForInStat each i e b -> hangBrace (text txt <> parens (jsToDocR r i <+> text "in" <+> jsToDocR r e)) (jsToDocR r b)+ where txt | each = "for each"+ | otherwise = "for"+ SwitchStat e l d -> hangBrace (text "switch" <+> parens (jsToDocR r e)) cases+ where l' = map (\(c,s) -> (text "case" <+> parens (jsToDocR r c) <> char ':') $$$ (jsToDocR r s)) l ++ [text "default:" $$$ (jsToDocR r d)]+ cases = vcat l'+ ReturnStat e -> text "return" <+> jsToDocR r e+ ApplStat e es -> jsToDocR r e <> (parens . hsep . punctuate comma $ map (jsToDocR r) es)+ TryStat s i s1 s2 -> hangBrace (text "try") (jsToDocR r s) $$ mbCatch $$ mbFinally+ where mbCatch | s1 == BlockStat [] = PP.empty+ | otherwise = hangBrace (text "catch" <> parens (jsToDocR r i)) (jsToDocR r s1)+ mbFinally | s2 == BlockStat [] = PP.empty+ | otherwise = hangBrace (text "finally") (jsToDocR r s2)+ AssignStat i x -> case x of+ -- special treatment for functions, otherwise there is too much left padding+ -- (more than the length of the expression assigned to). E.g.+ --+ -- var long_variable_name = (function()+ -- {+ -- ...+ -- });+ --+ ValExpr (JFunc is b) -> sep [jsToDocR r i <+> text "= function" <> parens (hsep . punctuate comma . map (jsToDocR r) $ is) <> char '{', nest 2 (jsToDocR r b), text "}"]+ _ -> jsToDocR r i <+> char '=' <+> jsToDocR r x+ UOpStat op x+ | isPre op && isAlphaOp op -> ftext (uOpText op) <+> optParens r x+ | isPre op -> ftext (uOpText op) <> optParens r x+ | otherwise -> optParens r x <> ftext (uOpText op)+ BlockStat xs -> jsToDocR r (flattenBlocks xs)++flattenBlocks :: [JStat] -> [JStat]+flattenBlocks = \case+ BlockStat y:ys -> flattenBlocks y ++ flattenBlocks ys+ y:ys -> y : flattenBlocks ys+ [] -> []++optParens :: RenderJs -> JExpr -> Doc+optParens r x = case x of+ UOpExpr _ _ -> parens (jsToDocR r x)+ _ -> jsToDocR r x++defRenderJsE :: RenderJs -> JExpr -> Doc+defRenderJsE r = \case+ ValExpr x -> jsToDocR r x+ SelExpr x y -> jsToDocR r x <> char '.' <> jsToDocR r y+ IdxExpr x y -> jsToDocR r x <> brackets (jsToDocR r y)+ IfExpr x y z -> parens (jsToDocR r x <+> char '?' <+> jsToDocR r y <+> char ':' <+> jsToDocR r z)+ InfixExpr op x y -> parens $ hsep [jsToDocR r x, ftext (opText op), jsToDocR r y]+ UOpExpr op x+ | isPre op && isAlphaOp op -> ftext (uOpText op) <+> optParens r x+ | isPre op -> ftext (uOpText op) <> optParens r x+ | otherwise -> optParens r x <> ftext (uOpText op)+ ApplExpr je xs -> jsToDocR r je <> (parens . hsep . punctuate comma $ map (jsToDocR r) xs)+ UnsatExpr e -> jsToDocR r $ pseudoSaturate e++defRenderJsV :: RenderJs -> JVal -> Doc+defRenderJsV r = \case+ JVar i -> jsToDocR r i+ JList xs -> brackets . hsep . punctuate comma $ map (jsToDocR r) xs+ JDouble (SaneDouble d)+ | d < 0 || isNegativeZero d -> parens (double d)+ | otherwise -> double d+ JInt i+ | i < 0 -> parens (integer i)+ | otherwise -> integer i+ JStr s -> pprStringLit s+ JRegEx s -> hcat [char '/',ftext s, char '/']+ JHash m+ | isNullUniqMap m -> text "{}"+ | otherwise -> braceNest . hsep . punctuate comma .+ map (\(x,y) -> squotes (ftext x) <> colon <+> jsToDocR r y)+ -- nonDetEltsUniqMap doesn't introduce non-determinism here+ -- because we sort the elements lexically+ $ sortOn (LexicalFastString . fst) (nonDetEltsUniqMap m)+ JFunc is b -> parens $ hangBrace (text "function" <> parens (hsep . punctuate comma . map (jsToDocR r) $ is)) (jsToDocR r b)+ UnsatVal f -> jsToDocR r $ pseudoSaturate f++defRenderJsI :: RenderJs -> Ident -> Doc+defRenderJsI _ (TxtI t) = ftext t+++pprStringLit :: FastString -> Doc+pprStringLit s = hcat [char '\"',encodeJson s, char '\"']++encodeJson :: FastString -> Doc+encodeJson xs = hcat (map encodeJsonChar (unpackFS xs))++encodeJsonChar :: Char -> Doc+encodeJsonChar = \case+ '/' -> text "\\/"+ '\b' -> text "\\b"+ '\f' -> text "\\f"+ '\n' -> text "\\n"+ '\r' -> text "\\r"+ '\t' -> text "\\t"+ '"' -> text "\\\""+ '\\' -> text "\\\\"+ c+ | not (isControl c) && ord c <= 127 -> char c+ | ord c <= 0xff -> hexxs "\\x" 2 (ord c)+ | ord c <= 0xffff -> hexxs "\\u" 4 (ord c)+ | otherwise -> let cp0 = ord c - 0x10000 -- output surrogate pair+ in hexxs "\\u" 4 ((cp0 `shiftR` 10) + 0xd800) <>+ hexxs "\\u" 4 ((cp0 .&. 0x3ff) + 0xdc00)+ where hexxs prefix pad cp =+ let h = showHex cp ""+ in text (prefix ++ replicate (pad - length h) '0' ++ h)++uOpText :: JUOp -> FastString+uOpText = \case+ NotOp -> "!"+ BNotOp -> "~"+ NegOp -> "-"+ PlusOp -> "+"+ NewOp -> "new"+ TypeofOp -> "typeof"+ DeleteOp -> "delete"+ YieldOp -> "yield"+ VoidOp -> "void"+ PreIncOp -> "++"+ PostIncOp -> "++"+ PreDecOp -> "--"+ PostDecOp -> "--"++opText :: JOp -> FastString+opText = \case+ EqOp -> "=="+ StrictEqOp -> "==="+ NeqOp -> "!="+ StrictNeqOp -> "!=="+ GtOp -> ">"+ GeOp -> ">="+ LtOp -> "<"+ LeOp -> "<="+ AddOp -> "+"+ SubOp -> "-"+ MulOp -> "*"+ DivOp -> "/"+ ModOp -> "%"+ LeftShiftOp -> "<<"+ RightShiftOp -> ">>"+ ZRightShiftOp -> ">>>"+ BAndOp -> "&"+ BOrOp -> "|"+ BXorOp -> "^"+ LAndOp -> "&&"+ LOrOp -> "||"+ InstanceofOp -> "instanceof"+ InOp -> "in"+++isPre :: JUOp -> Bool+isPre = \case+ PostIncOp -> False+ PostDecOp -> False+ _ -> True++isAlphaOp :: JUOp -> Bool+isAlphaOp = \case+ NewOp -> True+ TypeofOp -> True+ DeleteOp -> True+ YieldOp -> True+ VoidOp -> True+ _ -> False
+ compiler/GHC/JS/Syntax.hs view
@@ -0,0 +1,392 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingVia #-}+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE PatternSynonyms #-}++-----------------------------------------------------------------------------+-- |+-- Module : GHC.JS.Syntax+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Sylvain Henry <sylvain.henry@iohk.io>+-- Josh Meredith <josh.meredith@iohk.io>+-- Stability : experimental+--+--+-- * Domain and Purpose+--+-- GHC.JS.Syntax defines the Syntax for the JS backend in GHC. It comports+-- with the [ECMA-262](https://tc39.es/ecma262/) although not every+-- production rule of the standard is represented. Code in this module is a+-- fork of [JMacro](https://hackage.haskell.org/package/jmacro) (BSD 3+-- Clause) by Gershom Bazerman, heavily modified to accomodate GHC's+-- constraints.+--+--+-- * Strategy+--+-- Nothing fancy in this module, this is a classic deeply embeded AST for+-- JS. We define numerous ADTs and pattern synonyms to make pattern matching+-- and constructing ASTs easier.+--+--+-- * Consumers+--+-- The entire JS backend consumes this module, e.g., the modules in+-- GHC.StgToJS.\*. Please see 'GHC.JS.Make' for a module which provides+-- helper functions that use the deeply embedded DSL defined in this module+-- to provide some of the benefits of a shallow embedding.+-----------------------------------------------------------------------------+module GHC.JS.Syntax+ ( -- * Deeply embedded JS datatypes+ JStat(..)+ , JExpr(..)+ , JVal(..)+ , JOp(..)+ , JUOp(..)+ , Ident(..)+ , identFS+ , JsLabel+ -- * pattern synonyms over JS operators+ , pattern New+ , pattern Not+ , pattern Negate+ , pattern Add+ , pattern Sub+ , pattern Mul+ , pattern Div+ , pattern Mod+ , pattern BOr+ , pattern BAnd+ , pattern BXor+ , pattern BNot+ , pattern LOr+ , pattern LAnd+ , pattern Int+ , pattern String+ , pattern PreInc+ , pattern PostInc+ , pattern PreDec+ , pattern PostDec+ -- * Ident supply+ , IdentSupply(..)+ , newIdentSupply+ , pseudoSaturate+ -- * Utility+ , SaneDouble(..)+ ) where++import GHC.Prelude++import Control.DeepSeq++import Data.Function+import Data.Data+import Data.Word+import qualified Data.Semigroup as Semigroup++import GHC.Generics++import GHC.Data.FastString+import GHC.Utils.Monad.State.Strict+import GHC.Types.Unique+import GHC.Types.Unique.Map++-- | A supply of identifiers, possibly empty+newtype IdentSupply a+ = IS {runIdentSupply :: State [Ident] a}+ deriving Typeable++instance NFData (IdentSupply a) where rnf IS{} = ()++inIdentSupply :: (State [Ident] a -> State [Ident] b) -> IdentSupply a -> IdentSupply b+inIdentSupply f x = IS $ f (runIdentSupply x)++instance Functor IdentSupply where+ fmap f x = inIdentSupply (fmap f) x++newIdentSupply :: Maybe FastString -> [Ident]+newIdentSupply Nothing = newIdentSupply (Just "jmId")+newIdentSupply (Just pfx) = [ TxtI (mconcat [pfx,"_",mkFastString (show x)])+ | x <- [(0::Word64)..]+ ]++-- | Given a Pseudo-saturate a value with garbage @<<unsatId>>@ identifiers.+pseudoSaturate :: IdentSupply a -> a+pseudoSaturate x = evalState (runIdentSupply x) $ newIdentSupply (Just "<<unsatId>>")++instance Eq a => Eq (IdentSupply a) where+ (==) = (==) `on` pseudoSaturate+instance Ord a => Ord (IdentSupply a) where+ compare = compare `on` pseudoSaturate+instance Show a => Show (IdentSupply a) where+ show x = "(" ++ show (pseudoSaturate x) ++ ")"+++--------------------------------------------------------------------------------+-- Statements+--------------------------------------------------------------------------------+-- | JavaScript statements, see the [ECMA262+-- Reference](https://tc39.es/ecma262/#sec-ecmascript-language-statements-and-declarations)+-- for details+data JStat+ = DeclStat !Ident !(Maybe JExpr) -- ^ Variable declarations: var foo [= e]+ | ReturnStat JExpr -- ^ Return+ | IfStat JExpr JStat JStat -- ^ If+ | WhileStat Bool JExpr JStat -- ^ While, bool is "do" when True+ | ForInStat Bool Ident JExpr JStat -- ^ For-in, bool is "each' when True+ | SwitchStat JExpr [(JExpr, JStat)] JStat -- ^ Switch+ | TryStat JStat Ident JStat JStat -- ^ Try+ | BlockStat [JStat] -- ^ Blocks+ | ApplStat JExpr [JExpr] -- ^ Application+ | UOpStat JUOp JExpr -- ^ Unary operators+ | AssignStat JExpr JExpr -- ^ Binding form: @foo = bar@+ | UnsatBlock (IdentSupply JStat) -- ^ /Unsaturated/ blocks see 'pseudoSaturate'+ | LabelStat JsLabel JStat -- ^ Statement Labels, makes me nostalgic for qbasic+ | BreakStat (Maybe JsLabel) -- ^ Break+ | ContinueStat (Maybe JsLabel) -- ^ Continue+ deriving (Eq, Typeable, Generic)++-- | A Label used for 'JStat', specifically 'BreakStat', 'ContinueStat' and of+-- course 'LabelStat'+type JsLabel = LexicalFastString++instance Semigroup JStat where+ (<>) = appendJStat++instance Monoid JStat where+ mempty = BlockStat []++-- | Append a statement to another statement. 'appendJStat' only returns a+-- 'JStat' that is /not/ a 'BlockStat' when either @mx@ or @my is an empty+-- 'BlockStat'. That is:+-- > (BlockStat [] , y ) = y+-- > (x , BlockStat []) = x+appendJStat :: JStat -> JStat -> JStat+appendJStat mx my = case (mx,my) of+ (BlockStat [] , y ) -> y+ (x , BlockStat []) -> x+ (BlockStat xs , BlockStat ys) -> BlockStat $ xs ++ ys+ (BlockStat xs , ys ) -> BlockStat $ xs ++ [ys]+ (xs , BlockStat ys) -> BlockStat $ xs : ys+ (xs , ys ) -> BlockStat [xs,ys]+++--------------------------------------------------------------------------------+-- Expressions+--------------------------------------------------------------------------------+-- | JavaScript Expressions+data JExpr+ = ValExpr JVal -- ^ All values are trivially expressions+ | SelExpr JExpr Ident -- ^ Selection: Obj.foo, see 'GHC.JS.Make..^'+ | IdxExpr JExpr JExpr -- ^ Indexing: Obj[foo], see 'GHC.JS.Make..!'+ | InfixExpr JOp JExpr JExpr -- ^ Infix Expressions, see 'JExpr'+ -- pattern synonyms+ | UOpExpr JUOp JExpr -- ^ Unary Expressions+ | IfExpr JExpr JExpr JExpr -- ^ If-expression+ | ApplExpr JExpr [JExpr] -- ^ Application+ | UnsatExpr (IdentSupply JExpr) -- ^ An /Unsaturated/ expression.+ -- See 'pseudoSaturate'+ deriving (Eq, Typeable, Generic)++-- * Useful pattern synonyms to ease programming with the deeply embedded JS+-- AST. Each pattern wraps @JUOp@ and @JOp@ into a @JExpr@s to save typing and+-- for convienience. In addition we include a string wrapper for JS string+-- and Integer literals.++-- | pattern synonym for a unary operator new+pattern New :: JExpr -> JExpr+pattern New x = UOpExpr NewOp x++-- | pattern synonym for prefix increment @++x@+pattern PreInc :: JExpr -> JExpr+pattern PreInc x = UOpExpr PreIncOp x++-- | pattern synonym for postfix increment @x++@+pattern PostInc :: JExpr -> JExpr+pattern PostInc x = UOpExpr PostIncOp x++-- | pattern synonym for prefix decrement @--x@+pattern PreDec :: JExpr -> JExpr+pattern PreDec x = UOpExpr PreDecOp x++-- | pattern synonym for postfix decrement @--x@+pattern PostDec :: JExpr -> JExpr+pattern PostDec x = UOpExpr PostDecOp x++-- | pattern synonym for logical not @!@+pattern Not :: JExpr -> JExpr+pattern Not x = UOpExpr NotOp x++-- | pattern synonym for unary negation @-@+pattern Negate :: JExpr -> JExpr+pattern Negate x = UOpExpr NegOp x++-- | pattern synonym for addition @+@+pattern Add :: JExpr -> JExpr -> JExpr+pattern Add x y = InfixExpr AddOp x y++-- | pattern synonym for subtraction @-@+pattern Sub :: JExpr -> JExpr -> JExpr+pattern Sub x y = InfixExpr SubOp x y++-- | pattern synonym for multiplication @*@+pattern Mul :: JExpr -> JExpr -> JExpr+pattern Mul x y = InfixExpr MulOp x y++-- | pattern synonym for division @*@+pattern Div :: JExpr -> JExpr -> JExpr+pattern Div x y = InfixExpr DivOp x y++-- | pattern synonym for remainder @%@+pattern Mod :: JExpr -> JExpr -> JExpr+pattern Mod x y = InfixExpr ModOp x y++-- | pattern synonym for Bitwise Or @|@+pattern BOr :: JExpr -> JExpr -> JExpr+pattern BOr x y = InfixExpr BOrOp x y++-- | pattern synonym for Bitwise And @&@+pattern BAnd :: JExpr -> JExpr -> JExpr+pattern BAnd x y = InfixExpr BAndOp x y++-- | pattern synonym for Bitwise XOr @^@+pattern BXor :: JExpr -> JExpr -> JExpr+pattern BXor x y = InfixExpr BXorOp x y++-- | pattern synonym for Bitwise Not @~@+pattern BNot :: JExpr -> JExpr+pattern BNot x = UOpExpr BNotOp x++-- | pattern synonym for logical Or @||@+pattern LOr :: JExpr -> JExpr -> JExpr+pattern LOr x y = InfixExpr LOrOp x y++-- | pattern synonym for logical And @&&@+pattern LAnd :: JExpr -> JExpr -> JExpr+pattern LAnd x y = InfixExpr LAndOp x y+++-- | pattern synonym to create integer values+pattern Int :: Integer -> JExpr+pattern Int x = ValExpr (JInt x)++-- | pattern synonym to create string values+pattern String :: FastString -> JExpr+pattern String x = ValExpr (JStr x)+++--------------------------------------------------------------------------------+-- Values+--------------------------------------------------------------------------------+-- | JavaScript values+data JVal+ = JVar Ident -- ^ A variable reference+ | JList [JExpr] -- ^ A JavaScript list, or what JS+ -- calls an Array+ | JDouble SaneDouble -- ^ A Double+ | JInt Integer -- ^ A BigInt+ | JStr FastString -- ^ A String+ | JRegEx FastString -- ^ A Regex+ | JHash (UniqMap FastString JExpr) -- ^ A JS HashMap: @{"foo": 0}@+ | JFunc [Ident] JStat -- ^ A function+ | UnsatVal (IdentSupply JVal) -- ^ An /Unsaturated/ value, see 'pseudoSaturate'+ deriving (Eq, Typeable, Generic)++--------------------------------------------------------------------------------+-- Operators+--------------------------------------------------------------------------------+-- | JS Binary Operators. We do not deeply embed the comma operator and the+-- assignment operators+data JOp+ = EqOp -- ^ Equality: `==`+ | StrictEqOp -- ^ Strict Equality: `===`+ | NeqOp -- ^ InEquality: `!=`+ | StrictNeqOp -- ^ Strict InEquality `!==`+ | GtOp -- ^ Greater Than: `>`+ | GeOp -- ^ Greater Than or Equal: `>=`+ | LtOp -- ^ Less Than: <+ | LeOp -- ^ Less Than or Equal: <=+ | AddOp -- ^ Addition: ++ | SubOp -- ^ Subtraction: -+ | MulOp -- ^ Multiplication \*+ | DivOp -- ^ Division: \/+ | ModOp -- ^ Remainder: %+ | LeftShiftOp -- ^ Left Shift: \<\<+ | RightShiftOp -- ^ Right Shift: \>\>+ | ZRightShiftOp -- ^ Unsigned RightShift: \>\>\>+ | BAndOp -- ^ Bitwise And: &+ | BOrOp -- ^ Bitwise Or: |+ | BXorOp -- ^ Bitwise XOr: ^+ | LAndOp -- ^ Logical And: &&+ | LOrOp -- ^ Logical Or: ||+ | InstanceofOp -- ^ @instanceof@+ | InOp -- ^ @in@+ deriving (Show, Eq, Ord, Enum, Data, Typeable, Generic)++instance NFData JOp++-- | JS Unary Operators+data JUOp+ = NotOp -- ^ Logical Not: @!@+ | BNotOp -- ^ Bitwise Not: @~@+ | NegOp -- ^ Negation: @-@+ | PlusOp -- ^ Unary Plus: @+x@+ | NewOp -- ^ new x+ | TypeofOp -- ^ typeof x+ | DeleteOp -- ^ delete x+ | YieldOp -- ^ yield x+ | VoidOp -- ^ void x+ | PreIncOp -- ^ Prefix Increment: @++x@+ | PostIncOp -- ^ Postfix Increment: @x++@+ | PreDecOp -- ^ Prefix Decrement: @--x@+ | PostDecOp -- ^ Postfix Decrement: @x--@+ deriving (Show, Eq, Ord, Enum, Data, Typeable, Generic)++instance NFData JUOp++-- | A newtype wrapper around 'Double' to ensure we never generate a 'Double'+-- that becomes a 'NaN', see 'Eq SaneDouble', 'Ord SaneDouble' for details on+-- Sane-ness+newtype SaneDouble = SaneDouble+ { unSaneDouble :: Double+ }+ deriving (Data, Typeable, Fractional, Num, Generic, NFData)++instance Eq SaneDouble where+ (SaneDouble x) == (SaneDouble y) = x == y || (isNaN x && isNaN y)++instance Ord SaneDouble where+ compare (SaneDouble x) (SaneDouble y) = compare (fromNaN x) (fromNaN y)+ where fromNaN z | isNaN z = Nothing+ | otherwise = Just z++instance Show SaneDouble where+ show (SaneDouble x) = show x+++--------------------------------------------------------------------------------+-- Identifiers+--------------------------------------------------------------------------------+-- We use FastString for identifiers in JS backend++-- | A newtype wrapper around 'FastString' for JS identifiers.+newtype Ident = TxtI { itxt :: FastString }+ deriving stock (Show, Eq)+ deriving newtype (Uniquable)++identFS :: Ident -> FastString+identFS = \case+ TxtI fs -> fs
+ compiler/GHC/JS/Transform.hs view
@@ -0,0 +1,264 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE BlockArguments #-}++module GHC.JS.Transform+ ( mapIdent+ , mapStatIdent+ , mapExprIdent+ , identsS+ , identsV+ , identsE+ -- * Saturation+ , jsSaturate+ -- * Generic traversal (via compos)+ , JMacro(..)+ , JMGadt(..)+ , Compos(..)+ , composOp+ , composOpM+ , composOpM_+ , composOpFold+ )+where++import GHC.Prelude++import GHC.JS.Syntax++import Data.Functor.Identity+import Control.Monad+import Data.Bifunctor++import GHC.Data.FastString+import GHC.Utils.Monad.State.Strict+import GHC.Types.Unique.Map++mapExprIdent :: (Ident -> JExpr) -> JExpr -> JExpr+mapExprIdent f = fst (mapIdent f)++mapStatIdent :: (Ident -> JExpr) -> JStat -> JStat+mapStatIdent f = snd (mapIdent f)++-- | Map on every variable ident+mapIdent :: (Ident -> JExpr) -> (JExpr -> JExpr, JStat -> JStat)+mapIdent f = (map_expr, map_stat)+ where+ map_expr = \case+ ValExpr v -> map_val v+ SelExpr e i -> SelExpr (map_expr e) i+ IdxExpr e1 e2 -> IdxExpr (map_expr e1) (map_expr e2)+ InfixExpr o e1 e2 -> InfixExpr o (map_expr e1) (map_expr e2)+ UOpExpr o e -> UOpExpr o (map_expr e)+ IfExpr e1 e2 e3 -> IfExpr (map_expr e1) (map_expr e2) (map_expr e3)+ ApplExpr e es -> ApplExpr (map_expr e) (fmap map_expr es)+ UnsatExpr me -> UnsatExpr (fmap map_expr me)++ map_val v = case v of+ JVar i -> f i+ JList es -> ValExpr $ JList (fmap map_expr es)+ JDouble{} -> ValExpr $ v+ JInt{} -> ValExpr $ v+ JStr{} -> ValExpr $ v+ JRegEx{} -> ValExpr $ v+ JHash me -> ValExpr $ JHash (fmap map_expr me)+ JFunc is s -> ValExpr $ JFunc is (map_stat s)+ UnsatVal v2 -> ValExpr $ UnsatVal v2++ map_stat s = case s of+ DeclStat i e -> DeclStat i (fmap map_expr e)+ ReturnStat e -> ReturnStat (map_expr e)+ IfStat e s1 s2 -> IfStat (map_expr e) (map_stat s1) (map_stat s2)+ WhileStat b e s2 -> WhileStat b (map_expr e) (map_stat s2)+ ForInStat b i e s2 -> ForInStat b i (map_expr e) (map_stat s2)+ SwitchStat e les s2 -> SwitchStat (map_expr e) (fmap (bimap map_expr map_stat) les) (map_stat s2)+ TryStat s2 i s3 s4 -> TryStat (map_stat s2) i (map_stat s3) (map_stat s4)+ BlockStat ls -> BlockStat (fmap map_stat ls)+ ApplStat e es -> ApplStat (map_expr e) (fmap map_expr es)+ UOpStat o e -> UOpStat o (map_expr e)+ AssignStat e1 e2 -> AssignStat (map_expr e1) (map_expr e2)+ UnsatBlock ms -> UnsatBlock (fmap map_stat ms)+ LabelStat l s2 -> LabelStat l (map_stat s2)+ BreakStat{} -> s+ ContinueStat{} -> s++{-# INLINE identsS #-}+identsS :: JStat -> [Ident]+identsS = \case+ DeclStat i e -> [i] ++ maybe [] identsE e+ ReturnStat e -> identsE e+ IfStat e s1 s2 -> identsE e ++ identsS s1 ++ identsS s2+ WhileStat _ e s -> identsE e ++ identsS s+ ForInStat _ i e s -> [i] ++ identsE e ++ identsS s+ SwitchStat e xs s -> identsE e ++ concatMap traverseCase xs ++ identsS s+ where traverseCase (e,s) = identsE e ++ identsS s+ TryStat s1 i s2 s3 -> identsS s1 ++ [i] ++ identsS s2 ++ identsS s3+ BlockStat xs -> concatMap identsS xs+ ApplStat e es -> identsE e ++ concatMap identsE es+ UOpStat _op e -> identsE e+ AssignStat e1 e2 -> identsE e1 ++ identsE e2+ UnsatBlock{} -> error "identsS: UnsatBlock"+ LabelStat _l s -> identsS s+ BreakStat{} -> []+ ContinueStat{} -> []++{-# INLINE identsE #-}+identsE :: JExpr -> [Ident]+identsE = \case+ ValExpr v -> identsV v+ SelExpr e _i -> identsE e -- do not rename properties+ IdxExpr e1 e2 -> identsE e1 ++ identsE e2+ InfixExpr _ e1 e2 -> identsE e1 ++ identsE e2+ UOpExpr _ e -> identsE e+ IfExpr e1 e2 e3 -> identsE e1 ++ identsE e2 ++ identsE e3+ ApplExpr e es -> identsE e ++ concatMap identsE es+ UnsatExpr{} -> error "identsE: UnsatExpr"++{-# INLINE identsV #-}+identsV :: JVal -> [Ident]+identsV = \case+ JVar i -> [i]+ JList xs -> concatMap identsE xs+ JDouble{} -> []+ JInt{} -> []+ JStr{} -> []+ JRegEx{} -> []+ JHash m -> concatMap (identsE . snd) (nonDetEltsUniqMap m)+ JFunc args s -> args ++ identsS s+ UnsatVal{} -> error "identsV: UnsatVal"+++{--------------------------------------------------------------------+ Compos+--------------------------------------------------------------------}+-- | Compos and ops for generic traversal as defined over+-- the JMacro ADT.++-- | Utility class to coerce the ADT into a regular structure.++class JMacro a where+ jtoGADT :: a -> JMGadt a+ jfromGADT :: JMGadt a -> a++instance JMacro Ident where+ jtoGADT = JMGId+ jfromGADT (JMGId x) = x++instance JMacro JStat where+ jtoGADT = JMGStat+ jfromGADT (JMGStat x) = x++instance JMacro JExpr where+ jtoGADT = JMGExpr+ jfromGADT (JMGExpr x) = x++instance JMacro JVal where+ jtoGADT = JMGVal+ jfromGADT (JMGVal x) = x++-- | Union type to allow regular traversal by compos.+data JMGadt a where+ JMGId :: Ident -> JMGadt Ident+ JMGStat :: JStat -> JMGadt JStat+ JMGExpr :: JExpr -> JMGadt JExpr+ JMGVal :: JVal -> JMGadt JVal++composOp :: Compos t => (forall a. t a -> t a) -> t b -> t b+composOp f = runIdentity . composOpM (Identity . f)++composOpM :: (Compos t, Monad m) => (forall a. t a -> m (t a)) -> t b -> m (t b)+composOpM = compos return ap++composOpM_ :: (Compos t, Monad m) => (forall a. t a -> m ()) -> t b -> m ()+composOpM_ = composOpFold (return ()) (>>)++composOpFold :: Compos t => b -> (b -> b -> b) -> (forall a. t a -> b) -> t c -> b+composOpFold z c f = unC . compos (\_ -> C z) (\(C x) (C y) -> C (c x y)) (C . f)++newtype C b a = C { unC :: b }++class Compos t where+ compos :: (forall a. a -> m a) -> (forall a b. m (a -> b) -> m a -> m b)+ -> (forall a. t a -> m (t a)) -> t c -> m (t c)++instance Compos JMGadt where+ compos = jmcompos++jmcompos :: forall m c. (forall a. a -> m a) -> (forall a b. m (a -> b) -> m a -> m b) -> (forall a. JMGadt a -> m (JMGadt a)) -> JMGadt c -> m (JMGadt c)+jmcompos ret app f' v =+ case v of+ JMGId _ -> ret v+ JMGStat v' -> ret JMGStat `app` case v' of+ DeclStat i e -> ret DeclStat `app` f i `app` mapMaybeM' f e+ ReturnStat i -> ret ReturnStat `app` f i+ IfStat e s s' -> ret IfStat `app` f e `app` f s `app` f s'+ WhileStat b e s -> ret (WhileStat b) `app` f e `app` f s+ ForInStat b i e s -> ret (ForInStat b) `app` f i `app` f e `app` f s+ SwitchStat e l d -> ret SwitchStat `app` f e `app` l' `app` f d+ where l' = mapM' (\(c,s) -> ret (,) `app` f c `app` f s) l+ BlockStat xs -> ret BlockStat `app` mapM' f xs+ ApplStat e xs -> ret ApplStat `app` f e `app` mapM' f xs+ TryStat s i s1 s2 -> ret TryStat `app` f s `app` f i `app` f s1 `app` f s2+ UOpStat o e -> ret (UOpStat o) `app` f e+ AssignStat e e' -> ret AssignStat `app` f e `app` f e'+ UnsatBlock _ -> ret v'+ ContinueStat l -> ret (ContinueStat l)+ BreakStat l -> ret (BreakStat l)+ LabelStat l s -> ret (LabelStat l) `app` f s+ JMGExpr v' -> ret JMGExpr `app` case v' of+ ValExpr e -> ret ValExpr `app` f e+ SelExpr e e' -> ret SelExpr `app` f e `app` f e'+ IdxExpr e e' -> ret IdxExpr `app` f e `app` f e'+ InfixExpr o e e' -> ret (InfixExpr o) `app` f e `app` f e'+ UOpExpr o e -> ret (UOpExpr o) `app` f e+ IfExpr e e' e'' -> ret IfExpr `app` f e `app` f e' `app` f e''+ ApplExpr e xs -> ret ApplExpr `app` f e `app` mapM' f xs+ UnsatExpr _ -> ret v'+ JMGVal v' -> ret JMGVal `app` case v' of+ JVar i -> ret JVar `app` f i+ JList xs -> ret JList `app` mapM' f xs+ JDouble _ -> ret v'+ JInt _ -> ret v'+ JStr _ -> ret v'+ JRegEx _ -> ret v'+ JHash m -> ret JHash `app` m'+ -- nonDetEltsUniqMap doesn't introduce nondeterminism here because the+ -- elements are treated independently before being re-added to a UniqMap+ where (ls, vs) = unzip (nonDetEltsUniqMap m)+ m' = ret (listToUniqMap . zip ls) `app` mapM' f vs+ JFunc xs s -> ret JFunc `app` mapM' f xs `app` f s+ UnsatVal _ -> ret v'++ where+ mapM' :: forall a. (a -> m a) -> [a] -> m [a]+ mapM' g = foldr (app . app (ret (:)) . g) (ret [])+ mapMaybeM' :: forall a. (a -> m a) -> Maybe a -> m (Maybe a)+ mapMaybeM' g = \case+ Nothing -> ret Nothing+ Just a -> app (ret Just) (g a)+ f :: forall b. JMacro b => b -> m b+ f x = ret jfromGADT `app` f' (jtoGADT x)++{--------------------------------------------------------------------+ Saturation+--------------------------------------------------------------------}++-- | Given an optional prefix, fills in all free variable names with a supply+-- of names generated by the prefix.+jsSaturate :: (JMacro a) => Maybe FastString -> a -> a+jsSaturate str x = evalState (runIdentSupply $ jsSaturate_ x) (newIdentSupply str)++jsSaturate_ :: (JMacro a) => a -> IdentSupply a+jsSaturate_ e = IS $ jfromGADT <$> go (jtoGADT e)+ where+ go :: forall a. JMGadt a -> State [Ident] (JMGadt a)+ go v = case v of+ JMGStat (UnsatBlock us) -> go =<< (JMGStat <$> runIdentSupply us)+ JMGExpr (UnsatExpr us) -> go =<< (JMGExpr <$> runIdentSupply us)+ JMGVal (UnsatVal us) -> go =<< (JMGVal <$> runIdentSupply us)+ _ -> composOpM go v
+ compiler/GHC/Linker.hs view
@@ -0,0 +1,36 @@+module GHC.Linker+ (+ )+where++import GHC.Prelude ()+ -- We need this dummy dependency for the make build system. Otherwise it+ -- tries to load GHC.Types which may not be built yet.++-- Note [Linkers and loaders]+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~+--+-- Linkers are used to produce linked objects (.so, executables); loaders are+-- used to link in memory (e.g., in GHCi) with the already loaded libraries+-- (ghc-lib, rts, etc.).+--+-- Linking can usually be done with an external linker program ("ld"), but+-- loading is more tricky:+--+-- * Fully dynamic:+-- when GHC is built as a set of dynamic libraries (ghc-lib, rts, etc.)+-- and the modules to load are also compiled for dynamic linking, a+-- solution is to fully rely on external tools:+--+-- 1) link a .so with the external linker+-- 2) load the .so with POSIX's "dlopen"+--+-- * When GHC is built as a static program or when libraries we want to load+-- aren't compiled for dynamic linking, GHC uses its own loader ("runtime+-- linker"). The runtime linker is part of the rts (rts/Linker.c).+--+-- Note that within GHC's codebase we often use the word "linker" to refer to+-- the static object loader in the runtime system.+--+-- Loading can be delegated to an external interpreter ("iserv") when+-- -fexternal-interpreter is used.
+ compiler/GHC/Linker/Config.hs view
@@ -0,0 +1,13 @@+-- | Linker configuration++module GHC.Linker.Config+ ( FrameworkOpts(..)+ ) where++import GHC.Prelude++-- used on darwin only+data FrameworkOpts = FrameworkOpts+ { foFrameworkPaths :: [String]+ , foCmdlineFrameworks :: [String]+ }
compiler/GHC/Linker/Dynamic.hs view
@@ -12,6 +12,7 @@ import GHC.Platform import GHC.Platform.Ways +import GHC.Driver.Config.Linker import GHC.Driver.Session import GHC.Unit.Env@@ -23,6 +24,7 @@ import GHC.Utils.Logger import GHC.Utils.TmpFs +import Control.Monad (when) import System.FilePath linkDynLib :: Logger -> TmpFs -> DynFlags -> UnitEnv -> [String] -> [UnitId] -> IO ()@@ -83,7 +85,10 @@ | gopt Opt_LinkRts dflags = pkgs_with_rts | otherwise = pkgs_without_rts pkg_link_opts = package_hs_libs ++ extra_libs ++ other_flags- where (package_hs_libs, extra_libs, other_flags) = collectLinkOpts dflags pkgs+ where+ namever = ghcNameVersion dflags+ ways_ = ways dflags+ (package_hs_libs, extra_libs, other_flags) = collectLinkOpts namever ways_ pkgs -- probably _stub.o files -- and last temporary shared object file@@ -91,7 +96,7 @@ -- frameworks pkg_framework_opts <- getUnitFrameworkOpts unit_env (map unitId pkgs)- let framework_opts = getFrameworkOpts dflags platform+ let framework_opts = getFrameworkOpts (initFrameworkOpts dflags) platform case os of OSMinGW32 -> do@@ -190,7 +195,9 @@ -- See Note [Dynamic linking on macOS] ++ [ Option "-Wl,-dead_strip_dylibs", Option "-Wl,-headerpad,8000" ] )- runInjectRPaths logger dflags pkg_lib_paths output_fn+ -- Make sure to honour -fno-use-rpaths if set on darwin as well; see #20004+ when (gopt Opt_RPath dflags) $+ runInjectRPaths logger (toolSettings dflags) pkg_lib_paths output_fn _ -> do ------------------------------------------------------------------- -- Making a DSO
compiler/GHC/Linker/ExtraObj.hs view
@@ -124,13 +124,13 @@ text " __conf.rts_opts_enabled = " <> text (show (rtsOptsEnabled dflags)) <> semi, text " __conf.rts_opts_suggestions = "- <> text (if rtsOptsSuggestions dflags- then "true"- else "false") <> semi,+ <> (if rtsOptsSuggestions dflags+ then text "true"+ else text "false") <> semi, text "__conf.keep_cafs = "- <> text (if gopt Opt_KeepCAFs dflags- then "true"- else "false") <> semi,+ <> (if gopt Opt_KeepCAFs dflags+ then text "true"+ else text "false") <> semi, case rtsOpts dflags of Nothing -> Outputable.empty Just opts -> text " __conf.rts_opts= " <>@@ -192,7 +192,7 @@ -- See Note [LinkInfo section] getLinkInfo :: DynFlags -> UnitEnv -> [UnitId] -> IO String getLinkInfo dflags unit_env dep_packages = do- package_link_opts <- getUnitLinkOpts dflags unit_env dep_packages+ package_link_opts <- getUnitLinkOpts (ghcNameVersion dflags) (ways dflags) unit_env dep_packages pkg_frameworks <- if not (platformUsesFrameworks (ue_platform unit_env)) then return [] else do
compiler/GHC/Linker/Loader.hs view
@@ -77,6 +77,7 @@ import GHC.Unit.Finder import GHC.Unit.Module import GHC.Unit.Module.ModIface+import GHC.Unit.Module.WholeCoreBindings import GHC.Unit.Module.Deps import GHC.Unit.Home.ModInfo import GHC.Unit.State as Packages@@ -837,11 +838,17 @@ while_linking_expr = text "while linking an interpreted expression" - -- This one is a build-system bug + -- See Note [Using Byte Code rather than Object Code for Template Haskell]+ homeModLinkable :: DynFlags -> HomeModInfo -> Maybe Linkable+ homeModLinkable dflags hmi =+ if gopt Opt_UseBytecodeRatherThanObjects dflags+ then homeModInfoByteCode hmi <|> homeModInfoObject hmi+ else homeModInfoObject hmi <|> homeModInfoByteCode hmi+ get_linkable osuf mod -- A home-package module | Just mod_info <- lookupHugByModule mod (hsc_HUG hsc_env)- = adjust_linkable (Maybes.expectJust "getLinkDeps" (hm_linkable mod_info))+ = adjust_linkable (Maybes.expectJust "getLinkDeps" (homeModLinkable dflags mod_info)) | otherwise = do -- It's not in the HPT because we are in one shot mode, -- so use the Finder to get a ModLocation...@@ -886,8 +893,35 @@ adjust_ul _ (DotA fp) = panic ("adjust_ul DotA " ++ show fp) adjust_ul _ (DotDLL fp) = panic ("adjust_ul DotDLL " ++ show fp) adjust_ul _ l@(BCOs {}) = return l+ adjust_ul _ l@LoadedBCOs{} = return l+ adjust_ul _ (CoreBindings (WholeCoreBindings _ mod _)) = pprPanic "Unhydrated core bindings" (ppr mod) +{-+Note [Using Byte Code rather than Object Code for Template Haskell]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The `-fprefer-byte-code` flag allows a user to specify that they want to use+byte code (if availble) rather than object code for home module dependenices+when executing Template Haskell splices.++Why might you want to use byte code rather than object code?++* Producing object code is much slower than producing byte code (for example if you're using -fno-code)+* Linking many large object files, which happens once per splice, is quite expensive. (#21700)++So we allow the user to choose to use byte code rather than object files if they want to avoid these+two pitfalls.++When using `-fprefer-byte-code` you have to arrange to have the byte code availble.+In normal --make mode it will not be produced unless you enable `-fbyte-code-and-object-code`.+See Note [Home module build products] for some more information about that.++The only other place where the flag is consulted is when enabling code generation+with `-fno-code`, which does so to anticipate what decision we will make at the+splice point about what we would prefer.++-}+ {- ********************************************************************** Loading a Decls statement@@ -916,7 +950,7 @@ new_bindings <- linkSomeBCOs bco_opts interp le2 [cbc] nms_fhvs <- makeForeignNamedHValueRefs interp new_bindings let ce2 = extendClosureEnv (closure_env le2) nms_fhvs- !pls2 = pls { linker_env = le2 { closure_env = ce2 } }+ pls2 = pls { linker_env = le2 { closure_env = ce2 } } return (pls2, (nms_fhvs, links_needed, units_needed)) where free_names = uniqDSetToList $@@ -1131,7 +1165,7 @@ unlinkeds = concatMap linkableUnlinked new_bcos cbcs :: [CompiledByteCode]- cbcs = map byteCodeOfObject unlinkeds+ cbcs = concatMap byteCodeOfObject unlinkeds le1 = linker_env pls@@ -1150,7 +1184,7 @@ new_binds <- makeForeignNamedHValueRefs interp to_add let ce2 = extendClosureEnv (closure_env le2) new_binds- return $! pls1 { linker_env = le2 { closure_env = ce2 } }+ return pls1 { linker_env = le2 { closure_env = ce2 } } -- Link a bunch of BCOs and return references to their values linkSomeBCOs :: BCOOpts@@ -1499,7 +1533,7 @@ else when (diag_wopt Opt_WarnMissedExtraSharedLib diag_opts) $ logMsg logger- (mkMCDiagnostic diag_opts $ WarningWithFlag Opt_WarnMissedExtraSharedLib)+ (mkMCDiagnostic diag_opts (WarningWithFlag Opt_WarnMissedExtraSharedLib) Nothing) noSrcSpan $ withPprStyle defaultUserStyle (note err) where diag_opts = initDiagOpts (hsc_dflags hsc_env)@@ -1687,7 +1721,7 @@ , not loading_dynamic_hs_libs , interpreterProfiled interp = do- let diag = mkMCDiagnostic diag_opts WarningWithoutFlag+ let diag = mkMCDiagnostic diag_opts WarningWithoutFlag Nothing logMsg logger diag noSrcSpan $ withPprStyle defaultErrStyle $ text "Interpreter failed to load profiled static library" <+> text lib <> char '.' $$ text " \tTrying dynamic library instead. If this fails try to rebuild" <+>@@ -1747,7 +1781,7 @@ -- fork/exec is expensive on Windows, for each time we ask GCC for a library we -- have to eat the cost of af least 3 of these: gcc -> real_gcc -> cc1. -- So instead get a list of location that GCC would search and use findDirs--- which hopefully is written in an optimized mannor to take advantage of+-- which hopefully is written in an optimized manner to take advantage of -- caching. At the very least we remove the overhead of the fork/exec and waits -- which dominate a large percentage of startup time on Windows. getGccSearchDirectory :: Logger -> DynFlags -> String -> IO [FilePath]@@ -1773,9 +1807,9 @@ find :: String -> String -> String find r x = let lst = lines x val = filter (r `isPrefixOf`) lst- in if null val- then []- else case break (=='=') (head val) of+ in case val of+ [] -> []+ x:_ -> case break (=='=') x of (_ , []) -> [] (_, (_:xs)) -> xs
compiler/GHC/Linker/MacOS.hs view
@@ -9,12 +9,15 @@ import GHC.Prelude import GHC.Platform +import GHC.Linker.Config+ import GHC.Driver.Session import GHC.Unit.Types import GHC.Unit.State import GHC.Unit.Env +import GHC.Settings import GHC.SysTools.Tasks import GHC.Runtime.Interpreter@@ -36,7 +39,7 @@ -- macOS Sierra (10.14). -- -- @-dead_strip_dylibs@ does not dead strip @-rpath@ entries, as such passing--- @-l@ and @-rpath@ to the linker will result in the unnecesasry libraries not+-- @-l@ and @-rpath@ to the linker will result in the unnecessary libraries not -- being included in the load commands, however the @-rpath@ entries are all -- forced to be included. This can lead to 100s of @-rpath@ entries being -- included when only a handful of libraries end up being truly linked.@@ -46,15 +49,13 @@ -- dynamic library through @-add_rpath@. -- -- See Note [Dynamic linking on macOS]-runInjectRPaths :: Logger -> DynFlags -> [FilePath] -> FilePath -> IO ()--- Make sure to honour -fno-use-rpaths if set on darwin as well see #20004-runInjectRPaths _ dflags _ _ | not (gopt Opt_RPath dflags) = return ()-runInjectRPaths logger dflags lib_paths dylib = do- info <- lines <$> askOtool logger dflags Nothing [Option "-L", Option dylib]+runInjectRPaths :: Logger -> ToolSettings -> [FilePath] -> FilePath -> IO ()+runInjectRPaths logger toolSettings lib_paths dylib = do+ info <- lines <$> askOtool logger toolSettings Nothing [Option "-L", Option dylib] -- filter the output for only the libraries. And then drop the @rpath prefix. let libs = fmap (drop 7) $ filter (isPrefixOf "@rpath") $ fmap (head.words) $ info -- find any pre-existing LC_PATH items- info <- lines <$> askOtool logger dflags Nothing [Option "-l", Option dylib]+ info <- lines <$> askOtool logger toolSettings Nothing [Option "-l", Option dylib] let paths = mapMaybe get_rpath info lib_paths' = [ p | p <- lib_paths, not (p `elem` paths) ] -- only find those rpaths, that aren't already in the library.@@ -62,7 +63,7 @@ -- inject the rpaths case rpaths of [] -> return ()- _ -> runInstallNameTool logger dflags $ map Option $ "-add_rpath":(intersperse "-add_rpath" rpaths) ++ [dylib]+ _ -> runInstallNameTool logger toolSettings $ map Option $ "-add_rpath":(intersperse "-add_rpath" rpaths) ++ [dylib] get_rpath :: String -> Maybe FilePath get_rpath l = case readP_to_S rpath_parser l of@@ -96,15 +97,15 @@ | otherwise = return [] -getFrameworkOpts :: DynFlags -> Platform -> [String]-getFrameworkOpts dflags platform+getFrameworkOpts :: FrameworkOpts -> Platform -> [String]+getFrameworkOpts fwOpts platform | platformUsesFrameworks platform = framework_path_opts ++ framework_opts | otherwise = [] where- framework_paths = frameworkPaths dflags+ framework_paths = foFrameworkPaths fwOpts framework_path_opts = map ("-F" ++) framework_paths - frameworks = cmdlineFrameworks dflags+ frameworks = foCmdlineFrameworks fwOpts -- reverse because they're added in reverse order from the cmd line: framework_opts = concat [ ["-framework", fw] | fw <- reverse frameworks ]
compiler/GHC/Linker/Static.hs view
@@ -1,6 +1,5 @@ module GHC.Linker.Static ( linkBinary- , linkBinary' , linkStaticLib ) where@@ -30,6 +29,7 @@ import GHC.Linker.Windows import GHC.Linker.Static.Utils +import GHC.Driver.Config.Linker import GHC.Driver.Session import System.FilePath@@ -73,23 +73,26 @@ unit_state = ue_units unit_env toolSettings' = toolSettings dflags verbFlags = getVerbFlags dflags- output_fn = exeFileName platform staticLink (outputFile_ dflags)-- -- get the full list of packages to link with, by combining the- -- explicit packages with the auto packages and all of their- -- dependencies, and eliminating duplicates.+ arch_os = platformArchOS platform+ output_fn = exeFileName arch_os staticLink (outputFile_ dflags)+ namever = ghcNameVersion dflags+ ways_ = ways dflags full_output_fn <- if isAbsolute output_fn then return output_fn else do d <- getCurrentDirectory return $ normalise (d </> output_fn)++ -- get the full list of packages to link with, by combining the+ -- explicit packages with the auto packages and all of their+ -- dependencies, and eliminating duplicates. pkgs <- mayThrowUnitErr (preloadUnitsInfo' unit_env dep_units)- let pkg_lib_paths = collectLibraryDirs (ways dflags) pkgs+ let pkg_lib_paths = collectLibraryDirs ways_ pkgs let pkg_lib_path_opts = concatMap get_pkg_lib_path_opts pkg_lib_paths get_pkg_lib_path_opts l | osElfTarget (platformOS platform) && dynLibLoader dflags == SystemDependent &&- ways dflags `hasWay` WayDyn+ ways_ `hasWay` WayDyn = let libpath = if gopt Opt_RelativeDynlibPaths dflags then "$ORIGIN" </> (l `makeRelativeTo` full_output_fn)@@ -110,7 +113,7 @@ in ["-L" ++ l] ++ rpathlink ++ rpath | osMachOTarget (platformOS platform) && dynLibLoader dflags == SystemDependent &&- ways dflags `hasWay` WayDyn &&+ ways_ `hasWay` WayDyn && useXLinkerRPath dflags (platformOS platform) = let libpath = if gopt Opt_RelativeDynlibPaths dflags then "@loader_path" </>@@ -122,7 +125,7 @@ pkg_lib_path_opts <- if gopt Opt_SingleLibFolder dflags then do- libs <- getLibs dflags unit_env dep_units+ libs <- getLibs namever ways_ unit_env dep_units tmpDir <- newTempDir logger tmpfs (tmpDir dflags) sequence_ [ copyFile lib (tmpDir </> basename) | (lib, basename) <- libs]@@ -152,14 +155,8 @@ = ([],[]) pkg_link_opts <- do- (package_hs_libs, extra_libs, other_flags) <- getUnitLinkOpts dflags unit_env dep_units- return $ if staticLink- then package_hs_libs -- If building an executable really means making a static- -- library (e.g. iOS), then we only keep the -l options for- -- HS packages, because libtool doesn't accept other options.- -- In the case of iOS these need to be added by hand to the- -- final link in Xcode.- else other_flags ++ dead_strip+ (package_hs_libs, extra_libs, other_flags) <- getUnitLinkOpts namever ways_ unit_env dep_units+ return $ other_flags ++ dead_strip ++ pre_hs_libs ++ package_hs_libs ++ post_hs_libs ++ extra_libs -- -Wl,-u,<sym> contained in other_flags@@ -175,7 +172,7 @@ -- frameworks pkg_framework_opts <- getUnitFrameworkOpts unit_env dep_units- let framework_opts = getFrameworkOpts dflags platform+ let framework_opts = getFrameworkOpts (initFrameworkOpts dflags) platform -- probably _stub.o files let extra_ld_inputs = ldInputs dflags@@ -184,11 +181,12 @@ OSMinGW32 | gopt Opt_GenManifest dflags -> maybeCreateManifest logger tmpfs dflags output_fn _ -> return [] - let link dflags args | staticLink = GHC.SysTools.runLibtool logger dflags args- | platformOS platform == OSDarwin+ let link dflags args | platformOS platform == OSDarwin = do GHC.SysTools.runLink logger tmpfs dflags args- GHC.Linker.MacOS.runInjectRPaths logger dflags pkg_lib_paths output_fn+ -- Make sure to honour -fno-use-rpaths if set on darwin as well; see #20004+ when (gopt Opt_RPath dflags) $+ GHC.Linker.MacOS.runInjectRPaths logger (toolSettings dflags) pkg_lib_paths output_fn | otherwise = GHC.SysTools.runLink logger tmpfs dflags args @@ -220,7 +218,6 @@ -- on x86. ++ (if not (gopt Opt_CompactUnwind dflags) && toolSettings_ldSupportsCompactUnwind toolSettings' &&- not staticLink && (platformOS platform == OSDarwin) && case platformArch platform of ArchX86 -> True@@ -238,8 +235,7 @@ -- whether this is something we ought to fix, but -- for now this flags silences them. ++ (if platformOS platform == OSDarwin &&- platformArch platform == ArchX86 &&- not staticLink+ platformArch platform == ArchX86 then ["-Wl,-read_only_relocs,suppress"] else []) @@ -278,7 +274,10 @@ let platform = ue_platform unit_env extra_ld_inputs = [ f | FileOption _ f <- ldInputs dflags ] modules = o_files ++ extra_ld_inputs- output_fn = exeFileName platform True (outputFile_ dflags)+ arch_os = platformArchOS platform+ output_fn = exeFileName arch_os True (outputFile_ dflags)+ namever = ghcNameVersion dflags+ ways_ = ways dflags full_output_fn <- if isAbsolute output_fn then return output_fn@@ -295,7 +294,7 @@ | otherwise = filter ((/= rtsUnitId) . unitId) pkg_cfgs_init - archives <- concatMapM (collectArchives dflags) pkg_cfgs+ archives <- concatMapM (collectArchives namever ways_) pkg_cfgs ar <- foldl mappend <$> (Archive <$> mapM loadObj modules)
compiler/GHC/Linker/Unit.hs view
@@ -18,7 +18,7 @@ import qualified GHC.Data.ShortText as ST -import GHC.Driver.Session+import GHC.Settings import Control.Monad import System.Directory@@ -26,26 +26,26 @@ -- | Find all the link options in these and the preload packages, -- returning (package hs lib options, extra library options, other flags)-getUnitLinkOpts :: DynFlags -> UnitEnv -> [UnitId] -> IO ([String], [String], [String])-getUnitLinkOpts dflags unit_env pkgs = do+getUnitLinkOpts :: GhcNameVersion -> Ways -> UnitEnv -> [UnitId] -> IO ([String], [String], [String])+getUnitLinkOpts namever ways unit_env pkgs = do ps <- mayThrowUnitErr $ preloadUnitsInfo' unit_env pkgs- return (collectLinkOpts dflags ps)+ return (collectLinkOpts namever ways ps) -collectLinkOpts :: DynFlags -> [UnitInfo] -> ([String], [String], [String])-collectLinkOpts dflags ps =+collectLinkOpts :: GhcNameVersion -> Ways -> [UnitInfo] -> ([String], [String], [String])+collectLinkOpts namever ways ps = (- concatMap (map ("-l" ++) . unitHsLibs (ghcNameVersion dflags) (ways dflags)) ps,+ concatMap (map ("-l" ++) . unitHsLibs namever ways) ps, concatMap (map ("-l" ++) . map ST.unpack . unitExtDepLibsSys) ps, concatMap (map ST.unpack . unitLinkerOptions) ps ) -collectArchives :: DynFlags -> UnitInfo -> IO [FilePath]-collectArchives dflags pc =+collectArchives :: GhcNameVersion -> Ways -> UnitInfo -> IO [FilePath]+collectArchives namever ways pc = filterM doesFileExist [ searchPath </> ("lib" ++ lib ++ ".a") | searchPath <- searchPaths , lib <- libs ]- where searchPaths = ordNub . filter notNull . libraryDirsForWay (ways dflags) $ pc- libs = unitHsLibs (ghcNameVersion dflags) (ways dflags) pc ++ map ST.unpack (unitExtDepLibsSys pc)+ where searchPaths = ordNub . filter notNull . libraryDirsForWay ways $ pc+ libs = unitHsLibs namever ways pc ++ map ST.unpack (unitExtDepLibsSys pc) -- | Either the 'unitLibraryDirs' or 'unitLibraryDynDirs' as appropriate for the way. libraryDirsForWay :: Ways -> UnitInfo -> [String]@@ -53,11 +53,11 @@ | hasWay ws WayDyn = map ST.unpack . unitLibraryDynDirs | otherwise = map ST.unpack . unitLibraryDirs -getLibs :: DynFlags -> UnitEnv -> [UnitId] -> IO [(String,String)]-getLibs dflags unit_env pkgs = do+getLibs :: GhcNameVersion -> Ways -> UnitEnv -> [UnitId] -> IO [(String,String)]+getLibs namever ways unit_env pkgs = do ps <- mayThrowUnitErr $ preloadUnitsInfo' unit_env pkgs fmap concat . forM ps $ \p -> do- let candidates = [ (l </> f, f) | l <- collectLibraryDirs (ways dflags) [p]- , f <- (\n -> "lib" ++ n ++ ".a") <$> unitHsLibs (ghcNameVersion dflags) (ways dflags) p ]+ let candidates = [ (l </> f, f) | l <- collectLibraryDirs ways [p]+ , f <- (\n -> "lib" ++ n ++ ".a") <$> unitHsLibs namever ways p ] filterM (doesFileExist . fst) candidates
compiler/GHC/Llvm/Ppr.hs view
@@ -517,8 +517,8 @@ ppPlainName :: LlvmCgConfig -> LlvmVar -> SDoc ppPlainName opts v = case v of (LMGlobalVar x _ _ _ _ _) -> ftext x- (LMLocalVar x LMLabel ) -> text (show x)- (LMLocalVar x _ ) -> text ('l' : show x)+ (LMLocalVar x LMLabel ) -> pprUniqueAlways x+ (LMLocalVar x _ ) -> char 'l' <> pprUniqueAlways x (LMNLocalVar x _ ) -> ftext x (LMLitVar x ) -> ppLit opts x
compiler/GHC/Llvm/Types.hs view
@@ -181,7 +181,7 @@ getLitType (LMIntLit _ t) = t getLitType (LMFloatLit _ t) = t getLitType (LMVectorLit []) = panic "getLitType"-getLitType (LMVectorLit ls) = LMVector (length ls) (getLitType (head ls))+getLitType (LMVectorLit ls@(l:_)) = LMVector (length ls) (getLitType l) getLitType (LMNullLit t) = t getLitType (LMUndefLit t) = t
compiler/GHC/Plugins.hs view
@@ -17,6 +17,8 @@ , module GHC.Types.Id.Info , module GHC.Types.PkgQual , module GHC.Core.Opt.Monad+ , module GHC.Core.Opt.Pipeline.Types+ , module GHC.Core.Opt.Stats , module GHC.Core , module GHC.Types.Literal , module GHC.Core.DataCon@@ -83,6 +85,8 @@ -- Core import GHC.Core.Opt.Monad+import GHC.Core.Opt.Pipeline.Types+import GHC.Core.Opt.Stats import GHC.Core import GHC.Types.Literal import GHC.Core.DataCon@@ -193,7 +197,7 @@ -- For now, the easiest and recommended way to ensure a consistent -- 'NameCache' is used it to retrieve the preexisting one from an active -- 'HscEnv'. A single 'HscEnv' is created per GHC "session", and this--- ensures everything in that sesssion will getthe same name cache.+-- ensures everything in that session will get the same name cache. thNameToGhcNameIO :: NameCache -> TH.Name -> IO (Maybe Name) thNameToGhcNameIO cache th_name = do { names <- mapMaybeM lookup (thRdrNameGuesses th_name)
compiler/GHC/Rename/Bind.hs view
@@ -3,7 +3,6 @@ {-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}-{-# OPTIONS_GHC -Wno-incomplete-record-updates #-} {- (c) The GRASP/AQUA Project, Glasgow University, 1992-1998@@ -70,6 +69,8 @@ import GHC.Data.OrdList import qualified GHC.LanguageExtensions as LangExt +import Language.Haskell.Syntax.Basic (FieldLabelString(..))+ import Control.Monad import Data.Foldable ( toList ) import Data.List ( partition, sortBy )@@ -491,18 +492,10 @@ bind' = bind { pat_rhs = grhss' , pat_ext = fvs' } - ok_nobind_pat- = -- See Note [Pattern bindings that bind no variables]- case unLoc pat of- WildPat {} -> True- BangPat {} -> True -- #9127, #13646- SplicePat {} -> True- _ -> False- -- Warn if the pattern binds no variables -- See Note [Pattern bindings that bind no variables] ; whenWOptM Opt_WarnUnusedPatternBinds $- when (null bndrs && not ok_nobind_pat) $+ when (null bndrs && not (isOkNoBindPattern pat)) $ addTcRnDiagnostic (TcRnUnusedPatternBinds bind') ; fvs' `seq` -- See Note [Free-variable space leak]@@ -538,29 +531,66 @@ rnBind _ b = pprPanic "rnBind" (ppr b) + -- See Note [Pattern bindings that bind no variables]+isOkNoBindPattern :: LPat GhcRn -> Bool+isOkNoBindPattern (L _ pat) =+ case pat of+ WildPat{} -> True -- Exception (1)+ BangPat {} -> True -- Exception (2) #9127, #13646+ p -> patternContainsSplice p -- Exception (3)++ where+ lpatternContainsSplice :: LPat GhcRn -> Bool+ lpatternContainsSplice (L _ p) = patternContainsSplice p+ patternContainsSplice :: Pat GhcRn -> Bool+ patternContainsSplice p =+ case p of+ -- A top-level splice has been evaluated by this point, so we know the pattern it is evaluated to+ SplicePat (HsUntypedSpliceTop _ p) _ -> patternContainsSplice p+ -- A nested splice isn't evaluated so we can't guess what it will expand to+ SplicePat (HsUntypedSpliceNested {}) _ -> True+ -- The base cases+ VarPat {} -> False+ WildPat {} -> False+ LitPat {} -> False+ NPat {} -> False+ NPlusKPat {} -> False+ -- Recursive cases+ BangPat _ lp -> lpatternContainsSplice lp+ LazyPat _ lp -> lpatternContainsSplice lp+ AsPat _ _ _ lp -> lpatternContainsSplice lp+ ParPat _ _ lp _ -> lpatternContainsSplice lp+ ViewPat _ _ lp -> lpatternContainsSplice lp+ SigPat _ lp _ -> lpatternContainsSplice lp+ ListPat _ lps -> any lpatternContainsSplice lps+ TuplePat _ lps _ -> any lpatternContainsSplice lps+ SumPat _ lp _ _ -> lpatternContainsSplice lp+ ConPat _ _ cpd -> any lpatternContainsSplice (hsConPatArgs cpd)+ XPat (HsPatExpanded _orig new) -> patternContainsSplice new+ {- Note [Pattern bindings that bind no variables] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Generally, we want to warn about pattern bindings like Just _ = e because they don't do anything! But we have three exceptions: -* A wildcard pattern+(1) A wildcard pattern _ = rhs which (a) is not that different from _v = rhs (b) is sometimes used to give a type sig for, or an occurrence of, a variable on the RHS -* A strict pattern binding; that is, one with an outermost bang+(2) A strict pattern binding; that is, one with an outermost bang !Just _ = e This can fail, so unlike the lazy variant, it is not a no-op. Moreover, #13646 argues that even for single constructor types, you might want to write the constructor. See also #9127. -* A splice pattern+(3) A splice pattern $(th-lhs) = rhs It is impossible to determine whether or not th-lhs really- binds any variable. We should disable the warning for any pattern- which contain splices, but that is a more expensive check.+ binds any variable. You have to recurse all the way into the pattern to check+ it doesn't contain any splices like this. See #22057. Note [Free-variable space leak] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -666,7 +696,7 @@ dupFixityDecl :: SrcSpan -> RdrName -> TcRnMessage dupFixityDecl loc rdr_name- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [text "Multiple fixity declarations for" <+> quotes (ppr rdr_name), text "also at " <+> ppr loc] @@ -710,7 +740,7 @@ RecCon vars -> do { checkDupRdrNames (map (foLabel . recordPatSynField) vars) ; fls <- lookupConstructorFields name- ; let fld_env = mkFsEnv [ (flLabel fl, fl) | fl <- fls ]+ ; let fld_env = mkFsEnv [ (field_label $ flLabel fl, fl) | fl <- fls ] ; let rnRecordPatSynField (RecordPatSynField { recordPatSynField = visible , recordPatSynPatVar = hidden })@@ -757,7 +787,7 @@ patternSynonymErr :: TcRnMessage patternSynonymErr- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Illegal pattern synonym declaration") 2 (text "Use -XPatternSynonyms to enable this extension") @@ -765,7 +795,7 @@ Note [Renaming pattern synonym variables] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -We rename pattern synonym declaractions backwards to normal to reuse+We rename pattern synonym declarations backwards to normal to reuse the logic already implemented for renaming patterns. We first rename the RHS of a declaration which brings into@@ -950,7 +980,7 @@ -- Report error for all other forms of bindings -- This is why we use a fold rather than map rnMethodBindLHS is_cls_decl _ (L loc bind) rest- = do { addErrAt (locA loc) $ TcRnUnknownMessage $ mkPlainError noHints $+ = do { addErrAt (locA loc) $ mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ what <+> text "not allowed in" <+> decl_sort , nest 2 (ppr bind) ] ; return rest }@@ -1007,9 +1037,6 @@ -- Doesn't seem worth much trouble to sort this. renameSig :: HsSigCtxt -> Sig GhcPs -> RnM (Sig GhcRn, FreeVars)-renameSig _ (IdSig _ x)- = return (IdSig noExtField x, emptyFVs) -- Actually this never occurs- renameSig ctxt sig@(TypeSig _ vs ty) = do { new_vs <- mapM (lookupSigOccRnN ctxt sig) vs ; let doc = TypeSigCtx (ppr_sig_bndrs vs)@@ -1029,7 +1056,7 @@ ty_ctxt = GenericCtx (text "a class method signature for" <+> quotes (ppr v1)) -renameSig _ (SpecInstSig _ src ty)+renameSig _ (SpecInstSig (_, src) ty) = do { checkInferredVars doc inf_msg ty ; (new_ty, fvs) <- rnHsSigType doc TypeLevel ty -- Check if there are any nested `forall`s or contexts, which are@@ -1038,7 +1065,7 @@ -- GHC.Hs.Type). ; addNoNestedForallsContextsErr doc (text "SPECIALISE instance type") (getLHsInstDeclHead new_ty)- ; return (SpecInstSig noAnn src new_ty,fvs) }+ ; return (SpecInstSig (noAnn, src) new_ty,fvs) } where doc = SpecInstSigCtx inf_msg = Just (text "Inferred type variables are not allowed")@@ -1068,9 +1095,9 @@ = do { new_fsig <- rnSrcFixityDecl ctxt fsig ; return (FixSig noAnn new_fsig, emptyFVs) } -renameSig ctxt sig@(MinimalSig _ s (L l bf))+renameSig ctxt sig@(MinimalSig (_, s) (L l bf)) = do new_bf <- traverse (lookupSigOccRnN ctxt sig) bf- return (MinimalSig noAnn s (L l new_bf), emptyFVs)+ return (MinimalSig (noAnn, s) (L l new_bf), emptyFVs) renameSig ctxt sig@(PatSynSig _ vs ty) = do { new_vs <- mapM (lookupSigOccRnN ctxt sig) vs@@ -1080,13 +1107,13 @@ ty_ctxt = GenericCtx (text "a pattern synonym signature for" <+> ppr_sig_bndrs vs) -renameSig ctxt sig@(SCCFunSig _ st v s)+renameSig ctxt sig@(SCCFunSig (_, st) v s) = do { new_v <- lookupSigOccRnN ctxt sig v- ; return (SCCFunSig noAnn st new_v s, emptyFVs) }+ ; return (SCCFunSig (noAnn, st) new_v s, emptyFVs) } -- COMPLETE Sigs can refer to imported IDs which is why we use -- lookupLocatedOccRn rather than lookupSigOccRn-renameSig _ctxt sig@(CompleteMatchSig _ s (L l bf) mty)+renameSig _ctxt sig@(CompleteMatchSig (_, s) (L l bf) mty) = do new_bf <- traverse lookupLocatedOccRn bf new_mty <- traverse lookupLocatedOccRn mty @@ -1095,10 +1122,10 @@ -- Why 'any'? See Note [Orphan COMPLETE pragmas] addErrCtxt (text "In" <+> ppr sig) $ failWithTc orphanError - return (CompleteMatchSig noAnn s (L l new_bf) new_mty, emptyFVs)+ return (CompleteMatchSig (noAnn, s) (L l new_bf) new_mty, emptyFVs) where orphanError :: TcRnMessage- orphanError = TcRnUnknownMessage $ mkPlainError noHints $+ orphanError = mkTcRnUnknownMessage $ mkPlainError noHints $ text "Orphan COMPLETE pragmas not supported" $$ text "A COMPLETE pragma must mention at least one data constructor" $$ text "or pattern synonym defined in the same module."@@ -1145,10 +1172,6 @@ (FixSig {}, InstDeclCtxt {}) -> False (FixSig {}, _) -> True - (IdSig {}, TopSigCtxt {}) -> True- (IdSig {}, InstDeclCtxt {}) -> True- (IdSig {}, _) -> False- (InlineSig {}, HsBootCtxt {}) -> False (InlineSig {}, _) -> True @@ -1169,6 +1192,11 @@ (CompleteMatchSig {}, TopSigCtxt {} ) -> True (CompleteMatchSig {}, _) -> False + (XSig {}, TopSigCtxt {}) -> True+ (XSig {}, InstDeclCtxt {}) -> True+ (XSig {}, _) -> False++ ------------------- findDupSigs :: [LSig GhcPs] -> [NonEmpty (LocatedN RdrName, Sig GhcPs)] -- Check for duplicates on RdrName version,@@ -1188,7 +1216,7 @@ expand_sig sig@(TypeSig _ ns _) = [(n,sig) | n <- ns] expand_sig sig@(ClassOpSig _ _ ns _) = [(n,sig) | n <- ns] expand_sig sig@(PatSynSig _ ns _ ) = [(n,sig) | n <- ns]- expand_sig sig@(SCCFunSig _ _ n _) = [(n,sig)]+ expand_sig sig@(SCCFunSig (_, _) n _) = [(n,sig)] expand_sig _ = [] matching_sig :: (LocatedN RdrName, Sig GhcPs) -> (LocatedN RdrName, Sig GhcPs) -> Bool --AZ@@ -1253,7 +1281,7 @@ -> (LocatedA (body GhcPs) -> RnM (LocatedA (body GhcRn), FreeVars)) -> MatchGroup GhcPs (LocatedA (body GhcPs)) -> RnM (MatchGroup GhcRn (LocatedA (body GhcRn)), FreeVars)-rnMatchGroup ctxt rnBody (MG { mg_alts = L lm ms, mg_origin = origin })+rnMatchGroup ctxt rnBody (MG { mg_alts = L lm ms, mg_ext = origin }) -- see Note [Empty MatchGroups] = do { whenM ((null ms &&) <$> mustn't_be_empty) (addErr (emptyCaseErr ctxt)) ; (new_ms, ms_fvs) <- mapFvRn (rnMatch ctxt rnBody) ms@@ -1287,7 +1315,7 @@ , m_grhss = grhss'}, grhss_fvs ) } emptyCaseErr :: HsMatchContext GhcRn -> TcRnMessage-emptyCaseErr ctxt = TcRnUnknownMessage $ mkPlainError noHints $ message ctxt+emptyCaseErr ctxt = mkTcRnUnknownMessage $ mkPlainError noHints $ message ctxt where pp_ctxt :: HsMatchContext GhcRn -> SDoc pp_ctxt c = case c of@@ -1345,7 +1373,7 @@ rnBody rhs ; unless (pattern_guards_allowed || is_standard_guard guards') $- let diag = TcRnUnknownMessage $+ let diag = mkTcRnUnknownMessage $ mkPlainDiagnostic WarningWithoutFlag noHints (nonStdGuardErr guards') in addDiagnostic diag @@ -1400,7 +1428,7 @@ dupSigDeclErr :: NonEmpty (LocatedN RdrName, Sig GhcPs) -> RnM () dupSigDeclErr pairs@((L loc name, sig) :| _)- = addErrAt (locA loc) $ TcRnUnknownMessage $ mkPlainError noHints $+ = addErrAt (locA loc) $ mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ text "Duplicate" <+> what_it_is <> text "s for" <+> quotes (ppr name) , text "at" <+> vcat (map ppr $ sortBy SrcLoc.leftmost_smallest@@ -1412,18 +1440,18 @@ misplacedSigErr :: LSig GhcRn -> RnM () misplacedSigErr (L loc sig)- = addErrAt (locA loc) $ TcRnUnknownMessage $ mkPlainError noHints $+ = addErrAt (locA loc) $ mkTcRnUnknownMessage $ mkPlainError noHints $ sep [text "Misplaced" <+> hsSigDoc sig <> colon, ppr sig] defaultSigErr :: Sig GhcPs -> TcRnMessage-defaultSigErr sig = TcRnUnknownMessage $ mkPlainError noHints $+defaultSigErr sig = mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ hang (text "Unexpected default signature:") 2 (ppr sig) , text "Use DefaultSignatures to enable default signatures" ] bindInHsBootFileErr :: LHsBindLR GhcRn GhcPs -> RnM () bindInHsBootFileErr (L loc _)- = addErrAt (locA loc) $ TcRnUnknownMessage $ mkPlainError noHints $+ = addErrAt (locA loc) $ mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ text "Bindings in hs-boot files are not allowed" ] nonStdGuardErr :: (Outputable body,@@ -1435,7 +1463,7 @@ dupMinimalSigErr :: [LSig GhcPs] -> RnM () dupMinimalSigErr sigs@(L loc _ : _)- = addErrAt (locA loc) $ TcRnUnknownMessage $ mkPlainError noHints $+ = addErrAt (locA loc) $ mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ text "Multiple minimal complete definitions" , text "at" <+> vcat (map ppr $ sortBy SrcLoc.leftmost_smallest $ map getLocA sigs) , text "Combine alternative minimal complete definitions with `|'" ]
compiler/GHC/Rename/Env.hs view
@@ -77,7 +77,7 @@ import GHC.Types.Error import GHC.Unit.Module import GHC.Unit.Module.ModIface-import GHC.Unit.Module.Warnings ( WarningTxt, pprWarningTxtForMsg )+import GHC.Unit.Module.Warnings ( WarningTxt ) import GHC.Core.ConLike import GHC.Core.DataCon import GHC.Core.TyCon@@ -104,6 +104,7 @@ import GHC.Types.FieldLabel import GHC.Data.Bag import GHC.Types.PkgQual+import Language.Haskell.Syntax.Basic (FieldLabelString(..)) {- *********************************************************@@ -206,6 +207,11 @@ -- the nice Exact name for the TyCon gets swizzled to an Orig name. -- Hence the badOrigBinding error message. --++ -- MP 2022: I suspect this code path is never called for `rOOT_MAIN` anymore+ -- because External Core has been removed but we instead have some similar logic for+ -- serialising whole programs into interface files in GHC.IfaceToCore.mk_top_id.+ -- Except for the ":Main.main = ..." definition inserted into -- the Main module; ugh! @@ -275,7 +281,7 @@ let occ = rdrNameOcc rdr_name ; when (isTcOcc occ && isSymOcc occ) (do { op_ok <- xoptM LangExt.TypeOperators- ; unless op_ok (addErr (opDeclErr rdr_name)) })+ ; unless op_ok (addErr (TcRnIllegalTypeOperatorDecl rdr_name)) }) ; env <- getGlobalRdrEnv ; case filter isLocalGRE (lookupGRE_RdrName rdr_name env) of@@ -498,7 +504,7 @@ = lookupExactOrOrig rdr_name id $ -- See Note [Record field names and Template Haskell] do { flds <- lookupConstructorFields con ; env <- getGlobalRdrEnv- ; let lbl = occNameFS (rdrNameOcc rdr_name)+ ; let lbl = FieldLabelString $ occNameFS (rdrNameOcc rdr_name) mb_field = do fl <- find ((== lbl) . flLabel) flds -- We have the label, now check it is in scope. If -- there is a qualifier, use pickGREs to check that@@ -761,7 +767,7 @@ -- monoid will combine them to this failing case. instance Outputable DisambigInfo where- ppr NoOccurrence = text "NoOccurence"+ ppr NoOccurrence = text "NoOccurrence" ppr (UniqueOccurrence gre) = text "UniqueOccurrence:" <+> ppr gre ppr (DisambiguatedOccurrence gre) = text "DiambiguatedOccurrence:" <+> ppr gre ppr (AmbiguousOccurrence gres) = text "Ambiguous:" <+> ppr gres@@ -865,7 +871,7 @@ one associated type called G. This is exactly what happens for methods, and it is only consistent to do the same thing for types. That's the role of the function lookupTcdName; the (Maybe Name) give the class of-the encloseing instance decl, if any.+the enclosing instance decl, if any. Note [Looking up Exact RdrNames] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -1111,10 +1117,10 @@ badVarInType :: RdrName -> RnM Name badVarInType rdr_name- = do { addErr (TcRnUnknownMessage $ mkPlainError noHints- (text "Illegal promoted term variable in a type:"- <+> ppr rdr_name))- ; return (mkUnboundNameRdr rdr_name) }+ = do { addErr (TcRnUnpromotableThing name TermVariablePE)+ ; return name }+ where+ name = mkUnboundNameRdr rdr_name {- Note [Promoted variables in types] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -1553,34 +1559,22 @@ -- See Note [Handling of deprecations] do { iface <- loadInterfaceForName doc name ; case lookupImpDeprec iface gre of- Just txt -> do- let msg = TcRnUnknownMessage $- mkPlainDiagnostic (WarningWithFlag Opt_WarnWarningsDeprecations)- noHints- (mk_msg imp_spec txt)-- addDiagnostic msg+ Just deprText -> addDiagnostic $+ TcRnPragmaWarning {+ pragma_warning_occ = occ,+ pragma_warning_msg = deprText,+ pragma_warning_import_mod = importSpecModule imp_spec,+ pragma_warning_defined_mod = definedMod+ } Nothing -> return () } } | otherwise = return () where occ = greOccName gre name = greMangledName gre- name_mod = assertPpr (isExternalName name) (ppr name) (nameModule name)+ definedMod = moduleName $ assertPpr (isExternalName name) (ppr name) (nameModule name) doc = text "The name" <+> quotes (ppr occ) <+> text "is mentioned explicitly" - mk_msg imp_spec txt- = sep [ sep [ text "In the use of"- <+> pprNonVarNameSpace (occNameSpace occ)- <+> quotes (ppr occ)- , parens imp_msg <> colon ]- , pprWarningTxtForMsg txt ]- where- imp_mod = importSpecModule imp_spec- imp_msg = text "imported from" <+> ppr imp_mod <> extra- extra | imp_mod == moduleName name_mod = Outputable.empty- | otherwise = text ", but defined in" <+> ppr name_mod- lookupImpDeprec :: ModIface -> GlobalRdrElt -> Maybe (WarningTxt GhcRn) lookupImpDeprec iface gre = mi_warn_fn (mi_final_exts iface) (greOccName gre) `mplus` -- Bleat if the thing,@@ -2093,25 +2087,9 @@ -- Error messages -opDeclErr :: RdrName -> TcRnMessage-opDeclErr n- = TcRnUnknownMessage $ mkPlainError noHints $- hang (text "Illegal declaration of a type or class operator" <+> quotes (ppr n))- 2 (text "Use TypeOperators to declare operators in type and declarations")- badOrigBinding :: RdrName -> TcRnMessage badOrigBinding name- | Just _ <- isBuiltInOcc_maybe occ- = TcRnUnknownMessage $ mkPlainError noHints $ text "Illegal binding of built-in syntax:" <+> ppr occ- -- Use an OccName here because we don't want to print Prelude.(,)- | otherwise- = TcRnUnknownMessage $ mkPlainError noHints $- text "Cannot redefine a Name retrieved by a Template Haskell quote:" <+> ppr name- -- This can happen when one tries to use a Template Haskell splice to- -- define a top-level identifier with an already existing name, e.g.,- --- -- $(pure [ValD (VarP 'succ) (NormalB (ConE 'True)) []])- --- -- (See #13968.)+ | Just _ <- isBuiltInOcc_maybe occ = TcRnIllegalBindingOfBuiltIn occ+ | otherwise = TcRnNameByTemplateHaskellQuote name where occ = rdrNameOcc $ filterCTuple name
compiler/GHC/Rename/Expr.hs view
@@ -6,6 +6,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE ViewPatterns #-} {-# OPTIONS_GHC -Wno-incomplete-record-updates #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}@@ -23,11 +24,12 @@ -} module GHC.Rename.Expr (- rnLExpr, rnExpr, rnStmts,- AnnoBody+ rnLExpr, rnExpr, rnStmts, mkExpandedExpr,+ AnnoBody, UnexpectedStatement(..) ) where import GHC.Prelude+import GHC.Data.FastString import GHC.Rename.Bind ( rnLocalBindsAndThen, rnLocalValBindsLHS, rnLocalValBindsRHS , rnMatchGroup, rnGRHS, makeMiniFixityEnv)@@ -47,7 +49,7 @@ , genHsVar, genLHsVar, genHsApp, genHsApps , genAppType ) import GHC.Rename.Unbound ( reportUnboundName )-import GHC.Rename.Splice ( rnTypedBracket, rnUntypedBracket, rnSpliceExpr, checkThLocalName )+import GHC.Rename.Splice ( rnTypedBracket, rnUntypedBracket, rnTypedSplice, rnUntypedSpliceExpr, checkThLocalName ) import GHC.Rename.HsType import GHC.Rename.Pat import GHC.Driver.Session@@ -55,7 +57,6 @@ import GHC.Types.FieldLabel import GHC.Types.Fixity-import GHC.Types.Hint (suggestExtension) import GHC.Types.Id.Make import GHC.Types.Name import GHC.Types.Name.Set@@ -73,8 +74,10 @@ import GHC.Builtin.Types ( nilDataConName ) import qualified GHC.LanguageExtensions as LangExt +import Language.Haskell.Syntax.Basic (FieldLabelString(..))+ import Data.List (unzip4, minimumBy)-import Data.List.NonEmpty ( NonEmpty(..) )+import Data.List.NonEmpty ( NonEmpty(..), nonEmpty ) import Data.Maybe (isJust, isNothing) import Control.Arrow (first) import Data.Ord@@ -122,7 +125,7 @@ * HsIf (if-the-else) if b then e1 else e2 ==> ifThenElse b e1 e2 We do this /only/ if rebindable syntax is on, because the coverage- checker looks for HsIf (see GHC.HsToCore.Coverage.addTickHsExpr)+ checker looks for HsIf (see GHC.HsToCore.Ticks.addTickHsExpr) That means the typechecker and desugarer need to understand HsIf for the non-rebindable-syntax case. @@ -151,6 +154,41 @@ on the fly, in GHC.Tc.Gen.Head.splitHsApps. RebindableSyntax does not affect this. +* RecordUpd: we desugar record updates into case expressions,+ in GHC.Tc.Gen.Expr.tcExpr.++ Example:++ data T p q = T1 { x :: Int, y :: Bool, z :: Char }+ | T2 { v :: Char }+ | T3 { x :: Int }+ | T4 { p :: Float, y :: Bool, x :: Int }+ | T5++ e { x=e1, y=e2 }+ ===>+ let { x' = e1; y' = e2 } in+ case e of+ T1 _ _ z -> T1 x' y' z+ T4 p _ _ -> T4 p y' x'++ See Note [Record Updates] in GHC.Tc.Gen.Expr for more details.++ This is done in the typechecker, not the renamer, for two reasons:++ - (Until we implement GHC proposal #366)+ We need to know the type of the record to disambiguate its fields.++ - We use the type signature of the data constructor to provide IdSigs+ to the let-bound variables (x', y' in the example above). This is+ needed to accept programs such as++ data R b = MkR { f :: (forall a. a -> a) -> (Int,b), c :: Int }+ foo r = r { f = \ k -> (k 3, k 'x') }++ in which an updated field has a higher-rank type.+ See Wrinkle [Using IdSig] in Note [Record Updates] in GHC.Tc.Gen.Expr.+ Note [Overloaded labels] ~~~~~~~~~~~~~~~~~~~~~~~~ For overloaded labels, note that we /only/ apply `fromLabel` to the@@ -210,8 +248,9 @@ rnUnboundVar :: RdrName -> RnM (HsExpr GhcRn, FreeVars) rnUnboundVar v = do deferOutofScopeVariables <- goptM Opt_DeferOutOfScopeVariables+ -- See Note [Reporting unbound names] for difference between qualified and unqualified names. unless (isUnqual v || deferOutofScopeVariables) (reportUnboundName v >> return ())- return (HsUnboundVar noExtField (rdrNameOcc v), emptyFVs)+ return (HsUnboundVar noExtField v, emptyFVs) rnExpr (HsVar _ (L l v)) = do { dflags <- getDynFlags@@ -245,10 +284,10 @@ = return (HsUnboundVar noExtField v, emptyFVs) -- HsOverLabel: see Note [Handling overloaded and rebindable constructs]-rnExpr (HsOverLabel _ v)+rnExpr (HsOverLabel _ src v) = do { (from_label, fvs) <- lookupSyntaxName fromLabelClassOpName- ; return ( mkExpandedExpr (HsOverLabel noAnn v) $- HsAppType noExtField (genLHsVar from_label) hs_ty_arg+ ; return ( mkExpandedExpr (HsOverLabel noAnn src v) $+ HsAppType noExtField (genLHsVar from_label) noHsTok hs_ty_arg , fvs ) } where hs_ty_arg = mkEmptyWildCardBndrs $ wrapGenSpan $@@ -279,12 +318,12 @@ ; (arg',fvArg) <- rnLExpr arg ; return (HsApp x fun' arg', fvFun `plusFV` fvArg) } -rnExpr (HsAppType _ fun arg)+rnExpr (HsAppType _ fun at arg) = do { type_app <- xoptM LangExt.TypeApplications ; unless type_app $ addErr $ typeAppErr "type" $ hswc_body arg ; (fun',fvFun) <- rnLExpr fun ; (arg',fvArg) <- rnHsWcType HsTypeCtx arg- ; return (HsAppType NoExtField fun' arg', fvFun `plusFV` fvArg) }+ ; return (HsAppType NoExtField fun' at arg', fvFun `plusFV` fvArg) } rnExpr (OpApp _ e1 op e2) = do { (e1', fv_e1) <- rnLExpr e1@@ -340,7 +379,8 @@ rnExpr e@(HsTypedBracket _ br_body) = rnTypedBracket e br_body rnExpr e@(HsUntypedBracket _ br_body) = rnUntypedBracket e br_body -rnExpr (HsSpliceE _ splice) = rnSpliceExpr splice+rnExpr (HsTypedSplice _ splice) = rnTypedSplice splice+rnExpr (HsUntypedSplice _ splice) = rnUntypedSpliceExpr splice --------------------------------------------- -- Sections@@ -368,7 +408,7 @@ ; return (HsPragE x (rn_prag prag) expr', fvs_expr) } where rn_prag :: HsPragE GhcPs -> HsPragE GhcRn- rn_prag (HsPragSCC x1 src ann) = HsPragSCC x1 src ann+ rn_prag (HsPragSCC x ann) = HsPragSCC x ann rnExpr (HsLam x matches) = do { (matches', fvMatch) <- rnMatchGroup LambdaExpr rnLExpr matches@@ -446,13 +486,11 @@ } Right flds -> -- 'OverloadedRecordUpdate' is in effect. Record dot update desugaring. do { ; unlessXOptM LangExt.RebindableSyntax $- addErr $ TcRnUnknownMessage $ mkPlainError noHints $- text "RebindableSyntax is required if OverloadedRecordUpdate is enabled."+ addErr TcRnNoRebindableSyntaxRecordDot ; let punnedFields = [fld | (L _ fld) <- flds, hfbPun fld] ; punsEnabled <-xoptM LangExt.NamedFieldPuns ; unless (null punnedFields || punsEnabled) $- addErr $ TcRnUnknownMessage $ mkPlainError noHints $- text "For this to work enable NamedFieldPuns."+ addErr TcRnNoFieldPunsRecordDot ; (getField, fv_getField) <- lookupSyntaxName getFieldName ; (setField, fv_setField) <- lookupSyntaxName setFieldName ; (e, fv_e) <- rnLExpr expr@@ -527,16 +565,11 @@ -- absolutely prepared to cope with static forms, we check for -- -XStaticPointers here as well. unlessXOptM LangExt.StaticPointers $- addErr $ TcRnUnknownMessage $ mkPlainError noHints $- hang (text "Illegal static expression:" <+> ppr e)- 2 (text "Use StaticPointers to enable this extension")+ addErr $ TcRnIllegalStaticExpression e (expr',fvExpr) <- rnLExpr expr stage <- getStage case stage of- Splice _ -> addErr $ TcRnUnknownMessage $ mkPlainError noHints $ sep- [ text "static forms cannot be used in splices:"- , nest 2 $ ppr e- ]+ Splice _ -> addErr $ TcRnIllegalStaticFormInSplice e _ -> return () mod <- getModule let fvExpr' = filterNameSet (nameIsLocalOrFrom mod) fvExpr@@ -659,6 +692,16 @@ (e `op`) ==> op e with no auxiliary function at all. Simple! +* leftSection and rightSection switch on ImpredicativeTypes locally,+ during Quick Look; see GHC.Tc.Gen.App.wantQuickLook. Consider+ test DeepSubsumption08:+ type Setter st t a b = forall f. Identical f => blah+ (.~) :: Setter s t a b -> b -> s -> t+ clear :: Setter a a' b (Maybe b') -> a -> a'+ clear = (.~ Nothing)+ The expansion look like (rightSection (.~) Nothing). So we must+ instantiate `rightSection` first type argument to a polytype!+ Hence the special magic in App.wantQuickLook. Historical Note [Desugaring operator sections] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -709,6 +752,28 @@ case expr of y -> (\x -> op y x) See #18151.++Note [Reporting unbound names]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Faced with an out-of-scope `RdrName` there are two courses of action+A. Report an error immediately (and return a HsUnboundVar). This will halt GHC after the renamer is complete+B. Return a HsUnboundVar without reporting an error. That will allow the typechecker to run, which in turn+ can give a better error message, notably giving the type of the variable via the "typed holes" mechanism.++When `-fdefer-out-of-scope-variables` is on we follow plan B.++When it is not, we follow plan B for unqualified names, and plan A for qualified names.++If a name is qualified, and out of scope, then by default an error will be raised+because the user was already more precise. They specified a specific qualification+and either+ * The qualification didn't exist, so that precision was wrong.+ * Or the qualification existed and the thing we were looking for wasn't where+ the qualification said it would be.++However we can still defer this error completely, and we do defer it if+`-fdefer-out-of-scope-variables` is enabled.+ -} {-@@ -972,9 +1037,6 @@ type AnnoBody body = ( Outputable (body GhcPs)- , Anno (StmtLR GhcPs GhcPs (LocatedA (body GhcPs))) ~ SrcSpanAnnA- , Anno (StmtLR GhcRn GhcPs (LocatedA (body GhcPs))) ~ SrcSpanAnnA- , Anno (StmtLR GhcRn GhcRn (LocatedA (body GhcRn))) ~ SrcSpanAnnA ) -- | Rename some Stmts@@ -1039,15 +1101,13 @@ ; (thing, fvs) <- thing_inside [] ; return (([], thing), fvs) } -rnStmtsWithFreeVars mDoExpr@(HsDoStmt MDoExpr{}) rnBody stmts thing_inside -- Deal with mdo+rnStmtsWithFreeVars mDoExpr@(HsDoStmt MDoExpr{}) rnBody (nonEmpty -> Just stmts) thing_inside -- Deal with mdo = -- Behave like do { rec { ...all but last... }; last } do { ((stmts1, (stmts2, thing)), fvs)- <- rnStmt mDoExpr rnBody (noLocA $ mkRecStmt noAnn (noLocA all_but_last)) $ \ _ ->- do { last_stmt' <- checkLastStmt mDoExpr last_stmt+ <- rnStmt mDoExpr rnBody (noLocA $ mkRecStmt noAnn (noLocA (NE.init stmts))) $ \ _ ->+ do { last_stmt' <- checkLastStmt mDoExpr (NE.last stmts) ; rnStmt mDoExpr rnBody last_stmt' thing_inside } ; return (((stmts1 ++ stmts2), thing), fvs) }- where- Just (all_but_last, last_stmt) = snocView stmts rnStmtsWithFreeVars ctxt rnBody (lstmt@(L loc _) : lstmts) thing_inside | null lstmts@@ -1059,7 +1119,7 @@ = do { ((stmts1, (stmts2, thing)), fvs) <- setSrcSpanA loc $ do { checkStmt ctxt lstmt- ; rnStmt ctxt rnBody lstmt $ \ bndrs1 ->+ ; rnStmt ctxt rnBody lstmt $ \ bndrs1 -> rnStmtsWithFreeVars ctxt rnBody lstmts $ \ bndrs2 -> thing_inside (bndrs1 ++ bndrs2) } ; return (((stmts1 ++ stmts2), thing), fvs) }@@ -1154,9 +1214,10 @@ = do { (return_op, fvs1) <- lookupQualifiedDoStmtName ctxt returnMName ; (mfix_op, fvs2) <- lookupQualifiedDoStmtName ctxt mfixName ; (bind_op, fvs3) <- lookupQualifiedDoStmtName ctxt bindMName- ; let empty_rec_stmt = emptyRecStmtName { recS_ret_fn = return_op- , recS_mfix_fn = mfix_op- , recS_bind_fn = bind_op }+ ; let empty_rec_stmt = (emptyRecStmtName :: StmtLR GhcRn GhcRn (LocatedA (body GhcRn)))+ { recS_ret_fn = return_op+ , recS_mfix_fn = mfix_op+ , recS_bind_fn = bind_op } -- Step1: Bring all the binders of the mdo into scope -- (Remember that this also removes the binders from the@@ -1262,9 +1323,7 @@ ; return ((seg':segs', thing), fvs) } cmpByOcc n1 n2 = nameOccName n1 `compare` nameOccName n2- dupErr vs = addErr $ TcRnUnknownMessage $ mkPlainError noHints $- (text "Duplicate binding in parallel list comprehension for:"- <+> quotes (ppr (NE.head vs)))+ dupErr vs = addErr $ TcRnListComprehensionDuplicateBinding (NE.head vs) lookupQualifiedDoStmtName :: HsStmtContext GhcRn -> Name -> RnM (SyntaxExpr GhcRn, FreeVars) -- Like lookupStmtName, but respects QualifiedDo@@ -1332,7 +1391,7 @@ (c) The 'bs' in the second group must obviously not be captured by the binding in the first group -To satisfy (a) we nest the segements.+To satisfy (a) we nest the segments. To satisfy (b) we check for duplicates just before thing_inside. To satisfy (c) we reset the LocalRdrEnv each time. @@ -1353,8 +1412,8 @@ -- wrapper that does both the left- and right-hand sides-rnRecStmtsAndThen :: AnnoBody body =>- HsStmtContext GhcRn+rnRecStmtsAndThen :: AnnoBody body+ => HsStmtContext GhcRn -> (body GhcPs -> RnM (body GhcRn, FreeVars)) -> [LStmt GhcPs (LocatedA (body GhcPs))] -- assumes that the FreeVars returned includes@@ -1417,8 +1476,9 @@ return [(L loc (BindStmt noAnn pat' body), fv_pat)] rn_rec_stmt_lhs _ (L _ (LetStmt _ binds@(HsIPBinds {})))- = failWith (badIpBinds (text "an mdo expression") binds)+ = failWith (badIpBinds (Left binds)) + rn_rec_stmt_lhs fix_env (L loc (LetStmt _ (HsValBinds x binds))) = do (_bound_names, binds') <- rnLocalValBindsLHS fix_env binds return [(L loc (LetStmt noAnn (HsValBinds x binds')),@@ -1491,7 +1551,7 @@ L loc (BindStmt xbsrn pat' (L lb body')))] } rn_rec_stmt _ _ _ (L _ (LetStmt _ binds@(HsIPBinds {})), _)- = failWith (badIpBinds (text "an mdo expression") binds)+ = failWith (badIpBinds (Right binds)) rn_rec_stmt _ _ all_bndrs (L loc (LetStmt _ (HsValBinds x binds')), _) = do { (binds', du_binds) <- rnLocalValBindsRHS (mkNameSet all_bndrs) binds'@@ -1516,8 +1576,8 @@ rn_rec_stmt _ _ _ stmt@(L _ (ApplicativeStmt {}), _) = pprPanic "rn_rec_stmt: ApplicativeStmt" (ppr stmt) -rn_rec_stmts :: AnnoBody body =>- HsStmtContext GhcRn+rn_rec_stmts :: AnnoBody body+ => HsStmtContext GhcRn -> (body GhcPs -> RnM (body GhcRn, FreeVars)) -> [Name] -> [(LStmtLR GhcRn GhcPs (LocatedA (body GhcPs)), FreeVars)]@@ -1527,8 +1587,7 @@ ; return (concat segs_s) } ----------------------------------------------segmentRecStmts :: AnnoBody body- => SrcSpan -> HsStmtContext GhcRn+segmentRecStmts :: SrcSpan -> HsStmtContext GhcRn -> Stmt GhcRn (LocatedA (body GhcRn)) -> [Segment (LStmt GhcRn (LocatedA (body GhcRn)))] -> (FreeVars, Bool)@@ -1759,7 +1818,7 @@ The algorithm works by first splitting the sequence of statements into independent "segments", and a separate "tail" (the final statement). In-our example above, the segements would be+our example above, the segments would be [ x <- A , y <- B x ]@@ -2114,7 +2173,9 @@ if | L _ ApplicativeStmt{} <- last stmts' -> return (unLoc tup, emptyNameSet) | otherwise -> do- (ret, _) <- lookupQualifiedDoExpr (HsDoStmt ctxt) returnMName+ -- Need 'pureAName' and not 'returnMName' here, so that it requires+ -- 'Applicative' and not 'Monad' whenever possible (until #20540 is fixed).+ (ret, _) <- lookupQualifiedDoExpr (HsDoStmt ctxt) pureAName let expr = HsApp noComments (noLocA ret) tup return (expr, emptyFVs) return ( ApplicativeArgMany@@ -2132,19 +2193,19 @@ segments :: [(ExprLStmt GhcRn, FreeVars)] -> [[(ExprLStmt GhcRn, FreeVars)]]-segments stmts = map fst $ merge $ reverse $ map reverse $ walk (reverse stmts)+segments stmts = merge $ reverse $ map reverse $ walk (reverse stmts) where allvars = mkNameSet (concatMap (collectStmtBinders CollNoDictBinders . unLoc . fst) stmts) -- We would rather not have a segment that just has LetStmts in- -- it, so combine those with an adjacent segment where possible.+ -- it, so combine those with the next segment where possible.+ -- We don't merge it with the previous segment because the merged segment+ -- would require 'Monad' while it may otherwise only require 'Applicative'. merge [] = [] merge (seg : segs) = case rest of- [] -> [(seg,all_lets)]- ((s,s_lets):ss) | all_lets || s_lets- -> (seg ++ s, all_lets && s_lets) : ss- _otherwise -> (seg,all_lets) : rest+ s:ss | all_lets -> (seg ++ s) : ss+ _otherwise -> seg : rest where rest = merge segs all_lets = all (isLetStmt . fst) seg@@ -2182,7 +2243,7 @@ {- Note [ApplicativeDo and strict patterns]-+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A strict pattern match is really a dependency. For example, do@@ -2211,7 +2272,7 @@ WildPat{} -> False VarPat{} -> False LazyPat{} -> False- AsPat _ _ p -> isStrictPattern p+ AsPat _ _ _ p -> isStrictPattern p ParPat _ _ p _ -> isStrictPattern p ViewPat _ _ p -> isStrictPattern p SigPat _ p _ -> isStrictPattern p@@ -2237,7 +2298,7 @@ {- Note [ApplicativeDo and refutable patterns]-+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Refutable patterns in do blocks are desugared to use the monadic 'fail' operation. This means that sometimes an applicative block needs to be wrapped in 'join' simply because of a refutable pattern, in order for the types to work out.@@ -2384,7 +2445,7 @@ _otherwise -> Nothing where is_var f (L _ (HsPar _ _ e _)) = is_var f e- is_var f (L _ (HsAppType _ e _)) = is_var f e+ is_var f (L _ (HsAppType _ e _ _)) = is_var f e is_var f (L _ (HsVar _ (L _ r))) = f r -- TODO: I don't know how to get this right for rebindable syntax is_var _ _ = False@@ -2405,24 +2466,18 @@ checkEmptyStmts :: HsStmtContext GhcRn -> RnM () -- We've seen an empty sequence of Stmts... is that ok? checkEmptyStmts ctxt- = unless (okEmpty ctxt) (addErr (emptyErr ctxt))--okEmpty :: HsStmtContext a -> Bool-okEmpty (PatGuard {}) = True-okEmpty _ = False--emptyErr :: HsStmtContext GhcRn -> TcRnMessage-emptyErr (ParStmtCtxt {}) = TcRnUnknownMessage $ mkPlainError noHints $- text "Empty statement group in parallel comprehension"-emptyErr (TransStmtCtxt {}) = TcRnUnknownMessage $ mkPlainError noHints $- text "Empty statement group preceding 'group' or 'then'"-emptyErr ctxt@(HsDoStmt _) = TcRnUnknownMessage $ mkPlainError [suggestExtension LangExt.NondecreasingIndentation] $- text "Empty" <+> pprStmtContext ctxt-emptyErr ctxt = TcRnUnknownMessage $ mkPlainError noHints $- text "Empty" <+> pprStmtContext ctxt+ = mapM_ (addErr . TcRnEmptyStmtsGroup) mb_err+ where+ mb_err = case ctxt of+ PatGuard {} -> Nothing -- Pattern guards can be empty+ ParStmtCtxt {} -> Just EmptyStmtsGroupInParallelComp+ TransStmtCtxt {} -> Just EmptyStmtsGroupInTransformListComp+ HsDoStmt flav -> Just $ EmptyStmtsGroupInDoNotation flav+ ArrowExpr -> Just EmptyStmtsGroupInArrowNotation -----------------------checkLastStmt :: AnnoBody body => HsStmtContext GhcRn+checkLastStmt :: AnnoBody body+ => HsStmtContext GhcRn -> LStmt GhcPs (LocatedA (body GhcPs)) -> RnM (LStmt GhcPs (LocatedA (body GhcPs))) checkLastStmt ctxt lstmt@(L loc stmt)@@ -2439,12 +2494,11 @@ BodyStmt _ e _ _ -> return (L loc (mkLastStmt e)) LastStmt {} -> return lstmt -- "Deriving" clauses may generate a -- LastStmt directly (unlike the parser)- _ -> do { addErr $ TcRnUnknownMessage $ mkPlainError noHints $- (hang last_error 2 (ppr stmt))+ _ -> do { addErr $ TcRnLastStmtNotExpr ctxt+ $ UnexpectedStatement stmt ; return lstmt }- last_error = (text "The last statement in" <+> pprAStmtContext ctxt- <+> text "must be an expression") + check_comp -- Expect LastStmt; this should be enforced by the parser! = case stmt of LastStmt {} -> return lstmt@@ -2454,35 +2508,25 @@ = do { checkStmt ctxt lstmt; return lstmt } -- Checking when a particular Stmt is ok-checkStmt :: HsStmtContext GhcRn+checkStmt :: AnnoBody body+ => HsStmtContext GhcRn -> LStmt GhcPs (LocatedA (body GhcPs)) -> RnM () checkStmt ctxt (L _ stmt) = do { dflags <- getDynFlags ; case okStmt dflags ctxt stmt of- IsValid -> return ()- NotValid extra -> addErr $ TcRnUnknownMessage $ mkPlainError noHints (msg $$ extra) }- where- msg = sep [ text "Unexpected" <+> pprStmtCat stmt <+> text "statement"- , text "in" <+> pprAStmtContext ctxt ]--pprStmtCat :: Stmt (GhcPass a) body -> SDoc-pprStmtCat (TransStmt {}) = text "transform"-pprStmtCat (LastStmt {}) = text "return expression"-pprStmtCat (BodyStmt {}) = text "body"-pprStmtCat (BindStmt {}) = text "binding"-pprStmtCat (LetStmt {}) = text "let"-pprStmtCat (RecStmt {}) = text "rec"-pprStmtCat (ParStmt {}) = text "parallel"-pprStmtCat (ApplicativeStmt {}) = panic "pprStmtCat: ApplicativeStmt"+ IsValid -> return ()+ NotValid ext -> addErr $+ TcRnUnexpectedStatementInContext+ ctxt (UnexpectedStatement stmt) ext } -------------emptyInvalid :: Validity -- Payload is the empty document-emptyInvalid = NotValid Outputable.empty+emptyInvalid :: Validity' (Maybe LangExt.Extension)+emptyInvalid = NotValid Nothing -- Invalid, and no extension to suggest okStmt, okDoStmt, okCompStmt, okParStmt :: DynFlags -> HsStmtContext GhcRn- -> Stmt GhcPs (LocatedA (body GhcPs)) -> Validity+ -> Stmt GhcPs (LocatedA (body GhcPs)) -> Validity' (Maybe LangExt.Extension) -- Return Nothing if OK, (Just extra) if not ok -- The "extra" is an SDoc that is appended to a generic error message @@ -2496,7 +2540,7 @@ okDoFlavourStmt :: DynFlags -> HsDoFlavour -> HsStmtContext GhcRn- -> Stmt GhcPs (LocatedA (body GhcPs)) -> Validity+ -> Stmt GhcPs (LocatedA (body GhcPs)) -> Validity' (Maybe LangExt.Extension) okDoFlavourStmt dflags flavour ctxt stmt = case flavour of DoExpr{} -> okDoStmt dflags ctxt stmt MDoExpr{} -> okDoStmt dflags ctxt stmt@@ -2505,7 +2549,7 @@ MonadComp -> okCompStmt dflags ctxt stmt --------------okPatGuardStmt :: Stmt GhcPs (LocatedA (body GhcPs)) -> Validity+okPatGuardStmt :: Stmt GhcPs (LocatedA (body GhcPs)) -> Validity' (Maybe LangExt.Extension) okPatGuardStmt stmt = case stmt of BodyStmt {} -> IsValid@@ -2525,7 +2569,7 @@ RecStmt {} | LangExt.RecursiveDo `xopt` dflags -> IsValid | ArrowExpr <- ctxt -> IsValid -- Arrows allows 'rec'- | otherwise -> NotValid (text "Use RecursiveDo")+ | otherwise -> NotValid (Just LangExt.RecursiveDo) BindStmt {} -> IsValid LetStmt {} -> IsValid BodyStmt {} -> IsValid@@ -2539,10 +2583,10 @@ BodyStmt {} -> IsValid ParStmt {} | LangExt.ParallelListComp `xopt` dflags -> IsValid- | otherwise -> NotValid (text "Use ParallelListComp")+ | otherwise -> NotValid (Just LangExt.ParallelListComp) TransStmt {} | LangExt.TransformListComp `xopt` dflags -> IsValid- | otherwise -> NotValid (text "Use TransformListComp")+ | otherwise -> NotValid (Just LangExt.TransformListComp) RecStmt {} -> emptyInvalid LastStmt {} -> emptyInvalid -- Should not happen (dealt with by checkLastStmt) ApplicativeStmt {} -> emptyInvalid@@ -2554,21 +2598,14 @@ ; checkErr (all tupArgPresent args || tuple_section) msg } where msg :: TcRnMessage- msg = TcRnUnknownMessage $ mkPlainError noHints $- text "Illegal tuple section: use TupleSections"+ msg = TcRnIllegalTupleSection --------- sectionErr :: HsExpr GhcPs -> TcRnMessage-sectionErr expr- = TcRnUnknownMessage $ mkPlainError noHints $- hang (text "A section must be enclosed in parentheses")- 2 (text "thus:" <+> (parens (ppr expr)))+sectionErr = TcRnSectionWithoutParentheses -badIpBinds :: Outputable a => SDoc -> a -> TcRnMessage-badIpBinds what binds- = TcRnUnknownMessage $ mkPlainError noHints $- hang (text "Implicit-parameter bindings illegal in" <+> what)- 2 (ppr binds)+badIpBinds :: Either (HsLocalBindsLR GhcPs GhcPs) (HsLocalBindsLR GhcRn GhcPs) -> TcRnMessage+badIpBinds = TcRnIllegalImplicitParameterBindings --------- @@ -2638,7 +2675,7 @@ | (isQualifiedDo || rebindableSyntax) && overloadedStrings = do (failExpr, failFvs) <- lookupQualifiedDoExpr ctxt failMName (fromStringExpr, fromStringFvs) <- lookupSyntaxExpr fromStringName- let arg_lit = mkVarOcc "arg"+ let arg_lit = mkVarOccFS (fsLit "arg") arg_name <- newSysName arg_lit let arg_syn_expr = nlHsVar arg_name body :: LHsExpr GhcRn =@@ -2673,7 +2710,7 @@ -- -- See Note [Overview of record dot syntax] in GHC.Hs.Expr. --- mkGetField arg field calcuates a get_field @field arg expression.+-- mkGetField arg field calculates a get_field @field arg expression. -- e.g. z.x = mkGetField z x = get_field @x z mkGetField :: Name -> LHsExpr GhcRn -> LocatedAn NoEpAnns FieldLabelString -> HsExpr GhcRn mkGetField get_field arg field = unLoc (head $ mkGet get_field [arg] field)@@ -2681,11 +2718,11 @@ -- mkSetField a field b calculates a set_field @field expression. -- e.g mkSetSetField a field b = set_field @"field" a b (read as "set field 'field' on a to b"). mkSetField :: Name -> LHsExpr GhcRn -> LocatedAn NoEpAnns FieldLabelString -> LHsExpr GhcRn -> HsExpr GhcRn-mkSetField set_field a (L _ field) b =+mkSetField set_field a (L _ (FieldLabelString field)) b = genHsApp (genHsApp (genHsVar set_field `genAppType` genHsTyLit field) a) b mkGet :: Name -> [LHsExpr GhcRn] -> LocatedAn NoEpAnns FieldLabelString -> [LHsExpr GhcRn]-mkGet get_field l@(r : _) (L _ field) =+mkGet get_field l@(r : _) (L _ (FieldLabelString field)) = wrapGenSpan (genHsApp (genHsVar get_field `genAppType` genHsTyLit field) r) : l mkGet _ [] _ = panic "mkGet : The impossible has happened!" @@ -2702,7 +2739,7 @@ f acc field = genHsApps circName $ map wrapGenSpan [proj field, acc] proj :: LocatedAn NoEpAnns FieldLabelString -> HsExpr GhcRn- proj (L _ f) = genHsVar getFieldName `genAppType` genHsTyLit f+ proj (L _ (FieldLabelString f)) = genHsVar getFieldName `genAppType` genHsTyLit f -- mkProjUpdateSetField calculates functions representing dot notation record updates. -- e.g. Suppose an update like foo.bar = 1.
compiler/GHC/Rename/Expr.hs-boot view
@@ -1,12 +1,6 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ConstraintKinds #-} module GHC.Rename.Expr where -#if MIN_VERSION_GLASGOW_HASKELL(9,3,0,0)-import Data.Type.Equality (type (~))-#endif- import GHC.Types.Name import GHC.Hs import GHC.Types.Name.Set ( FreeVars )@@ -21,10 +15,8 @@ type AnnoBody body = ( Outputable (body GhcPs)- , Anno (StmtLR GhcPs GhcPs (LocatedA (body GhcPs))) ~ SrcSpanAnnA- , Anno (StmtLR GhcRn GhcPs (LocatedA (body GhcPs))) ~ SrcSpanAnnA- , Anno (StmtLR GhcRn GhcRn (LocatedA (body GhcRn))) ~ SrcSpanAnnA )+ rnStmts :: --forall thing body. AnnoBody body => HsStmtContext GhcRn -> (body GhcPs -> RnM (body GhcRn, FreeVars))
compiler/GHC/Rename/Fixity.hs view
@@ -127,7 +127,7 @@ lookupFixityRn_help' name occ | isUnboundName name = return (False, Fixity NoSourceText minPrecedence InfixL)- -- Minimise errors from ubound names; eg+ -- Minimise errors from unbound names; eg -- a>0 `foo` b>0 -- where 'foo' is not in scope, should not give an error (#7937)
compiler/GHC/Rename/HsType.hs view
@@ -57,7 +57,7 @@ import GHC.Rename.Unbound ( notInScopeErr, WhereLooking(WL_LocalOnly) ) import GHC.Tc.Errors.Types import GHC.Tc.Errors.Ppr ( pprScopeError- , inHsDocContext, withHsDocContext, pprHsDocContext )+ , inHsDocContext, pprHsDocContext ) import GHC.Tc.Utils.Monad import GHC.Types.Name.Reader import GHC.Builtin.Names@@ -71,14 +71,16 @@ import GHC.Utils.Misc import GHC.Types.Fixity ( compareFixity, negateFixity , Fixity(..), FixityDirection(..), LexicalFixity(..) )-import GHC.Types.Basic ( PromotionFlag(..), isPromoted, TypeOrKind(..) )+import GHC.Types.Basic ( TypeOrKind(..) ) import GHC.Utils.Outputable import GHC.Utils.Panic import GHC.Utils.Panic.Plain import GHC.Data.Maybe import qualified GHC.LanguageExtensions as LangExt -import Data.List (sortBy, nubBy, partition)+import Language.Haskell.Syntax.Basic (FieldLabelString(..))++import Data.List (nubBy, partition) import qualified Data.List.NonEmpty as NE import Data.List.NonEmpty (NonEmpty(..)) import Control.Monad@@ -213,7 +215,7 @@ -- Should the inner `a` refer to the outer one? shadow it? We are, as yet, undecided, -- so we currently reject. when (not (null varsInScope)) $- addErr $ TcRnUnknownMessage $ mkPlainError noHints $+ addErr $ mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ text "Type variable" <> plural varsInScope <+> hcat (punctuate (text ",") (map (quotes . ppr) varsInScope))@@ -441,10 +443,10 @@ -> ([Name] -> RnM (a, FreeVars)) -> RnM (a, FreeVars) rnImplicitTvBndrs ctx mb_assoc implicit_vs_with_dups thing_inside- = do { implicit_vs <- forM (NE.groupBy eqLocated $ sortBy cmpLocated $ implicit_vs_with_dups) $ \case+ = do { implicit_vs <- forM (NE.groupAllWith unLoc $ implicit_vs_with_dups) $ \case (x :| []) -> return x (x :| _) -> do- let msg = TcRnUnknownMessage $ mkPlainError noHints $+ let msg = mkTcRnUnknownMessage $ mkPlainError noHints $ text "Variable" <+> text "`" <> ppr x <> text "'" <+> text "would be bound multiple times by" <+> pprHsDocContext ctx <> text "." addErr msg return x@@ -620,8 +622,9 @@ rnHsTyKi env (HsTyVar _ ip (L loc rdr_name)) = do { when (isRnKindLevel env && isRdrTyVar rdr_name) $- unlessXOptM LangExt.PolyKinds $ addErr $ TcRnUnknownMessage $ mkPlainError noHints $- withHsDocContext (rtke_ctxt env) $+ unlessXOptM LangExt.PolyKinds $ addErr $+ TcRnWithHsDocContext (rtke_ctxt env) $+ mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ text "Unexpected kind variable" <+> quotes (ppr rdr_name) , text "Perhaps you intended to use PolyKinds" ] -- Any type variable at the kind level is illegal without the use@@ -661,7 +664,7 @@ get_fields (ConDeclCtx names) = concatMapM (lookupConstructorFields . unLoc) names get_fields _- = do { addErr $ TcRnUnknownMessage $ mkPlainError noHints $+ = do { addErr $ mkTcRnUnknownMessage $ mkPlainError noHints $ (hang (text "Record syntax is illegal here:") 2 (ppr ty)) ; return [] } @@ -703,17 +706,18 @@ ; return (HsSumTy x tys', fvs) } -- Ensure that a type-level integer is nonnegative (#8306, #8412)-rnHsTyKi env tyLit@(HsTyLit _ t)+rnHsTyKi env tyLit@(HsTyLit src t) = do { data_kinds <- xoptM LangExt.DataKinds ; unless data_kinds (addErr (dataKindsErr env tyLit)) ; when (negLit t) (addErr negLitErr)- ; return (HsTyLit noExtField t, emptyFVs) }+ ; return (HsTyLit src (rnHsTyLit t), emptyFVs) } where+ negLit :: HsTyLit (GhcPass p) -> Bool negLit (HsStrTy _ _) = False negLit (HsNumTy _ i) = i < 0 negLit (HsCharTy _ _) = False negLitErr :: TcRnMessage- negLitErr = TcRnUnknownMessage $ mkPlainError noHints $+ negLitErr = mkTcRnUnknownMessage $ mkPlainError noHints $ text "Illegal literal in type (type literals must not be negative):" <+> ppr tyLit rnHsTyKi env (HsAppTy _ ty1 ty2)@@ -755,10 +759,11 @@ check_in_scope :: RdrName -> RnM () check_in_scope rdr_name = do mb_name <- lookupLocalOccRn_maybe rdr_name- -- TODO: refactor this to avoid TcRnUnknownMessage+ -- TODO: refactor this to avoid mkTcRnUnknownMessage when (isNothing mb_name) $- addErr $ TcRnUnknownMessage $ mkPlainError noHints $- withHsDocContext (rtke_ctxt env) $+ addErr $+ TcRnWithHsDocContext (rtke_ctxt env) $+ mkTcRnUnknownMessage $ mkPlainError noHints $ pprScopeError rdr_name (notInScopeErr WL_LocalOnly rdr_name) rnHsTyKi env ty@(HsExplicitListTy _ ip tys)@@ -779,6 +784,13 @@ = do { checkAnonWildCard env ; return (HsWildCardTy noExtField, emptyFVs) } ++rnHsTyLit :: HsTyLit GhcPs -> HsTyLit GhcRn+rnHsTyLit (HsStrTy x s) = HsStrTy x s+rnHsTyLit (HsNumTy x i) = HsNumTy x i+rnHsTyLit (HsCharTy x c) = HsCharTy x c++ rnHsArrow :: RnTyKiEnv -> HsArrow GhcPs -> RnM (HsArrow GhcRn, FreeVars) rnHsArrow _env (HsUnrestrictedArrow arr) = return (HsUnrestrictedArrow arr, emptyFVs) rnHsArrow _env (HsLinearArrow (HsPct1 pct1 arr)) = return (HsLinearArrow (HsPct1 pct1 arr), emptyFVs)@@ -850,7 +862,8 @@ -> Maybe BadAnonWildcardContext -> RnM () checkWildCard env mb_name (Just bad)- = addErr $ TcRnIllegalWildcardInType mb_name bad (Just $ rtke_ctxt env)+ = addErr $ TcRnWithHsDocContext (rtke_ctxt env) $+ TcRnIllegalWildcardInType mb_name bad checkWildCard _ _ Nothing = return () @@ -914,7 +927,7 @@ | isRnKindLevel env = do { polykinds <- xoptM LangExt.PolyKinds ; unless polykinds $- addErr $ TcRnUnknownMessage $ mkPlainError noHints $+ addErr $ mkTcRnUnknownMessage $ mkPlainError noHints $ (text "Illegal kind:" <+> ppr ty $$ text "Did you mean to enable PolyKinds?") } checkPolyKinds _ _ = return ()@@ -925,7 +938,7 @@ -> RnM () notInKinds env ty | isRnKindLevel env- = addErr $ TcRnUnknownMessage $ mkPlainError noHints $+ = addErr $ mkTcRnUnknownMessage $ mkPlainError noHints $ text "Illegal kind:" <+> ppr ty notInKinds _ _ = return () @@ -1274,7 +1287,7 @@ = mapFvRn (rnField fl_env env) fields where env = mkTyKiEnv ctxt TypeLevel RnTypeBody- fl_env = mkFsEnv [ (flLabel fl, fl) | fl <- fls ]+ fl_env = mkFsEnv [ (field_label $ flLabel fl, fl) | fl <- fls ] rnField :: FastStringEnv FieldLabel -> RnTyKiEnv -> LConDeclField GhcPs -> RnM (LConDeclField GhcRn, FreeVars)@@ -1332,7 +1345,7 @@ = do { fix2 <- lookupTyFixityRn op2 ; mk_hs_op_ty prom1 op1 fix1 ty1 prom2 op2 fix2 ty2a ty2b loc2 } -mkHsOpTyRn prom1 op1 _ ty1 ty2 -- Default case, no rearrangment+mkHsOpTyRn prom1 op1 _ ty1 ty2 -- Default case, no rearrangement = return (HsOpTy noAnn prom1 ty1 op1 ty2) ---------------@@ -1400,7 +1413,7 @@ --------------------------- -- Default case-mkOpAppRn _ e1 op fix e2 -- Default case, no rearrangment+mkOpAppRn _ e1 op fix e2 -- Default case, no rearrangement = assertPpr (right_op_ok fix (unLoc e2)) (ppr e1 $$ text "---" $$ ppr op $$ text "---" $$ ppr fix $$ text "---" $$ ppr e2) $ return (OpApp fix e1 op e2)@@ -1412,7 +1425,7 @@ -- | Name of an operator in an operator application or section data OpName = NormalOp Name -- ^ A normal identifier | NegateOp -- ^ Prefix negation- | UnboundOp OccName -- ^ An unbound indentifier+ | UnboundOp RdrName -- ^ An unbound identifier | RecFldOp (FieldOcc GhcRn) -- ^ A record field occurrence instance Outputable OpName where@@ -1476,7 +1489,7 @@ (nofix_error, associate_right) = compareFixity fix1 fix2 -- Default case-mkOpFormRn arg1 op fix arg2 -- Default case, no rearrangment+mkOpFormRn arg1 op fix arg2 -- Default case, no rearrangement = return (HsCmdArrForm noExtField op Infix (Just fix) [arg1, arg2]) @@ -1514,7 +1527,7 @@ } } -mkConOpPatRn op _ p1 p2 -- Default case, no rearrangment+mkConOpPatRn op _ p1 p2 -- Default case, no rearrangement = assert (not_op_pat (unLoc p2)) $ return $ ConPat { pat_con_ext = noExtField@@ -1594,7 +1607,7 @@ lookupFixityOp :: OpName -> RnM Fixity lookupFixityOp (NormalOp n) = lookupFixityRn n lookupFixityOp NegateOp = lookupFixityRn negateName-lookupFixityOp (UnboundOp u) = lookupFixityRn (mkUnboundName u)+lookupFixityOp (UnboundOp u) = lookupFixityRn (mkUnboundName (occName u)) lookupFixityOp (RecFldOp f) = lookupFieldFixityRn f @@ -1605,7 +1618,7 @@ | is_unbound n1 || is_unbound n2 = return () -- Avoid error cascade | otherwise- = addErr $ TcRnUnknownMessage $ mkPlainError noHints $+ = addErr $ mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Precedence parsing error") 4 (hsep [text "cannot mix", ppr_opfix op1, text "and", ppr_opfix op2,@@ -1616,7 +1629,7 @@ | is_unbound n1 || is_unbound n2 = return () -- Avoid error cascade | otherwise- = addErr $ TcRnUnknownMessage $ mkPlainError noHints $+ = addErr $ mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [text "The operator" <+> ppr_opfix op <+> text "of a section", nest 4 (sep [text "must have lower precedence than that of the operand,", nest 2 (text "namely" <+> ppr_opfix arg_op)]),@@ -1642,20 +1655,21 @@ unexpectedPatSigTypeErr :: HsPatSigType GhcPs -> TcRnMessage unexpectedPatSigTypeErr ty- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Illegal type signature:" <+> quotes (ppr ty)) 2 (text "Type signatures are only allowed in patterns with ScopedTypeVariables") badKindSigErr :: HsDocContext -> LHsType GhcPs -> TcM () badKindSigErr doc (L loc ty)- = setSrcSpanA loc $ addErr $ TcRnUnknownMessage $ mkPlainError noHints $- withHsDocContext doc $+ = setSrcSpanA loc $ addErr $+ TcRnWithHsDocContext doc $+ mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Illegal kind signature:" <+> quotes (ppr ty)) 2 (text "Perhaps you intended to use KindSignatures") dataKindsErr :: RnTyKiEnv -> HsType GhcPs -> TcRnMessage dataKindsErr env thing- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Illegal" <+> pp_what <> colon <+> quotes (ppr thing)) 2 (text "Perhaps you intended to use DataKinds") where@@ -1666,7 +1680,7 @@ => HsDocContext -> LHsTyVarBndr flag GhcRn -> FreeVars -> TcM () warnUnusedForAll doc (L loc tv) used_names = unless (hsTyVarName tv `elemNameSet` used_names) $ do- let msg = TcRnUnknownMessage $+ let msg = mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag Opt_WarnUnusedForalls) noHints $ vcat [ text "Unused quantified type variable" <+> quotes (ppr tv) , inHsDocContext doc ]
compiler/GHC/Rename/Module.hs view
@@ -4,7 +4,6 @@ {-# LANGUAGE TypeFamilies #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}-{-# OPTIONS_GHC -Wno-incomplete-record-updates #-} {- (c) The GRASP/AQUA Project, Glasgow University, 1992-1998@@ -16,7 +15,7 @@ rnSrcDecls, addTcgDUs, findSplice, rnWarningTxt ) where -import GHC.Prelude+import GHC.Prelude hiding ( head ) import {-# SOURCE #-} GHC.Rename.Expr( rnLExpr ) import {-# SOURCE #-} GHC.Rename.Splice ( rnSpliceDecl, rnTopSpliceDecls )@@ -38,7 +37,7 @@ import GHC.Rename.Unbound ( mkUnboundName, notInScopeErr, WhereLooking(WL_Global) ) import GHC.Rename.Names import GHC.Tc.Errors.Types-import GHC.Tc.Errors.Ppr (withHsDocContext, pprScopeError )+import GHC.Tc.Errors.Ppr (pprScopeError) import GHC.Tc.Gen.Annotation ( annCtxt ) import GHC.Tc.Utils.Monad @@ -69,12 +68,14 @@ import GHC.Types.Unique.Set import GHC.Data.OrdList import qualified GHC.LanguageExtensions as LangExt+import GHC.Core.DataCon ( isSrcStrict ) import Control.Monad import Control.Arrow ( first )+import Data.Foldable ( toList ) import Data.List ( mapAccumL ) import qualified Data.List.NonEmpty as NE-import Data.List.NonEmpty ( NonEmpty(..) )+import Data.List.NonEmpty ( NonEmpty(..), head ) import Data.Maybe ( isNothing, fromMaybe, mapMaybe ) import qualified Data.Set as Set ( difference, fromList, toList, null ) import Data.Function ( on )@@ -319,12 +320,12 @@ -} rnAnnDecl :: AnnDecl GhcPs -> RnM (AnnDecl GhcRn, FreeVars)-rnAnnDecl ann@(HsAnnotation _ s provenance expr)+rnAnnDecl ann@(HsAnnotation (_, s) provenance expr) = addErrCtxt (annCtxt ann) $ do { (provenance', provenance_fvs) <- rnAnnProvenance provenance ; (expr', expr_fvs) <- setStage (Splice Untyped) $ rnLExpr expr- ; return (HsAnnotation noAnn s provenance' expr',+ ; return (HsAnnotation (noAnn, s) provenance' expr', provenance_fvs `plusFV` expr_fvs) } rnAnnProvenance :: AnnProvenance GhcPs@@ -381,7 +382,7 @@ ; (ty', fvs) <- rnHsSigType (ForeignDeclCtx name) TypeLevel ty ; return (ForeignExport { fd_e_ext = noExtField , fd_name = name', fd_sig_ty = ty'- , fd_fe = spec }+ , fd_fe = (\(CExport x c) -> CExport x c) spec } , fvs `addOneFV` unLoc name') } -- NB: a foreign export is an *occurrence site* for name, so -- we add it to the free-variable list. It might, for example,@@ -392,9 +393,9 @@ -- package, so if they get inlined across a package boundary we'll still -- know where they're from. ---patchForeignImport :: Unit -> ForeignImport -> ForeignImport-patchForeignImport unit (CImport cconv safety fs spec src)- = CImport cconv safety fs (patchCImportSpec unit spec) src+patchForeignImport :: Unit -> (ForeignImport GhcPs) -> (ForeignImport GhcRn)+patchForeignImport unit (CImport ext cconv safety fs spec)+ = CImport ext cconv safety fs (patchCImportSpec unit spec) patchCImportSpec :: Unit -> CImportSpec -> CImportSpec patchCImportSpec unit spec@@ -552,7 +553,7 @@ -- got "lhs = rhs" but expected something different addWarnNonCanonicalMethod1 refURL flag lhs rhs = do- let dia = TcRnUnknownMessage $+ let dia = mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag flag) noHints $ vcat [ text "Noncanonical" <+> quotes (text (lhs ++ " = " ++ rhs)) <+>@@ -568,7 +569,7 @@ -- expected "lhs = rhs" but got something else addWarnNonCanonicalMethod2 refURL flag lhs rhs = do- let dia = TcRnUnknownMessage $+ let dia = mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag flag) noHints $ vcat [ text "Noncanonical" <+> quotes (text lhs) <+>@@ -616,7 +617,8 @@ Just (L _ cls) -> Right cls Nothing -> Left ( getLocA head_ty'- , hang (text "Illegal head of an instance declaration:"+ , mkTcRnUnknownMessage $ mkPlainError noHints $+ hang (text "Illegal head of an instance declaration:" <+> quotes (ppr head_ty')) 2 (vcat [ text "Instance heads must be of the form" , nest 2 $ text "C ty_1 ... ty_n"@@ -679,7 +681,7 @@ -- reach the typechecker, lest we encounter different errors that are -- hopelessly confusing (such as the one in #16114). bail_out (l, err_msg) = do- addErrAt l $ TcRnUnknownMessage $ mkPlainError noHints (withHsDocContext ctxt err_msg)+ addErrAt l $ TcRnWithHsDocContext ctxt err_msg pure $ mkUnboundName (mkTcOccFS (fsLit "<class>")) rnFamEqn :: HsDocContext@@ -843,7 +845,7 @@ badAssocRhs :: [Name] -> RnM () badAssocRhs ns- = addErr $ TcRnUnknownMessage $ mkPlainError noHints $+ = addErr $ mkTcRnUnknownMessage $ mkPlainError noHints $ (hang (text "The RHS of an associated type declaration mentions" <+> text "out-of-scope variable" <> plural ns <+> pprWithCommas (quotes . ppr) ns)@@ -1206,7 +1208,7 @@ standaloneDerivErr :: TcRnMessage standaloneDerivErr- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Illegal standalone deriving declaration") 2 (text "Use StandaloneDeriving to enable this extension") @@ -1219,15 +1221,15 @@ -} rnHsRuleDecls :: RuleDecls GhcPs -> RnM (RuleDecls GhcRn, FreeVars)-rnHsRuleDecls (HsRules { rds_src = src+rnHsRuleDecls (HsRules { rds_ext = (_, src) , rds_rules = rules }) = do { (rn_rules,fvs) <- rnList rnHsRuleDecl rules- ; return (HsRules { rds_ext = noExtField- , rds_src = src+ ; return (HsRules { rds_ext = src , rds_rules = rn_rules }, fvs) } rnHsRuleDecl :: RuleDecl GhcPs -> RnM (RuleDecl GhcRn, FreeVars)-rnHsRuleDecl (HsRule { rd_name = rule_name+rnHsRuleDecl (HsRule { rd_ext = (_, st)+ , rd_name = rule_name , rd_act = act , rd_tyvs = tyvs , rd_tmvs = tmvs@@ -1238,13 +1240,13 @@ ; checkDupRdrNamesN rdr_names_w_loc ; checkShadowedRdrNames rdr_names_w_loc ; names <- newLocalBndrsRn rdr_names_w_loc- ; let doc = RuleCtx (snd $ unLoc rule_name)+ ; let doc = RuleCtx (unLoc rule_name) ; bindRuleTyVars doc tyvs $ \ tyvs' -> bindRuleTmVars doc tyvs' tmvs names $ \ tmvs' -> do { (lhs', fv_lhs') <- rnLExpr lhs ; (rhs', fv_rhs') <- rnLExpr rhs- ; checkValidRule (snd $ unLoc rule_name) names lhs' fv_lhs'- ; return (HsRule { rd_ext = HsRuleRn fv_lhs' fv_rhs'+ ; checkValidRule (unLoc rule_name) names lhs' fv_lhs'+ ; return (HsRule { rd_ext = (HsRuleRn fv_lhs' fv_rhs', st) , rd_name = rule_name , rd_act = act , rd_tyvs = tyvs'@@ -1299,7 +1301,7 @@ (e.g. a case expression is not allowed: too elaborate.) But there are legitimate non-trivial args ei, like sections and-lambdas. So it seems simmpler not to check at all, and that is why+lambdas. So it seems simpler not to check at all, and that is why check_e is commented out. -} @@ -1325,7 +1327,7 @@ check (OpApp _ e1 op e2) = checkl op `mplus` checkl_e e1 `mplus` checkl_e e2 check (HsApp _ e1 e2) = checkl e1 `mplus` checkl_e e2- check (HsAppType _ e _) = checkl e+ check (HsAppType _ e _ _) = checkl e check (HsVar _ lv) | (unLoc lv) `notElem` foralls = Nothing check other = Just other -- Failure@@ -1351,14 +1353,14 @@ badRuleVar :: FastString -> Name -> TcRnMessage badRuleVar name var- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ sep [text "Rule" <+> doubleQuotes (ftext name) <> colon, text "Forall'd variable" <+> quotes (ppr var) <+> text "does not appear on left hand side"] badRuleLhsErr :: FastString -> LHsExpr GhcRn -> HsExpr GhcRn -> TcRnMessage badRuleLhsErr name lhs bad_e- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ sep [text "Rule" <+> pprRuleName name <> colon, nest 2 (vcat [err, text "in left-hand side:" <+> ppr lhs])]@@ -1368,8 +1370,7 @@ err = case bad_e of HsUnboundVar _ uv ->- let rdr = mkRdrUnqual uv- in pprScopeError rdr $ notInScopeErr WL_Global (mkRdrUnqual uv)+ pprScopeError uv $ notInScopeErr WL_Global uv _ -> text "Illegal expression:" <+> ppr bad_e {- **************************************************************@@ -1396,7 +1397,7 @@ A TyClGroup represents a strongly connected components of type/class/instance decls, together with the role annotations for the type/class declarations. The renamer uses strongly connected-comoponent analysis to build these groups. We do this for a number of+component analysis to build these groups. We do this for a number of reasons: * Improve kind error messages. Consider@@ -1516,7 +1517,7 @@ -> RnM ([TyClGroup GhcRn], FreeVars) -- Rename the declarations and do dependency analysis on them rnTyClDecls tycl_ds- = do { -- Rename the type/class, instance, and role declaraations+ = do { -- Rename the type/class, instance, and role declarations ; tycls_w_fvs <- mapM (wrapLocFstMA rnTyClDecl) (tyClGroupTyClDecls tycl_ds) ; let tc_names = mkNameSet (map (tcdName . unLoc . fst) tycls_w_fvs) ; kisigs_w_fvs <- rnStandaloneKindSignatures tc_names (tyClGroupKindSigs tycl_ds)@@ -1623,7 +1624,7 @@ } where standaloneKiSigErr :: TcRnMessage- standaloneKiSigErr = TcRnUnknownMessage $ mkPlainError noHints $+ standaloneKiSigErr = mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Illegal standalone kind signature") 2 (text "Did you mean to enable StandaloneKindSignatures?") @@ -1696,7 +1697,7 @@ dupRoleAnnotErr :: NonEmpty (LRoleAnnotDecl GhcPs) -> RnM () dupRoleAnnotErr list- = addErrAt (locA loc) $ TcRnUnknownMessage $ mkPlainError noHints $+ = addErrAt (locA loc) $ mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Duplicate role annotations for" <+> quotes (ppr $ roleAnnotDeclName first_decl) <> colon) 2 (vcat $ map pp_role_annot $ NE.toList sorted_list)@@ -1711,7 +1712,7 @@ dupKindSig_Err :: NonEmpty (LStandaloneKindSig GhcPs) -> RnM () dupKindSig_Err list- = addErrAt (locA loc) $ TcRnUnknownMessage $ mkPlainError noHints $+ = addErrAt (locA loc) $ mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Duplicate standalone kind signatures for" <+> quotes (ppr $ standaloneKindSigName first_decl) <> colon) 2 (vcat $ map pp_kisig $ NE.toList sorted_list)@@ -1819,11 +1820,11 @@ rnTyClDecl (DataDecl { tcdLName = tycon, tcdTyVars = tyvars, tcdFixity = fixity,- tcdDataDefn = defn@HsDataDefn{ dd_ND = new_or_data- , dd_kindSig = kind_sig} })+ tcdDataDefn = defn@HsDataDefn{ dd_cons = cons, dd_kindSig = kind_sig} }) = do { tycon' <- lookupLocatedTopConstructorRnN tycon ; let kvs = extractDataDefnKindVars defn doc = TyDataCtx tycon+ new_or_data = dataDefnConsNewOrData cons ; traceRn "rntycl-data" (ppr tycon <+> ppr kvs) ; bindHsQTyVars doc Nothing kvs tyvars $ \ tyvars' no_rhs_kvs -> do { (defn', fvs) <- rnDataDefn doc defn@@ -1837,7 +1838,8 @@ , tcdDataDefn = defn' , tcdDExt = rn_info }, fvs) } } -rnTyClDecl (ClassDecl { tcdCtxt = context, tcdLName = lcls,+rnTyClDecl (ClassDecl { tcdLayout = layout,+ tcdCtxt = context, tcdLName = lcls, tcdTyVars = tyvars, tcdFixity = fixity, tcdFDs = fds, tcdSigs = sigs, tcdMeths = mbinds, tcdATs = ats, tcdATDefs = at_defs,@@ -1891,7 +1893,8 @@ ; let all_fvs = meth_fvs `plusFV` stuff_fvs `plusFV` fv_at_defs ; docs' <- traverse rnLDocDecl docs- ; return (ClassDecl { tcdCtxt = context', tcdLName = lcls',+ ; return (ClassDecl { tcdLayout = rnLayoutInfo layout,+ tcdCtxt = context', tcdLName = lcls', tcdTyVars = tyvars', tcdFixity = fixity, tcdFDs = fds', tcdSigs = sigs', tcdMeths = mbinds', tcdATs = ats', tcdATDefs = at_defs',@@ -1900,6 +1903,11 @@ where cls_doc = ClassDeclCtx lcls +rnLayoutInfo :: LayoutInfo GhcPs -> LayoutInfo GhcRn+rnLayoutInfo (ExplicitBraces ob cb) = ExplicitBraces ob cb+rnLayoutInfo (VirtualBraces n) = VirtualBraces n+rnLayoutInfo NoLayoutInfo = NoLayoutInfo+ -- Does the data type declaration include a CUSK? data_decl_has_cusk :: LHsQTyVars (GhcPass p) -> NewOrData -> Bool -> Maybe (LHsKind (GhcPass p')) -> RnM Bool data_decl_has_cusk tyvars new_or_data no_rhs_kvs kind_sig = do@@ -1940,14 +1948,17 @@ rnDataDefn :: HsDocContext -> HsDataDefn GhcPs -> RnM (HsDataDefn GhcRn, FreeVars)-rnDataDefn doc (HsDataDefn { dd_ND = new_or_data, dd_cType = cType- , dd_ctxt = context, dd_cons = condecls+rnDataDefn doc (HsDataDefn { dd_cType = cType, dd_ctxt = context, dd_cons = condecls , dd_kindSig = m_sig, dd_derivs = derivs }) = do { -- DatatypeContexts (i.e., stupid contexts) can't be combined with -- GADT syntax. See Note [The stupid context] in GHC.Core.DataCon. checkTc (h98_style || null (fromMaybeContext context)) (badGadtStupidTheta doc) + -- Check restrictions on "type data" declarations.+ -- See Note [Type data declarations].+ ; when (isTypeDataDefnCons condecls) check_type_data+ ; (m_sig', sig_fvs) <- case m_sig of Just sig -> first Just <$> rnLHsKind doc sig Nothing -> return (Nothing, emptyFVs)@@ -1966,17 +1977,14 @@ ; let all_fvs = fvs1 `plusFV` fvs3 `plusFV` con_fvs `plusFV` sig_fvs- ; return ( HsDataDefn { dd_ext = noExtField- , dd_ND = new_or_data, dd_cType = cType+ ; return ( HsDataDefn { dd_ext = noExtField, dd_cType = cType , dd_ctxt = context', dd_kindSig = m_sig' , dd_cons = condecls' , dd_derivs = derivs' } , all_fvs ) } where- h98_style = case condecls of -- Note [Stupid theta]- (L _ (ConDeclGADT {})) : _ -> False- _ -> True+ h98_style = not $ anyLConIsGadt condecls -- Note [Stupid theta] rn_derivs ds = do { deriv_strats_ok <- xoptM LangExt.DerivingStrategies@@ -1985,6 +1993,202 @@ ; (ds', fvs) <- mapFvRn (rnLHsDerivingClause doc) ds ; return (ds', fvs) } + -- Given a "type data" declaration, check that the TypeData extension+ -- is enabled and check restrictions (R1), (R2), (R3) and (R5)+ -- on the declaration. See Note [Type data declarations].+ check_type_data+ = do { unlessXOptM LangExt.TypeData $ failWith TcRnIllegalTypeData+ ; unless (null (fromMaybeContext context)) $+ failWith $ TcRnTypeDataForbids TypeDataForbidsDatatypeContexts+ ; mapM_ (addLocMA check_type_data_condecl) condecls+ ; unless (null derivs) $+ failWith $ TcRnTypeDataForbids TypeDataForbidsDerivingClauses+ }++ -- Check restrictions (R2) and (R3) on a "type data" constructor.+ -- See Note [Type data declarations].+ check_type_data_condecl :: ConDecl GhcPs -> RnM ()+ check_type_data_condecl condecl+ = do {+ ; when (has_labelled_fields condecl) $+ failWith $ TcRnTypeDataForbids TypeDataForbidsLabelledFields+ ; when (has_strictness_flags condecl) $+ failWith $ TcRnTypeDataForbids TypeDataForbidsStrictnessAnnotations+ }++ has_labelled_fields (ConDeclGADT { con_g_args = RecConGADT _ _ }) = True+ has_labelled_fields (ConDeclH98 { con_args = RecCon rec })+ = not (null (unLoc rec))+ has_labelled_fields _ = False++ has_strictness_flags condecl+ = any (is_strict . getBangStrictness . hsScaledThing) (con_args condecl)++ is_strict (HsSrcBang _ _ s) = isSrcStrict s++ con_args (ConDeclGADT { con_g_args = PrefixConGADT args }) = args+ con_args (ConDeclH98 { con_args = PrefixCon _ args }) = args+ con_args (ConDeclH98 { con_args = InfixCon arg1 arg2 }) = [arg1, arg2]+ con_args _ = []++{-+Note [Type data declarations]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+With the TypeData extension (GHC proposal #106), one can write `type data`+declarations, like++ type data Nat = Zero | Succ Nat++or equivalently in GADT style:++ type data Nat where+ Zero :: Nat+ Succ :: Nat -> Nat++This defines the constructors `Zero` and `Succ` in the TcCls namespace+(type constructors and classes) instead of the Data namespace (data+constructors). This contrasts with the DataKinds extension, which+allows constructors defined in the Data namespace to be promoted to the+TcCls namespace at the point of use in a type.++Type data declarations have the syntax of `data` declarations (but not+`newtype` declarations), either ordinary algebraic data types or GADTs,+preceded by `type`, with the following restrictions:++(R0) 'data' decls only, not 'newtype' decls. This is checked by+ the parser.++(R1) There are no data type contexts (even with the DatatypeContexts+ extension).++(R2) There are no labelled fields. Perhaps these could be supported+ using type families, but they are omitted for now.++(R3) There are no strictness flags, because they don't make sense at+ the type level.++(R4) The types of the constructors contain no constraints other than+ equality constraints. (This is the same restriction imposed+ on constructors to be promoted with the DataKinds extension in+ dc_theta_illegal_constraint called from GHC.Tc.Gen.HsType.tcTyVar,+ but in that case the restriction is imposed if and when a data+ constructor is used in a type, whereas here it is imposed at+ the point of definition. See also Note [Constraints in kinds]+ in GHC.Core.TyCo.Rep.)++(R5) There are no deriving clauses.++The main parts of the implementation are:++* (R0): The parser recognizes `type data` (but not `type newtype`).++* During the initial construction of the AST,+ GHC.Parser.PostProcess.checkNewOrData sets the `Bool` argument of the+ `DataTypeCons` inside a `HsDataDefn` to mark a `type data` declaration.+ It also puts the the constructor names (`Zero` and `Succ` in our+ example) in the TcCls namespace.++* GHC.Rename.Module.rnDataDefn calls `check_type_data` on these+ declarations, which checks that the TypeData extension is enabled and+ checks restrictions (R1), (R2), (R3) and (R5). They could equally+ well be checked in the typechecker, but we err on the side of catching+ imposters early.++* GHC.Tc.TyCl.checkValidDataCon checks restriction (R4) on these declarations.++* When beginning to type check a mutually recursive group of declarations,+ the `type data` constructors (`Zero` and `Succ` in our example) are+ added to the type-checker environment as `APromotionErr TyConPE` by+ GHC.Tc.TyCl.mkPromotionErrorEnv, so they cannot be used within the+ recursive group. This mirrors the DataKinds behaviour described+ at Note [Recursion and promoting data constructors] in GHC.Tc.TyCl.+ For example, this is rejected:++ type data T f = K (f (K Int)) -- illegal: tycon K is recursively defined++* The `type data` data type, such as `Nat` in our example, is represented+ by a `TyCon` that is an `AlgTyCon`, but its `AlgTyConRhs` has the+ `is_type_data` field set.++* The constructors of the data type, `Zero` and `Succ` in our example,+ are each represented by a `DataCon` as usual. That `DataCon`'s+ `dcPromotedField` is a `TyCon` (for `Zero`, say) that you can use+ in a type.++* After a `type data` declaration has been type-checked, the+ type-checker environment entry (a `TyThing`) for each constructor+ (`Zero` and `Succ` in our example) is+ - just an `ATyCon` for the promoted type constructor,+ - not the bundle (`ADataCon` for the data con, `AnId` for the work id,+ wrap id) required for a normal data constructor+ See GHC.Types.TyThing.implicitTyConThings.++* GHC.Core.TyCon.isDataKindsPromotedDataCon ignores promoted constructors+ from `type data`, which do not use the distinguishing quote mark added+ to constructors promoted by DataKinds.++* GHC.Core.TyCon.isDataTyCon ignores types coming from a `type data`+ declaration (by checking the `is_type_data` field), so that these do+ not contribute executable code such as constructor wrappers.++* The `is_type_data` field is copied into a Boolean argument+ of the `IfDataTyCon` constructor of `IfaceConDecls` by+ GHC.Iface.Make.tyConToIfaceDecl.++* The Template Haskell `Dec` type has an constructor `TypeDataD` for+ `type data` declarations. When these are converted back to Hs types+ in a splice, the constructors are placed in the TcCls namespace.++* A `type data` declaration _never_ generates wrappers for its data+ constructors, as they only make sense for value-level data constructors.+ See `wrapped_reqd` in GHC.Types.Id.Make.mkDataConRep` for the place where+ this check is implemented.++ This includes `type data` declarations implemented as GADTs, such as+ this example from #22948:++ type data T a where+ A :: T Int+ B :: T a++ If `T` were an ordinary `data` declaration, then `A` would have a wrapper+ to account for the GADT-like equality in its return type. Because `T` is+ declared as a `type data` declaration, however, the wrapper is omitted.++* Although `type data` data constructors do not exist at the value level,+ it is still possible to match on a value whose type is headed by a `type data`+ type constructor, such as this example from #22964:++ type data T a where+ A :: T Int+ B :: T a++ f :: T a -> ()+ f x = case x of {}++ This has two consequences:++ * During checking the coverage of `f`'s pattern matches, we treat `T` as if it+ were an empty data type so that GHC does not warn the user to match against+ `A` or `B`. (Otherwise, you end up with the bug reported in #22964.)+ See GHC.HsToCore.Pmc.Solver.vanillaCompleteMatchTC.++ * In `GHC.Core.Utils.refineDataAlt`, do /not/ fill in the DEFAULT case with+ the data constructor. See+ Note [Refine DEFAULT case alternatives] Exception 2, in GHC.Core.Utils.++* To prevent users from conjuring up `type data` values at the term level, we+ disallow using the tagToEnum# function on a type headed by a `type data`+ type. For instance, GHC will reject this code:++ type data Letter = A | B | C++ f :: Letter+ f = tagToEnum# 0#++ See `GHC.Tc.Gen.App.checkTagToEnum`, specifically `check_enumeration`.+-}+ warnNoDerivStrat :: Maybe (LDerivStrategy GhcRn) -> SrcSpan -> RnM ()@@ -1992,7 +2196,7 @@ = do { dyn_flags <- getDynFlags ; case mds of Nothing ->- let dia = TcRnUnknownMessage $+ let dia = mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag Opt_WarnMissingDerivingStrategies) noHints $ (if xopt LangExt.DerivingStrategies dyn_flags then no_strat_warning@@ -2100,13 +2304,13 @@ badGadtStupidTheta :: HsDocContext -> TcRnMessage badGadtStupidTheta _- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [text "No context is allowed on a GADT-style data declaration", text "(You can put a context on each constructor, though.)"] illegalDerivStrategyErr :: DerivStrategy GhcPs -> TcRnMessage illegalDerivStrategyErr ds- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ text "Illegal deriving strategy" <> colon <+> derivStrategyName ds , text enableStrategy ] @@ -2120,7 +2324,7 @@ multipleDerivClausesErr :: TcRnMessage multipleDerivClausesErr- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ text "Illegal use of multiple, consecutive deriving clauses" , text "Use DerivingStrategies to allow this" ] @@ -2186,7 +2390,7 @@ rdr_env <- getLocalRdrEnv ; let resName = hsLTyVarName tvbndr ; when (resName `elemLocalRdrEnv` rdr_env) $- addErrAt (getLocA tvbndr) $ TcRnUnknownMessage $ mkPlainError noHints $+ addErrAt (getLocA tvbndr) $ mkTcRnUnknownMessage $ mkPlainError noHints $ (hsep [ text "Type variable", quotes (ppr resName) <> comma , text "naming a type family result," ] $$@@ -2260,7 +2464,7 @@ -- not-in-scope variables) don't check the validity of injectivity -- annotation. This gives better error messages. ; when (noRnErrors && not lhsValid) $- addErrAt (getLocA injFrom) $ TcRnUnknownMessage $ mkPlainError noHints $+ addErrAt (getLocA injFrom) $ mkTcRnUnknownMessage $ mkPlainError noHints $ ( vcat [ text $ "Incorrect type variable on the LHS of " ++ "injectivity condition" , nest 5@@ -2269,7 +2473,7 @@ ; when (noRnErrors && not (Set.null rhsValid)) $ do { let errorVars = Set.toList rhsValid- ; addErrAt (locA srcSpan) $ TcRnUnknownMessage $ mkPlainError noHints $+ ; addErrAt (locA srcSpan) $ mkTcRnUnknownMessage $ mkPlainError noHints $ ( hsep [ text "Unknown type variable" <> plural errorVars , text "on the RHS of injectivity condition:"@@ -2312,7 +2516,7 @@ ***************************************************** -} ------------------rnConDecls :: [LConDecl GhcPs] -> RnM ([LConDecl GhcRn], FreeVars)+rnConDecls :: DataDefnCons (LConDecl GhcPs) -> RnM (DataDefnCons (LConDecl GhcRn), FreeVars) rnConDecls = mapFvRn (wrapLocFstMA rnConDecl) rnConDecl :: ConDecl GhcPs -> RnM (ConDecl GhcRn, FreeVars)@@ -2350,6 +2554,7 @@ all_fvs) }} rnConDecl (ConDeclGADT { con_names = names+ , con_dcolon = dcol , con_bndrs = L l outer_bndrs , con_mb_cxt = mcxt , con_g_args = args@@ -2369,7 +2574,7 @@ extractConDeclGADTDetailsTyVars args $ extractHsTysRdrTyVars [res_ty] [] - ; let ctxt = ConDeclCtx new_names+ ; let ctxt = ConDeclCtx (toList new_names) ; bindHsOuterTyVarBndrs ctxt Nothing implicit_bndrs outer_bndrs $ \outer_bndrs' -> do { (new_cxt, fvs1) <- rnMbContext ctxt mcxt@@ -2388,6 +2593,7 @@ (ppr names $$ ppr outer_bndrs') ; new_mb_doc <- traverse rnLHsDoc mb_doc ; return (ConDeclGADT { con_g_ext = noAnn, con_names = new_names+ , con_dcolon = dcol , con_bndrs = L l outer_bndrs', con_mb_cxt = new_cxt , con_g_args = new_args, con_res_ty = new_res_ty , con_doc = new_mb_doc },@@ -2543,15 +2749,15 @@ = do { -- We've found a top-level splice. If it is an *implicit* one -- (i.e. a naked top level expression) case flag of- ExplicitSplice -> return ()- ImplicitSplice -> do { th_on <- xoptM LangExt.TemplateHaskell+ DollarSplice -> return ()+ BareSplice -> do { th_on <- xoptM LangExt.TemplateHaskell ; unless th_on $ setSrcSpan (locA loc) $ failWith badImplicitSplice } ; return (gp, Just (splice, ds)) } where badImplicitSplice :: TcRnMessage- badImplicitSplice = TcRnUnknownMessage $ mkPlainError noHints $+ badImplicitSplice = mkTcRnUnknownMessage $ mkPlainError noHints $ text "Parse error: module header, import declaration" $$ text "or top-level declaration expected." -- The compiler should suggest the above, and not using
compiler/GHC/Rename/Names.hs view
@@ -11,9 +11,6 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE LambdaCase #-} -{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}-{-# OPTIONS_GHC -Wno-incomplete-record-updates #-}- module GHC.Rename.Names ( rnImports, getLocalNonValBinders, newRecordSelector, extendGlobalRdrEnvRn,@@ -30,7 +27,7 @@ ImportDeclUsage ) where -import GHC.Prelude+import GHC.Prelude hiding ( head, init, last, tail ) import GHC.Driver.Env import GHC.Driver.Session@@ -56,7 +53,6 @@ import GHC.Utils.Outputable as Outputable import GHC.Utils.Misc as Utils import GHC.Utils.Panic-import GHC.Utils.Trace import GHC.Types.Fixity.Env import GHC.Types.SafeHaskell@@ -86,14 +82,19 @@ import GHC.Data.FastString import GHC.Data.FastString.Env +import Language.Haskell.Syntax.Basic (FieldLabelString(..))+ import Control.Monad import Data.Either ( partitionEithers ) import Data.Map ( Map ) import qualified Data.Map as Map import Data.Ord ( comparing )-import Data.List ( partition, (\\), find, sortBy, groupBy, sortOn )+import Data.List ( partition, (\\), find, sortBy )+import Data.List.NonEmpty (NonEmpty(..))+import qualified Data.List.NonEmpty as NE import Data.Function ( on ) import qualified Data.Set as S+import Data.Foldable ( toList ) import System.FilePath ((</>)) import System.IO@@ -114,7 +115,7 @@ and packages. Doing this without caching any trust information would be very slow as we would need to touch all packages and interface files a module depends on. To avoid this we make use of the property that if a modules Safe Haskell-mode changes, this triggers a recompilation from that module in the dependecy+mode changes, this triggers a recompilation from that module in the dependency graph. So we can just worry mostly about direct imports. There is one trust property that can change for a package though without@@ -308,8 +309,9 @@ (L loc decl@(ImportDecl { ideclName = loc_imp_mod_name , ideclPkgQual = raw_pkg_qual , ideclSource = want_boot, ideclSafe = mod_safe- , ideclQualified = qual_style, ideclImplicit = implicit- , ideclAs = as_mod, ideclHiding = imp_details }), import_reason)+ , ideclQualified = qual_style+ , ideclExt = XImportDeclPass { ideclImplicit = implicit }+ , ideclAs = as_mod, ideclImportList = imp_details }), import_reason) = setSrcSpanA loc $ do case raw_pkg_qual of@@ -349,17 +351,17 @@ NoPkgQual -> True ThisPkg uid -> uid == homeUnitId_ (hsc_dflags hsc_env) OtherPkg _ -> False))- (addErr $ TcRnUnknownMessage $ mkPlainError noHints $+ (addErr $ mkTcRnUnknownMessage $ mkPlainError noHints $ (text "A module cannot import itself:" <+> ppr imp_mod_name)) -- Check for a missing import list (Opt_WarnMissingImportList also -- checks for T(..) items but that is done in checkDodgyImport below) case imp_details of- Just (False, _) -> return () -- Explicit import list+ Just (Exactly, _) -> return () -- Explicit import list _ | implicit -> return () -- Do not bleat for implicit imports | qual_only -> return () | otherwise -> whenWOptM Opt_WarnMissingImportList $ do- let msg = TcRnUnknownMessage $+ let msg = mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag Opt_WarnMissingImportList) noHints (missingImportListWarn imp_mod_name)@@ -384,7 +386,7 @@ warnIf ((want_boot == IsBoot) && (mi_boot iface == NotBoot) && isOneShot (ghcMode dflags)) (warnRedundantSourceImport imp_mod_name) when (mod_safe && not (safeImportsOn dflags)) $- addErr $ TcRnUnknownMessage $ mkPlainError noHints $+ addErr $ mkTcRnUnknownMessage $ mkPlainError noHints $ (text "safe import can't be used as Safe Haskell isn't on!" $+$ text ("please enable Safe Haskell through either Safe, Trustworthy or Unsafe")) @@ -402,8 +404,8 @@ let gbl_env = mkGlobalRdrEnv gres - is_hiding | Just (True,_) <- imp_details = True- | otherwise = False+ is_hiding | Just (EverythingBut,_) <- imp_details = True+ | otherwise = False -- should the import be safe? mod_safe' = mod_safe@@ -426,7 +428,7 @@ -- Complain if we import a deprecated module case mi_warns iface of WarnAll txt -> do- let msg = TcRnUnknownMessage $+ let msg = mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag Opt_WarnWarningsDeprecations) noHints (moduleWarn imp_mod_name txt)@@ -437,16 +439,14 @@ warnUnqualifiedImport decl iface let new_imp_decl = ImportDecl- { ideclExt = noExtField- , ideclSourceSrc = ideclSourceSrc decl+ { ideclExt = ideclExt decl , ideclName = ideclName decl , ideclPkgQual = pkg_qual , ideclSource = ideclSource decl , ideclSafe = mod_safe' , ideclQualified = ideclQualified decl- , ideclImplicit = ideclImplicit decl , ideclAs = ideclAs decl- , ideclHiding = new_imp_details+ , ideclImportList = new_imp_details } return (L loc new_imp_decl, gbl_env, imports, mi_hpc iface)@@ -609,7 +609,7 @@ warnUnqualifiedImport :: ImportDecl GhcPs -> ModIface -> RnM () warnUnqualifiedImport decl iface = when bad_import $ do- let msg = TcRnUnknownMessage $+ let msg = mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag Opt_WarnCompatUnqualifiedImports) noHints warning@@ -622,8 +622,8 @@ has_import_list = -- We treat a `hiding` clause as not having an import list although -- it's not entirely clear this is the right choice.- case ideclHiding decl of- Just (False, _) -> True+ case ideclImportList decl of+ Just (Exactly, _) -> True _ -> False bad_import = not is_qual@@ -642,7 +642,7 @@ warnRedundantSourceImport :: ModuleName -> TcRnMessage warnRedundantSourceImport mod_name- = TcRnUnknownMessage $ mkPlainDiagnostic WarningWithoutFlag noHints $+ = mkTcRnUnknownMessage $ mkPlainDiagnostic WarningWithoutFlag noHints $ text "Unnecessary {-# SOURCE #-} in the import of module" <+> quotes (ppr mod_name) {-@@ -751,7 +751,7 @@ -- This establishes INVARIANT 1 of GlobalRdrEnvs add_gre env gre | not (null dups) -- Same OccName defined twice- = do { addDupDeclErr (gre : dups); return env }+ = do { addDupDeclErr (gre :| dups); return env } | otherwise = return (extendGlobalRdrEnv env gre)@@ -916,8 +916,15 @@ -- foreign decls and pattern synonyms for an ordinary module -- type sigs in case of a hs-boot file only ; is_boot <- tcIsHsBootOrSig- ; let val_bndrs | is_boot = hs_boot_sig_bndrs- | otherwise = for_hs_bndrs+ ; let val_bndrs+ | is_boot = case binds of+ ValBinds _ _val_binds val_sigs ->+ -- In a hs-boot file, the value binders come from the+ -- *signatures*, and there should be no foreign binders+ [ L (l2l decl_loc) (unLoc n)+ | L decl_loc (TypeSig _ ns _) <- val_sigs, n <- ns]+ _ -> panic "Non-ValBinds in hs-boot group"+ | otherwise = for_hs_bndrs ; val_avails <- mapM new_simple val_bndrs ; let avails = concat nti_availss ++ val_avails@@ -938,16 +945,9 @@ ; traceRn "getLocalNonValBinders 3" (vcat [ppr flds, ppr field_env]) ; return (envs, new_bndrs) } } where- ValBinds _ _val_binds val_sigs = binds- for_hs_bndrs :: [LocatedN RdrName] for_hs_bndrs = hsForeignDeclsBinders foreign_decls - -- In a hs-boot file, the value binders come from the- -- *signatures*, and there should be no foreign binders- hs_boot_sig_bndrs = [ L (l2l decl_loc) (unLoc n)- | L decl_loc (TypeSig _ ns _) <- val_sigs, n <- ns]- -- the SrcSpan attached to the input should be the span of the -- declaration, not just the name new_simple :: LocatedN RdrName -> RnM AvailInfo@@ -981,7 +981,7 @@ , con_g_args = RecConGADT flds _ })) = [ ( find_con_name rdr , concatMap find_con_decl_flds (unLoc flds))- | L _ rdr <- rdrs ]+ | L _ rdr <- toList rdrs ] find_con_flds _ = [] @@ -994,7 +994,7 @@ find_con_decl_fld (L _ (FieldOcc _ (L _ rdr))) = expectJust "getLocalNonValBinders/find_con_decl_fld" $ find (\ fl -> flLabel fl == lbl) flds- where lbl = occNameFS (rdrNameOcc rdr)+ where lbl = FieldLabelString $ occNameFS (rdrNameOcc rdr) new_assoc :: DuplicateRecordFields -> FieldSelectors -> LInstDecl GhcPs -> RnM ([AvailInfo], [(Name, [FieldLabel])])@@ -1056,7 +1056,7 @@ , flHasFieldSelector = has_sel , flSelector = selName } } where- fieldLabelString = occNameFS $ rdrNameOcc fld+ fieldLabelString = FieldLabelString $ occNameFS $ rdrNameOcc fld selOccName = fieldSelectorOccName fieldLabelString (nameOccName dc) dup_fields_ok has_sel field | isExact fld = fld -- use an Exact RdrName as is to preserve the bindings@@ -1164,7 +1164,7 @@ data T = mkT { foo :: Int } module N where- import M (foo) -- this is an ambiguity error (A)+ import M (foo) -- this is allowed (A) import M (S(foo)) -- this is allowed (B) Here M exports the OccName 'foo' twice, so we get an imp_occ_env where 'foo'@@ -1175,8 +1175,8 @@ , $sel:foo:MKT -> (foo, T(foo), Nothing) ] -Then when we look up 'foo' in lookup_name for case (A) we get both entries and-hence report an ambiguity error. Whereas in case (B) we reach the lookup_ie+Then when we look up 'foo' in lookup_names for case (A) we get both entries and+hence two Avails. Whereas in case (B) we reach the lookup_ie case for IEThingWith, which looks up 'S' and then finds the unique 'foo' amongst its children. @@ -1187,8 +1187,8 @@ filterImports :: ModIface -> ImpDeclSpec -- The span for the entire import decl- -> Maybe (Bool, LocatedL [LIE GhcPs]) -- Import spec; True => hiding- -> RnM (Maybe (Bool, LocatedL [LIE GhcRn]), -- Import spec w/ Names+ -> Maybe (ImportListInterpretation, LocatedL [LIE GhcPs]) -- Import spec; True => hiding+ -> RnM (Maybe (ImportListInterpretation, LocatedL [LIE GhcRn]), -- Import spec w/ Names [GlobalRdrElt]) -- Same again, but in GRE form filterImports iface decl_spec Nothing = return (Nothing, gresFromAvails (Just imp_spec) (mi_exports iface))@@ -1210,8 +1210,8 @@ pruned_avails = filterAvails keep all_avails hiding_spec = ImpSpec { is_decl = decl_spec, is_item = ImpAll } - gres | want_hiding = gresFromAvails (Just hiding_spec) pruned_avails- | otherwise = concatMap (gresFromIE decl_spec) items2+ gres | want_hiding == EverythingBut = gresFromAvails (Just hiding_spec) pruned_avails+ | otherwise = concatMap (gresFromIE decl_spec) items2 return (Just (want_hiding, L l (map fst items2)), gres) where@@ -1251,13 +1251,21 @@ isAvailTC AvailTC{} = True isAvailTC _ = False + -- Look up a RdrName used in an import, failing if it is ambiguous+ -- (e.g. because it refers to multiple record fields) lookup_name :: IE GhcPs -> RdrName -> IELookupM (Name, AvailInfo, Maybe Name)- lookup_name ie rdr+ lookup_name ie rdr = do+ xs <- lookup_names ie rdr+ case xs of+ [cax] -> return cax+ _ -> failLookupWith (AmbiguousImport rdr (map sndOf3 xs))++ -- Look up a RdrName used in an import, returning multiple values if there+ -- are several fields with the same name exposed by the module+ lookup_names :: IE GhcPs -> RdrName -> IELookupM [(Name, AvailInfo, Maybe Name)]+ lookup_names ie rdr | isQual rdr = failLookupWith (QualImportError rdr)- | Just succ <- mb_success = case nonDetNameEnvElts succ of- -- See Note [Importing DuplicateRecordFields]- [(c,a,x)] -> return (greNameMangledName c, a, x)- xs -> failLookupWith (AmbiguousImport rdr (map sndOf3 xs))+ | Just succ <- mb_success = return $ map (\ (c,a,x) -> (greNameMangledName c, a, x)) (nonDetNameEnvElts succ) | otherwise = failLookupWith (BadImport ie) where mb_success = lookupOccEnv imp_occ_env (rdrNameOcc rdr)@@ -1276,7 +1284,7 @@ emit_warning MissingImportList = whenWOptM Opt_WarnMissingImportList $ addTcRnDiagnostic (TcRnMissingImportList ieRdr) emit_warning (BadImportW ie) = whenWOptM Opt_WarnDodgyImports $ do- let msg = TcRnUnknownMessage $+ let msg = mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag Opt_WarnDodgyImports) noHints (lookup_err_msg (BadImport ie))@@ -1285,7 +1293,7 @@ run_lookup :: IELookupM a -> TcRn (Maybe a) run_lookup m = case m of Failed err -> do- addErr $ TcRnUnknownMessage $ mkPlainError noHints (lookup_err_msg err)+ addErr $ mkTcRnUnknownMessage $ mkPlainError noHints (lookup_err_msg err) return Nothing Succeeded a -> return (Just a) @@ -1310,9 +1318,11 @@ lookup_ie ie = handle_bad_import $ case ie of IEVar _ (L l n) -> do- (name, avail, _) <- lookup_name ie $ ieWrappedName n+ -- See Note [Importing DuplicateRecordFields]+ xs <- lookup_names ie (ieWrappedName n) return ([(IEVar noExtField (L l (replaceWrappedName n name)),- trimAvail avail name)], [])+ trimAvail avail name)+ | (name, avail, _) <- xs ], []) IEThingAll _ (L l tc) -> do (name, avail, mb_parent) <- lookup_name ie $ ieWrappedName tc@@ -1341,7 +1351,7 @@ -- associated type IEThingAbs _ (L l tc')- | want_hiding -- hiding ( C )+ | want_hiding == EverythingBut -- hiding ( C ) -- Here the 'C' can be a data constructor -- *or* a type/class, or even both -> let tc = ieWrappedName tc'@@ -1403,8 +1413,8 @@ , availTC parent [n] []) handle_bad_import m = catchIELookup m $ \err -> case err of- BadImport ie | want_hiding -> return ([], [BadImportW ie])- _ -> failLookupWith err+ BadImport ie | want_hiding == EverythingBut -> return ([], [BadImportW ie])+ _ -> failLookupWith err type IELookupM = MaybeErr IELookupError @@ -1480,8 +1490,8 @@ findChildren :: NameEnv [a] -> Name -> [a] findChildren env n = lookupNameEnv env n `orElse` [] -lookupChildren :: [GreName] -> [LIEWrappedName RdrName]- -> MaybeErr [LIEWrappedName RdrName] -- The ones for which the lookup failed+lookupChildren :: [GreName] -> [LIEWrappedName GhcPs]+ -> MaybeErr [LIEWrappedName GhcPs] -- The ones for which the lookup failed ([LocatedA Name], [Located FieldLabel]) -- (lookupChildren all_kids rdr_items) maps each rdr_item to its -- corresponding Name all_kids, if the former exists@@ -1699,7 +1709,7 @@ warnUnusedImportDecls gbl_env hsc_src = do { uses <- readMutVar (tcg_used_gres gbl_env) ; let user_imports = filterOut- (ideclImplicit . unLoc)+ (ideclImplicit . ideclExt . unLoc) (tcg_rn_imports gbl_env) -- This whole function deals only with *user* imports -- both for warning about unnecessary ones, and for@@ -1731,7 +1741,7 @@ import_usage = mkImportMap used_gres unused_decl :: LImportDecl GhcRn -> (LImportDecl GhcRn, [GlobalRdrElt], [Name])- unused_decl decl@(L loc (ImportDecl { ideclHiding = imps }))+ unused_decl decl@(L loc (ImportDecl { ideclImportList = imps })) = (decl, used_gres, nameSetElemsStable unused_imps) where used_gres = lookupSrcLoc (srcSpanEnd $ locA loc) import_usage@@ -1743,7 +1753,7 @@ unused_imps -- Not trivial; see eg #7454 = case imps of- Just (False, L _ imp_ies) ->+ Just (Exactly, L _ imp_ies) -> foldr (add_unused . unLoc) emptyNameSet imp_ies _other -> emptyNameSet -- No explicit import list => no unused-name list @@ -1822,18 +1832,18 @@ warnUnusedImport flag fld_env (L loc decl, used, unused) -- Do not warn for 'import M()'- | Just (False,L _ []) <- ideclHiding decl+ | Just (Exactly, L _ []) <- ideclImportList decl = return () -- Note [Do not warn about Prelude hiding]- | Just (True, L _ hides) <- ideclHiding decl+ | Just (EverythingBut, L _ hides) <- ideclImportList decl , not (null hides) , pRELUDE_NAME == unLoc (ideclName decl) = return () -- Nothing used; drop entire declaration | null used- = let dia = TcRnUnknownMessage $+ = let dia = mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag flag) noHints msg1 in addDiagnosticAt (locA loc) dia @@ -1843,15 +1853,15 @@ -- Only one import is unused, with `SrcSpan` covering only the unused item instead of -- the whole import statement- | Just (_, L _ imports) <- ideclHiding decl+ | Just (_, L _ imports) <- ideclImportList decl , length unused == 1 , Just (L loc _) <- find (\(L _ ie) -> ((ieName ie) :: Name) `elem` unused) imports- = let dia = TcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag flag) noHints msg2+ = let dia = mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag flag) noHints msg2 in addDiagnosticAt (locA loc) dia -- Some imports are unused | otherwise- = let dia = TcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag flag) noHints msg2+ = let dia = mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag flag) noHints msg2 in addDiagnosticAt (locA loc) dia where@@ -1910,7 +1920,7 @@ where mk_minimal (L l decl, used_gres, unused) | null unused- , Just (False, _) <- ideclHiding decl+ , Just (Exactly, _) <- ideclImportList decl = return (L l decl) | otherwise = do { let ImportDecl { ideclName = L _ mod_name@@ -1919,7 +1929,7 @@ ; iface <- loadSrcInterface doc mod_name is_boot pkg_qual ; let used_avails = gresToAvailInfo used_gres lies = map (L l) (concatMap (to_ie iface) used_avails)- ; return (L l (decl { ideclHiding = Just (False, L (l2l l) lies) })) }+ ; return (L l (decl { ideclImportList = Just (Exactly, L (l2l l) lies) })) } where doc = text "Compute minimal imports for" <+> ppr decl @@ -1956,7 +1966,7 @@ all_non_overloaded = all (not . flIsOverloaded) combine :: [LImportDecl GhcRn] -> [LImportDecl GhcRn]- combine = map merge . groupBy ((==) `on` getKey) . sortOn getKey+ combine = map merge . NE.groupAllWith getKey getKey :: LImportDecl GhcRn -> (Bool, Maybe ModuleName, ModuleName) getKey decl =@@ -1968,10 +1978,9 @@ idecl :: ImportDecl GhcRn idecl = unLoc decl - merge :: [LImportDecl GhcRn] -> LImportDecl GhcRn- merge [] = error "getMinimalImports: unexpected empty list"- merge decls@((L l decl) : _) = L l (decl { ideclHiding = Just (False, L (noAnnSrcSpan (locA l)) lies) })- where lies = concatMap (unLoc . snd) $ mapMaybe (ideclHiding . unLoc) decls+ merge :: NonEmpty (LImportDecl GhcRn) -> LImportDecl GhcRn+ merge decls@((L l decl) :| _) = L l (decl { ideclImportList = Just (Exactly, L (noAnnSrcSpan (locA l)) lies) })+ where lies = concatMap (unLoc . snd) $ mapMaybe (ideclImportList . unLoc) $ NE.toList decls printMinimalImports :: HscSource -> [ImportDeclUsage] -> RnM ()@@ -2000,16 +2009,16 @@ basefn = moduleNameString (moduleName this_mod) ++ suffix -to_ie_post_rn_var :: (HasOccName name) => LocatedA name -> LIEWrappedName name+to_ie_post_rn_var :: LocatedA (IdP GhcRn) -> LIEWrappedName GhcRn to_ie_post_rn_var (L l n)- | isDataOcc $ occName n = L l (IEPattern (EpaSpan $ la2r l) (L (la2na l) n))- | otherwise = L l (IEName (L (la2na l) n))+ | isDataOcc $ occName n = L l (IEPattern (la2e l) (L (la2na l) n))+ | otherwise = L l (IEName noExtField (L (la2na l) n)) -to_ie_post_rn :: (HasOccName name) => LocatedA name -> LIEWrappedName name+to_ie_post_rn :: LocatedA (IdP GhcRn) -> LIEWrappedName GhcRn to_ie_post_rn (L l n)- | isTcOcc occ && isSymOcc occ = L l (IEType (EpaSpan $ la2r l) (L (la2na l) n))- | otherwise = L l (IEName (L (la2na l) n))+ | isTcOcc occ && isSymOcc occ = L l (IEType (la2e l) (L (la2na l) n))+ | otherwise = L l (IEName noExtField (L (la2na l) n)) where occ = occName n {-@@ -2140,10 +2149,9 @@ illegalImportItemErr :: SDoc illegalImportItemErr = text "Illegal import item" -addDupDeclErr :: [GlobalRdrElt] -> TcRn ()-addDupDeclErr [] = panic "addDupDeclErr: empty list"-addDupDeclErr gres@(gre : _)- = addErrAt (getSrcSpan (last sorted_names)) $ TcRnUnknownMessage $ mkPlainError noHints $+addDupDeclErr :: NonEmpty GlobalRdrElt -> TcRn ()+addDupDeclErr gres@(gre :| _)+ = addErrAt (getSrcSpan (NE.last sorted_names)) $ mkTcRnUnknownMessage $ mkPlainError noHints $ -- Report the error at the later location vcat [text "Multiple declarations of" <+> quotes (ppr (greOccName gre)),@@ -2151,11 +2159,11 @@ -- latter might not be in scope in the RdrEnv and so will -- be printed qualified. text "Declared at:" <+>- vcat (map (ppr . nameSrcLoc) sorted_names)]+ vcat (toList $ ppr . nameSrcLoc <$> sorted_names)] where sorted_names =- sortBy (SrcLoc.leftmost_smallest `on` nameSrcSpan)- (map greMangledName gres)+ NE.sortBy (SrcLoc.leftmost_smallest `on` nameSrcSpan)+ (fmap greMangledName gres) @@ -2174,7 +2182,7 @@ packageImportErr :: TcRnMessage packageImportErr- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ text "Package-qualified imports are not enabled; use PackageImports" -- This data decl will parse OK@@ -2186,11 +2194,15 @@ -- We can get an operator as the constructor, even in the prefix form: -- data T = :% Int Int -- from interface files, which always print in prefix form+--+-- We also allow type constructor names, which are defined by "type data"+-- declarations. See Note [Type data declarations] in GHC.Rename.Module. checkConName :: RdrName -> TcRn ()-checkConName name = checkErr (isRdrDataCon name) (badDataCon name)+checkConName name+ = checkErr (isRdrDataCon name || isRdrTc name) (badDataCon name) badDataCon :: RdrName -> TcRnMessage badDataCon name- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ hsep [text "Illegal data constructor name", quotes (ppr name)]
compiler/GHC/Rename/Pat.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeApplications #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-}@@ -7,9 +7,6 @@ {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE DisambiguateRecordFields #-} -{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}-{-# OPTIONS_GHC -Wno-incomplete-record-updates #-}- {- (c) The GRASP/AQUA Project, Glasgow University, 1992-1998 @@ -22,7 +19,7 @@ free variables. -} module GHC.Rename.Pat (-- main entry points- rnPat, rnPats, rnBindPat, rnPatAndThen,+ rnPat, rnPats, rnBindPat, NameMaker, applyNameMaker, -- a utility for making names: localRecNameMaker, topRecNameMaker, -- sometimes we want to make local names,@@ -77,11 +74,14 @@ import GHC.Driver.Session ( getDynFlags, xopt_DuplicateRecordFields ) import qualified GHC.LanguageExtensions as LangExt -import Control.Monad ( when, ap, guard, forM, unless )+import Control.Monad ( when, ap, guard, unless )+import Data.Foldable+import Data.Functor.Identity ( Identity (..) ) import qualified Data.List.NonEmpty as NE import Data.Maybe import Data.Ratio import GHC.Types.FieldLabel (DuplicateRecordFields(..))+import Language.Haskell.Syntax.Basic (FieldLabelString(..)) {- *********************************************************@@ -404,9 +404,10 @@ -- * local namemaker -- * unused and duplicate checking -- * no fixities-rnPats :: HsMatchContext GhcRn -- for error messages- -> [LPat GhcPs]- -> ([LPat GhcRn] -> RnM (a, FreeVars))+rnPats :: Traversable f+ => HsMatchContext GhcRn -- for error messages+ -> f (LPat GhcPs)+ -> (f (LPat GhcRn) -> RnM (a, FreeVars)) -> RnM (a, FreeVars) rnPats ctxt pats thing_inside = do { envs_before <- getRdrEnvs@@ -423,7 +424,7 @@ -- complain *twice* about duplicates e.g. f (x,x) = ... -- -- See Note [Don't report shadowing for pattern synonyms]- ; let bndrs = collectPatsBinders CollNoDictBinders pats'+ ; let bndrs = collectPatsBinders CollNoDictBinders (toList pats') ; addErrCtxt doc_pat $ if isPatSynCtxt ctxt then checkDupNames bndrs@@ -431,6 +432,8 @@ ; thing_inside pats' } } where doc_pat = text "In" <+> pprMatchContext ctxt+{-# SPECIALIZE rnPats :: HsMatchContext GhcRn -> [LPat GhcPs] -> ([LPat GhcRn] -> RnM (a, FreeVars)) -> RnM (a, FreeVars) #-}+{-# SPECIALIZE rnPats :: HsMatchContext GhcRn -> Identity (LPat GhcPs) -> (Identity (LPat GhcRn) -> RnM (a, FreeVars)) -> RnM (a, FreeVars) #-} rnPat :: HsMatchContext GhcRn -- for error messages -> LPat GhcPs@@ -438,7 +441,7 @@ -> RnM (a, FreeVars) -- Variables bound by pattern do not -- appear in the result FreeVars rnPat ctxt pat thing_inside- = rnPats ctxt [pat] (\pats' -> let [pat'] = pats' in thing_inside pat')+ = rnPats ctxt (Identity pat) (thing_inside . runIdentity) applyNameMaker :: NameMaker -> LocatedN RdrName -> RnM (LocatedN Name) applyNameMaker mk rdr = do { (n, _fvs) <- runCps (newPatLName mk rdr)@@ -468,9 +471,9 @@ -} -- ----------- Entry point 3: rnLPatAndThen ---------------------- General version: parametrized by how you make new names+-- General version: parameterized by how you make new names -rnLPatsAndThen :: NameMaker -> [LPat GhcPs] -> CpsRn [LPat GhcRn]+rnLPatsAndThen :: Traversable f => NameMaker -> f (LPat GhcPs) -> CpsRn (f (LPat GhcRn)) rnLPatsAndThen mk = mapM (rnLPatAndThen mk) -- Despite the map, the monad ensures that each pattern binds -- variables that may be mentioned in subsequent patterns in the list@@ -551,10 +554,10 @@ (L l lit') lit' ge minus) } -- The Report says that n+k patterns must be in Integral -rnPatAndThen mk (AsPat _ rdr pat)+rnPatAndThen mk (AsPat _ rdr at pat) = do { new_name <- newPatLName mk rdr ; pat' <- rnLPatAndThen mk pat- ; return (AsPat noExtField new_name pat') }+ ; return (AsPat noExtField new_name at pat') } rnPatAndThen mk p@(ViewPat _ expr pat) = do { liftCps $ do { vp_flag <- xoptM LangExt.ViewPatterns@@ -609,15 +612,13 @@ ; return (SumPat noExtField pat alt arity) } --- If a splice has been run already, just rename the result.-rnPatAndThen mk (SplicePat x (HsSpliced x2 mfs (HsSplicedPat pat)))- = SplicePat x . HsSpliced x2 mfs . HsSplicedPat <$> rnPatAndThen mk pat- rnPatAndThen mk (SplicePat _ splice) = do { eith <- liftCpsFV $ rnSplicePat splice ; case eith of -- See Note [rnSplicePat] in GHC.Rename.Splice- Left not_yet_renamed -> rnPatAndThen mk not_yet_renamed- Right already_renamed -> return already_renamed }+ (rn_splice, HsUntypedSpliceTop mfs pat) -> -- Splice was top-level and thus run, creating Pat GhcPs+ gParPat . (fmap (flip SplicePat rn_splice . HsUntypedSpliceTop mfs)) <$> rnLPatAndThen mk pat+ (rn_splice, HsUntypedSpliceNested splice_name) -> return (SplicePat (HsUntypedSpliceNested splice_name) rn_splice) -- Splice was nested and thus already renamed+ } -------------------- rnConPatAndThen :: NameMaker@@ -628,8 +629,7 @@ rnConPatAndThen mk con (PrefixCon tyargs pats) = do { con' <- lookupConCps con ; liftCps check_lang_exts- ; tyargs' <- forM tyargs $ \t ->- liftCpsWithCont $ rnHsPatSigTypeBindingVars HsTypeCtx t+ ; tyargs' <- mapM rnConPatTyArg tyargs ; pats' <- rnLPatsAndThen mk pats ; return $ ConPat { pat_con_ext = noExtField@@ -644,12 +644,15 @@ type_app <- xoptM LangExt.TypeApplications unless (scoped_tyvars && type_app) $ case listToMaybe tyargs of- Nothing -> pure ()- Just tyarg -> addErr $ TcRnUnknownMessage $ mkPlainError noHints $+ Nothing -> pure ()+ Just tyarg -> addErr $ mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Illegal visible type application in a pattern:"- <+> quotes (char '@' <> ppr tyarg))+ <+> quotes (ppr tyarg)) 2 (text "Both ScopedTypeVariables and TypeApplications are" <+> text "required to use this feature")+ rnConPatTyArg (HsConPatTyArg at t) = do+ t' <- liftCpsWithCont $ rnHsPatSigTypeBindingVars HsTypeCtx t+ return (HsConPatTyArg at t') rnConPatAndThen mk con (InfixCon pat1 pat2) = do { con' <- lookupConCps con@@ -689,13 +692,13 @@ where mkVarPat l n = VarPat noExtField (L (noAnnSrcSpan l) n) rn_field (L l fld, n') =- do { arg' <- rnLPatAndThen (nested_mk dd mk n') (hfbRHS fld)+ do { arg' <- rnLPatAndThen (nested_mk dd mk (RecFieldsDotDot n')) (hfbRHS fld) ; return (L l (fld { hfbRHS = arg' })) } loc = maybe noSrcSpan getLoc dd -- Get the arguments of the implicit binders- implicit_binders fs (unLoc -> n) = collectPatsBinders CollNoDictBinders implicit_pats+ implicit_binders fs (unLoc -> RecFieldsDotDot n) = collectPatsBinders CollNoDictBinders implicit_pats where implicit_pats = map (hfbRHS . unLoc) (drop n fs) @@ -794,12 +797,12 @@ , hfbPun = pun })) } - rn_dotdot :: Maybe (Located Int) -- See Note [DotDot fields] in GHC.Hs.Pat+ rn_dotdot :: Maybe (Located RecFieldsDotDot) -- See Note [DotDot fields] in GHC.Hs.Pat -> Maybe Name -- The constructor (Nothing for an -- out of scope constructor) -> [LHsRecField GhcRn (LocatedA arg)] -- Explicit fields -> RnM ([LHsRecField GhcRn (LocatedA arg)]) -- Field Labels we need to fill in- rn_dotdot (Just (L loc n)) (Just con) flds -- ".." on record construction / pat match+ rn_dotdot (Just (L loc (RecFieldsDotDot n))) (Just con) flds -- ".." on record construction / pat match | not (isUnboundName con) -- This test is because if the constructor -- isn't in scope the constructor lookup will add -- an error but still return an unbound name. We@@ -822,7 +825,7 @@ (dot_dot_fields, dot_dot_gres) = unzip [ (fl, gre) | fl <- con_fields- , let lbl = mkVarOccFS (flLabel fl)+ , let lbl = mkVarOccFS (field_label $ flLabel fl) , not (lbl `elemOccSet` present_flds) , Just gre <- [lookupGRE_FieldLabel rdr_env fl] -- Check selector is in scope@@ -840,7 +843,7 @@ , hfbPun = False }) | fl <- dot_dot_fields , let sel = flSelector fl- , let arg_rdr = mkVarUnqual (flLabel fl) ] }+ , let arg_rdr = mkVarUnqual (field_label $ flLabel fl) ] } rn_dotdot _dotdot _mb_con _flds = return []@@ -962,7 +965,7 @@ , denominator val == 1 = HsIntegral (IL {il_text=src,il_neg=neg,il_value=numerator val}) generalizeOverLitVal lit = lit -isNegativeZeroOverLit :: HsOverLit t -> Bool+isNegativeZeroOverLit :: (XXOverLit t ~ DataConCantHappen) => HsOverLit t -> Bool isNegativeZeroOverLit lit = case ol_val lit of HsIntegral i -> 0 == il_value i && il_neg i@@ -984,7 +987,7 @@ can apply it explicitly. In this case it stays negative zero. #13211 -} -rnOverLit :: HsOverLit t ->+rnOverLit :: (XXOverLit t ~ DataConCantHappen) => HsOverLit t -> RnM ((HsOverLit GhcRn, Maybe (HsExpr GhcRn)), FreeVars) rnOverLit origLit = do { opt_NumDecimals <- xoptM LangExt.NumDecimals
compiler/GHC/Rename/Splice.hs view
@@ -1,14 +1,18 @@-+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-} -{-# OPTIONS_GHC -Wno-incomplete-record-updates #-}- module GHC.Rename.Splice ( rnTopSpliceDecls,- rnSpliceType, rnSpliceExpr, rnSplicePat, rnSpliceDecl,++ -- Typed splices+ rnTypedSplice,+ -- Untyped splices+ rnSpliceType, rnUntypedSpliceExpr, rnSplicePat, rnSpliceDecl,++ -- Brackets rnTypedBracket, rnUntypedBracket,- checkThLocalName- , traceSplice, SpliceInfo(..)++ checkThLocalName, traceSplice, SpliceInfo(..) ) where import GHC.Prelude@@ -27,7 +31,7 @@ import GHC.Rename.Module ( rnSrcDecls, findSplice ) import GHC.Rename.Pat ( rnPat ) import GHC.Types.Error-import GHC.Types.Basic ( TopLevelFlag, isTopLevel )+import GHC.Types.Basic ( TopLevelFlag, isTopLevel, maxPrec ) import GHC.Types.SourceText ( SourceText(..) ) import GHC.Utils.Outputable import GHC.Unit.Module@@ -78,7 +82,7 @@ checkForTemplateHaskellQuotes e = do { thQuotesEnabled <- xoptM LangExt.TemplateHaskellQuotes ; unless thQuotesEnabled $- failWith ( TcRnUnknownMessage $ mkPlainError noHints $ vcat+ failWith ( mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ text "Syntax error on" <+> ppr e , text ("Perhaps you intended to use TemplateHaskell" ++ " or TemplateHaskellQuotes") ] )@@ -229,21 +233,21 @@ 2 (ppr br_body) illegalBracket :: TcRnMessage-illegalBracket = TcRnUnknownMessage $ mkPlainError noHints $+illegalBracket = mkTcRnUnknownMessage $ mkPlainError noHints $ text "Template Haskell brackets cannot be nested" <+> text "(without intervening splices)" illegalTypedBracket :: TcRnMessage-illegalTypedBracket = TcRnUnknownMessage $ mkPlainError noHints $+illegalTypedBracket = mkTcRnUnknownMessage $ mkPlainError noHints $ text "Typed brackets may only appear in typed splices." illegalUntypedBracket :: TcRnMessage-illegalUntypedBracket = TcRnUnknownMessage $ mkPlainError noHints $+illegalUntypedBracket = mkTcRnUnknownMessage $ mkPlainError noHints $ text "Untyped brackets may only appear in untyped splices." quotedNameStageErr :: HsQuote GhcPs -> TcRnMessage quotedNameStageErr br- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ sep [ text "Stage error: the non-top-level quoted name" <+> ppr br , text "must be used at the same stage at which it is bound" ] @@ -284,63 +288,53 @@ We don't want the type checker to see these bogus unbound variables. -} -rnSpliceGen :: (HsSplice GhcRn -> RnM (a, FreeVars))- -- Outside brackets, run splice- -> (HsSplice GhcRn -> (PendingRnSplice, a))- -- Inside brackets, make it pending- -> HsSplice GhcPs- -> RnM (a, FreeVars)-rnSpliceGen run_splice pend_splice splice+rnUntypedSpliceGen :: (HsUntypedSplice GhcRn -> RnM (a, FreeVars))+ -- Outside brackets, run splice+ -> (Name -> HsUntypedSplice GhcRn -> (PendingRnSplice, a))+ -- Inside brackets, make it pending+ -> HsUntypedSplice GhcPs+ -> RnM (a, FreeVars)+rnUntypedSpliceGen run_splice pend_splice splice = addErrCtxt (spliceCtxt splice) $ do { stage <- getStage ; case stage of- Brack pop_stage RnPendingTyped- -> do { checkTc is_typed_splice illegalUntypedSplice- ; (splice', fvs) <- setStage pop_stage $- rnSplice splice- ; let (_pending_splice, result) = pend_splice splice'- ; return (result, fvs) }+ Brack _ RnPendingTyped+ -> failWithTc illegalUntypedSplice Brack pop_stage (RnPendingUntyped ps_var)- -> do { checkTc (not is_typed_splice) illegalTypedSplice- ; (splice', fvs) <- setStage pop_stage $- rnSplice splice- ; let (pending_splice, result) = pend_splice splice'+ -> do { (splice', fvs) <- setStage pop_stage $+ rnUntypedSplice splice+ ; loc <- getSrcSpanM+ ; splice_name <- newLocalBndrRn (L (noAnnSrcSpan loc) unqualSplice)+ ; let (pending_splice, result) = pend_splice splice_name splice' ; ps <- readMutVar ps_var ; writeMutVar ps_var (pending_splice : ps) ; return (result, fvs) } _ -> do { checkTopSpliceAllowed splice ; (splice', fvs1) <- checkNoErrs $- setStage (Splice splice_type) $- rnSplice splice+ setStage (Splice Untyped) $+ rnUntypedSplice splice -- checkNoErrs: don't attempt to run the splice if -- renaming it failed; otherwise we get a cascade of -- errors from e.g. unbound variables ; (result, fvs2) <- run_splice splice' ; return (result, fvs1 `plusFV` fvs2) } }- where- is_typed_splice = isTypedSplice splice- splice_type = if is_typed_splice- then Typed- else Untyped -- Nested splices are fine without TemplateHaskell because they -- are not executed until the top-level splice is run.-checkTopSpliceAllowed :: HsSplice GhcPs -> RnM ()+checkTopSpliceAllowed :: HsUntypedSplice GhcPs -> RnM () checkTopSpliceAllowed splice = do let (herald, ext) = spliceExtension splice extEnabled <- xoptM ext unless extEnabled- (failWith $ TcRnUnknownMessage $ mkPlainError noHints $+ (failWith $ mkTcRnUnknownMessage $ mkPlainError noHints $ text herald <+> text "are not permitted without" <+> ppr ext) where- spliceExtension :: HsSplice GhcPs -> (String, LangExt.Extension)+ spliceExtension :: HsUntypedSplice GhcPs -> (String, LangExt.Extension) spliceExtension (HsQuasiQuote {}) = ("Quasi-quotes", LangExt.QuasiQuotes)- spliceExtension (HsTypedSplice {}) = ("Top-level splices", LangExt.TemplateHaskell)- spliceExtension (HsUntypedSplice {}) = ("Top-level splices", LangExt.TemplateHaskell)- spliceExtension s@(HsSpliced {}) = pprPanic "spliceExtension" (ppr s)+ spliceExtension (HsUntypedSpliceExpr {}) = ("Top-level splices", LangExt.TemplateHaskell) ------------------ @@ -352,7 +346,7 @@ -> (LHsExpr GhcTc -> TcRn res) -> (res -> SDoc) -- How to pretty-print res -- Usually just ppr, but not for [Decl]- -> HsSplice GhcRn -- Always untyped+ -> HsUntypedSplice GhcRn -> TcRn (res, [ForeignRef (TH.Q ())]) runRnSplice flavour run_meta ppr_res splice = do { hooks <- hsc_hooks <$> getTopEnv@@ -361,10 +355,8 @@ Just h -> h splice ; let the_expr = case splice' of- HsUntypedSplice _ _ _ e -> e- HsQuasiQuote _ _ q qs str -> mkQuasiQuoteExpr flavour q qs str- HsTypedSplice {} -> pprPanic "runRnSplice" (ppr splice)- HsSpliced {} -> pprPanic "runRnSplice" (ppr splice)+ HsUntypedSpliceExpr _ e -> e+ HsQuasiQuote _ q str -> mkQuasiQuoteExpr flavour q str -- Typecheck the expression ; meta_exp_ty <- tcMetaTy meta_ty_name@@ -401,30 +393,28 @@ ------------------ makePending :: UntypedSpliceFlavour- -> HsSplice GhcRn+ -> Name+ -> HsUntypedSplice GhcRn -> PendingRnSplice-makePending flavour (HsUntypedSplice _ _ n e)+makePending flavour n (HsUntypedSpliceExpr _ e) = PendingRnSplice flavour n e-makePending flavour (HsQuasiQuote _ n quoter q_span quote)- = PendingRnSplice flavour n (mkQuasiQuoteExpr flavour quoter q_span quote)-makePending _ splice@(HsTypedSplice {})- = pprPanic "makePending" (ppr splice)-makePending _ splice@(HsSpliced {})- = pprPanic "makePending" (ppr splice)+makePending flavour n (HsQuasiQuote _ quoter quote)+ = PendingRnSplice flavour n (mkQuasiQuoteExpr flavour quoter quote) -------------------mkQuasiQuoteExpr :: UntypedSpliceFlavour -> Name -> SrcSpan -> FastString+mkQuasiQuoteExpr :: UntypedSpliceFlavour -> Name+ -> XRec GhcPs FastString -> LHsExpr GhcRn -- Return the expression (quoter "...quote...") -- which is what we must run in a quasi-quote-mkQuasiQuoteExpr flavour quoter q_span' quote+mkQuasiQuoteExpr flavour quoter (L q_span' quote) = L q_span $ HsApp noComments (L q_span $ HsApp noComments (L q_span (HsVar noExtField (L (la2na q_span) quote_selector))) quoterExpr) quoteExpr where- q_span = noAnnSrcSpan q_span'+ q_span = noAnnSrcSpan (locA q_span') quoterExpr = L q_span $! HsVar noExtField $! (L (la2na q_span) quoter) quoteExpr = L q_span $! HsLit noComments $! HsString NoSourceText quote quote_selector = case flavour of@@ -434,66 +424,90 @@ UntypedDeclSplice -> quoteDecName ----------------------rnSplice :: HsSplice GhcPs -> RnM (HsSplice GhcRn, FreeVars)--- Not exported...used for all-rnSplice (HsTypedSplice x hasParen splice_name expr)- = do { loc <- getSrcSpanM- ; n' <- newLocalBndrRn (L (noAnnSrcSpan loc) splice_name)- ; (expr', fvs) <- rnLExpr expr- ; return (HsTypedSplice x hasParen n' expr', fvs) }--rnSplice (HsUntypedSplice x hasParen splice_name expr)- = do { loc <- getSrcSpanM- ; n' <- newLocalBndrRn (L (noAnnSrcSpan loc) splice_name)- ; (expr', fvs) <- rnLExpr expr- ; return (HsUntypedSplice x hasParen n' expr', fvs) }+unqualSplice :: RdrName+-- The RdrName for a SplicePointName. See GHC.Hs.Expr+-- Note [Lifecycle of an untyped splice, and PendingRnSplice]+-- We use "spn" (which is arbitrary) because it is brief but grepable-for.+unqualSplice = mkRdrUnqual (mkVarOccFS (fsLit "spn")) -rnSplice (HsQuasiQuote x splice_name quoter q_loc quote)- = do { loc <- getSrcSpanM- ; splice_name' <- newLocalBndrRn (L (noAnnSrcSpan loc) splice_name)+rnUntypedSplice :: HsUntypedSplice GhcPs -> RnM (HsUntypedSplice GhcRn, FreeVars)+-- Not exported...used for all+rnUntypedSplice (HsUntypedSpliceExpr annCo expr)+ = do { (expr', fvs) <- rnLExpr expr+ ; return (HsUntypedSpliceExpr annCo expr', fvs) } - -- Rename the quoter; akin to the HsVar case of rnExpr+rnUntypedSplice (HsQuasiQuote ext quoter quote)+ = do { -- Rename the quoter; akin to the HsVar case of rnExpr ; quoter' <- lookupOccRn quoter ; this_mod <- getModule ; when (nameIsLocalOrFrom this_mod quoter') $ checkThLocalName quoter' - ; return (HsQuasiQuote x splice_name' quoter' q_loc quote- , unitFV quoter') }--rnSplice splice@(HsSpliced {}) = pprPanic "rnSplice" (ppr splice)+ ; return (HsQuasiQuote ext quoter' quote, unitFV quoter') } ----------------------rnSpliceExpr :: HsSplice GhcPs -> RnM (HsExpr GhcRn, FreeVars)-rnSpliceExpr splice- = rnSpliceGen run_expr_splice pend_expr_splice splice- where- pend_expr_splice :: HsSplice GhcRn -> (PendingRnSplice, HsExpr GhcRn)- pend_expr_splice rn_splice- = (makePending UntypedExpSplice rn_splice, HsSpliceE noAnn rn_splice)+rnTypedSplice :: LHsExpr GhcPs -- Typed splice expression+ -> RnM (HsExpr GhcRn, FreeVars)+rnTypedSplice expr+ = addErrCtxt (hang (text "In the typed splice:") 2 (pprTypedSplice Nothing expr)) $ do+ { stage <- getStage+ ; case stage of+ Brack pop_stage RnPendingTyped+ -> setStage pop_stage rn_splice - run_expr_splice :: HsSplice GhcRn -> RnM (HsExpr GhcRn, FreeVars)- run_expr_splice rn_splice- | isTypedSplice rn_splice -- Run it later, in the type checker- = do { -- Ugh! See Note [Splices] above- traceRn "rnSpliceExpr: typed expression splice" empty- ; lcl_rdr <- getLocalRdrEnv- ; gbl_rdr <- getGlobalRdrEnv- ; let gbl_names = mkNameSet [greMangledName gre | gre <- globalRdrEnvElts gbl_rdr- , isLocalGRE gre]- lcl_names = mkNameSet (localRdrEnvElts lcl_rdr)+ Brack _ (RnPendingUntyped _)+ -> failWithTc illegalTypedSplice - ; return (HsSpliceE noAnn rn_splice, lcl_names `plusFV` gbl_names) }+ _ -> do { extEnabled <- xoptM LangExt.TemplateHaskell+ ; unless extEnabled+ (failWith $ mkTcRnUnknownMessage $ mkPlainError noHints $+ text "Top-level splices are not permitted without"+ <+> ppr LangExt.TemplateHaskell) - | otherwise -- Run it here, see Note [Running splices in the Renamer]- = do { traceRn "rnSpliceExpr: untyped expression splice" empty+ ; (result, fvs1) <- checkNoErrs $ setStage (Splice Typed) rn_splice+ -- checkNoErrs: don't attempt to run the splice if+ -- renaming it failed; otherwise we get a cascade of+ -- errors from e.g. unbound variables++ -- Run typed splice later, in the type checker+ -- Ugh! See Note [Free variables of typed splices] above+ ; traceRn "rnTypedSplice: typed expression splice" empty+ ; lcl_rdr <- getLocalRdrEnv+ ; gbl_rdr <- getGlobalRdrEnv+ ; let gbl_names = mkNameSet [greMangledName gre | gre <- globalRdrEnvElts gbl_rdr+ , isLocalGRE gre]+ lcl_names = mkNameSet (localRdrEnvElts lcl_rdr)+ fvs2 = lcl_names `plusFV` gbl_names++ ; return (result, fvs1 `plusFV` fvs2) } }+ where+ rn_splice :: RnM (HsExpr GhcRn, FreeVars)+ rn_splice =+ do { loc <- getSrcSpanM+ -- The renamer allocates a splice-point name to every typed splice+ -- (incl the top level ones for which it will not ultimately be used)+ ; n' <- newLocalBndrRn (L (noAnnSrcSpan loc) unqualSplice)+ ; (expr', fvs) <- rnLExpr expr+ ; return (HsTypedSplice n' expr', fvs) }++rnUntypedSpliceExpr :: HsUntypedSplice GhcPs -> RnM (HsExpr GhcRn, FreeVars)+rnUntypedSpliceExpr splice+ = rnUntypedSpliceGen run_expr_splice pend_expr_splice splice+ where+ pend_expr_splice :: Name -> HsUntypedSplice GhcRn -> (PendingRnSplice, HsExpr GhcRn)+ pend_expr_splice name rn_splice+ = (makePending UntypedExpSplice name rn_splice, HsUntypedSplice (HsUntypedSpliceNested name) rn_splice)++ run_expr_splice :: HsUntypedSplice GhcRn -> RnM (HsExpr GhcRn, FreeVars)+ run_expr_splice rn_splice+ = do { traceRn "rnUntypedSpliceExpr: untyped expression splice" empty+ -- Run it here, see Note [Running splices in the Renamer] ; (rn_expr, mod_finalizers) <- runRnSplice UntypedExpSplice runMetaE ppr rn_splice ; (lexpr3, fvs) <- checkNoErrs (rnLExpr rn_expr) -- See Note [Delaying modFinalizers in untyped splices].- ; let e = HsSpliceE noAnn- . HsSpliced noExtField (ThModFinalizers mod_finalizers)- . HsSplicedExpr+ ; let e = flip HsUntypedSplice rn_splice+ . HsUntypedSpliceTop (ThModFinalizers mod_finalizers) <$> lexpr3 ; return (gHsPar e, fvs) }@@ -649,32 +663,43 @@ -} -----------------------rnSpliceType :: HsSplice GhcPs -> RnM (HsType GhcRn, FreeVars)+rnSpliceType :: HsUntypedSplice GhcPs -> RnM (HsType GhcRn, FreeVars) rnSpliceType splice- = rnSpliceGen run_type_splice pend_type_splice splice+ = rnUntypedSpliceGen run_type_splice pend_type_splice splice where- pend_type_splice rn_splice- = ( makePending UntypedTypeSplice rn_splice- , HsSpliceTy noExtField rn_splice)+ pend_type_splice name rn_splice+ = ( makePending UntypedTypeSplice name rn_splice+ , HsSpliceTy (HsUntypedSpliceNested name) rn_splice) + run_type_splice :: HsUntypedSplice GhcRn -> RnM (HsType GhcRn, FreeVars) run_type_splice rn_splice = do { traceRn "rnSpliceType: untyped type splice" empty ; (hs_ty2, mod_finalizers) <- runRnSplice UntypedTypeSplice runMetaT ppr rn_splice ; (hs_ty3, fvs) <- do { let doc = SpliceTypeCtx hs_ty2 ; checkNoErrs $ rnLHsType doc hs_ty2 }- -- checkNoErrs: see Note [Renamer errors]+ -- checkNoErrs: see Note [Renamer errors]+ -- See Note [Delaying modFinalizers in untyped splices].- ; return ( HsParTy noAnn- $ HsSpliceTy noExtField- . HsSpliced noExtField (ThModFinalizers mod_finalizers)- . HsSplicedTy <$>- hs_ty3+ ; return ( HsSpliceTy (HsUntypedSpliceTop (ThModFinalizers mod_finalizers)+ (mb_paren hs_ty3))+ rn_splice , fvs ) } -- Wrap the result of the splice in parens so that we don't -- lose the outermost location set by runQuasiQuote (#7918) + -- Wrap a non-atomic result in HsParTy parens;+ -- but not if it's atomic to avoid double parens for operators+ -- This is to account for, say foo :: $(blah) -> Int+ -- when we want $(blah) to expand to (this -> that), with parens.+ -- Sadly, it's awkward add precisely the correct parens, because+ -- that depends on the context.+ mb_paren :: LHsType GhcRn -> LHsType GhcRn+ mb_paren lhs_ty@(L loc hs_ty)+ | hsTypeNeedsParens maxPrec hs_ty = L loc (HsParTy noAnn lhs_ty)+ | otherwise = lhs_ty+ {- Note [Partial Type Splices] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Partial Type Signatures are partially supported in TH type splices: only@@ -717,50 +742,43 @@ ---------------------- -- | Rename a splice pattern. See Note [rnSplicePat]-rnSplicePat :: HsSplice GhcPs -> RnM ( Either (Pat GhcPs) (Pat GhcRn)- , FreeVars)+rnSplicePat :: HsUntypedSplice GhcPs -> RnM ( (HsUntypedSplice GhcRn, HsUntypedSpliceResult (LPat GhcPs))+ , FreeVars) rnSplicePat splice- = rnSpliceGen run_pat_splice pend_pat_splice splice+ = rnUntypedSpliceGen run_pat_splice pend_pat_splice splice where- pend_pat_splice :: HsSplice GhcRn ->- (PendingRnSplice, Either b (Pat GhcRn))- pend_pat_splice rn_splice- = (makePending UntypedPatSplice rn_splice- , Right (SplicePat noExtField rn_splice))+ pend_pat_splice name rn_splice+ = (makePending UntypedPatSplice name rn_splice+ , (rn_splice, HsUntypedSpliceNested name)) -- Pat splice is nested and thus simply renamed - run_pat_splice :: HsSplice GhcRn ->- RnM (Either (Pat GhcPs) (Pat GhcRn), FreeVars) run_pat_splice rn_splice = do { traceRn "rnSplicePat: untyped pattern splice" empty ; (pat, mod_finalizers) <- runRnSplice UntypedPatSplice runMetaP ppr rn_splice -- See Note [Delaying modFinalizers in untyped splices].- ; let p = SplicePat noExtField- . HsSpliced noExtField (ThModFinalizers mod_finalizers)- . HsSplicedPat- <$> pat- ; return (Left $ gParPat p, emptyFVs) }+ ; let p = HsUntypedSpliceTop (ThModFinalizers mod_finalizers) pat+ ; return ((rn_splice, p), emptyFVs) } -- Wrap the result of the quasi-quoter in parens so that we don't -- lose the outermost location set by runQuasiQuote (#7918) ---------------------- rnSpliceDecl :: SpliceDecl GhcPs -> RnM (SpliceDecl GhcRn, FreeVars) rnSpliceDecl (SpliceDecl _ (L loc splice) flg)- = rnSpliceGen run_decl_splice pend_decl_splice splice+ = rnUntypedSpliceGen run_decl_splice pend_decl_splice splice where- pend_decl_splice rn_splice- = ( makePending UntypedDeclSplice rn_splice+ pend_decl_splice name rn_splice+ = ( makePending UntypedDeclSplice name rn_splice , SpliceDecl noExtField (L loc rn_splice) flg) - run_decl_splice rn_splice = pprPanic "rnSpliceDecl" (ppr rn_splice)+ run_decl_splice rn_splice = pprPanic "rnSpliceDecl" (pprUntypedSplice True Nothing rn_splice) -rnTopSpliceDecls :: HsSplice GhcPs -> RnM ([LHsDecl GhcPs], FreeVars)+rnTopSpliceDecls :: HsUntypedSplice GhcPs -> RnM ([LHsDecl GhcPs], FreeVars) -- Declaration splice at the very top level of the module rnTopSpliceDecls splice = do { checkTopSpliceAllowed splice ; (rn_splice, fvs) <- checkNoErrs $ setStage (Splice Untyped) $- rnSplice splice+ rnUntypedSplice splice -- As always, be sure to checkNoErrs above lest we end up with -- holes making it to typechecking, hence #12584. --@@ -803,33 +821,33 @@ management is effectively done by using continuation-passing style in GHC.Rename.Pat, through the CpsRn monad. We don't wish to be in that monad here (it would create import cycles and generally conflict with renaming other-splices), so we really want to return a (Pat RdrName) -- the result of+splices), so we really want to return a (Pat GhcPs) -- the result of running the splice -- which can then be further renamed in GHC.Rename.Pat, in the CpsRn monad. The problem is that if we're renaming a splice within a bracket, we *don't* want to run the splice now. We really do just want to rename-it to an HsSplice Name. Of course, then we can't know what variables+it to an HsUntypedSplice Name. Of course, then we can't know what variables are bound within the splice. So we accept any unbound variables and rename them again when the bracket is spliced in. If a variable is brought into scope by a pattern splice all is fine. If it is not then an error is reported. -In any case, when we're done in rnSplicePat, we'll either have a-Pat RdrName (the result of running a top-level splice) or a Pat Name-(the renamed nested splice). Thus, the awkward return type of-rnSplicePat.+In any case, when we're done in rnSplicePat, we'll have both the renamed+splice, and either a Pat RdrName and ThModFinalizers (the result of running a+top-level splice) or a splice point name. Thus, rnSplicePat returns both+HsUntypedSplice GhcRn, and HsUntypedSpliceResult (Pat GhcPs) -- which models+the existence of either the result of running the splice (HsUntypedSpliceTop),+or its splice point name if nested (HsUntypedSpliceNested) -} -spliceCtxt :: HsSplice GhcPs -> SDoc+spliceCtxt :: HsUntypedSplice GhcPs -> SDoc spliceCtxt splice- = hang (text "In the" <+> what) 2 (ppr splice)+ = hang (text "In the" <+> what) 2 (pprUntypedSplice True Nothing splice) where what = case splice of- HsUntypedSplice {} -> text "untyped splice:"- HsTypedSplice {} -> text "typed splice:"- HsQuasiQuote {} -> text "quasi-quotation:"- HsSpliced {} -> text "spliced expression:"+ HsUntypedSpliceExpr {} -> text "untyped splice:"+ HsQuasiQuote {} -> text "quasi-quotation:" -- | The splice data to be logged data SpliceInfo@@ -878,11 +896,11 @@ , gen ] illegalTypedSplice :: TcRnMessage-illegalTypedSplice = TcRnUnknownMessage $ mkPlainError noHints $+illegalTypedSplice = mkTcRnUnknownMessage $ mkPlainError noHints $ text "Typed splices may not appear in untyped brackets" illegalUntypedSplice :: TcRnMessage-illegalUntypedSplice = TcRnUnknownMessage $ mkPlainError noHints $+illegalUntypedSplice = mkTcRnUnknownMessage $ mkPlainError noHints $ text "Untyped splices may not appear in typed brackets" checkThLocalName :: Name -> RnM ()
compiler/GHC/Rename/Splice.hs-boot view
@@ -1,14 +1,13 @@ module GHC.Rename.Splice where -import GHC.Prelude import GHC.Hs import GHC.Tc.Utils.Monad import GHC.Types.Name.Set -rnSpliceType :: HsSplice GhcPs -> RnM (HsType GhcRn, FreeVars)-rnSplicePat :: HsSplice GhcPs -> RnM ( Either (Pat GhcPs) (Pat GhcRn)- , FreeVars )+rnSpliceType :: HsUntypedSplice GhcPs -> RnM (HsType GhcRn, FreeVars)+rnSplicePat :: HsUntypedSplice GhcPs -> RnM ( (HsUntypedSplice GhcRn, HsUntypedSpliceResult (LPat GhcPs))+ , FreeVars) rnSpliceDecl :: SpliceDecl GhcPs -> RnM (SpliceDecl GhcRn, FreeVars) -rnTopSpliceDecls :: HsSplice GhcPs -> RnM ([LHsDecl GhcPs], FreeVars)+rnTopSpliceDecls :: HsUntypedSplice GhcPs -> RnM ([LHsDecl GhcPs], FreeVars)
compiler/GHC/Rename/Unbound.hs view
@@ -57,6 +57,7 @@ import Data.List (sortBy, partition, nub) import Data.List.NonEmpty ( pattern (:|), NonEmpty ) import Data.Function ( on )+import qualified Data.Semigroup as S {- ************************************************************************@@ -303,10 +304,7 @@ pick = listToMaybe . sortBy cmp . filter select where select imv = case mod_name of Just name -> imv_name imv == name Nothing -> not (imv_qualified imv)- cmp a b =- (compare `on` imv_is_hiding) a b- `thenCmp`- (SrcLoc.leftmost_smallest `on` imv_span) a b+ cmp = on compare imv_is_hiding S.<> on SrcLoc.leftmost_smallest imv_span -- Which of these would export a 'foo' -- (all of these are restricted imports, because if they were not, we@@ -330,8 +328,7 @@ | otherwise = True where hpt_uniques = map fst (udfmToList hpt)- is_last_loaded_mod _ [] = False- is_last_loaded_mod modnam uniqs = last uniqs == getUnique modnam+ is_last_loaded_mod modnam uniqs = lastMaybe uniqs == Just (getUnique modnam) glob_mods = nub [ mod | gre <- globalRdrEnvElts global_env , (mod, _) <- qualsInScope gre
compiler/GHC/Rename/Utils.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE GADTs #-} -{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} {- This module contains miscellaneous functions related to renaming.@@ -20,7 +20,9 @@ mkFieldEnv, badQualBndrErr, typeAppErr, badFieldConErr, wrapGenSpan, genHsVar, genLHsVar, genHsApp, genHsApps, genAppType,- genHsIntegralLit, genHsTyLit,+ genHsIntegralLit, genHsTyLit, genSimpleConPat,+ genVarPat, genWildPat,+ genSimpleFunBind, genFunBind, newLocalBndrRn, newLocalBndrsRn, @@ -35,13 +37,12 @@ where -import GHC.Prelude+import GHC.Prelude hiding (unzip) import GHC.Core.Type import GHC.Hs import GHC.Types.Name.Reader import GHC.Tc.Errors.Types-import GHC.Tc.Errors.Ppr (withHsDocContext) import GHC.Tc.Utils.Env import GHC.Tc.Utils.Monad import GHC.Types.Error@@ -55,7 +56,7 @@ import GHC.Utils.Outputable import GHC.Utils.Panic import GHC.Utils.Misc-import GHC.Types.Basic ( TopLevelFlag(..) )+import GHC.Types.Basic ( TopLevelFlag(..), Origin(Generated) ) import GHC.Data.List.SetOps ( removeDups ) import GHC.Data.Maybe ( whenIsJust ) import GHC.Driver.Session@@ -66,6 +67,7 @@ import qualified Data.List.NonEmpty as NE import qualified GHC.LanguageExtensions as LangExt import GHC.Data.Bag+import qualified Data.List as List {- *********************************************************@@ -201,7 +203,9 @@ let bndrs = sig_ty_bndrs ty in case find ((==) InferredSpec . hsTyVarBndrFlag) bndrs of Nothing -> return ()- Just _ -> addErr $ TcRnUnknownMessage $ mkPlainError noHints (withHsDocContext ctxt msg)+ Just _ -> addErr $+ TcRnWithHsDocContext ctxt $+ mkTcRnUnknownMessage $ mkPlainError noHints msg where sig_ty_bndrs :: LHsSigType GhcPs -> [HsTyVarBndr Specificity GhcPs] sig_ty_bndrs (L _ (HsSig{sig_bndrs = outer_bndrs}))@@ -284,7 +288,7 @@ -- "GHC.Rename.Module" and 'renameSig' in "GHC.Rename.Bind"). -- See @Note [No nested foralls or contexts in instance types]@ in -- "GHC.Hs.Type".-noNestedForallsContextsErr :: SDoc -> LHsType GhcRn -> Maybe (SrcSpan, SDoc)+noNestedForallsContextsErr :: SDoc -> LHsType GhcRn -> Maybe (SrcSpan, TcRnMessage) noNestedForallsContextsErr what lty = case ignoreParens lty of L l (HsForAllTy { hst_tele = tele })@@ -293,9 +297,7 @@ -- types of terms, so we give a slightly more descriptive error -- message in the event that they contain visible dependent -- quantification (currently only allowed in kinds).- -> Just (locA l, vcat [ text "Illegal visible, dependent quantification" <+>- text "in the type of a term"- , text "(GHC does not yet support this)" ])+ -> Just (locA l, TcRnVDQInTermType Nothing) | HsForAllInvis{} <- tele -> Just (locA l, nested_foralls_contexts_err) L l (HsQualTy {})@@ -303,6 +305,7 @@ _ -> Nothing where nested_foralls_contexts_err =+ mkTcRnUnknownMessage $ mkPlainError noHints $ what <+> text "cannot contain nested" <+> quotes forAllLit <> text "s or contexts" @@ -310,7 +313,7 @@ addNoNestedForallsContextsErr :: HsDocContext -> SDoc -> LHsType GhcRn -> RnM () addNoNestedForallsContextsErr ctxt what lty = whenIsJust (noNestedForallsContextsErr what lty) $ \(l, err_msg) ->- addErrAt l $ TcRnUnknownMessage $ mkPlainError noHints (withHsDocContext ctxt err_msg)+ addErrAt l $ TcRnWithHsDocContext ctxt err_msg {- ************************************************************************@@ -325,11 +328,18 @@ addFvRn fvs1 thing_inside = do { (res, fvs2) <- thing_inside ; return (res, fvs1 `plusFV` fvs2) } -mapFvRn :: (a -> RnM (b, FreeVars)) -> [a] -> RnM ([b], FreeVars)-mapFvRn f xs = do stuff <- mapM f xs- case unzip stuff of- (ys, fvs_s) -> return (ys, plusFVs fvs_s)+mapFvRn :: Traversable f => (a -> RnM (b, FreeVars)) -> f a -> RnM (f b, FreeVars)+mapFvRn f xs = do+ stuff <- mapM f xs+ case unzip stuff of+ (ys, fvs_s) -> return (ys, foldl' (flip plusFV) emptyFVs fvs_s)+{-# SPECIALIZE mapFvRn :: (a -> RnM (b, FreeVars)) -> [a] -> RnM ([b], FreeVars) #-} +unzip :: Functor f => f (a, b) -> (f a, f b)+unzip = \ xs -> (fmap fst xs, fmap snd xs)+{-# NOINLINE [1] unzip #-}+{-# RULES "unzip/List" unzip = List.unzip #-}+ mapMaybeFvRn :: (a -> RnM (b, FreeVars)) -> Maybe a -> RnM (Maybe b, FreeVars) mapMaybeFvRn _ Nothing = return (Nothing, emptyFVs) mapMaybeFvRn f (Just x) = do { (y, fvs) <- f x; return (Just y, fvs) }@@ -388,7 +398,7 @@ warnRedundantRecordWildcard :: RnM () warnRedundantRecordWildcard = whenWOptM Opt_WarnRedundantRecordWildcards $- let msg = TcRnUnknownMessage $+ let msg = mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag Opt_WarnRedundantRecordWildcards) noHints redundantWildcardWarning@@ -487,7 +497,7 @@ addUnusedWarning :: WarningFlag -> OccName -> SrcSpan -> SDoc -> RnM () addUnusedWarning flag occ span msg = do- let diag = TcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag flag) noHints $+ let diag = mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag flag) noHints $ sep [msg <> colon, nest 2 $ pprNonVarNameSpace (occNameSpace occ) <+> quotes (ppr occ)]@@ -495,7 +505,7 @@ unusedRecordWildcardWarning :: TcRnMessage unusedRecordWildcardWarning =- TcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag Opt_WarnUnusedRecordWildcards) noHints $+ mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag Opt_WarnUnusedRecordWildcards) noHints $ wildcardDoc $ text "No variables bound in the record wildcard match are used" redundantWildcardWarning :: SDoc@@ -545,7 +555,7 @@ -- already, and we don't want an error cascade. = return () | otherwise- = addErr $ TcRnUnknownMessage $ mkPlainError noHints $+ = addErr $ mkTcRnUnknownMessage $ mkPlainError noHints $ (vcat [ text "Ambiguous occurrence" <+> quotes (ppr rdr_name) , text "It could refer to" , nest 3 (vcat (msg1 : msgs)) ])@@ -598,7 +608,7 @@ dupNamesErr :: Outputable n => (n -> SrcSpan) -> NE.NonEmpty n -> RnM () dupNamesErr get_loc names- = addErrAt big_loc $ TcRnUnknownMessage $ mkPlainError noHints $+ = addErrAt big_loc $ mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [text "Conflicting definitions for" <+> quotes (ppr (NE.head names)), locations] where@@ -608,19 +618,19 @@ badQualBndrErr :: RdrName -> TcRnMessage badQualBndrErr rdr_name- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ text "Qualified name in binding position:" <+> ppr rdr_name typeAppErr :: String -> LHsType GhcPs -> TcRnMessage typeAppErr what (L _ k)- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Illegal visible" <+> text what <+> text "application" <+> quotes (char '@' <> ppr k)) 2 (text "Perhaps you intended to use TypeApplications") badFieldConErr :: Name -> FieldLabelString -> TcRnMessage badFieldConErr con field- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ hsep [text "Constructor" <+> quotes (ppr con), text "does not have field", quotes (ppr field)] @@ -631,7 +641,7 @@ | tup_size <= mAX_TUPLE_SIZE = return () | otherwise- = addErr $ TcRnUnknownMessage $ mkPlainError noHints $+ = addErr $ mkTcRnUnknownMessage $ mkPlainError noHints $ sep [text "A" <+> int tup_size <> text "-tuple is too large for GHC", nest 2 (parens (text "max size is" <+> int mAX_TUPLE_SIZE)), nest 2 (text "Workaround: use nested tuples or define a data type")]@@ -642,7 +652,7 @@ | tup_size <= mAX_CTUPLE_SIZE = return () | otherwise- = addErr $ TcRnUnknownMessage $ mkPlainError noHints $+ = addErr $ mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Constraint tuple arity too large:" <+> int tup_size <+> parens (text "max arity =" <+> int mAX_CTUPLE_SIZE)) 2 (text "Instead, use a nested tuple")@@ -672,10 +682,40 @@ genHsVar nm = HsVar noExtField $ wrapGenSpan nm genAppType :: HsExpr GhcRn -> HsType (NoGhcTc GhcRn) -> HsExpr GhcRn-genAppType expr = HsAppType noExtField (wrapGenSpan expr) . mkEmptyWildCardBndrs . wrapGenSpan+genAppType expr ty = HsAppType noExtField (wrapGenSpan expr) noHsTok (mkEmptyWildCardBndrs (wrapGenSpan ty)) genHsIntegralLit :: IntegralLit -> LocatedAn an (HsExpr GhcRn) genHsIntegralLit lit = wrapGenSpan $ HsLit noAnn (HsInt noExtField lit) genHsTyLit :: FastString -> HsType GhcRn genHsTyLit = HsTyLit noExtField . HsStrTy NoSourceText++genSimpleConPat :: Name -> [LPat GhcRn] -> LPat GhcRn+-- The pattern (C p1 .. pn)+genSimpleConPat con pats+ = wrapGenSpan $ ConPat { pat_con_ext = noExtField+ , pat_con = wrapGenSpan con+ , pat_args = PrefixCon [] pats }++genVarPat :: Name -> LPat GhcRn+genVarPat n = wrapGenSpan $ VarPat noExtField (wrapGenSpan n)++genWildPat :: LPat GhcRn+genWildPat = wrapGenSpan $ WildPat noExtField++genSimpleFunBind :: Name -> [LPat GhcRn]+ -> LHsExpr GhcRn -> LHsBind GhcRn+genSimpleFunBind fun pats expr+ = L gen $ genFunBind (L gen fun)+ [mkMatch (mkPrefixFunRhs (L gen fun)) pats expr+ emptyLocalBinds]+ where+ gen = noAnnSrcSpan generatedSrcSpan++genFunBind :: LocatedN Name -> [LMatch GhcRn (LHsExpr GhcRn)]+ -> HsBind GhcRn+genFunBind fn ms+ = FunBind { fun_id = fn+ , fun_matches = mkMatchGroup Generated (wrapGenSpan ms)+ , fun_ext = emptyNameSet+ }
+ compiler/GHC/Runtime/Debugger.hs view
@@ -0,0 +1,271 @@+-----------------------------------------------------------------------------+--+-- GHCi Interactive debugging commands+--+-- Pepe Iborra (supported by Google SoC) 2006+--+-- ToDo: lots of violation of layering here. This module should+-- decide whether it is above the GHC API (import GHC and nothing+-- else) or below it.+--+-----------------------------------------------------------------------------++module GHC.Runtime.Debugger (pprintClosureCommand, showTerm, pprTypeAndContents) where++import GHC.Prelude++import GHC++import GHC.Driver.Session+import GHC.Driver.Ppr+import GHC.Driver.Monad+import GHC.Driver.Env++import GHC.Linker.Loader++import GHC.Runtime.Heap.Inspect+import GHC.Runtime.Interpreter+import GHC.Runtime.Context++import GHC.Iface.Syntax ( showToHeader )+import GHC.Iface.Env ( newInteractiveBinder )+import GHC.Core.Type++import GHC.Utils.Outputable+import GHC.Utils.Error+import GHC.Utils.Monad+import GHC.Utils.Exception+import GHC.Utils.Logger++import GHC.Types.Id+import GHC.Types.Id.Make (ghcPrimIds)+import GHC.Types.Name+import GHC.Types.Var hiding ( varName )+import GHC.Types.Var.Set+import GHC.Types.Unique.Set+import GHC.Types.TyThing.Ppr+import GHC.Types.TyThing++import Control.Monad+import Control.Monad.Catch as MC+import Data.List ( (\\), partition )+import qualified Data.List.NonEmpty as NE+import Data.Maybe+import Data.IORef++-------------------------------------+-- | The :print & friends commands+-------------------------------------+pprintClosureCommand :: GhcMonad m => Bool -> Bool -> String -> m ()+pprintClosureCommand bindThings force str = do+ tythings <- (catMaybes . concatMap NE.toList) `liftM`+ mapM (\w -> GHC.parseName w >>=+ mapM GHC.lookupName)+ (words str)++ -- Sort out good and bad tythings for :print and friends+ let (pprintables, unpprintables) = partition can_pprint tythings++ -- Obtain the terms and the recovered type information+ let ids = [id | AnId id <- pprintables]+ (subst, terms) <- mapAccumLM go emptySubst ids++ -- Apply the substitutions obtained after recovering the types+ modifySession $ \hsc_env ->+ hsc_env{hsc_IC = substInteractiveContext (hsc_IC hsc_env) subst}++ -- Finally, print the Results+ docterms <- mapM showTerm terms+ let sdocTerms = zipWith (\id docterm -> ppr id <+> char '=' <+> docterm)+ ids+ docterms+ printSDocs $ (no_pprint <$> unpprintables) ++ sdocTerms+ where+ -- Check whether a TyThing can be processed by :print and friends.+ -- Take only Ids, exclude pseudoops, they don't have any HValues.+ can_pprint :: TyThing -> Bool -- #19394+ can_pprint (AnId x)+ | x `notElem` ghcPrimIds = True+ | otherwise = False+ can_pprint _ = False++ -- Create a short message for a TyThing, that cannot processed by :print+ no_pprint :: TyThing -> SDoc+ no_pprint tything = ppr tything <+>+ text "is not eligible for the :print, :sprint or :force commands."++ -- Helper to print out the results of :print and friends+ printSDocs :: GhcMonad m => [SDoc] -> m ()+ printSDocs sdocs = do+ logger <- getLogger+ name_ppr_ctx <- GHC.getNamePprCtx+ liftIO $ printOutputForUser logger name_ppr_ctx $ vcat sdocs++ -- Do the obtainTerm--bindSuspensions-computeSubstitution dance+ go :: GhcMonad m => Subst -> Id -> m (Subst, Term)+ go subst id = do+ let id' = updateIdTypeAndMult (substTy subst) id+ id_ty' = idType id'+ term_ <- GHC.obtainTermFromId maxBound force id'+ term <- tidyTermTyVars term_+ term' <- if bindThings+ then bindSuspensions term+ else return term+ -- Before leaving, we compare the type obtained to see if it's more specific+ -- Then, we extract a substitution,+ -- mapping the old tyvars to the reconstructed types.+ let reconstructed_type = termType term+ hsc_env <- getSession+ case (improveRTTIType hsc_env id_ty' reconstructed_type) of+ Nothing -> return (subst, term')+ Just subst' -> do { logger <- getLogger+ ; liftIO $+ putDumpFileMaybe logger Opt_D_dump_rtti "RTTI"+ FormatText+ (fsep $ [text "RTTI Improvement for", ppr id,+ text "old substitution:" , ppr subst,+ text "new substitution:" , ppr subst'])+ ; return (subst `unionSubst` subst', term')}++ tidyTermTyVars :: GhcMonad m => Term -> m Term+ tidyTermTyVars t =+ withSession $ \hsc_env -> do+ let env_tvs = tyThingsTyCoVars $ ic_tythings $ hsc_IC hsc_env+ my_tvs = termTyCoVars t+ tvs = env_tvs `minusVarSet` my_tvs+ tyvarOccName = nameOccName . tyVarName+ tidyEnv = (initTidyOccEnv (map tyvarOccName (nonDetEltsUniqSet tvs))+ -- It's OK to use nonDetEltsUniqSet here because initTidyOccEnv+ -- forgets the ordering immediately by creating an env+ , getUniqSet $ env_tvs `intersectVarSet` my_tvs)+ return $ mapTermType (snd . tidyOpenType tidyEnv) t++-- | Give names, and bind in the interactive environment, to all the suspensions+-- included (inductively) in a term+bindSuspensions :: GhcMonad m => Term -> m Term+bindSuspensions t = do+ hsc_env <- getSession+ inScope <- GHC.getBindings+ let ictxt = hsc_IC hsc_env+ prefix = "_t"+ alreadyUsedNames = map (occNameString . nameOccName . getName) inScope+ availNames = map ((prefix++) . show) [(1::Int)..] \\ alreadyUsedNames+ availNames_var <- liftIO $ newIORef availNames+ (t', stuff) <- liftIO $ foldTerm (nameSuspensionsAndGetInfos hsc_env availNames_var) t+ let (names, tys, fhvs) = unzip3 stuff+ let ids = [ mkVanillaGlobal name ty+ | (name,ty) <- zip names tys]+ new_ic = extendInteractiveContextWithIds ictxt ids+ interp = hscInterp hsc_env+ liftIO $ extendLoadedEnv interp (zip names fhvs)+ setSession hsc_env {hsc_IC = new_ic }+ return t'+ where++-- Processing suspensions. Give names and collect info+ nameSuspensionsAndGetInfos :: HscEnv -> IORef [String]+ -> TermFold (IO (Term, [(Name,Type,ForeignHValue)]))+ nameSuspensionsAndGetInfos hsc_env freeNames = TermFold+ {+ fSuspension = doSuspension hsc_env freeNames+ , fTerm = \ty dc v tt -> do+ tt' <- sequence tt+ let (terms,names) = unzip tt'+ return (Term ty dc v terms, concat names)+ , fPrim = \ty n ->return (Prim ty n,[])+ , fNewtypeWrap =+ \ty dc t -> do+ (term, names) <- t+ return (NewtypeWrap ty dc term, names)+ , fRefWrap = \ty t -> do+ (term, names) <- t+ return (RefWrap ty term, names)+ }+ doSuspension hsc_env freeNames ct ty hval _name = do+ name <- atomicModifyIORef' freeNames (\x->(tail x, head x))+ n <- newGrimName hsc_env name+ return (Suspension ct ty hval (Just n), [(n,ty,hval)])+++-- A custom Term printer to enable the use of Show instances+showTerm :: GhcMonad m => Term -> m SDoc+showTerm term = do+ dflags <- GHC.getSessionDynFlags+ if gopt Opt_PrintEvldWithShow dflags+ then cPprTerm (liftM2 (++) (\_y->[cPprShowable]) cPprTermBase) term+ else cPprTerm cPprTermBase term+ where+ cPprShowable prec t@Term{ty=ty, val=fhv} =+ if not (isFullyEvaluatedTerm t)+ then return Nothing+ else do+ let set_session = do+ hsc_env <- getSession+ (new_env, bname) <- bindToFreshName hsc_env ty "showme"+ setSession new_env++ -- this disables logging of errors+ let noop_log _ _ _ _ = return ()+ pushLogHookM (const noop_log)++ return (hsc_env, bname)++ reset_session (old_env,_) = setSession old_env++ MC.bracket set_session reset_session $ \(_,bname) -> do+ hsc_env <- getSession+ dflags <- GHC.getSessionDynFlags+ let expr = "Prelude.return (Prelude.show " +++ showPpr dflags bname +++ ") :: Prelude.IO Prelude.String"+ interp = hscInterp hsc_env+ txt_ <- withExtendedLoadedEnv interp+ [(bname, fhv)]+ (GHC.compileExprRemote expr)+ let myprec = 10 -- application precedence. TODO Infix constructors+ txt <- liftIO $ evalString interp txt_+ if not (null txt) then+ return $ Just $ cparen (prec >= myprec && needsParens txt)+ (text txt)+ else return Nothing++ cPprShowable prec NewtypeWrap{ty=new_ty,wrapped_term=t} =+ cPprShowable prec t{ty=new_ty}+ cPprShowable _ _ = return Nothing++ needsParens ('"':_) = False -- some simple heuristics to see whether parens+ -- are redundant in an arbitrary Show output+ needsParens ('(':_) = False+ needsParens txt = ' ' `elem` txt+++ bindToFreshName hsc_env ty userName = do+ name <- newGrimName hsc_env userName+ let id = mkVanillaGlobal name ty+ new_ic = extendInteractiveContextWithIds (hsc_IC hsc_env) [id]+ return (hsc_env {hsc_IC = new_ic }, name)++-- Create new uniques and give them sequentially numbered names+newGrimName :: MonadIO m => HscEnv -> String -> m Name+newGrimName hsc_env userName+ = liftIO (newInteractiveBinder hsc_env occ noSrcSpan)+ where+ occ = mkOccName varName userName++pprTypeAndContents :: GhcMonad m => Id -> m SDoc+pprTypeAndContents id = do+ dflags <- GHC.getSessionDynFlags+ let pcontents = gopt Opt_PrintBindContents dflags+ pprdId = (pprTyThing showToHeader . AnId) id+ if pcontents+ then do+ let depthBound = 100+ -- If the value is an exception, make sure we catch it and+ -- show the exception, rather than propagating the exception out.+ e_term <- MC.try $ GHC.obtainTermFromId depthBound False id+ docs_term <- case e_term of+ Right term -> showTerm term+ Left exn -> return (text "*** Exception:" <+>+ text (show (exn :: SomeException)))+ return $ pprdId <+> equals <+> docs_term+ else return pprdId
compiler/GHC/Runtime/Eval.hs view
@@ -62,17 +62,16 @@ import GHCi.RemoteTypes import GHC.ByteCode.Types -import GHC.Linker.Types import GHC.Linker.Loader as Loader import GHC.Hs import GHC.Core.Predicate import GHC.Core.InstEnv-import GHC.Core.FamInstEnv ( FamInst )-import GHC.Core.FVs ( orphNamesOfFamInst )+import GHC.Core.FamInstEnv ( FamInst, orphNamesOfFamInst ) import GHC.Core.TyCon import GHC.Core.Type hiding( typeKind )+import GHC.Core.TyCo.Ppr import qualified GHC.Core.Type as Type import GHC.Iface.Env ( newInteractiveBinder )@@ -92,7 +91,6 @@ import GHC.Utils.Outputable import GHC.Utils.Misc import GHC.Utils.Logger-import GHC.Utils.Trace import GHC.Types.RepType import GHC.Types.Fixity.Env@@ -122,6 +120,7 @@ import Data.IntMap (IntMap) import qualified Data.IntMap as IntMap import Data.List (find,intercalate)+import Data.List.NonEmpty (NonEmpty) import Control.Monad import Control.Monad.Catch as MC import Data.Array@@ -629,10 +628,10 @@ = do { name <- newInteractiveBinder hsc_env occ (getSrcSpan old_id) ; return (Id.mkVanillaGlobalWithInfo name ty (idInfo old_id)) } - newTyVars :: UniqSupply -> [TcTyVar] -> TCvSubst+ newTyVars :: UniqSupply -> [TcTyVar] -> Subst -- Similarly, clone the type variables mentioned in the types -- we have here, *and* make them all RuntimeUnk tyvars- newTyVars us tvs = foldl' new_tv emptyTCvSubst (tvs `zip` uniqsFromSupply us)+ newTyVars us tvs = foldl' new_tv emptySubst (tvs `zip` uniqsFromSupply us) new_tv subst (tv,uniq) = extendTCvSubstWithClone subst tv new_tv where new_tv = mkRuntimeUnkTyVar (setNameUnique (tyVarName tv) uniq)@@ -678,9 +677,12 @@ Nothing -> return hsc_env Just new_ty -> do case improveRTTIType hsc_env old_ty new_ty of- Nothing -> return $- warnPprTrace True (":print failed to calculate the "- ++ "improvement for a type") empty hsc_env+ Nothing -> warnPprTrace True (":print failed to calculate the "+ ++ "improvement for a type")+ (vcat [ text "id" <+> ppr id+ , text "old_ty" <+> debugPprType old_ty+ , text "new_ty" <+> debugPprType new_ty ]) $+ return hsc_env Just subst -> do let logger = hsc_logger hsc_env putDumpFileMaybe logger Opt_D_dump_rtti "RTTI"@@ -713,7 +715,7 @@ There are 3 situations where items are removed from the Id list (or replaced with `Nothing`): 1.) If function `GHC.StgToByteCode.schemeER_wrk` (which creates- the Id list) doesn't find an Id in the ByteCode environement.+ the Id list) doesn't find an Id in the ByteCode environment. 2.) If function `GHC.Runtime.Eval.bindLocalsAtBreakpoint` filters out unboxed elements from the Id list, because GHCi cannot yet handle them.@@ -857,7 +859,7 @@ _not_a_home_module -> return False -- | Looks up an identifier in the current interactive context (for :info)--- Filter the instances by the ones whose tycons (or clases resp)+-- Filter the instances by the ones whose tycons (or classes resp) -- are in scope (qualified or otherwise). Otherwise we list a whole lot too many! -- The exact choice of which ones to show, and which to hide, is a judgement call. -- (see #1581)@@ -909,7 +911,7 @@ -- | Parses a string as an identifier, and returns the list of 'Name's that -- the identifier can refer to in the current interactive context.-parseName :: GhcMonad m => String -> m [Name]+parseName :: GhcMonad m => String -> m (NonEmpty Name) parseName str = withSession $ \hsc_env -> liftIO $ do { lrdr_name <- hscParseIdentifier hsc_env str ; hscTcRnLookupRdrName hsc_env lrdr_name }@@ -993,7 +995,7 @@ {- Note [Querying instances for a type]-+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Here is the implementation of GHC proposal 41. (https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0041-ghci-instances.rst) @@ -1058,7 +1060,7 @@ -- Get all the constraints required of a dictionary binding getDictionaryBindings :: PredType -> TcM CtEvidence getDictionaryBindings theta = do- dictName <- newName (mkDictOcc (mkVarOcc "magic"))+ dictName <- newName (mkDictOcc (mkVarOccFS (fsLit "magic"))) let dict_var = mkVanillaGlobal dictName theta loc <- getCtLocM (GivenOrigin (getSkolemInfo unkSkol)) Nothing @@ -1081,8 +1083,8 @@ k -> Constraint where k is the type of the queried type. -} try_cls ies cls- | Just (_, arg_kind, res_kind) <- splitFunTy_maybe (tyConKind $ classTyCon cls)- , tcIsConstraintKind res_kind+ | Just (_, _, arg_kind, res_kind) <- splitFunTy_maybe (tyConKind $ classTyCon cls)+ , isConstraintKind res_kind , Type.typeKind ty `eqType` arg_kind , (matches, _, _) <- lookupInstEnv True ies cls [ty] = matches@@ -1154,7 +1156,7 @@ Just (_, tys@(_:_)) -> all isTyVarTy tys _ -> isTyVarTy ty - empty_subst = mkEmptyTCvSubst (mkInScopeSet (tyCoVarsOfType (idType $ is_dfun clsInst)))+ empty_subst = mkEmptySubst (mkInScopeSet (tyCoVarsOfType (idType $ is_dfun clsInst))) {- Create a ClsInst with instantiated arguments and constraints. @@ -1250,17 +1252,18 @@ showModule :: GhcMonad m => ModSummary -> m String showModule mod_summary = withSession $ \hsc_env -> do- interpreted <- moduleIsBootOrNotObjectLinkable mod_summary let dflags = hsc_dflags hsc_env+ let interpreted =+ case lookupHug (hsc_HUG hsc_env) (ms_unitid mod_summary) (ms_mod_name mod_summary) of+ Nothing -> panic "missing linkable"+ Just mod_info -> isJust (homeModInfoByteCode mod_info) && isNothing (homeModInfoObject mod_info) return (showSDoc dflags $ showModMsg dflags interpreted (ModuleNode [] mod_summary)) moduleIsBootOrNotObjectLinkable :: GhcMonad m => ModSummary -> m Bool moduleIsBootOrNotObjectLinkable mod_summary = withSession $ \hsc_env ->- case lookupHpt (hsc_HPT hsc_env) (ms_mod_name mod_summary) of+ case lookupHug (hsc_HUG hsc_env) (ms_unitid mod_summary) (ms_mod_name mod_summary) of Nothing -> panic "missing linkable"- Just mod_info -> return $ case hm_linkable mod_info of- Nothing -> True- Just linkable -> not (isObjectLinkable linkable)+ Just mod_info -> return . isNothing $ homeModInfoByteCode mod_info ---------------------------------------------------------------------------- -- RTTI primitives
compiler/GHC/Runtime/Heap/Inspect.hs view
@@ -23,7 +23,7 @@ constrClosToName -- exported to use in test T4891 ) where -import GHC.Prelude+import GHC.Prelude hiding (head, init, last, tail) import GHC.Platform import GHC.Runtime.Interpreter as GHCi@@ -36,7 +36,8 @@ import GHC.Types.RepType import GHC.Core.Multiplicity import qualified GHC.Core.Unify as U-import GHC.Types.Var+import GHC.Core.TyCon+ import GHC.Tc.Utils.Monad import GHC.Tc.Utils.TcType import GHC.Tc.Utils.TcMType@@ -44,7 +45,7 @@ import GHC.Tc.Utils.Unify import GHC.Tc.Utils.Env -import GHC.Core.TyCon+import GHC.Types.Var import GHC.Types.Name import GHC.Types.Name.Occurrence as OccName import GHC.Unit.Module@@ -67,6 +68,8 @@ import Control.Monad import Data.Maybe import Data.List ((\\))+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NE import GHC.Exts import qualified Data.Sequence as Seq import Data.Sequence (viewl, ViewL(..))@@ -431,13 +434,14 @@ --Note pprinting of list terms is not lazy ppr_list :: Precedence -> Term -> m SDoc ppr_list p (Term{subTerms=[h,t]}) = do- let elems = h : getListTerms t- isConsLast = not (termType (last elems) `eqType` termType h)+ let elems = h :| getListTerms t+ elemList = toList elems+ isConsLast = not (termType (NE.last elems) `eqType` termType h) is_string = all (isCharTy . ty) elems chars = [ chr (fromIntegral w)- | Term{subTerms=[Prim{valRaw=[w]}]} <- elems ]+ | Term{subTerms=[Prim{valRaw=[w]}]} <- elemList ] - print_elems <- mapM (y cons_prec) elems+ print_elems <- mapM (y cons_prec) elemList if is_string then return (Ppr.doubleQuotes (Ppr.text chars)) else if isConsLast@@ -637,7 +641,7 @@ -} -instTyVars :: [TyVar] -> TR (TCvSubst, [TcTyVar])+instTyVars :: [TyVar] -> TR (Subst, [TcTyVar]) -- Instantiate fresh mutable type variables from some TyVars -- This function preserves the print-name, which helps error messages instTyVars tvs@@ -672,7 +676,7 @@ where do_pair (tc_tv, rtti_tv) = do { tc_ty <- zonkTcTyVar tc_tv- ; case tcGetTyVar_maybe tc_ty of+ ; case getTyVar_maybe tc_ty of Just tv | isMetaTyVar tv -> writeMetaTyVar tv (mkTyVarTy rtti_tv) _ -> return () } @@ -745,10 +749,13 @@ -- we have unsound types. Replace constructor types in -- subterms with tyvars zterm' <- mapTermTypeM- (\ty -> case tcSplitTyConApp_maybe ty of- Just (tc, _:_) | tc /= funTyCon- -> newOpenVar- _ -> return ty)+ (\ty -> case splitTyConApp_maybe ty of+ -- SPJ: I have no idea why we are+ -- matching on (:) here, nor+ -- what the isFunTy is for+ Just (_tc, _ : _) | not (isFunTy ty)+ -> newOpenVar+ _ -> return ty) term zonkTerm zterm' traceTR (text "Term reconstruction completed." $$@@ -781,7 +788,7 @@ EvalSuccess _ -> go (pred max_depth) my_ty old_ty a EvalException ex -> do -- Report the exception to the UI- traceTR $ text "Exception occured:" <+> text (show ex)+ traceTR $ text "Exception occurred:" <+> text (show ex) liftIO $ throwIO $ fromSerializableException ex -- Blackholes are indirections iff the payload is not TSO or BLOCKING_QUEUE. If -- the indirection is a TSO or BLOCKING_QUEUE, we return the BLACKHOLE itself as@@ -836,7 +843,7 @@ Nothing -> do -- This can happen for private constructors compiled -O0 -- where the .hi descriptor does not export them -- In such case, we return a best approximation:- -- ignore the unpointed args, and recover the pointeds+ -- ignore the unpointed args, and recover the pointed ones -- This preserves laziness, and should be safe. traceTR (text "Not constructor" <+> ppr dcname) let dflags = hsc_dflags hsc_env@@ -1101,7 +1108,7 @@ -- improveType <base_type> <rtti_type> -- The types can contain skolem type variables, which need to be treated as normal vars. -- In particular, we want them to unify with things.-improveRTTIType :: HscEnv -> RttiType -> RttiType -> Maybe TCvSubst+improveRTTIType :: HscEnv -> RttiType -> RttiType -> Maybe Subst improveRTTIType _ base_ty new_ty = U.tcUnifyTyKi base_ty new_ty getDataConArgTys :: DataCon -> Type -> TR [Type]@@ -1219,7 +1226,7 @@ ~~~~~~~~~~~ Consider a function obtainType that takes a value and a type and produces the Term representation and a substitution (the improvement).-Assume an auxiliar rtti' function which does the actual job if recovering+Assume an auxiliary rtti' function which does the actual job if recovering the type, but which may produce a false type. In pseudocode:@@ -1343,12 +1350,13 @@ ppr tv, equals, ppr ty_v] go ty_v r -- FunTy inductive case- | Just (w1,l1,l2) <- splitFunTy_maybe l- , Just (w2,r1,r2) <- splitFunTy_maybe r+ | Just (af1,w1,l1,l2) <- splitFunTy_maybe l+ , Just (af2,w2,r1,r2) <- splitFunTy_maybe r+ , af1==af2 , w1 `eqType` w2 = do r2' <- go l2 r2 r1' <- go l1 r1- return (mkVisFunTy w1 r1' r2')+ return (mkFunTy af1 w1 r1' r2') -- TyconApp Inductive case; this is the interesting bit. | Just (tycon_l, _) <- tcSplitTyConApp_maybe lhs , Just (tycon_r, _) <- tcSplitTyConApp_maybe rhs@@ -1413,7 +1421,7 @@ , concrete_args <- [ arg | (tyv,arg) <- tyConTyVars tc `zip` all_args , tyv `notElem` phantom_vars] = all isMonomorphicOnNonPhantomArgs concrete_args- | Just (_, ty1, ty2) <- splitFunTy_maybe ty+ | Just (_, _, ty1, ty2) <- splitFunTy_maybe ty = all isMonomorphicOnNonPhantomArgs [ty1,ty2] | otherwise = isMonomorphic ty
compiler/GHC/Runtime/Loader.hs view
@@ -2,7 +2,7 @@ -- | Dynamically lookup up values from modules and loading them. module GHC.Runtime.Loader (- initializePlugins,+ initializePlugins, initializeSessionPlugins, -- * Loading plugins loadFrontendPlugin, @@ -21,11 +21,13 @@ ) where import GHC.Prelude+import GHC.Data.FastString import GHC.Driver.Session import GHC.Driver.Ppr import GHC.Driver.Hooks import GHC.Driver.Plugins+import GHC.Driver.Plugins.External import GHC.Linker.Loader ( loadModule, loadName ) import GHC.Runtime.Interpreter ( wormhole )@@ -37,15 +39,16 @@ import GHC.Builtin.Names ( pluginTyConName, frontendPluginTyConName ) import GHC.Driver.Env-import GHCi.RemoteTypes ( HValue )-import GHC.Core.Type ( Type, eqType, mkTyConTy )-import GHC.Core.TyCon ( TyCon )+import GHCi.RemoteTypes ( HValue )+import GHC.Core.Type ( Type, mkTyConTy )+import GHC.Core.TyCo.Compare( eqType )+import GHC.Core.TyCon ( TyCon ) import GHC.Types.SrcLoc ( noSrcSpan ) import GHC.Types.Name ( Name, nameModule_maybe ) import GHC.Types.Id ( idType ) import GHC.Types.TyThing-import GHC.Types.Name.Occurrence ( OccName, mkVarOcc )+import GHC.Types.Name.Occurrence ( OccName, mkVarOccFS ) import GHC.Types.Name.Reader ( RdrName, ImportSpec(..), ImpDeclSpec(..) , ImpItemSpec(..), mkGlobalRdrEnv, lookupGRE_RdrName , greMangledName, mkRdrQual )@@ -68,29 +71,60 @@ import GHC.Linker.Types import GHC.Types.Unique.DFM import Data.List (unzip4)+import GHC.Driver.Monad +-- | Initialise plugins specified by the current DynFlags and update the session.+initializeSessionPlugins :: GhcMonad m => m ()+initializeSessionPlugins = getSession >>= liftIO . initializePlugins >>= setSession+ -- | Loads the plugins specified in the pluginModNames field of the dynamic -- flags. Should be called after command line arguments are parsed, but before -- actual compilation starts. Idempotent operation. Should be re-called if -- pluginModNames or pluginModNameOpts changes. initializePlugins :: HscEnv -> IO HscEnv initializePlugins hsc_env- -- plugins not changed+ -- check that plugin specifications didn't change++ -- dynamic plugins | loaded_plugins <- loadedPlugins (hsc_plugins hsc_env) , map lpModuleName loaded_plugins == reverse (pluginModNames dflags)- -- arguments not changed , all same_args loaded_plugins- = return hsc_env -- no need to reload plugins FIXME: doesn't take static plugins into account++ -- external plugins+ , external_plugins <- externalPlugins (hsc_plugins hsc_env)+ , check_external_plugins external_plugins (externalPluginSpecs dflags)++ -- FIXME: we should check static plugins too++ = return hsc_env -- no change, no need to reload plugins+ | otherwise = do (loaded_plugins, links, pkgs) <- loadPlugins hsc_env- let plugins' = (hsc_plugins hsc_env) { loadedPlugins = loaded_plugins, loadedPluginDeps = (links, pkgs) }+ external_plugins <- loadExternalPlugins (externalPluginSpecs dflags)+ let plugins' = (hsc_plugins hsc_env) { staticPlugins = staticPlugins (hsc_plugins hsc_env)+ , externalPlugins = external_plugins+ , loadedPlugins = loaded_plugins+ , loadedPluginDeps = (links, pkgs)+ } let hsc_env' = hsc_env { hsc_plugins = plugins' } withPlugins (hsc_plugins hsc_env') driverPlugin hsc_env' where+ dflags = hsc_dflags hsc_env+ -- dynamic plugins plugin_args = pluginModNameOpts dflags same_args p = paArguments (lpPlugin p) == argumentsForPlugin p plugin_args argumentsForPlugin p = map snd . filter ((== lpModuleName p) . fst)- dflags = hsc_dflags hsc_env+ -- external plugins+ check_external_plugin p spec = and+ [ epUnit p == esp_unit_id spec+ , epModule p == esp_module spec+ , paArguments (epPlugin p) == esp_args spec+ ]+ check_external_plugins eps specs = case (eps,specs) of+ ([] , []) -> True+ (_ , []) -> False -- some external plugin removed+ ([] , _ ) -> False -- some external plugin added+ (p:ps,s:ss) -> check_external_plugin p s && check_external_plugins ps ss loadPlugins :: HscEnv -> IO ([LoadedPlugin], [Linkable], PkgsLoaded) loadPlugins hsc_env@@ -109,14 +143,14 @@ where options = [ option | (opt_mod_nm, option) <- pluginModNameOpts dflags , opt_mod_nm == mod_nm ]- loadPlugin = loadPlugin' (mkVarOcc "plugin") pluginTyConName hsc_env+ loadPlugin = loadPlugin' (mkVarOccFS (fsLit "plugin")) pluginTyConName hsc_env loadFrontendPlugin :: HscEnv -> ModuleName -> IO (FrontendPlugin, [Linkable], PkgsLoaded) loadFrontendPlugin hsc_env mod_name = do checkExternalInterpreter hsc_env (plugin, _iface, links, pkgs)- <- loadPlugin' (mkVarOcc "frontendPlugin") frontendPluginTyConName+ <- loadPlugin' (mkVarOccFS (fsLit "frontendPlugin")) frontendPluginTyConName hsc_env mod_name return (plugin, links, pkgs)
compiler/GHC/Settings/IO.hs view
@@ -94,7 +94,6 @@ cc_args = words cc_args_str ++ unreg_cc_args cxx_args = words cxx_args_str ldSupportsCompactUnwind <- getBooleanSetting "ld supports compact unwind"- ldSupportsBuildId <- getBooleanSetting "ld supports build-id" ldSupportsFilelist <- getBooleanSetting "ld supports filelist" ldIsGnuLd <- getBooleanSetting "ld is GNU ld" arSupportsDashL <- getBooleanSetting "ar supports -L"@@ -108,7 +107,6 @@ unlit_path <- getToolSetting "unlit command" windres_path <- getToolSetting "windres command"- libtool_path <- getToolSetting "libtool command" ar_path <- getToolSetting "ar command" otool_path <- getToolSetting "otool command" install_name_tool_path <- getToolSetting "install_name_tool command"@@ -164,7 +162,6 @@ , sToolSettings = ToolSettings { toolSettings_ldSupportsCompactUnwind = ldSupportsCompactUnwind- , toolSettings_ldSupportsBuildId = ldSupportsBuildId , toolSettings_ldSupportsFilelist = ldSupportsFilelist , toolSettings_ldIsGnuLd = ldIsGnuLd , toolSettings_ccSupportsNoPie = gccSupportsNoPie@@ -182,7 +179,6 @@ , toolSettings_pgm_dll = (mkdll_prog,mkdll_args) , toolSettings_pgm_T = touch_path , toolSettings_pgm_windres = windres_path- , toolSettings_pgm_libtool = libtool_path , toolSettings_pgm_ar = ar_path , toolSettings_pgm_otool = otool_path , toolSettings_pgm_install_name_tool = install_name_tool_path
compiler/GHC/Stg/BcPrep.hs view
@@ -144,7 +144,7 @@ newId :: Type -> BcPrepM Id newId ty = do uniq <- newUnique- return $ mkSysLocal prepFS uniq Many ty+ return $ mkSysLocal prepFS uniq ManyTy ty prepFS :: FastString prepFS = fsLit "bcprep"
compiler/GHC/Stg/CSE.hs view
@@ -117,6 +117,11 @@ , sam_lit :: LiteralMap a } +-- TODO(22292): derive+instance Functor StgArgMap where+ fmap f SAM { sam_var = varm, sam_lit = litm } = SAM+ { sam_var = fmap f varm, sam_lit = fmap f litm }+ instance TrieMap StgArgMap where type Key StgArgMap = StgArg emptyTM = SAM { sam_var = emptyTM@@ -126,13 +131,16 @@ alterTM (StgVarArg var) f m = m { sam_var = sam_var m |> xtDFreeVar var f } alterTM (StgLitArg lit) f m = m { sam_lit = sam_lit m |> alterTM lit f } foldTM k m = foldTM k (sam_var m) . foldTM k (sam_lit m)- mapTM f (SAM {sam_var = varm, sam_lit = litm}) =- SAM { sam_var = mapTM f varm, sam_lit = mapTM f litm } filterTM f (SAM {sam_var = varm, sam_lit = litm}) = SAM { sam_var = filterTM f varm, sam_lit = filterTM f litm } newtype ConAppMap a = CAM { un_cam :: DNameEnv (ListMap StgArgMap a) } +-- TODO(22292): derive+instance Functor ConAppMap where+ fmap f = CAM . fmap (fmap f) . un_cam+ {-# INLINE fmap #-}+ instance TrieMap ConAppMap where type Key ConAppMap = (DataCon, [StgArg]) emptyTM = CAM emptyTM@@ -140,8 +148,7 @@ alterTM (dataCon, args) f m = m { un_cam = un_cam m |> xtDNamed dataCon |>> alterTM args f } foldTM k = un_cam >.> foldTM (foldTM k)- mapTM f = un_cam >.> mapTM (mapTM f) >.> CAM- filterTM f = un_cam >.> mapTM (filterTM f) >.> CAM+ filterTM f = un_cam >.> fmap (filterTM f) >.> CAM ----------------- -- The CSE Env --
compiler/GHC/Stg/Debug.hs view
@@ -252,7 +252,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In the old times, each usage of a data constructor used the same info table.-This made it impossible to distinguish which actual usuage of a data constructor was+This made it impossible to distinguish which actual usage of a data constructor was contributing primarily to the allocation in a program. Using the `-fdistinct-info-tables` flag you can cause code generation to generate a distinct info table for each usage of a constructor. Then, when inspecting the heap you can see precisely which usage of a constructor
compiler/GHC/Stg/InferTags.hs view
@@ -183,7 +183,7 @@ careful to make sure we still enter them when needed. As it makes little difference for runtime performance I've treated functions as TagDunno in a few places where it made the code simpler. But besides implementation complexity there isn't any reason-why we couldn't be more rigourous in dealing with functions.+why we couldn't be more rigorous in dealing with functions. NB: It turned in #21193 that PAPs get tag zero, so the tag check can't be omitted for functions. So option two isn't really an option without reworking this anyway.@@ -212,7 +212,7 @@ a) Tags for let-bindings: When compiling a binding for a constructor like `let x = Just True`- Whether `x` will be properly tagged depends on the backend.+ Weither or not `x` results in x pointing depends on the backend. For the interpreter x points to a BCO which once evaluated returns a properly tagged pointer to the heap object. In the Cmm backend for the same binding we would allocate the constructor right@@ -602,7 +602,7 @@ let f<?> z = case z of z'<TagProper> -> (# c, x #) Here we will infer for f the Signature <TagTuple[TagProper,TagDunno]>.-This information will be used if we scrutinze a saturated application of+This information will be used if we scrutinize a saturated application of `f` in order to determine the taggedness of the result. That is for `case f x of (# r1,r2 #) -> rhs` we can infer r1<TagProper> and r2<TagDunno> which allows us to skip all tag checks on `r1`
compiler/GHC/Stg/InferTags/Rewrite.hs view
@@ -20,26 +20,29 @@ import GHC.Prelude +import GHC.Builtin.PrimOps ( PrimOp(..) )+import GHC.Types.Basic ( CbvMark (..), isMarkedCbv+ , TopLevelFlag(..), isTopLevel+ , Levity(..) ) import GHC.Types.Id import GHC.Types.Name import GHC.Types.Unique.Supply import GHC.Types.Unique.FM import GHC.Types.RepType-import GHC.Unit.Types (Module, isInteractiveModule)+import GHC.Types.Var.Set+import GHC.Unit.Types import GHC.Core.DataCon-import GHC.Core (AltCon(..) )+import GHC.Core ( AltCon(..) ) import GHC.Core.Type import GHC.StgToCmm.Types-import GHC.StgToCmm.Closure (mkLFImported) import GHC.Stg.Utils import GHC.Stg.Syntax as StgSyn import GHC.Data.Maybe import GHC.Utils.Panic-import GHC.Utils.Panic.Plain import GHC.Utils.Outputable import GHC.Utils.Monad.State.Strict@@ -48,10 +51,6 @@ import GHC.Stg.InferTags.Types import Control.Monad-import GHC.Types.Basic (CbvMark (NotMarkedCbv, MarkedCbv), isMarkedCbv, TopLevelFlag(..), isTopLevel)-import GHC.Types.Var.Set--- import GHC.Utils.Trace--- import GHC.Driver.Ppr newtype RM a = RM { unRM :: (State (UniqFM Id TagSig, UniqSupply, Module, IdSet) a) } deriving (Functor, Monad, Applicative)@@ -89,12 +88,12 @@ $wf :: (a -> b -> d -> c) -> a -> b -> c -> d $wf m1 a b c = m1 a b c -It's notable that the worker is called *undersatured* in the wrapper.+It's notable that the worker is called *undersaturated* in the wrapper. At runtime what happens is that the wrapper will allocate a PAP which once fully applied will call the worker. And all is fine. But what about a call by value function! Well the function returned by `f` would-be a unknown call, so we lose the ability to enfore the invariant that+be a unknown call, so we lose the ability to enforce the invariant that cbv marked arguments from StictWorkerId's are actually properly tagged as the annotations would be unavailable at the (unknown) call site. @@ -129,7 +128,7 @@ getMap = RM $ ((\(fst,_,_,_) -> fst) <$> get) setMap :: (UniqFM Id TagSig) -> RM ()-setMap m = RM $ do+setMap !m = RM $ do (_,us,mod,lcls) <- get put (m, us,mod,lcls) @@ -140,7 +139,7 @@ getFVs = RM $ ((\(_,_,_,lcls) -> lcls) <$> get) setFVs :: IdSet -> RM ()-setFVs fvs = RM $ do+setFVs !fvs = RM $ do (tag_map,us,mod,_lcls) <- get put (tag_map, us,mod,fvs) @@ -196,9 +195,9 @@ withClosureLcls :: DIdSet -> RM a -> RM a withClosureLcls fvs act = do old_fvs <- getFVs- let fvs' = nonDetStrictFoldDVarSet (flip extendVarSet) old_fvs fvs+ let !fvs' = nonDetStrictFoldDVarSet (flip extendVarSet) old_fvs fvs setFVs fvs'- r <- act+ !r <- act setFVs old_fvs return r @@ -207,9 +206,9 @@ withLcl :: Id -> RM a -> RM a withLcl fv act = do old_fvs <- getFVs- let fvs' = extendVarSet old_fvs fv+ let !fvs' = extendVarSet old_fvs fv setFVs fvs'- r <- act+ !r <- act setFVs old_fvs return r @@ -217,7 +216,8 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When compiling bytecode we call myCoreToStg to get STG code first. myCoreToStg in turn calls out to stg2stg which runs the STG to STG-passes followed by free variables analysis and tag inference at the end.+passes followed by free variables analysis and the tag inference pass including+it's rewriting phase at the end. Running tag inference is important as it upholds Note [Strict Field Invariant]. While code executed by GHCi doesn't take advantage of the SFI it can call into compiled code which does. So it must still make sure that the SFI is upheld.@@ -257,7 +257,9 @@ (TagSig TagDunno) case nameIsLocalOrFrom this_mod (idName v) of True- | isUnliftedType (idType v)+ | Just Unlifted <- typeLevity_maybe (idType v)+ -- NB: v might be the Id of a representation-polymorphic join point,+ -- so we shouldn't use isUnliftedType here. See T22212. -> return True | otherwise -> do -- Local binding !s <- getMap@@ -270,10 +272,13 @@ TagTagged -> True TagTuple _ -> True -- Consider unboxed tuples tagged. False -- Imported+ | Just con <- (isDataConWorkId_maybe v)+ , isNullaryRepDataCon con+ -> return True+ | Just lf_info <- idLFInfo_maybe v -> return $!- -- Determine whether it is tagged from the LFInfo of the imported id.- -- See Note [The LFInfo of Imported Ids]- case mkLFImported v of+ -- Can we treat the thing as tagged based on it's LFInfo?+ case lf_info of -- Function, applied not entered. LFReEntrant {} -> True@@ -291,7 +296,10 @@ -- Shouldn't be possible. I don't think we can export letNoEscapes -> True + | otherwise+ -> return False + isArgTagged :: StgArg -> RM Bool isArgTagged (StgLitArg _) = return True isArgTagged (StgVarArg v) = isTagged v@@ -366,14 +374,11 @@ fvs <- fvArgs args -- lcls <- getFVs -- pprTraceM "RhsClosureConversion" (ppr (StgRhsClosure fvs ccs ReEntrant [] $! conExpr) $$ text "lcls:" <> ppr lcls)-- -- We mark the closure updatable to retain sharing in the case that- -- conExpr is an infinite recursive data type. See #23783.- return $! (StgRhsClosure fvs ccs Updatable [] $! conExpr)+ return $! (StgRhsClosure fvs ccs ReEntrant [] $! conExpr) rewriteRhs _binding (StgRhsClosure fvs ccs flag args body) = do withBinders NotTopLevel args $ withClosureLcls fvs $- StgRhsClosure fvs ccs flag (map fst args) <$> rewriteExpr False body+ StgRhsClosure fvs ccs flag (map fst args) <$> rewriteExpr body -- return (closure) fvArgs :: [StgArg] -> RM DVarSet@@ -382,24 +387,36 @@ -- pprTraceM "fvArgs" (text "args:" <> ppr args $$ text "lcls:" <> pprVarSet (fv_lcls) (braces . fsep . map ppr) ) return $ mkDVarSet [ v | StgVarArg v <- args, elemVarSet v fv_lcls] -type IsScrut = Bool+rewriteArgs :: [StgArg] -> RM [StgArg]+rewriteArgs = mapM rewriteArg+rewriteArg :: StgArg -> RM StgArg+rewriteArg (StgVarArg v) = StgVarArg <$!> rewriteId v+rewriteArg (lit@StgLitArg{}) = return lit -rewriteExpr :: IsScrut -> InferStgExpr -> RM TgStgExpr-rewriteExpr _ (e@StgCase {}) = rewriteCase e-rewriteExpr _ (e@StgLet {}) = rewriteLet e-rewriteExpr _ (e@StgLetNoEscape {}) = rewriteLetNoEscape e-rewriteExpr isScrut (StgTick t e) = StgTick t <$!> rewriteExpr isScrut e-rewriteExpr _ e@(StgConApp {}) = rewriteConApp e+rewriteId :: Id -> RM Id+rewriteId v = do+ !is_tagged <- isTagged v+ if is_tagged then return $! setIdTagSig v (TagSig TagProper)+ else return v -rewriteExpr isScrut e@(StgApp {}) = rewriteApp isScrut e-rewriteExpr _ (StgLit lit) = return $! (StgLit lit)-rewriteExpr _ (StgOpApp op args res_ty) = return $! (StgOpApp op args res_ty)+rewriteExpr :: InferStgExpr -> RM TgStgExpr+rewriteExpr (e@StgCase {}) = rewriteCase e+rewriteExpr (e@StgLet {}) = rewriteLet e+rewriteExpr (e@StgLetNoEscape {}) = rewriteLetNoEscape e+rewriteExpr (StgTick t e) = StgTick t <$!> rewriteExpr e+rewriteExpr e@(StgConApp {}) = rewriteConApp e+rewriteExpr e@(StgApp {}) = rewriteApp e+rewriteExpr (StgLit lit) = return $! (StgLit lit)+rewriteExpr (StgOpApp op@(StgPrimOp DataToTagOp) args res_ty) = do+ (StgOpApp op) <$!> rewriteArgs args <*> pure res_ty+rewriteExpr (StgOpApp op args res_ty) = return $! (StgOpApp op args res_ty) + rewriteCase :: InferStgExpr -> RM TgStgExpr rewriteCase (StgCase scrut bndr alt_type alts) = withBinder NotTopLevel bndr $ pure StgCase <*>- rewriteExpr True scrut <*>+ rewriteExpr scrut <*> pure (fst bndr) <*> pure alt_type <*> mapM rewriteAlt alts@@ -409,7 +426,7 @@ rewriteAlt :: InferStgAlt -> RM TgStgAlt rewriteAlt alt@GenStgAlt{alt_con=_, alt_bndrs=bndrs, alt_rhs=rhs} = withBinders NotTopLevel bndrs $ do- !rhs' <- rewriteExpr False rhs+ !rhs' <- rewriteExpr rhs return $! alt {alt_bndrs = map fst bndrs, alt_rhs = rhs'} rewriteLet :: InferStgExpr -> RM TgStgExpr@@ -417,7 +434,7 @@ (!bind') <- rewriteBinds NotTopLevel bind withBind NotTopLevel bind $ do -- pprTraceM "withBindLet" (ppr $ bindersOfX bind)- !expr' <- rewriteExpr False expr+ !expr' <- rewriteExpr expr return $! (StgLet xt bind' expr') rewriteLet _ = panic "Impossible" @@ -425,7 +442,7 @@ rewriteLetNoEscape (StgLetNoEscape xt bind expr) = do (!bind') <- rewriteBinds NotTopLevel bind withBind NotTopLevel bind $ do- !expr' <- rewriteExpr False expr+ !expr' <- rewriteExpr expr return $! (StgLetNoEscape xt bind' expr') rewriteLetNoEscape _ = panic "Impossible" @@ -445,24 +462,18 @@ rewriteConApp _ = panic "Impossible" --- Special case: Expressions like `case x of { ... }`-rewriteApp :: IsScrut -> InferStgExpr -> RM TgStgExpr-rewriteApp True (StgApp f []) = do- -- pprTraceM "rewriteAppScrut" (ppr f)- f_tagged <- isTagged f- -- isTagged looks at more than the result of our analysis.- -- So always update here if useful.- let f' = if f_tagged- then setIdTagSig f (TagSig TagProper)- else f+-- Special case: Atomic binders, usually in a case context like `case f of ...`.+rewriteApp :: InferStgExpr -> RM TgStgExpr+rewriteApp (StgApp f []) = do+ f' <- rewriteId f return $! StgApp f' []-rewriteApp _ (StgApp f args)+rewriteApp (StgApp f args) -- pprTrace "rewriteAppOther" (ppr f <+> ppr args) False -- = undefined | Just marks <- idCbvMarks_maybe f , relevant_marks <- dropWhileEndLE (not . isMarkedCbv) marks , any isMarkedCbv relevant_marks- = assert (length relevant_marks <= length args)+ = assertPpr (length relevant_marks <= length args) (ppr f $$ ppr args $$ ppr relevant_marks) unliftArg relevant_marks where@@ -477,8 +488,8 @@ cbvArgIds = [x | StgVarArg x <- map fstOf3 cbvArgInfo] :: [Id] mkSeqs args cbvArgIds (\cbv_args -> StgApp f cbv_args) -rewriteApp _ (StgApp f args) = return $ StgApp f args-rewriteApp _ _ = panic "Impossible"+rewriteApp (StgApp f args) = return $ StgApp f args+rewriteApp _ = panic "Impossible" -- `mkSeq` x x' e generates `case x of x' -> e` -- We could also substitute x' for x in e but that's so rarely beneficial@@ -517,14 +528,20 @@ -- Out of all arguments passed at runtime only return these ending up in a -- strict field-getStrictConArgs :: DataCon -> [a] -> [a]+getStrictConArgs :: Outputable a => DataCon -> [a] -> [a] getStrictConArgs con args -- These are always lazy in their arguments. | isUnboxedTupleDataCon con = [] | isUnboxedSumDataCon con = [] -- For proper data cons we have to check. | otherwise =+ assertPpr (length args == length (dataConRuntimeRepStrictness con))+ (text "Mismatched con arg and con rep strictness lengths:" $$+ text "Con" <> ppr con <+> text "is applied to" <+> ppr args $$+ text "But seems to have arity" <> ppr (length repStrictness)) $ [ arg | (arg,MarkedStrict) <- zipEqual "getStrictConArgs" args- (dataConRuntimeRepStrictness con)]+ repStrictness]+ where+ repStrictness = (dataConRuntimeRepStrictness con)
compiler/GHC/Stg/Lift/Analysis.hs view
@@ -326,7 +326,7 @@ rhsCard :: Id -> Card rhsCard bndr | is_thunk = oneifyCard n- | otherwise = fst (peelManyCalls (idArity bndr) cd)+ | otherwise = n `multCard` (fst $ peelManyCalls (idArity bndr) cd) where is_thunk = idArity bndr == 0 -- Let's pray idDemandInfo is still OK after unarise...
compiler/GHC/Stg/Lift/Monad.hs view
@@ -276,13 +276,13 @@ withLiftedBndr :: DIdSet -> Id -> (Id -> LiftM a) -> LiftM a withLiftedBndr abs_ids bndr inner = do uniq <- getUniqueM- let str = "$l" ++ occNameString (getOccName bndr)+ let str = fsLit "$l" `appendFS` occNameFS (getOccName bndr) let ty = mkLamTypes (dVarSetElems abs_ids) (idType bndr) let bndr' -- See Note [transferPolyIdInfo] in GHC.Types.Id. We need to do this at least -- for arity information. = transferPolyIdInfo bndr (dVarSetElems abs_ids)- . mkSysLocal (mkFastString str) uniq Many+ . mkSysLocal str uniq ManyTy $ ty LiftM $ RWS.local (\e -> e
compiler/GHC/Stg/Lint.hs view
@@ -6,7 +6,7 @@ - Variables should be defined before used. - Let bindings should not have unboxed types (unboxed bindings should only- appear in case), except when they're join points (see Note [Core let/app+ appear in case), except when they're join points (see Note [Core let-can-float invariant] and #14117). - If linting after unarisation, invariants listed in Note [Post-unarisation@@ -77,8 +77,8 @@ More precisely, if f :: ty1 -> ty2, then in the application (f e) where e :: ty1', we must have kind(ty1) = kind(ty1'). -So the STG type system does not distinguish beteen Int and Bool,-but it /does/ distinguish beteen Int and Int#, because they have+So the STG type system does not distinguish between Int and Bool,+but it /does/ distinguish between Int and Int#, because they have different kinds. Actually, since all terms have kind (TYPE rep), we might say that the STG language is well-runtime-rep'd. @@ -101,7 +101,6 @@ import GHC.Stg.Syntax import GHC.Stg.Utils -import GHC.Core.Lint ( interactiveInScope ) import GHC.Core.DataCon import GHC.Core ( AltCon(..) ) import GHC.Core.Type@@ -121,11 +120,9 @@ import qualified GHC.Utils.Error as Err import GHC.Unit.Module ( Module )-import GHC.Runtime.Context ( InteractiveContext ) import GHC.Data.Bag ( Bag, emptyBag, isEmptyBag, snocBag, bagToList ) -import Control.Applicative ((<|>)) import Control.Monad import Data.Maybe import GHC.Utils.Misc@@ -139,14 +136,14 @@ -> Logger -> DiagOpts -> StgPprOpts- -> InteractiveContext+ -> [Var] -- ^ extra vars in scope from GHCi -> Module -- ^ module being compiled -> Bool -- ^ have we run Unarise yet? -> String -- ^ who produced the STG? -> [GenStgTopBinding a] -> IO () -lintStgTopBindings platform logger diag_opts opts ictxt this_mod unarised whodunnit binds+lintStgTopBindings platform logger diag_opts opts extra_vars this_mod unarised whodunit binds = {-# SCC "StgLint" #-} case initL platform diag_opts this_mod unarised opts top_level_binds (lint_binds binds) of Nothing ->@@ -155,7 +152,7 @@ logMsg logger Err.MCDump noSrcSpan $ withPprStyle defaultDumpStyle (vcat [ text "*** Stg Lint ErrMsgs: in" <+>- text whodunnit <+> text "***",+ text whodunit <+> text "***", msg, text "*** Offending Program ***", pprGenStgTopBindings opts binds,@@ -165,7 +162,7 @@ -- Bring all top-level binds into scope because CoreToStg does not generate -- bindings in dependency order (so we may see a use before its definition). top_level_binds = extendVarSetList (mkVarSet (bindersOfTopBinds binds))- (interactiveInScope ictxt)+ extra_vars lint_binds :: [GenStgTopBinding a] -> LintM () @@ -261,18 +258,21 @@ lintStgExpr e@(StgApp fun args) = do lintStgVar fun mapM_ lintStgArg args- lintAppCbvMarks e lintStgAppReps fun args ++ lintStgExpr app@(StgConApp con _n args _arg_tys) = do -- unboxed sums should vanish during unarise lf <- getLintFlags- opts <- getStgPprOpts- when (lf_unarised lf && isUnboxedSumDataCon con) $ do+ let !unarised = lf_unarised lf+ when (unarised && isUnboxedSumDataCon con) $ do+ opts <- getStgPprOpts addErrL (text "Unboxed sum after unarise:" $$ pprStgExpr opts app) + opts <- getStgPprOpts lintConApp con args (pprStgExpr opts app) mapM_ lintStgArg args@@ -524,15 +524,13 @@ -- Post-unarisation args and case alt binders should not have unboxed tuple, -- unboxed sum, or void types. Return what the binder is if it is one of these. checkPostUnariseId :: Id -> Maybe String-checkPostUnariseId id =- let- id_ty = idType id- is_sum, is_tuple, is_void :: Maybe String- is_sum = guard (isUnboxedSumType id_ty) >> return "unboxed sum"- is_tuple = guard (isUnboxedTupleType id_ty) >> return "unboxed tuple"- is_void = guard (isZeroBitTy id_ty) >> return "void"- in- is_sum <|> is_tuple <|> is_void+checkPostUnariseId id+ | isUnboxedSumType id_ty = Just "unboxed sum"+ | isUnboxedTupleType id_ty = Just "unboxed tuple"+ | isZeroBitTy id_ty = Just "void"+ | otherwise = Nothing+ where+ id_ty = idType id addErrL :: SDoc -> LintM () addErrL msg = LintM $ \_mod _lf df _opts loc _scope errs -> ((), addErr df errs msg loc)@@ -542,7 +540,7 @@ = errs_so_far `snocBag` mk_msg locs where mk_msg (loc:_) = let (l,hdr) = dumpLoc loc- in mkLocMessage (Err.mkMCDiagnostic diag_opts WarningWithoutFlag)+ in mkLocMessage (Err.mkMCDiagnostic diag_opts WarningWithoutFlag Nothing) l (hdr $$ msg) mk_msg [] = msg
compiler/GHC/Stg/Pipeline.hs view
@@ -18,6 +18,8 @@ import GHC.Prelude +import GHC.Driver.Flags+ import GHC.Stg.Syntax import GHC.Stg.Lint ( lintStgTopBindings )@@ -28,10 +30,9 @@ import GHC.Stg.CSE ( stgCse ) import GHC.Stg.Lift ( StgLiftConfig, stgLiftLams ) import GHC.Unit.Module ( Module )-import GHC.Runtime.Context ( InteractiveContext ) -import GHC.Driver.Flags (DumpFlag(..)) import GHC.Utils.Error+import GHC.Types.Var import GHC.Types.Unique.Supply import GHC.Utils.Outputable import GHC.Utils.Logger@@ -70,12 +71,12 @@ runStgM mask (StgM m) = runReaderT m mask stg2stg :: Logger- -> InteractiveContext+ -> [Var] -- ^ extra vars in scope from GHCi -> StgPipelineOpts- -> Module -- module being compiled- -> [StgTopBinding] -- input program+ -> Module -- ^ module being compiled+ -> [StgTopBinding] -- ^ input program -> IO ([CgStgTopBinding], StgCgInfos) -- output program-stg2stg logger ictxt opts this_mod binds+stg2stg logger extra_vars opts this_mod binds = do { dump_when Opt_D_dump_stg_from_core "Initial STG:" binds ; showPass logger "Stg2Stg" -- Do the main business!@@ -103,9 +104,9 @@ = lintStgTopBindings (stgPlatform opts) logger diag_opts ppr_opts- ictxt this_mod unarised+ extra_vars this_mod unarised | otherwise- = \ _whodunnit _binds -> return ()+ = \ _whodunit _binds -> return () ------------------------------------------- do_stg_pass :: Module -> [StgTopBinding] -> StgToDo -> StgM [StgTopBinding]
compiler/GHC/Stg/Subst.hs view
@@ -11,7 +11,13 @@ import GHC.Utils.Outputable import GHC.Utils.Misc import GHC.Utils.Panic-import GHC.Utils.Trace++-- TODO: This code might make folly of the work done in CorePrep where+-- we clone local ids in order to ensure *all* local binders are unique.+-- It's my understanding that here we use "the rapier"/uniqAway which makes up+-- uniques based on the ids in scope. Which can give the same unique to different+-- binders as long as they are in different scopes. A guarantee which isn't+-- strong enough for code generation in general. See Note [CorePrep Overview]. -- | A renaming substitution from 'Id's to 'Id's. Like 'RnEnv2', but not -- maintaining pairs of substitutions. Like 'GHC.Core.Subst.Subst', but
compiler/GHC/Stg/Unarise.hs view
@@ -2,8 +2,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TupleSections #-} -{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}- {- (c) The GRASP/AQUA Project, Glasgow University, 1992-2012 @@ -391,6 +389,7 @@ import GHC.Utils.Outputable import GHC.Utils.Panic import GHC.Utils.Panic.Plain+import GHC.Types.RepType import GHC.Stg.Syntax import GHC.Stg.Utils import GHC.Core.Type@@ -400,9 +399,9 @@ import GHC.Types.Unique import GHC.Utils.Misc import GHC.Types.Var.Env-import GHC.Types.RepType import Data.Bifunctor (second)+import Data.List.NonEmpty (NonEmpty (..)) import Data.Maybe (mapMaybe) import qualified Data.IntMap as IM import GHC.Builtin.PrimOps@@ -440,7 +439,7 @@ initUnariseEnv = UnariseEnv data UnariseVal = MultiVal [OutStgArg] -- MultiVal to tuple. Can be empty list (void).- | UnaryVal OutStgArg -- See NOTE [Renaming during unarisation].+ | UnaryVal OutStgArg -- See Note [Renaming during unarisation]. instance Outputable UnariseVal where ppr (MultiVal args) = text "MultiVal" <+> ppr args@@ -595,13 +594,13 @@ -- Doesn't return void args. unariseRubbish_maybe :: Literal -> Maybe [OutStgArg]-unariseRubbish_maybe (LitRubbish rep)+unariseRubbish_maybe (LitRubbish torc rep) | [prep] <- preps , not (isVoidRep prep) = Nothing -- Single, non-void PrimRep. Nothing to do! | otherwise -- Multiple reps, possibly with VoidRep. Eliminate via elimCase- = Just [ StgLitArg (LitRubbish (primRepToType prep))+ = Just [ StgLitArg (LitRubbish torc (primRepToRuntimeRep prep)) | prep <- preps, not (isVoidRep prep) ] where preps = runtimeRepPrimRep (text "unariseRubbish_maybe") rep@@ -632,10 +631,9 @@ unariseExpr rho2 rhs' -elimCase rho args bndr (MultiValAlt _) alts+elimCase rho args@(tag_arg : real_args) bndr (MultiValAlt _) alts | isUnboxedSumBndr bndr- = do let (tag_arg : real_args) = args- tag_bndr <- mkId (mkFastString "tag") tagTy+ = do tag_bndr <- mkId (mkFastString "tag") tagTy -- this won't be used but we need a binder anyway let rho1 = extendRho rho bndr (MultiVal args) scrut' = case tag_arg of@@ -848,7 +846,7 @@ -- Construct a case binder used when casting sums, of a given type and unique. mkCastVar :: Unique -> Type -> Id-mkCastVar uq ty = mkSysLocal (fsLit "cst_sum") uq Many ty+mkCastVar uq ty = mkSysLocal (fsLit "cst_sum") uq ManyTy ty mkCast :: StgArg -> PrimOp -> OutId -> Type -> StgExpr -> StgExpr mkCast arg_in cast_op out_id out_ty in_rhs =@@ -879,7 +877,7 @@ ) mkUbxSum dc ty_args args0 us = let- (_ : sum_slots) = ubxSumRepType (map typePrimRep ty_args)+ _ :| sum_slots = ubxSumRepType (map typePrimRep ty_args) -- drop tag slot field_slots = (mapMaybe (typeSlotTy . stgArgType) args0) tag = dataConTag dc@@ -946,20 +944,18 @@ -- See Note [aBSENT_SUM_FIELD_ERROR_ID] in "GHC.Core.Make" -- ubxSumRubbishArg :: SlotTy -> StgArg-ubxSumRubbishArg PtrLiftedSlot = StgVarArg aBSENT_SUM_FIELD_ERROR_ID-ubxSumRubbishArg PtrUnliftedSlot = StgVarArg aBSENT_SUM_FIELD_ERROR_ID-ubxSumRubbishArg WordSlot = StgLitArg (LitNumber LitNumWord 0)-ubxSumRubbishArg Word64Slot = StgLitArg (LitNumber LitNumWord64 0)-ubxSumRubbishArg FloatSlot = StgLitArg (LitFloat 0)-ubxSumRubbishArg DoubleSlot = StgLitArg (LitDouble 0)-ubxSumRubbishArg (VecSlot n e) = StgLitArg (LitRubbish vec_rep)+ubxSumRubbishArg PtrLiftedSlot = StgVarArg aBSENT_SUM_FIELD_ERROR_ID+ubxSumRubbishArg PtrUnliftedSlot = StgVarArg aBSENT_SUM_FIELD_ERROR_ID+ubxSumRubbishArg WordSlot = StgLitArg (LitNumber LitNumWord 0)+ubxSumRubbishArg Word64Slot = StgLitArg (LitNumber LitNumWord64 0)+ubxSumRubbishArg FloatSlot = StgLitArg (LitFloat 0)+ubxSumRubbishArg DoubleSlot = StgLitArg (LitDouble 0)+ubxSumRubbishArg (VecSlot n e) = StgLitArg (LitRubbish TypeLike vec_rep) where vec_rep = primRepToRuntimeRep (VecRep n e) -------------------------------------------------------------------------------- {--Note [Unarisation of Void binders and arguments]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For arguments (StgArg) and binders (Id) we have two kind of unarisation: - When unarising function arg binders and arguments, we don't want to remove@@ -971,7 +967,7 @@ Here after unarise we should still get a function with arity 3. Similarly in the call site we shouldn't remove void arguments: - f (# (# #), (# #) #) voidId rw+ f (# (# #), (# #) #) void# rw When unarising <body>, we extend the environment with these binders:
compiler/GHC/Stg/Utils.hs view
@@ -35,7 +35,7 @@ mkUnarisedIds fs tys = mapM (mkUnarisedId fs) tys mkUnarisedId :: MonadUnique m => FastString -> UnaryType -> m Id-mkUnarisedId s t = mkSysLocalM s Many t+mkUnarisedId s t = mkSysLocalM s ManyTy t -- Checks if id is a top level error application. -- isErrorAp_maybe :: Id ->@@ -72,8 +72,7 @@ prim_reps = typePrimRep bndr_ty _is_poly_alt_tycon tc- = isFunTyCon tc- || isPrimTyCon tc -- "Any" is lifted but primitive+ = isPrimTyCon tc -- "Any" is lifted but primitive || isFamilyTyCon tc -- Type family; e.g. Any, or arising from strict -- function application where argument has a -- type-family type
compiler/GHC/StgToByteCode.hs view
@@ -5,7 +5,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# OPTIONS_GHC -fprof-auto-top #-}-{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} -- -- (c) The University of Glasgow 2002-2006@@ -44,6 +43,7 @@ import GHC.Builtin.PrimOps import GHC.Builtin.PrimOps.Ids (primOpId) import GHC.Core.Type+import GHC.Core.TyCo.Compare (eqType) import GHC.Types.RepType import GHC.Core.DataCon import GHC.Core.TyCon@@ -319,7 +319,7 @@ -- by just re-using the single top-level definition. So -- for the worker itself, we must allocate it directly. -- ioToBc (putStrLn $ "top level BCO")- emitBc (mkProtoBCO platform (getName id) (toOL [PACK data_con 0, RETURN P])+ emitBc (mkProtoBCO platform (getName id) (toOL [PACK data_con 0, RETURN]) (Right rhs) 0 0 [{-no bitmap-}] False{-not alts-}) | otherwise@@ -480,13 +480,14 @@ non_void VoidRep = False non_void _ = True ret <- case filter non_void reps of- -- use RETURN for nullary/unary representations- [] -> return (unitOL $ RETURN V)- [rep] -> return (unitOL $ RETURN (toArgRep platform rep))+ -- use RETURN_UBX for unary representations+ [] -> return (unitOL $ RETURN_UNLIFTED V)+ [rep] -> return (unitOL $ RETURN_UNLIFTED (toArgRep platform rep)) -- otherwise use RETURN_TUPLE with a tuple descriptor nv_reps -> do let (call_info, args_offsets) = layoutNativeCall profile NativeTupleReturn 0 (primRepCmmType platform) nv_reps- tuple_bco <- emitBc (tupleBCO platform call_info args_offsets)+ args_ptrs = map (\(rep, off) -> (isFollowableArg (toArgRep platform rep), off)) args_offsets+ tuple_bco <- emitBc (tupleBCO platform call_info args_ptrs) return $ PUSH_UBX (mkNativeCallInfoLit platform call_info) 1 `consOL` PUSH_BCO tuple_bco `consOL` unitOL RETURN_TUPLE@@ -526,7 +527,7 @@ :: StackDepth -> Sequel -> BCEnv -> CgStgExpr -> BcM BCInstrList schemeE d s p (StgLit lit) = returnUnliftedAtom d s p (StgLitArg lit) schemeE d s p (StgApp x [])- | isUnliftedType (idType x) = returnUnliftedAtom d s p (StgVarArg x)+ | not (usePlainReturn (idType x)) = returnUnliftedAtom d s p (StgVarArg x) -- Delegate tail-calls to schemeT. schemeE d s p e@(StgApp {}) = schemeT d s p e schemeE d s p e@(StgConApp {}) = schemeT d s p e@@ -681,8 +682,8 @@ schemeT d s p (StgOpApp (StgPrimCallOp (PrimCall label unit)) args result_ty) = generatePrimCall d s p label (Just unit) result_ty args -schemeT d s p (StgConApp con _cn args _tys) -- Case 2: Unboxed tuple+schemeT d s p (StgConApp con _cn args _tys) | isUnboxedTupleDataCon con || isUnboxedSumDataCon con = returnUnboxedTuple d s p args @@ -691,7 +692,7 @@ = do alloc_con <- mkConAppCode d s p con args platform <- profilePlatform <$> getProfile return (alloc_con `appOL`- mkSlideW 1 (bytesToWords platform $ d - s) `snocOL` RETURN P)+ mkSlideW 1 (bytesToWords platform $ d - s) `snocOL` RETURN) -- Case 4: Tail call of function schemeT d s p (StgApp fn args)@@ -831,11 +832,14 @@ -- have the same runtime rep. We have more efficient specialized -- return frames for the situations with one non-void element. - non_void_arg_reps = non_void (typeArgReps platform bndr_ty) ubx_tuple_frame = (isUnboxedTupleType bndr_ty || isUnboxedSumType bndr_ty) && length non_void_arg_reps > 1 + ubx_frame = not ubx_tuple_frame && not (usePlainReturn bndr_ty)++ non_void_arg_reps = non_void (typeArgReps platform bndr_ty)+ profiling | Just interp <- hsc_interp hsc_env = interpreterProfiled interp@@ -844,8 +848,7 @@ -- Top of stack is the return itbl, as usual. -- underneath it is the pointer to the alt_code BCO. -- When an alt is entered, it assumes the returned value is- -- on top of the itbl; see Note [Return convention for non-tuple values]- -- for details.+ -- on top of the itbl. ret_frame_size_b :: StackDepth ret_frame_size_b | ubx_tuple_frame = (if profiling then 5 else 4) * wordSize platform@@ -859,6 +862,7 @@ -- The size of the return frame info table pointer if one exists unlifted_itbl_size_b :: StackDepth unlifted_itbl_size_b | ubx_tuple_frame = wordSize platform+ | ubx_frame = wordSize platform | otherwise = 0 (bndr_size, call_info, args_offsets)@@ -1046,14 +1050,24 @@ p scrut alt_bco' <- emitBc alt_bco if ubx_tuple_frame- then do tuple_bco <- emitBc (tupleBCO platform call_info args_offsets)- return (PUSH_ALTS_TUPLE alt_bco' call_info tuple_bco- `consOL` scrut_code)- else let scrut_rep = case non_void_arg_reps of- [] -> V- [rep] -> rep- _ -> panic "schemeE(StgCase).push_alts"- in return (PUSH_ALTS alt_bco' scrut_rep `consOL` scrut_code)+ then do+ let args_ptrs =+ map (\(rep, off) -> (isFollowableArg (toArgRep platform rep), off))+ args_offsets+ tuple_bco <- emitBc (tupleBCO platform call_info args_ptrs)+ return (PUSH_ALTS_TUPLE alt_bco' call_info tuple_bco+ `consOL` scrut_code)+ else let push_alts+ | not ubx_frame+ = PUSH_ALTS alt_bco'+ | otherwise+ = let unlifted_rep =+ case non_void_arg_reps of+ [] -> V+ [rep] -> rep+ _ -> panic "schemeE(StgCase).push_alts"+ in PUSH_ALTS_UNLIFTED alt_bco' unlifted_rep+ in return (push_alts `consOL` scrut_code) -- -----------------------------------------------------------------------------@@ -1124,38 +1138,21 @@ (orig_stk_params ++ map get_byte_off new_stk_params) ) -{- Note [Return convention for non-tuple values]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-The RETURN and ENTER instructions are used to return values. RETURN directly-returns the value at the top of the stack while ENTER evaluates it first (so-RETURN is only used when the result is already known to be evaluated), but the-end result is the same: control returns to the enclosing stack frame with the-result at the top of the stack.--The PUSH_ALTS instruction pushes a two-word stack frame that receives a single-lifted value. Its payload is a BCO that is executed when control returns, with-the stack set up as if a RETURN instruction had just been executed: the returned-value is at the top of the stack, and beneath it is the two-word frame being-returned to. It is the continuation BCO’s job to pop its own frame off the-stack, so the simplest possible continuation consists of two instructions:-- SLIDE 1 2 -- pop the return frame off the stack, keeping the returned value- RETURN P -- return the returned value to our caller--RETURN and PUSH_ALTS are not really instructions but are in fact representation--polymorphic *families* of instructions indexed by ArgRep. ENTER, however, is a-single real instruction, since it is only used to return lifted values, which-are always pointers.+{-+ We use the plain return convention (ENTER/PUSH_ALTS) for+ lifted types and unlifted algebraic types. -The RETURN, ENTER, and PUSH_ALTS instructions are only used when the returned-value has nullary or unary representation. Returning/receiving an unboxed-tuple (or, indirectly, an unboxed sum, since unboxed sums have been desugared to-unboxed tuples by Unarise) containing two or more results uses the special-RETURN_TUPLE/PUSH_ALTS_TUPLE instructions, which use a different return-convention. See Note [unboxed tuple bytecodes and tuple_BCO] for details.+ Other types use PUSH_ALTS_UNLIFTED/PUSH_ALTS_TUPLE which expect+ additional data on the stack.+ -}+usePlainReturn :: Type -> Bool+usePlainReturn t+ | isUnboxedTupleType t || isUnboxedSumType t = False+ | otherwise = typePrimRep t == [LiftedRep] ||+ (typePrimRep t == [UnliftedRep] && isAlgType t) -Note [unboxed tuple bytecodes and tuple_BCO]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+{- Note [unboxed tuple bytecodes and tuple_BCO]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We have the bytecode instructions RETURN_TUPLE and PUSH_ALTS_TUPLE to return and receive arbitrary unboxed tuples, respectively. These instructions use the helper data tuple_BCO and call_info.@@ -1250,10 +1247,11 @@ -} -tupleBCO :: Platform -> NativeCallInfo -> [(PrimRep, ByteOff)] -> [FFIInfo] -> ProtoBCO Name-tupleBCO platform args_info args =+tupleBCO :: Platform -> NativeCallInfo -> [(Bool, ByteOff)] -> [FFIInfo] -> ProtoBCO Name+tupleBCO platform info pointers = mkProtoBCO platform invented_name body_code (Left []) 0{-no arity-} bitmap_size bitmap False{-is alts-}+ where {- The tuple BCO is never referred to by name, so we can get away@@ -1265,16 +1263,18 @@ -- the first word in the frame is the call_info word, -- which is not a pointer- nptrs_prefix = 1- (bitmap_size, bitmap) = mkStackBitmap platform nptrs_prefix args_info args-+ bitmap_size = trunc16W $ 1 + nativeCallSize info+ bitmap = intsToReverseBitmap platform (fromIntegral bitmap_size) $+ map ((+1) . fromIntegral . bytesToWords platform . snd)+ (filter fst pointers) body_code = mkSlideW 0 1 -- pop frame header `snocOL` RETURN_TUPLE -- and add it again -primCallBCO :: Platform -> NativeCallInfo -> [(PrimRep, ByteOff)] -> [FFIInfo] -> ProtoBCO Name-primCallBCO platform args_info args =+primCallBCO :: Platform -> NativeCallInfo -> [(Bool, ByteOff)] -> [FFIInfo] -> ProtoBCO Name+primCallBCO platform args_info pointers = mkProtoBCO platform invented_name body_code (Left []) 0{-no arity-} bitmap_size bitmap False{-is alts-}+ where {- The primcall BCO is never referred to by name, so we can get away@@ -1284,52 +1284,20 @@ -} invented_name = mkSystemVarName (mkPseudoUniqueE 0) (fsLit "primcall") - -- The first two words in the frame (after the BCO) are the call_info word- -- and the pointer to the Cmm function being called. Neither of these is a- -- pointer that should be followed by the garbage collector.- nptrs_prefix = 2- (bitmap_size, bitmap) = mkStackBitmap platform nptrs_prefix args_info args-+ -- the first three words in the frame are the BCO describing the+ -- pointers in the frame, the call_info word and the pointer+ -- to the Cmm function being called. None of these is a pointer that+ -- should be followed by the garbage collector+ bitmap_size = trunc16W $ 2 + nativeCallSize args_info+ bitmap = intsToReverseBitmap platform (fromIntegral bitmap_size) $+ map ((+2) . fromIntegral . bytesToWords platform . snd)+ (filter fst pointers) -- if the primcall BCO is ever run it's a bug, since the BCO should only -- be pushed immediately before running the PRIMCALL bytecode instruction, -- which immediately leaves the interpreter to jump to the stg_primcall_info -- Cmm function body_code = unitOL CASEFAIL --- | Builds a bitmap for a stack layout with a nonpointer prefix followed by--- some number of arguments.-mkStackBitmap- :: Platform- -> WordOff- -- ^ The number of nonpointer words that prefix the arguments.- -> NativeCallInfo- -> [(PrimRep, ByteOff)]- -- ^ The stack layout of the arguments, where each offset is relative to the- -- /bottom/ of the stack space they occupy. Their offsets must be word-aligned,- -- and the list must be sorted in order of ascending offset (i.e. bottom to top).- -> (Word16, [StgWord])-mkStackBitmap platform nptrs_prefix args_info args- = (bitmap_size, bitmap)- where- bitmap_size = trunc16W $ nptrs_prefix + arg_bottom- bitmap = intsToReverseBitmap platform (fromIntegral bitmap_size) ptr_offsets-- arg_bottom = nativeCallSize args_info- ptr_offsets = reverse $ map (fromIntegral . convert_arg_offset)- $ mapMaybe get_ptr_offset args-- get_ptr_offset :: (PrimRep, ByteOff) -> Maybe ByteOff- get_ptr_offset (rep, byte_offset)- | isFollowableArg (toArgRep platform rep) = Just byte_offset- | otherwise = Nothing-- convert_arg_offset :: ByteOff -> WordOff- convert_arg_offset arg_offset =- -- The argument offsets are relative to `arg_bottom`, but- -- `intsToReverseBitmap` expects offsets from the top, so we need to flip- -- them around.- nptrs_prefix + (arg_bottom - bytesToWords platform arg_offset)- -- ----------------------------------------------------------------------------- -- Deal with a primitive call to native code. @@ -1357,12 +1325,15 @@ (args_info, args_offsets) = layoutNativeCall profile NativePrimCall- 0+ d (primRepCmmType platform . argPrimRep) nv_args - prim_args_offsets = mapFst argPrimRep args_offsets- shifted_args_offsets = mapSnd (+ d) args_offsets+ args_ptrs :: [(Bool, ByteOff)]+ args_ptrs =+ map (\(r, off) ->+ (isFollowableArg (toArgRep platform . argPrimRep $ r), off))+ args_offsets push_target = PUSH_UBX (LitLabel target Nothing IsFunction) 1 push_info = PUSH_UBX (mkNativeCallInfoLit platform args_info) 1@@ -1379,8 +1350,8 @@ go !dd pushes ((a, off):cs) = do (push, szb) <- pushAtom dd p a massert (off == dd + szb) go (dd + szb) (push:pushes) cs- push_args <- go d [] shifted_args_offsets- args_bco <- emitBc (primCallBCO platform args_info prim_args_offsets)+ push_args <- go d [] args_offsets+ args_bco <- emitBc (primCallBCO platform args_info args_ptrs) return $ mconcat push_args `appOL` (push_target `consOL` push_info `consOL`@@ -1463,10 +1434,11 @@ push_args = concatOL pushs_arg !d_after_args = d0 + wordsToBytes platform a_reps_sizeW a_reps_pushed_RAW- | null a_reps_pushed_r_to_l || not (isVoidRep (head a_reps_pushed_r_to_l))- = panic "GHC.StgToByteCode.generateCCall: missing or invalid World token?"+ | x:xs <- a_reps_pushed_r_to_l+ , isVoidRep x+ = reverse xs | otherwise- = reverse (tail a_reps_pushed_r_to_l)+ = panic "GHC.StgToByteCode.generateCCall: missing or invalid World token?" -- Now: a_reps_pushed_RAW are the reps which are actually on the stack. -- push_args is the code to do that.@@ -1494,7 +1466,7 @@ Addr# address_of_C_fn <placeholder-for-result#> (must be an unboxed type) - The interpreter then calls the marshall code mentioned+ The interpreter then calls the marshal code mentioned in the CCALL insn, passing it (& <placeholder-for-result#>), that is, the addr of the topmost word in the stack. When this returns, the placeholder will have been@@ -1510,7 +1482,7 @@ copies the args to the C stack, calls the stacked addr, and parks the result back in the placeholder. The interpreter calls it as a normal C call, assuming it has a signature- void marshall_code ( StgWord* ptr_to_top_of_stack )+ void marshal_code ( StgWord* ptr_to_top_of_stack ) -} -- resolve static address maybe_static_target :: Maybe Literal@@ -1535,9 +1507,8 @@ -- Get the arg reps, zapping the leading Addr# in the dynamic case a_reps -- | trace (showSDoc (ppr a_reps_pushed_RAW)) False = error "???" | is_static = a_reps_pushed_RAW- | otherwise = if null a_reps_pushed_RAW- then panic "GHC.StgToByteCode.generateCCall: dyn with no args"- else tail a_reps_pushed_RAW+ | _:xs <- a_reps_pushed_RAW = xs+ | otherwise = panic "GHC.StgToByteCode.generateCCall: dyn with no args" -- push the Addr# (push_Addr, d_after_Addr)@@ -1591,7 +1562,7 @@ -- slide and return d_after_r_min_s = bytesToWords platform (d_after_r - s) wrapup = mkSlideW (trunc16W r_sizeW) (d_after_r_min_s - r_sizeW)- `snocOL` RETURN (toArgRep platform r_rep)+ `snocOL` RETURN_UNLIFTED (toArgRep platform r_rep) --trace (show (arg1_offW, args_offW , (map argRepSizeW a_reps) )) $ return ( push_args `appOL`@@ -1705,6 +1676,7 @@ The code we generate is this: push arg+ push bogus-word TESTEQ_I 0 L1 PUSH_G <lbl for first data con>@@ -1722,6 +1694,13 @@ L_exit: SLIDE 1 n ENTER++The 'bogus-word' push is because TESTEQ_I expects the top of the stack+to have an info-table, and the next word to have the value to be+tested. This is very weird, but it's the way it is right now. See+Interpreter.c. We don't actually need an info-table here; we just+need to have the argument to be one-from-top on the stack, hence pushing+a 1-word null. See #8383. -} @@ -1747,10 +1726,14 @@ slide_ws = bytesToWords platform (d - s + arg_bytes) return (push_arg+ `appOL` unitOL (PUSH_UBX LitNullAddr 1)+ -- Push bogus word (see Note [Implementing tagToEnum#]) `appOL` concatOL steps `appOL` toOL [ LABEL label_fail, CASEFAIL, LABEL label_exit ]- `appOL` mkSlideW 1 slide_ws+ `appOL` mkSlideW 1 (slide_ws + 1)+ -- "+1" to account for bogus word+ -- (see Note [Implementing tagToEnum#]) `appOL` unitOL ENTER) where mkStep l_exit (my_label, next_label, n, name_for_n)@@ -1875,7 +1858,9 @@ LitChar {} -> code WordRep LitNullAddr -> code AddrRep LitString {} -> code AddrRep- LitRubbish {} -> code WordRep+ LitRubbish _ rep-> case runtimeRepPrimRep (text "pushLiteral") rep of+ [pr] -> code pr+ _ -> pprPanic "pushLiteral" (ppr lit) LitNumber nt _ -> case nt of LitNumInt -> code IntRep LitNumWord -> code WordRep@@ -2022,11 +2007,9 @@ testEQ NoDiscr _ = panic "mkMultiBranch NoDiscr" -- None of these will be needed if there are no non-default alts- (init_lo, init_hi)- | null notd_ways- = panic "mkMultiBranch: awesome foursome"- | otherwise- = case fst (head notd_ways) of+ (init_lo, init_hi) = case notd_ways of+ [] -> panic "mkMultiBranch: awesome foursome"+ (discr, _):_ -> case discr of DiscrI _ -> ( DiscrI minBound, DiscrI maxBound ) DiscrI8 _ -> ( DiscrI8 minBound, DiscrI8 maxBound ) DiscrI16 _ -> ( DiscrI16 minBound, DiscrI16 maxBound )
compiler/GHC/StgToCmm.hs view
@@ -179,7 +179,8 @@ StgTopStringLit id str -> do let label = mkBytesLabel (idName id) -- emit either a CmmString literal or dump the string in a file and emit a- -- CmmFileEmbed literal.+ -- CmmFileEmbed literal. If binary blobs aren't supported,+ -- the threshold in `cfg` will be 0. -- See Note [Embedding large binary blobs] in GHC.CmmToAsm.Ppr let asString = case stgToCmmBinBlobThresh cfg of Just bin_blob_threshold -> fromIntegral (BS.length str) <= bin_blob_threshold@@ -187,10 +188,10 @@ (lit,decl) = if asString then mkByteStringCLit label str- else mkFileEmbedLit label $ unsafePerformIO $ do+ else unsafePerformIO $ do bFile <- newTempName logger tmpfs (stgToCmmTmpDir cfg) TFL_CurrentModule ".dat" BS.writeFile bFile str- return bFile+ return $ mkFileEmbedLit label bFile (BS.length str) emitDecl decl addBindC (litIdInfo (stgToCmmPlatform cfg) id mkLFStringLit lit)
compiler/GHC/StgToCmm/Bind.hs view
@@ -16,6 +16,7 @@ import GHC.Prelude hiding ((<*>)) import GHC.Core ( AltCon(..) )+import GHC.Core.Opt.Arity( isOneShotBndr ) import GHC.Runtime.Heap.Layout import GHC.Unit.Module @@ -24,6 +25,8 @@ import GHC.Platform import GHC.Platform.Profile +import GHC.Builtin.Names (unpackCStringName, unpackCStringUtf8Name)+ import GHC.StgToCmm.Config import GHC.StgToCmm.Expr import GHC.StgToCmm.Monad@@ -86,6 +89,9 @@ lf_info = mkClosureLFInfo platform id TopLevel [] upd_flag args in (cg_id_info, gen_code lf_info closure_label) where++ gen_code :: LambdaFormInfo -> CLabel -> FCode ()+ -- special case for a indirection (f = g). We create an IND_STATIC -- closure pointing directly to the indirectee. This is exactly -- what the CAF will eventually evaluate to anyway, we're just@@ -100,11 +106,44 @@ -- concurrent/should_run/4030 fails, for instance. -- gen_code _ closure_label- | StgApp f [] <- body, null args, isNonRec rec+ | StgApp f [] <- body+ , null args+ , isNonRec rec = do cg_info <- getCgIdInfo f emitDataCon closure_label indStaticInfoTable ccs [unLit (idInfoToAmode cg_info)] + -- Emit standard stg_unpack_cstring closures for top-level unpackCString# thunks.+ --+ -- Note that we do not do this for thunks enclosured in code ticks (e.g. hpc+ -- ticks) since we want to ensure that these ticks are not lost (e.g.+ -- resulting in Strings being reported by hpc as uncovered). However, we+ -- don't worry about standard profiling ticks since unpackCString tends not+ -- be terribly interesting in profiles. See Note [unpack_cstring closures] in+ -- StgStdThunks.cmm.+ gen_code _ closure_label+ | null args+ , StgApp f [arg] <- stripStgTicksTopE (not . tickishIsCode) body+ , Just unpack <- is_string_unpack_op f+ = do arg' <- getArgAmode (NonVoid arg)+ case arg' of+ CmmLit lit -> do+ let info = CmmInfoTable+ { cit_lbl = unpack+ , cit_rep = HeapRep True 0 1 Thunk+ , cit_prof = NoProfilingInfo+ , cit_srt = Nothing+ , cit_clo = Nothing+ }+ emitDecl $ CmmData (Section Data closure_label) $+ CmmStatics closure_label info ccs [] [lit]+ _ -> panic "cgTopRhsClosure.gen_code"+ where+ is_string_unpack_op f+ | idName f == unpackCStringName = Just mkRtsUnpackCStringLabel+ | idName f == unpackCStringUtf8Name = Just mkRtsUnpackCStringUtf8Label+ | otherwise = Nothing+ gen_code lf_info _closure_label = do { profile <- getProfile ; let name = idName id@@ -220,7 +259,6 @@ {- See Note [GC recovery] in "GHC.StgToCmm.Closure" -} cgRhs id (StgRhsClosure fvs cc upd_flag args body) = do- checkFunctionArgTags (text "TagCheck Failed: Rhs of" <> ppr id) id args profile <- getProfile check_tags <- stgToCmmDoTagCheck <$> getStgToCmmConfig use_std_ap_thunk <- stgToCmmTickyAP <$> getStgToCmmConfig@@ -489,6 +527,7 @@ lf_info = closureLFInfo cl_info info_tbl = mkCmmInfo cl_info bndr cc +-- Functions closureCodeBody top_lvl bndr cl_info cc args@(arg0:_) body fv_details = let nv_args = nonVoidIds args arity = length args@@ -530,7 +569,7 @@ -- Load free vars out of closure *after* -- heap check, to reduce live vars over check ; when node_points $ load_fvs node lf_info fv_bindings- ; checkFunctionArgTags (text "TagCheck failed - Argument to local function:" <> ppr bndr) bndr (map fromNonVoid nv_args)+ ; checkFunctionArgTags (text "TagCheck failed - Argument to local function:" <> ppr bndr) bndr args ; void $ cgExpr body }}} @@ -664,8 +703,8 @@ whenUpdRemSetEnabled $ emitUpdRemSetPushThunk node emitStore (cmmOffsetW platform node (fixedHdrSizeW profile)) currentTSOExpr -- See Note [Heap memory barriers] in SMP.h.- emitPrimCall [] MO_WriteBarrier []- emitStore node (CmmReg (CmmGlobal EagerBlackholeInfo))+ let w = wordWidth platform+ emitPrimCall [] (MO_AtomicWrite w MemOrderRelease) [node, CmmReg (CmmGlobal EagerBlackholeInfo)] setupUpdate :: ClosureInfo -> LocalReg -> FCode () -> FCode () -- Nota Bene: this function does not change Node (even if it's a CAF),@@ -782,5 +821,5 @@ -- Not called for StgRhsCon which have global info tables built in -- CgConTbls.hs with a description generated from the data constructor closureDescription mod_name name- = renderWithContext defaultSDocContext+ = showSDocOneLine defaultSDocContext (char '<' <> pprFullName mod_name name <> char '>')
compiler/GHC/StgToCmm/CgUtils.hs view
@@ -173,18 +173,15 @@ BaseReg -> baseAddr _other -> CmmLoad baseAddr (globalRegType platform reg) NaturallyAligned - CmmRegOff greg@(CmmGlobal reg) offset ->+ CmmRegOff (CmmGlobal reg) offset -> -- RegOf leaves are just a shorthand form. If the reg maps -- to a real reg, we keep the shorthand, otherwise, we just -- expand it and defer to the above code.- -- NB: to ensure type correctness we need to ensure the Add- -- as well as the Int need to be of the same size as the- -- register. case reg `elem` activeStgRegs platform of True -> expr- False -> CmmMachOp (MO_Add (cmmRegWidth platform greg)) [- fixExpr (CmmReg greg),+ False -> CmmMachOp (MO_Add (wordWidth platform)) [+ fixExpr (CmmReg (CmmGlobal reg)), CmmLit (CmmInt (fromIntegral offset)- (cmmRegWidth platform greg))]+ (wordWidth platform))] other_expr -> other_expr
compiler/GHC/StgToCmm/Closure.hs view
@@ -76,7 +76,6 @@ import GHC.Runtime.Heap.Layout import GHC.Cmm import GHC.Cmm.Utils-import GHC.Cmm.Ppr.Expr() -- For Outputable instances import GHC.StgToCmm.Types import GHC.StgToCmm.Sequel @@ -265,124 +264,22 @@ -- Use the LambdaFormInfo from the interface lf_info Nothing- -- Interface doesn't have a LambdaFormInfo, so make a conservative one from the type.- -- See Note [The LFInfo of Imported Ids]; The order of the guards musn't be changed!- | arity > 0- -> LFReEntrant TopLevel arity True ArgUnknown-- | Just con <- isDataConId_maybe id- -- See Note [Imported unlifted nullary datacon wrappers must have correct LFInfo] in GHC.StgToCmm.Types- -- and Note [The LFInfo of Imported Ids] below- -> assert (hasNoNonZeroWidthArgs con) $- LFCon con -- An imported nullary constructor+ -- Interface doesn't have a LambdaFormInfo, make a conservative one from+ -- the type.+ | Just con <- isDataConWorkId_maybe id+ , isNullaryRepDataCon con+ -> LFCon con -- An imported nullary constructor -- We assume that the constructor is evaluated so that -- the id really does point directly to the constructor + | arity > 0+ -> LFReEntrant TopLevel arity True ArgUnknown+ | otherwise -> mkLFArgument id -- Not sure of exact arity where arity = idFunRepArity id- hasNoNonZeroWidthArgs = all (isZeroBitTy . scaledThing) . dataConRepArgTys -{--Note [The LFInfo of Imported Ids]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-As explained in Note [Conveying CAF-info and LFInfo between modules] and-Note [Imported unlifted nullary datacon wrappers must have correct LFInfo], the-LambdaFormInfo records the details of a closure representation and is often,-when optimisations are enabled, serialized to the interface of a module.--In particular, the `lfInfo` field of the `IdInfo` field of an `Id`-* For Ids defined in this module: is `Nothing`-* For imported Ids:- * is (Just lf_info) if the LFInfo was serialised into the interface file- (typically, when the exporting module was compiled with -O)- * is Nothing if it wasn't serialised--However, when an interface doesn't have a LambdaFormInfo for some imported Id-(so that its `lfInfo` field is `Nothing`), we can conservatively create one-using `mkLFImported`.--The LambdaFormInfo we give an Id is used in determining how to tag its pointer-(see `litIdInfo`). Therefore, it's crucial we re-construct a LambdaFormInfo as-faithfully as possible or otherwise risk having pointers incorrectly tagged,-which can lead to performance issues and even segmentation faults (see #23231-and #23146). In particular, saturated data constructor applications *must* be-unambiguously given `LFCon`, and the invariant-- If the LFInfo (serialised or built with mkLFImported) says LFCon, then it- really is a static data constructor, and similar for LFReEntrant--must be upheld.--In `mkLFImported`, we make a conservative approximation to the real-LambdaFormInfo as follows:--(1) Ids with an `idFunRepArity > 0` are `LFReEntrant` and pointers to them are-tagged (by `litIdInfo`) with the corresponding arity.- - This is also true of data con wrappers and workers with arity > 0,- regardless of the runtime relevance of the arguments- - For example, `Just :: a -> Maybe a` is given `LFReEntrant`- and `HNil :: (a ~# '[]) -> HList a` is given `LFReEntrant` too--(2) Data constructors with `idFunRepArity == 0` should be given `LFCon` because-they are fully saturated data constructor applications and pointers to them-should be tagged with the constructor index.--(2.1) A datacon *wrapper* with zero arity must be a fully saturated application-of the worker to zero-width arguments only (which are dropped after unarisation)--(2.2) A datacon *worker* with zero arity is trivially fully saturated, it takes-no arguments whatsoever (not even zero-width args)--To ensure we properly give `LFReEntrant` to data constructors with some arity,-and `LFCon` only to data constructors with zero arity, we must first check for-`arity > 0` and only afterwards `isDataConId` -- the order of the guards in-`mkLFImported` is quite important.--As an example, consider the following data constructors:-- data T1 a where- TCon1 :: {-# UNPACK #-} !(a :~: True) -> T1 a-- data T2 a where- TCon2 :: {-# UNPACK #-} !() -> T2 a-- data T3 a where- TCon3 :: T3 '[]--`TCon1`'s wrapper has a lifted equality argument, which is non-zero-width, while-the worker has an unlifted equality argument, which is zero-width.--`TCon2`'s wrapper has a lifted equality argument, which is non-zero-width,-while the worker has no arguments.--`TCon3`'s wrapper has no arguments, and the worker has 1 zero-width argument;-their Core representation:-- $WTCon3 :: T3 '[]- $WTCon3 = TCon3 @[] <Refl>-- TCon3 :: forall (a :: * -> *). (a ~# []) => T a- TCon3 = /\a. \(co :: a~#[]). TCon3 co--For `TCon1`, both the wrapper and worker will be given `LFReEntrant` since they-both have arity == 1.--For `TCon2`, the wrapper will be given `LFReEntrant` since it has arity == 1-while the worker is `LFCon` since its arity == 0--For `TCon3`, the wrapper will be given `LFCon` since its arity == 0 and the-worker `LFReEntrant` since its arity == 1--One might think we could give *workers* with only zero-width-args the `LFCon`-LambdaFormInfo, e.g. give `LFCon` to the worker of `TCon1` and `TCon3`.-However, these workers, albeit rarely used, are unambiguously functions--- which makes `LFReEntrant`, the LambdaFormInfo we give them, correct.-See also the discussion in #23158.---}- ------------- mkLFStringLit :: LambdaFormInfo mkLFStringLit = LFUnlifted@@ -411,7 +308,8 @@ -- Also see Note [Tagging big families] in GHC.StgToCmm.Expr -- -- The interpreter also needs to be updated if we change the--- tagging strategy; see tagConstr in rts/Interpreter.c.+-- tagging strategy. See Note [Data constructor dynamic tags] in+-- rts/Interpreter.c isSmallFamily :: Platform -> Int -> Bool isSmallFamily platform fam_size = fam_size <= mAX_PTR_TAG platform
compiler/GHC/StgToCmm/Env.hs view
@@ -36,6 +36,7 @@ import GHC.Cmm.Graph import GHC.Types.Name import GHC.Core.Type+import GHC.Core.TyCo.Compare( eqType ) import GHC.Builtin.Types.Prim import GHC.Types.Unique.FM import GHC.Types.Var.Env@@ -82,8 +83,8 @@ mkRhsInit platform reg lf_info expr = mkAssign (CmmLocal reg) (addDynTag platform expr (lfDynTag platform lf_info)) --- | Returns a 'CmmExpr' for the *tagged* pointer idInfoToAmode :: CgIdInfo -> CmmExpr+-- Returns a CmmExpr for the *tagged* pointer idInfoToAmode CgIdInfo { cg_loc = CmmLoc e } = e idInfoToAmode cg_info = pprPanic "idInfoToAmode" (ppr (cg_id cg_info)) -- LneLoc@@ -119,7 +120,7 @@ setBinds new_binds -- Inside GHC the average module creates 385 external references--- with noteable cgIdInfo (so not generated by mkLFArgument).+-- with notable cgIdInfo (so not generated by mkLFArgument). -- On average 200 of these are covered by True/False/[] -- and nullary constructors make up ~80. -- One would think it would be worthwhile to cache these.@@ -150,7 +151,7 @@ in return $ litIdInfo platform id (mkLFImported id) (CmmLabel ext_lbl) else- cgLookupPanic id -- Bug, id is neither in local binds nor is external+ cgLookupPanic id -- Bug }}} -- | Retrieve cg info for a name if it already exists.
compiler/GHC/StgToCmm/Expr.hs view
@@ -38,7 +38,6 @@ import GHC.Cmm hiding ( succ ) import GHC.Cmm.Info import GHC.Cmm.Utils ( zeroExpr, cmmTagMask, mkWordCLit, mAX_PTR_TAG )-import GHC.Cmm.Ppr import GHC.Core import GHC.Core.DataCon import GHC.Types.ForeignCall@@ -77,6 +76,8 @@ -- dataToTag# :: a -> Int# -- See Note [dataToTag# magic] in GHC.Core.Opt.ConstantFold+-- TODO: There are some more optimization ideas for this code path+-- in #21710 cgExpr (StgOpApp (StgPrimOp DataToTagOp) [StgVarArg a] _res_ty) = do platform <- getPlatform emitComment (mkFastString "dataToTag#")@@ -93,15 +94,7 @@ -- the constructor index is too large to fit in the pointer and therefore -- we must look in the info table. See Note [Tagging big families]. - slow_path <- getCode $ do- tmp <- newTemp (bWord platform)- _ <- withSequel (AssignTo [tmp] False) (cgIdApp a [])- profile <- getProfile- align_check <- stgToCmmAlignCheck <$> getStgToCmmConfig- emitAssign (CmmLocal result_reg)- $ getConstrTag profile align_check (cmmUntag platform (CmmReg (CmmLocal tmp)))-- fast_path <- getCode $ do+ (fast_path :: CmmAGraph) <- getCode $ do -- Return the constructor index from the pointer tag return_ptr_tag <- getCode $ do emitAssign (CmmLocal result_reg)@@ -114,8 +107,22 @@ $ getConstrTag profile align_check (cmmUntag platform amode) emit =<< mkCmmIfThenElse' is_too_big_tag return_info_tag return_ptr_tag (Just False)-- emit =<< mkCmmIfThenElse' is_tagged fast_path slow_path (Just True)+ -- If we know the argument is already tagged there is no need to generate code to evaluate it+ -- so we skip straight to the fast path. If we don't know if there is a tag we take the slow+ -- path which evaluates the argument before fetching the tag.+ case (idTagSig_maybe a) of+ Just sig+ | isTaggedSig sig+ -> emit fast_path+ _ -> do+ slow_path <- getCode $ do+ tmp <- newTemp (bWord platform)+ _ <- withSequel (AssignTo [tmp] False) (cgIdApp a [])+ profile <- getProfile+ align_check <- stgToCmmAlignCheck <$> getStgToCmmConfig+ emitAssign (CmmLocal result_reg)+ $ getConstrTag profile align_check (cmmUntag platform (CmmReg (CmmLocal tmp)))+ emit =<< mkCmmIfThenElse' is_tagged fast_path slow_path (Just True) emitReturn [CmmReg $ CmmLocal result_reg] @@ -386,7 +393,7 @@ * If f is recursive, and the hot loop wouldn't allocate, but the exit branch does then we do a redundant heap check.- * We use one more instruction to de-allocate the unused heap in the branch using less heap. (Neglible)+ * We use one more instruction to de-allocate the unused heap in the branch using less heap. (Negligible) * A small risk of running gc slightly more often than needed especially if one branch allocates a lot. The upsides are:@@ -905,7 +912,7 @@ -- tag from the info table, and switch on that. The only technically -- tricky part is that the default case needs (logical) duplication. -- To do this we emit an extra label for it and branch to that from--- the second switch. This avoids duplicated codegen. See Trac #14373.+-- the second switch. This avoids duplicated codegen. See #14373. -- See Note [Double switching for big families] for the mechanics -- involved. --@@ -1019,7 +1026,7 @@ assertTag = whenCheckTags $ do mod <- getModuleName emitTagAssertion (showPprUnsafe- (text "TagCheck failed on entry in" <+> ppr mod <+> text "- value:" <> ppr fun_id <+> pprExpr platform fun))+ (text "TagCheck failed on entry in" <+> ppr mod <+> text "- value:" <> ppr fun_id <+> pdoc platform fun)) fun EnterIt -> assert (null args) $ -- Discarding arguments
compiler/GHC/StgToCmm/Foreign.hs view
@@ -8,9 +8,7 @@ module GHC.StgToCmm.Foreign ( cgForeignCall,- emitPrimCall,- emitCCall,- emitCCallNeverReturns,+ emitPrimCall, emitCCall, emitForeignCall, emitSaveThreadState, saveThreadState,@@ -196,31 +194,17 @@ -} -emitCCall' :: CmmReturnInfo- -> [(CmmFormal,ForeignHint)]- -> CmmExpr- -> [(CmmActual,ForeignHint)]- -> FCode ()-emitCCall' ret_info hinted_results fn hinted_args+emitCCall :: [(CmmFormal,ForeignHint)]+ -> CmmExpr+ -> [(CmmActual,ForeignHint)]+ -> FCode ()+emitCCall hinted_results fn hinted_args = void $ emitForeignCall PlayRisky results target args where (args, arg_hints) = unzip hinted_args (results, result_hints) = unzip hinted_results target = ForeignTarget fn fc- fc = ForeignConvention CCallConv arg_hints result_hints ret_info--emitCCall :: [(CmmFormal,ForeignHint)]- -> CmmExpr- -> [(CmmActual,ForeignHint)]- -> FCode ()-emitCCall = emitCCall' CmmMayReturn--emitCCallNeverReturns- :: [(CmmFormal,ForeignHint)]- -> CmmExpr- -> [(CmmActual,ForeignHint)]- -> FCode ()-emitCCallNeverReturns = emitCCall' CmmNeverReturns+ fc = ForeignConvention CCallConv arg_hints result_hints CmmMayReturn emitPrimCall :: [CmmFormal] -> CallishMachOp -> [CmmActual] -> FCode ()
compiler/GHC/StgToCmm/Heap.hs view
@@ -6,8 +6,6 @@ -- ----------------------------------------------------------------------------- -{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}- module GHC.StgToCmm.Heap ( getVirtHp, setVirtHp, setRealHp, getHpRelOffset,@@ -161,19 +159,20 @@ -- Layout of static closures ----------------------------------------------------------- --- Make a static closure, adding on any extra padding needed for CAFs,--- and adding a static link field if necessary.-+-- | Make a static closure, adding on any extra padding needed for CAFs, and+-- adding a static link field if necessary. mkStaticClosureFields :: Profile -> CmmInfoTable -> CostCentreStack -> CafInfo- -> [CmmLit] -- Payload+ -> [CmmLit] -- ^ Payload+ -> [CmmLit] -- ^ Extra non-pointers that go to the end of the closure.+ -- See Note [unpack_cstring closures] in StgStdThunks.cmm. -> [CmmLit] -- The full closure-mkStaticClosureFields profile info_tbl ccs caf_refs payload+mkStaticClosureFields profile info_tbl ccs caf_refs payload extras = mkStaticClosure profile info_lbl ccs payload padding- static_link_field saved_info_field+ static_link_field saved_info_field extras where platform = profilePlatform profile info_lbl = cit_lbl info_tbl@@ -218,14 +217,15 @@ -- in rts/sm/Storage.h mkStaticClosure :: Profile -> CLabel -> CostCentreStack -> [CmmLit]- -> [CmmLit] -> [CmmLit] -> [CmmLit] -> [CmmLit]-mkStaticClosure profile info_lbl ccs payload padding static_link_field saved_info_field+ -> [CmmLit] -> [CmmLit] -> [CmmLit] -> [CmmLit] -> [CmmLit]+mkStaticClosure profile info_lbl ccs payload padding static_link_field saved_info_field extras = [CmmLabel info_lbl] ++ staticProfHdr profile ccs ++ payload ++ padding ++ static_link_field ++ saved_info_field+ ++ extras ----------------------------------------------------------- -- Heap overflow checking@@ -610,10 +610,6 @@ gc_id <- newBlockId let- Just alloc_lit = mb_alloc_lit-- bump_hp = cmmOffsetExprB platform hpExpr alloc_lit- -- Sp overflow if ((old + 0) - CmmHighStack < SpLim) -- At the beginning of a function old + 0 = Sp -- See Note [Single stack check]@@ -628,8 +624,6 @@ -- HpLim points to the LAST WORD of valid allocation space. hp_oflo = CmmMachOp (mo_wordUGt platform) [hpExpr, hpLimExpr] - alloc_n = mkAssign hpAllocReg alloc_lit- case mb_stk_hwm of Nothing -> return () Just stk_hwm -> tickyStackCheck@@ -644,12 +638,14 @@ | checkYield && isJust mb_stk_hwm -> emitLabel loop_header_id _otherwise -> return () - if isJust mb_alloc_lit- then do+ case mb_alloc_lit of+ Just alloc_lit -> do+ let bump_hp = cmmOffsetExprB platform hpExpr alloc_lit+ alloc_n = mkAssign hpAllocReg alloc_lit tickyHeapCheck emitAssign hpReg bump_hp emit =<< mkCmmIfThen' hp_oflo (alloc_n <*> mkBranch gc_id) (Just False)- else+ Nothing -> when (checkYield && not omit_yields) $ do -- Yielding if HpLim == 0 let yielding = CmmMachOp (mo_wordEq platform)
compiler/GHC/StgToCmm/InfoTableProv.hs view
@@ -4,6 +4,8 @@ import GHC.Platform import GHC.Unit.Module import GHC.Utils.Outputable+import GHC.Types.SrcLoc (pprUserRealSpan, srcSpanFile)+import GHC.Data.FastString (unpackFS) import GHC.Cmm.CLabel import GHC.Cmm.Expr@@ -16,7 +18,6 @@ import GHC.Data.ShortText (ShortText) import qualified GHC.Data.ShortText as ST -import Data.Bifunctor (first) import qualified Data.Map.Strict as M import Control.Monad.Trans.State.Strict import qualified Data.ByteString as BS@@ -45,7 +46,9 @@ , strtab_offset (ipeTypeDesc cg_ipe) , strtab_offset (ipeLabel cg_ipe) , strtab_offset (ipeModuleName cg_ipe)- , strtab_offset (ipeSrcLoc cg_ipe)+ , strtab_offset (ipeSrcFile cg_ipe)+ , strtab_offset (ipeSrcSpan cg_ipe)+ , int32 0 ] int n = mkIntCLit platform n@@ -61,19 +64,28 @@ toCgIPE :: Platform -> SDocContext -> StrTabOffset -> InfoProvEnt -> State StringTable CgInfoProvEnt toCgIPE platform ctx module_name ipe = do- table_name <- lookupStringTable $ ST.pack $ renderWithContext ctx (pprCLabel platform CStyle (infoTablePtr ipe))+ table_name <- lookupStringTable $ ST.pack $ renderWithContext ctx (pprCLabel platform (infoTablePtr ipe)) closure_desc <- lookupStringTable $ ST.pack $ show (infoProvEntClosureType ipe) type_desc <- lookupStringTable $ ST.pack $ infoTableType ipe- let (src_loc_str, label_str) = maybe ("", "") (first (renderWithContext ctx . ppr)) (infoTableProv ipe)+ let label_str = maybe "" snd (infoTableProv ipe)+ let (src_loc_file, src_loc_span) =+ case infoTableProv ipe of+ Nothing -> ("", "")+ Just (span, _) ->+ let file = unpackFS $ srcSpanFile span+ coords = renderWithContext ctx (pprUserRealSpan False span)+ in (file, coords) label <- lookupStringTable $ ST.pack label_str- src_loc <- lookupStringTable $ ST.pack src_loc_str+ src_file <- lookupStringTable $ ST.pack src_loc_file+ src_span <- lookupStringTable $ ST.pack src_loc_span return $ CgInfoProvEnt { ipeInfoTablePtr = infoTablePtr ipe , ipeTableName = table_name , ipeClosureDesc = closure_desc , ipeTypeDesc = type_desc , ipeLabel = label , ipeModuleName = module_name- , ipeSrcLoc = src_loc+ , ipeSrcFile = src_file+ , ipeSrcSpan = src_span } data CgInfoProvEnt = CgInfoProvEnt@@ -83,7 +95,8 @@ , ipeTypeDesc :: !StrTabOffset , ipeLabel :: !StrTabOffset , ipeModuleName :: !StrTabOffset- , ipeSrcLoc :: !StrTabOffset+ , ipeSrcFile :: !StrTabOffset+ , ipeSrcSpan :: !StrTabOffset } data StringTable = StringTable { stStrings :: DList ShortText
compiler/GHC/StgToCmm/Layout.hs view
@@ -208,7 +208,7 @@ r <- direct_call "slow_call" NativeNodeCall (mkRtsApFastLabel rts_fun) arity ((P,Just fun):argsreps) emitComment $ mkFastString ("slow_call for " ++- renderWithContext ctx (pdoc platform fun) +++ showSDocOneLine ctx (pdoc platform fun) ++ " with pat " ++ unpackFS rts_fun) return r @@ -297,7 +297,7 @@ platform <- getPlatform pprPanic "direct_call" $ text caller <+> ppr arity <+>- pdoc platform lbl <+> ppr (length args) <+>+ pprDebugCLabel platform lbl <+> ppr (length args) <+> pdoc platform (map snd args) <+> ppr (map fst args) | null rest_args -- Precisely the right number of arguments
compiler/GHC/StgToCmm/Lit.hs view
@@ -50,9 +50,9 @@ cgLit (LitString s) = CmmLit <$> newByteStringCLit s -- not unpackFS; we want the UTF-8 byte stream.-cgLit (LitRubbish rep) =+cgLit (LitRubbish _ rep) = case expectOnly "cgLit" prim_reps of -- Note [Post-unarisation invariants]- VoidRep -> panic "cgLit:VoidRep" -- dito+ VoidRep -> panic "cgLit:VoidRep" -- ditto LiftedRep -> idInfoToAmode <$> getCgIdInfo unitDataConId UnliftedRep -> idInfoToAmode <$> getCgIdInfo unitDataConId AddrRep -> cgLit LitNullAddr
compiler/GHC/StgToCmm/Monad.hs view
@@ -188,11 +188,9 @@ data CgIdInfo = CgIdInfo- { cg_id :: Id- -- ^ Id that this is the info for+ { cg_id :: Id -- Id that this is the info for , cg_lf :: LambdaFormInfo- , cg_loc :: CgLoc- -- ^ 'CmmExpr' for the *tagged* value+ , cg_loc :: CgLoc -- CmmExpr for the *tagged* value } instance OutputableP Platform CgIdInfo where@@ -296,7 +294,7 @@ data FCodeState = MkFCodeState { fcs_upframeoffset :: UpdFrameOffset -- ^ Size of current update frame UpdFrameOffset must be kept lazy or -- else the RTS will deadlock _and_ also experience a severe- -- performance degredation+ -- performance degradation , fcs_sequel :: !Sequel -- ^ What to do at end of basic block , fcs_selfloop :: Maybe SelfLoopInfo -- ^ Which tail calls can be compiled as local jumps? -- See Note [Self-recursive tail calls] in GHC.StgToCmm.Expr@@ -533,7 +531,7 @@ tickScope code = do cfg <- getStgToCmmConfig fstate <- getFCodeState- if stgToCmmDebugLevel cfg == 0 then code else do+ if not $ stgToCmmEmitDebugInfo cfg then code else do u <- newUnique let scope' = SubScope u (fcs_tickscope fstate) withFCodeState code fstate{ fcs_tickscope = scope' }@@ -719,8 +717,8 @@ emitUnwind :: [(GlobalReg, Maybe CmmExpr)] -> FCode () emitUnwind regs = do- debug_level <- stgToCmmDebugLevel <$> getStgToCmmConfig- when (debug_level > 0) $+ debug <- stgToCmmEmitDebugInfo <$> getStgToCmmConfig+ when debug $ emitCgStmt $ CgStmt $ CmmUnwind regs emitAssign :: CmmReg -> CmmExpr -> FCode ()
compiler/GHC/StgToCmm/Prim.hs view
@@ -311,7 +311,7 @@ -- #define sizzeofByteArrayzh(r,a) \ -- r = ((StgArrBytes *)(a))->bytes SizeofByteArrayOp -> \[arg] -> opIntoRegs $ \[res] ->- emitAssign (CmmLocal res) (byteArraySize platform profile arg)+ emit $ mkAssign (CmmLocal res) (cmmLoadIndexW platform arg (fixedHdrSizeW profile) (bWord platform)) -- #define sizzeofMutableByteArrayzh(r,a) \ -- r = ((StgArrBytes *)(a))->bytes@@ -320,7 +320,7 @@ -- #define getSizzeofMutableByteArrayzh(r,a) \ -- r = ((StgArrBytes *)(a))->bytes GetSizeofMutableByteArrayOp -> \[arg] -> opIntoRegs $ \[res] ->- emitAssign (CmmLocal res) (byteArraySize platform profile arg)+ emitAssign (CmmLocal res) (cmmLoadIndexW platform arg (fixedHdrSizeW profile) (bWord platform)) -- #define touchzh(o) /* nothing */@@ -394,10 +394,15 @@ -- Getting the size of pointer arrays SizeofArrayOp -> \[arg] -> opIntoRegs $ \[res] ->- emitAssign (CmmLocal res) (ptrArraySize platform profile arg)+ emit $ mkAssign (CmmLocal res) (cmmLoadIndexW platform arg+ (fixedHdrSizeW profile + bytesToWordsRoundUp platform (pc_OFFSET_StgMutArrPtrs_ptrs (platformConstants platform)))+ (bWord platform)) SizeofMutableArrayOp -> emitPrimOp cfg SizeofArrayOp SizeofSmallArrayOp -> \[arg] -> opIntoRegs $ \[res] ->- emitAssign (CmmLocal res) (smallPtrArraySize platform profile arg)+ emit $ mkAssign (CmmLocal res)+ (cmmLoadIndexW platform arg+ (fixedHdrSizeW profile + bytesToWordsRoundUp platform (pc_OFFSET_StgSmallMutArrPtrs_ptrs (platformConstants platform)))+ (bWord platform)) SizeofSmallMutableArrayOp -> emitPrimOp cfg SizeofSmallArrayOp GetSizeofSmallMutableArrayOp -> emitPrimOp cfg SizeofSmallArrayOp@@ -1099,6 +1104,7 @@ DoubleExpOp -> \args -> opCallish args MO_F64_Exp DoubleExpM1Op -> \args -> opCallish args MO_F64_ExpM1 DoubleSqrtOp -> \args -> opCallish args MO_F64_Sqrt+ DoubleFabsOp -> \args -> opCallish args MO_F64_Fabs FloatPowerOp -> \args -> opCallish args MO_F32_Pwr FloatSinOp -> \args -> opCallish args MO_F32_Sin@@ -1118,6 +1124,7 @@ FloatExpOp -> \args -> opCallish args MO_F32_Exp FloatExpM1Op -> \args -> opCallish args MO_F32_ExpM1 FloatSqrtOp -> \args -> opCallish args MO_F32_Sqrt+ FloatFabsOp -> \args -> opCallish args MO_F32_Fabs -- Native word signless ops @@ -1521,16 +1528,6 @@ then Left (MO_S_Mul2 (wordWidth platform)) else Right genericIntMul2Op - FloatFabsOp -> \args -> opCallishHandledLater args $- if allowFab- then Left MO_F32_Fabs- else Right $ genericFabsOp W32-- DoubleFabsOp -> \args -> opCallishHandledLater args $- if allowFab- then Left MO_F64_Fabs- else Right $ genericFabsOp W64- -- tagToEnum# is special: we need to pull the constructor -- out of the table, and perform an appropriate return. TagToEnumOp -> \[amode] -> PrimopCmmEmit_Internal $ \res_ty -> do@@ -1567,11 +1564,17 @@ CasMutVarOp -> alwaysExternal CatchOp -> alwaysExternal RaiseOp -> alwaysExternal+ RaiseUnderflowOp -> alwaysExternal+ RaiseOverflowOp -> alwaysExternal+ RaiseDivZeroOp -> alwaysExternal RaiseIOOp -> alwaysExternal MaskAsyncExceptionsOp -> alwaysExternal MaskUninterruptibleOp -> alwaysExternal UnmaskAsyncExceptionsOp -> alwaysExternal MaskStatus -> alwaysExternal+ NewPromptTagOp -> alwaysExternal+ PromptOp -> alwaysExternal+ Control0Op -> alwaysExternal AtomicallyOp -> alwaysExternal RetryOp -> alwaysExternal CatchRetryOp -> alwaysExternal@@ -1601,6 +1604,7 @@ LabelThreadOp -> alwaysExternal IsCurrentThreadBoundOp -> alwaysExternal NoDuplicateOp -> alwaysExternal+ GetThreadLabelOp -> alwaysExternal ThreadStatusOp -> alwaysExternal MkWeakOp -> alwaysExternal MkWeakNoFinalizerOp -> alwaysExternal@@ -1628,6 +1632,7 @@ MkApUpd0_Op -> alwaysExternal NewBCOOp -> alwaysExternal UnpackClosureOp -> alwaysExternal+ ListThreadsOp -> alwaysExternal ClosureSizeOp -> alwaysExternal WhereFromOp -> alwaysExternal GetApStackValOp -> alwaysExternal@@ -1730,7 +1735,6 @@ allowQuotRem2 = stgToCmmAllowQuotRem2 cfg allowExtAdd = stgToCmmAllowExtendedAddSubInstrs cfg allowInt2Mul = stgToCmmAllowIntMul2Instr cfg- allowFab = stgToCmmAllowFabsInstrs cfg data PrimopCmmEmit -- | Out of line fake primop that's actually just a foreign call to other@@ -2020,34 +2024,6 @@ ] genericIntMul2Op _ _ = panic "genericIntMul2Op" --- This replicates what we had in libraries/base/GHC/Float.hs:------ abs x | x == 0 = 0 -- handles (-0.0)--- | x > 0 = x--- | otherwise = negateFloat x-genericFabsOp :: Width -> GenericOp-genericFabsOp w [res_r] [aa]- = do platform <- getPlatform- let zero = CmmLit (CmmFloat 0 w)-- eq x y = CmmMachOp (MO_F_Eq w) [x, y]- gt x y = CmmMachOp (MO_F_Gt w) [x, y]-- neg x = CmmMachOp (MO_F_Neg w) [x]-- g1 = catAGraphs [mkAssign (CmmLocal res_r) zero]- g2 = catAGraphs [mkAssign (CmmLocal res_r) aa]-- res_t <- CmmLocal <$> newTemp (cmmExprType platform aa)- let g3 = catAGraphs [mkAssign res_t aa,- mkAssign (CmmLocal res_r) (neg (CmmReg res_t))]-- g4 <- mkCmmIfThenElse (gt aa zero) g2 g3-- emit =<< mkCmmIfThenElse (eq aa zero) g1 g4--genericFabsOp _ _ _ = panic "genericFabsOp"- ------------------------------------------------------------------------------ -- Helpers for translating various minor variants of array indexing. @@ -2064,7 +2040,7 @@ -> [CmmExpr] -> FCode () doIndexOffAddrOp maybe_post_read_cast rep [res] [addr,idx]- = mkBasicIndexedRead False NaturallyAligned 0 maybe_post_read_cast rep res addr rep idx+ = mkBasicIndexedRead NaturallyAligned 0 maybe_post_read_cast rep res addr rep idx doIndexOffAddrOp _ _ _ _ = panic "GHC.StgToCmm.Prim: doIndexOffAddrOp" @@ -2076,7 +2052,7 @@ -> FCode () doIndexOffAddrOpAs maybe_post_read_cast rep idx_rep [res] [addr,idx] = let alignment = alignmentFromTypes rep idx_rep- in mkBasicIndexedRead False alignment 0 maybe_post_read_cast rep res addr idx_rep idx+ in mkBasicIndexedRead alignment 0 maybe_post_read_cast rep res addr idx_rep idx doIndexOffAddrOpAs _ _ _ _ _ = panic "GHC.StgToCmm.Prim: doIndexOffAddrOpAs" @@ -2088,7 +2064,7 @@ doIndexByteArrayOp maybe_post_read_cast rep [res] [addr,idx] = do profile <- getProfile doByteArrayBoundsCheck idx addr rep rep- mkBasicIndexedRead False NaturallyAligned (arrWordsHdrSize profile) maybe_post_read_cast rep res addr rep idx+ mkBasicIndexedRead NaturallyAligned (arrWordsHdrSize profile) maybe_post_read_cast rep res addr rep idx doIndexByteArrayOp _ _ _ _ = panic "GHC.StgToCmm.Prim: doIndexByteArrayOp" @@ -2102,7 +2078,7 @@ = do profile <- getProfile doByteArrayBoundsCheck idx addr idx_rep rep let alignment = alignmentFromTypes rep idx_rep- mkBasicIndexedRead False alignment (arrWordsHdrSize profile) maybe_post_read_cast rep res addr idx_rep idx+ mkBasicIndexedRead alignment (arrWordsHdrSize profile) maybe_post_read_cast rep res addr idx_rep idx doIndexByteArrayOpAs _ _ _ _ _ = panic "GHC.StgToCmm.Prim: doIndexByteArrayOpAs" @@ -2114,15 +2090,15 @@ = do profile <- getProfile platform <- getPlatform doPtrArrayBoundsCheck idx addr- mkBasicIndexedRead True NaturallyAligned (arrPtrsHdrSize profile) Nothing (gcWord platform) res addr (gcWord platform) idx+ mkBasicIndexedRead NaturallyAligned (arrPtrsHdrSize profile) Nothing (gcWord platform) res addr (gcWord platform) idx doWriteOffAddrOp :: Maybe MachOp -> CmmType -> [LocalReg] -> [CmmExpr] -> FCode ()-doWriteOffAddrOp castOp idx_ty [] [addr,idx, val]- = mkBasicIndexedWrite False 0 addr idx_ty idx (maybeCast castOp val)+doWriteOffAddrOp maybe_pre_write_cast idx_ty [] [addr,idx,val]+ = mkBasicIndexedWrite 0 maybe_pre_write_cast addr idx_ty idx val doWriteOffAddrOp _ _ _ _ = panic "GHC.StgToCmm.Prim: doWriteOffAddrOp" @@ -2131,12 +2107,11 @@ -> [LocalReg] -> [CmmExpr] -> FCode ()-doWriteByteArrayOp castOp idx_ty [] [addr,idx, rawVal]+doWriteByteArrayOp maybe_pre_write_cast idx_ty [] [addr,idx,val] = do profile <- getProfile platform <- getPlatform- let val = maybeCast castOp rawVal doByteArrayBoundsCheck idx addr idx_ty (cmmExprType platform val)- mkBasicIndexedWrite False (arrWordsHdrSize profile) addr idx_ty idx val+ mkBasicIndexedWrite (arrWordsHdrSize profile) maybe_pre_write_cast addr idx_ty idx val doWriteByteArrayOp _ _ _ _ = panic "GHC.StgToCmm.Prim: doWriteByteArrayOp" @@ -2158,7 +2133,8 @@ -- This write barrier is to ensure that the heap writes to the object -- referred to by val have happened before we write val into the array. -- See #12469 for details.- mkBasicIndexedWrite True hdr_size addr ty idx val+ emitPrimCall [] MO_WriteBarrier []+ mkBasicIndexedWrite hdr_size Nothing addr ty idx val emit (setInfo addr (CmmLit (CmmLabel mkMAP_DIRTY_infoLabel))) -- the write barrier. We must write a byte into the mark table:@@ -2166,12 +2142,16 @@ emit $ mkStore ( cmmOffsetExpr platform (cmmOffsetExprW platform (cmmOffsetB platform addr hdr_size)- (ptrArraySize platform profile addr))+ (loadArrPtrsSize profile addr)) (CmmMachOp (mo_wordUShr platform) [idx, mkIntExpr platform (pc_MUT_ARR_PTRS_CARD_BITS (platformConstants platform))]) ) (CmmLit (CmmInt 1 W8)) -mkBasicIndexedRead :: Bool -- Should this imply an acquire barrier- -> AlignmentSpec+loadArrPtrsSize :: Profile -> CmmExpr -> CmmExpr+loadArrPtrsSize profile addr = cmmLoadBWord platform (cmmOffsetB platform addr off)+ where off = fixedHdrSize profile + pc_OFFSET_StgMutArrPtrs_ptrs (profileConstants profile)+ platform = profilePlatform profile++mkBasicIndexedRead :: AlignmentSpec -> ByteOff -- Initial offset in bytes -> Maybe MachOp -- Optional result cast -> CmmType -- Type of element we are accessing@@ -2180,40 +2160,27 @@ -> CmmType -- Type of element by which we are indexing -> CmmExpr -- Index -> FCode ()-mkBasicIndexedRead barrier alignment off mb_cast ty res base idx_ty idx+mkBasicIndexedRead alignment off Nothing ty res base idx_ty idx = do platform <- getPlatform- let addr = cmmIndexOffExpr platform off (typeWidth idx_ty) base idx- result <-- if barrier- then do- res <- newTemp ty- emitPrimCall [res] (MO_AtomicRead (typeWidth ty) MemOrderAcquire) [addr]- return $ CmmReg (CmmLocal res)- else- return $ CmmLoad addr ty alignment-- let casted =- case mb_cast of- Just cast -> CmmMachOp cast [result]- Nothing -> result- emitAssign (CmmLocal res) casted+ emitAssign (CmmLocal res) (cmmLoadIndexOffExpr platform alignment off ty base idx_ty idx)+mkBasicIndexedRead alignment off (Just cast) ty res base idx_ty idx+ = do platform <- getPlatform+ emitAssign (CmmLocal res) (CmmMachOp cast [+ cmmLoadIndexOffExpr platform alignment off ty base idx_ty idx]) -mkBasicIndexedWrite :: Bool -- Should this imply a release barrier- -> ByteOff -- Initial offset in bytes+mkBasicIndexedWrite :: ByteOff -- Initial offset in bytes+ -> Maybe MachOp -- Optional value cast -> CmmExpr -- Base address -> CmmType -- Type of element by which we are indexing -> CmmExpr -- Index -> CmmExpr -- Value to write -> FCode ()-mkBasicIndexedWrite barrier off base idx_ty idx val+mkBasicIndexedWrite off Nothing base idx_ty idx val = do platform <- getPlatform let alignment = alignmentFromTypes (cmmExprType platform val) idx_ty- addr = cmmIndexOffExpr platform off (typeWidth idx_ty) base idx- if barrier- then let w = typeWidth idx_ty- op = MO_AtomicWrite w MemOrderRelease- in emitPrimCall [] op [addr, val]- else emitStore' alignment addr val+ emitStore' alignment (cmmIndexOffExpr platform off (typeWidth idx_ty) base idx) val+mkBasicIndexedWrite off (Just cast) base idx_ty idx val+ = mkBasicIndexedWrite off Nothing base idx_ty idx (CmmMachOp cast [val]) -- ---------------------------------------------------------------------------- -- Misc utils@@ -2241,30 +2208,6 @@ setInfo :: CmmExpr -> CmmExpr -> CmmAGraph setInfo closure_ptr info_ptr = mkStore closure_ptr info_ptr -maybeCast :: Maybe MachOp -> CmmExpr -> CmmExpr-maybeCast Nothing val = val-maybeCast (Just cast) val = CmmMachOp cast [val]--ptrArraySize :: Platform -> Profile -> CmmExpr -> CmmExpr-ptrArraySize platform profile arr =- cmmLoadBWord platform (cmmOffsetB platform arr sz_off)- where sz_off = fixedHdrSize profile- + pc_OFFSET_StgMutArrPtrs_ptrs (platformConstants platform)--smallPtrArraySize :: Platform -> Profile -> CmmExpr -> CmmExpr-smallPtrArraySize platform profile arr =- cmmLoadBWord platform (cmmOffsetB platform arr sz_off)- where sz_off = fixedHdrSize profile- + pc_OFFSET_StgSmallMutArrPtrs_ptrs (platformConstants platform)--byteArraySize :: Platform -> Profile -> CmmExpr -> CmmExpr-byteArraySize platform profile arr =- cmmLoadBWord platform (cmmOffsetB platform arr sz_off)- where sz_off = fixedHdrSize profile- + pc_OFFSET_StgArrBytes_bytes (platformConstants platform)--- ------------------------------------------------------------------------------ -- Helpers for translating vector primops. @@ -2510,9 +2453,10 @@ profile <- getProfile platform <- getPlatform - whenCheckBounds $ ifNonZero n $ do- emitRangeBoundsCheck ba1_off n (byteArraySize platform profile ba1)- emitRangeBoundsCheck ba2_off n (byteArraySize platform profile ba2)+ ifNonZero n $ do+ let last_touched_idx off = cmmOffsetB platform (cmmAddWord platform off n) (-1)+ doByteArrayBoundsCheck (last_touched_idx ba1_off) ba1 b8 b8+ doByteArrayBoundsCheck (last_touched_idx ba2_off) ba2 b8 b8 ba1_p <- assignTempE $ cmmOffsetExpr platform (cmmOffsetB platform ba1 (arrWordsHdrSize profile)) ba1_off ba2_p <- assignTempE $ cmmOffsetExpr platform (cmmOffsetB platform ba2 (arrWordsHdrSize profile)) ba2_off@@ -2575,7 +2519,7 @@ -- Copy data (we assume the arrays aren't overlapping since -- they're of different types) copy _src _dst dst_p src_p bytes align =- emitCheckedMemcpyCall dst_p src_p bytes align+ emitMemcpyCall dst_p src_p bytes align -- | Takes a source 'MutableByteArray#', an offset in the source -- array, a destination 'MutableByteArray#', an offset into the@@ -2604,9 +2548,10 @@ profile <- getProfile platform <- getPlatform - whenCheckBounds $ ifNonZero n $ do- emitRangeBoundsCheck src_off n (byteArraySize platform profile src)- emitRangeBoundsCheck dst_off n (byteArraySize platform profile dst)+ ifNonZero n $ do+ let last_touched_idx off = cmmOffsetB platform (cmmAddWord platform off n) (-1)+ doByteArrayBoundsCheck (last_touched_idx src_off) src b8 b8+ doByteArrayBoundsCheck (last_touched_idx dst_off) dst b8 b8 let byteArrayAlignment = wordAlignment platform srcOffAlignment = cmmExprAlignment src_off@@ -2624,10 +2569,11 @@ -- Use memcpy (we are allowed to assume the arrays aren't overlapping) profile <- getProfile platform <- getPlatform- whenCheckBounds $ ifNonZero bytes $ do- emitRangeBoundsCheck src_off bytes (byteArraySize platform profile src)+ ifNonZero bytes $ do+ let last_touched_idx off = cmmOffsetB platform (cmmAddWord platform off bytes) (-1)+ doByteArrayBoundsCheck (last_touched_idx src_off) src b8 b8 src_p <- assignTempE $ cmmOffsetExpr platform (cmmOffsetB platform src (arrWordsHdrSize profile)) src_off- emitCheckedMemcpyCall dst_p src_p bytes (mkAlignment 1)+ emitMemcpyCall dst_p src_p bytes (mkAlignment 1) -- | Takes a source 'MutableByteArray#', an offset in the source array, a -- destination 'Addr#', and the number of bytes to copy. Copies the given@@ -2644,20 +2590,18 @@ -- Use memcpy (we are allowed to assume the arrays aren't overlapping) profile <- getProfile platform <- getPlatform- whenCheckBounds $ ifNonZero bytes $ do- emitRangeBoundsCheck dst_off bytes (byteArraySize platform profile dst)+ ifNonZero bytes $ do+ let last_touched_idx off = cmmOffsetB platform (cmmAddWord platform off bytes) (-1)+ doByteArrayBoundsCheck (last_touched_idx dst_off) dst b8 b8 dst_p <- assignTempE $ cmmOffsetExpr platform (cmmOffsetB platform dst (arrWordsHdrSize profile)) dst_off- emitCheckedMemcpyCall dst_p src_p bytes (mkAlignment 1)+ emitMemcpyCall dst_p src_p bytes (mkAlignment 1) ifNonZero :: CmmExpr -> FCode () -> FCode () ifNonZero e it = do platform <- getPlatform let pred = cmmNeWord platform e (zeroExpr platform) code <- getCode it- emit =<< mkCmmIfThen' pred code (Just True)- -- This function is used for range operation bounds-checks;- -- Most calls to those ops will not have range length zero.-+ emit =<< mkCmmIfThen' pred code (Just False) -- ---------------------------------------------------------------------------- -- Setting byte arrays@@ -2671,8 +2615,8 @@ profile <- getProfile platform <- getPlatform - whenCheckBounds $ ifNonZero len $- emitRangeBoundsCheck off len (byteArraySize platform profile ba)+ doByteArrayBoundsCheck off ba b8 b8+ doByteArrayBoundsCheck (cmmOffset platform (cmmAddWord platform off len) (-1)) ba b8 b8 let byteArrayAlignment = wordAlignment platform -- known since BA is allocated on heap offsetAlignment = cmmExprAlignment off@@ -2743,7 +2687,7 @@ -- they're of different types) copy _src _dst dst_p src_p bytes = do platform <- getPlatform- emitCheckedMemcpyCall dst_p src_p (mkIntExpr platform bytes)+ emitMemcpyCall dst_p src_p (mkIntExpr platform bytes) (wordAlignment platform) @@ -2785,11 +2729,8 @@ dst <- assignTempE dst0 dst_off <- assignTempE dst_off0 - whenCheckBounds $ do- emitRangeBoundsCheck src_off (mkIntExpr platform n)- (ptrArraySize platform profile src)- emitRangeBoundsCheck dst_off (mkIntExpr platform n)- (ptrArraySize platform profile dst)+ doPtrArrayBoundsCheck (cmmAddWord platform src_off (mkIntExpr platform n)) src+ doPtrArrayBoundsCheck (cmmAddWord platform dst_off (mkIntExpr platform n)) dst -- Nonmoving collector write barrier emitCopyUpdRemSetPush platform (arrPtrsHdrSize profile) dst dst_off n@@ -2808,7 +2749,7 @@ -- The base address of the destination card table dst_cards_p <- assignTempE $ cmmOffsetExprW platform dst_elems_p- (ptrArraySize platform profile dst)+ (loadArrPtrsSize profile dst) emitSetCards dst_off dst_cards_p n @@ -2820,7 +2761,7 @@ -- they're of different types) copy _src _dst dst_p src_p bytes = do platform <- getPlatform- emitCheckedMemcpyCall dst_p src_p (mkIntExpr platform bytes)+ emitMemcpyCall dst_p src_p (mkIntExpr platform bytes) (wordAlignment platform) @@ -2857,11 +2798,9 @@ src <- assignTempE src0 dst <- assignTempE dst0 - whenCheckBounds $ do- emitRangeBoundsCheck src_off (mkIntExpr platform n)- (smallPtrArraySize platform profile src)- emitRangeBoundsCheck dst_off (mkIntExpr platform n)- (smallPtrArraySize platform profile dst)+ when (n /= 0) $ do+ doSmallPtrArrayBoundsCheck (cmmAddWord platform src_off (mkIntExpr platform n)) src+ doSmallPtrArrayBoundsCheck (cmmAddWord platform dst_off (mkIntExpr platform n)) dst -- Nonmoving collector write barrier emitCopyUpdRemSetPush platform (smallArrPtrsHdrSize profile) dst dst_off n@@ -2956,7 +2895,7 @@ emit $ mkAssign (CmmLocal res_r) (CmmReg arr) --- | Takes an offset in the destination array, the base address of+-- | Takes and offset in the destination array, the base address of -- the card table, and the number of elements affected (*not* the -- number of cards). The number of elements may not be zero. -- Marks the relevant cards as dirty.@@ -2989,7 +2928,7 @@ profile <- getProfile platform <- getPlatform doSmallPtrArrayBoundsCheck idx addr- mkBasicIndexedRead True NaturallyAligned (smallArrPtrsHdrSize profile) Nothing (gcWord platform) res addr+ mkBasicIndexedRead NaturallyAligned (smallArrPtrsHdrSize profile) Nothing (gcWord platform) res addr (gcWord platform) idx doWriteSmallPtrArrayOp :: CmmExpr@@ -3005,11 +2944,11 @@ -- Update remembered set for non-moving collector tmp <- newTemp ty- mkBasicIndexedRead False NaturallyAligned (smallArrPtrsHdrSize profile) Nothing ty tmp addr ty idx+ mkBasicIndexedRead NaturallyAligned (smallArrPtrsHdrSize profile) Nothing ty tmp addr ty idx whenUpdRemSetEnabled $ emitUpdRemSetPush (CmmReg (CmmLocal tmp)) - -- Write barrier needed due to #12469- mkBasicIndexedWrite True (smallArrPtrsHdrSize profile) addr ty idx val+ emitPrimCall [] MO_WriteBarrier [] -- #12469+ mkBasicIndexedWrite (smallArrPtrsHdrSize profile) Nothing addr ty idx val emit (setInfo addr (CmmLit (CmmLabel mkSMAP_DIRTY_infoLabel))) ------------------------------------------------------------------------------@@ -3135,26 +3074,6 @@ (MO_Memcpy (alignmentBytes align)) [ dst, src, n ] --- | Emit a call to @memcpy@, but check for range--- overlap when -fcheck-prim-bounds is on.-emitCheckedMemcpyCall :: CmmExpr -> CmmExpr -> CmmExpr -> Alignment -> FCode ()-emitCheckedMemcpyCall dst src n align = do- whenCheckBounds (getPlatform >>= doCheck)- emitMemcpyCall dst src n align- where- doCheck platform = do- overlapCheckFailed <- getCode $- emitCCallNeverReturns [] (mkLblExpr mkMemcpyRangeOverlapLabel) []- emit =<< mkCmmIfThen' rangesOverlap overlapCheckFailed (Just False)- where- rangesOverlap = (checkDiff dst src `or` checkDiff src dst) `ne` zero- checkDiff p q = (p `minus` q) `uLT` n- or = cmmOrWord platform- minus = cmmSubWord platform- uLT = cmmULtWord platform- ne = cmmNeWord platform- zero = zeroExpr platform- -- | Emit a call to @memmove@. emitMemmoveCall :: CmmExpr -> CmmExpr -> CmmExpr -> Alignment -> FCode () emitMemmoveCall dst src n align =@@ -3249,68 +3168,50 @@ -- Array bounds checking --------------------------------------------------------------------------- -whenCheckBounds :: FCode () -> FCode ()-whenCheckBounds a = do- config <- getStgToCmmConfig- case stgToCmmDoBoundsCheck config of- False -> pure ()- True -> a--emitBoundsCheck :: CmmExpr -- ^ accessed index- -> CmmExpr -- ^ array size (in elements)- -> FCode ()-emitBoundsCheck idx sz = do- assertM (stgToCmmDoBoundsCheck <$> getStgToCmmConfig)- platform <- getPlatform- boundsCheckFailed <- getCode $- emitCCallNeverReturns [] (mkLblExpr mkOutOfBoundsAccessLabel) []- let isOutOfBounds = cmmUGeWord platform idx sz- emit =<< mkCmmIfThen' isOutOfBounds boundsCheckFailed (Just False)+doBoundsCheck :: CmmExpr -- ^ accessed index+ -> CmmExpr -- ^ array size (in elements)+ -> FCode ()+doBoundsCheck idx sz = do+ do_bounds_check <- stgToCmmDoBoundsCheck <$> getStgToCmmConfig+ platform <- getPlatform+ when do_bounds_check (doCheck platform)+ where+ doCheck platform = do+ boundsCheckFailed <- getCode $ emitCCall [] (mkLblExpr mkOutOfBoundsAccessLabel) []+ emit =<< mkCmmIfThen' isOutOfBounds boundsCheckFailed (Just False)+ where+ uGE = cmmUGeWord platform+ and = cmmAndWord platform+ zero = zeroExpr platform+ ne = cmmNeWord platform+ isOutOfBounds = ((idx `uGE` sz) `and` (idx `ne` zero)) `ne` zero -emitRangeBoundsCheck :: CmmExpr -- ^ first accessed index- -> CmmExpr -- ^ number of accessed indices (non-zero)- -> CmmExpr -- ^ array size (in elements)- -> FCode ()-emitRangeBoundsCheck idx len arrSizeExpr = do- assertM (stgToCmmDoBoundsCheck <$> getStgToCmmConfig)- config <- getStgToCmmConfig- platform <- getPlatform- arrSize <- assignTempE arrSizeExpr- -- arrSizeExpr is probably a load we don't want to duplicate- rangeTooLargeReg <- newTemp (bWord platform)- lastSafeIndexReg <- newTemp (bWord platform)- _ <- withSequel (AssignTo [lastSafeIndexReg, rangeTooLargeReg] False) $- cmmPrimOpApp config WordSubCOp [arrSize, len] Nothing- boundsCheckFailed <- getCode $- emitCCallNeverReturns [] (mkLblExpr mkOutOfBoundsAccessLabel) []- let- rangeTooLarge = CmmReg (CmmLocal rangeTooLargeReg)- lastSafeIndex = CmmReg (CmmLocal lastSafeIndexReg)- badStartIndex = (idx `uGT` lastSafeIndex)- isOutOfBounds = (rangeTooLarge `or` badStartIndex) `neq` zero- uGT = cmmUGtWord platform- or = cmmOrWord platform- neq = cmmNeWord platform- zero = zeroExpr platform- emit =<< mkCmmIfThen' isOutOfBounds boundsCheckFailed (Just False)+-- We want to make sure that the array size computation is pushed into the+-- Opt_DoBoundsChecking check to avoid regregressing compiler performance when+-- it's disabled.+{-# INLINE doBoundsCheck #-} doPtrArrayBoundsCheck :: CmmExpr -- ^ accessed index (in bytes) -> CmmExpr -- ^ pointer to @StgMutArrPtrs@ -> FCode ()-doPtrArrayBoundsCheck idx arr = whenCheckBounds $ do+doPtrArrayBoundsCheck idx arr = do profile <- getProfile platform <- getPlatform- emitBoundsCheck idx (ptrArraySize platform profile arr)+ let sz = cmmLoadBWord platform (cmmOffset platform arr sz_off)+ sz_off = fixedHdrSize profile + pc_OFFSET_StgMutArrPtrs_ptrs (platformConstants platform)+ doBoundsCheck idx sz doSmallPtrArrayBoundsCheck :: CmmExpr -- ^ accessed index (in bytes) -> CmmExpr -- ^ pointer to @StgMutArrPtrs@ -> FCode ()-doSmallPtrArrayBoundsCheck idx arr = whenCheckBounds $ do+doSmallPtrArrayBoundsCheck idx arr = do profile <- getProfile platform <- getPlatform- emitBoundsCheck idx (smallPtrArraySize platform profile arr)+ let sz = cmmLoadBWord platform (cmmOffset platform arr sz_off)+ sz_off = fixedHdrSize profile + pc_OFFSET_StgSmallMutArrPtrs_ptrs (platformConstants platform)+ doBoundsCheck idx sz doByteArrayBoundsCheck :: CmmExpr -- ^ accessed index (in elements)@@ -3318,18 +3219,18 @@ -> CmmType -- ^ indexing type -> CmmType -- ^ element type -> FCode ()-doByteArrayBoundsCheck idx arr idx_ty elem_ty = whenCheckBounds $ do+doByteArrayBoundsCheck idx arr idx_ty elem_ty = do profile <- getProfile platform <- getPlatform- let elem_w = typeWidth elem_ty- idx_w = typeWidth idx_ty- elem_sz = mkIntExpr platform $ widthInBytes elem_w- arr_sz = byteArraySize platform profile arr- effective_arr_sz =- cmmUShrWord platform arr_sz (mkIntExpr platform (widthInLog idx_w))- if elem_w == idx_w- then emitBoundsCheck idx effective_arr_sz -- aligned => simpler check- else assert (idx_w == W8) (emitRangeBoundsCheck idx elem_sz arr_sz)+ let sz = cmmLoadBWord platform (cmmOffset platform arr sz_off)+ sz_off = fixedHdrSize profile + pc_OFFSET_StgArrBytes_bytes (platformConstants platform)+ elem_sz = widthInBytes $ typeWidth elem_ty+ idx_sz = widthInBytes $ typeWidth idx_ty+ -- Ensure that the last byte of the access is within the array+ idx_bytes = cmmOffsetB platform+ (cmmMulWord platform idx (mkIntExpr platform idx_sz))+ (elem_sz - 1)+ doBoundsCheck idx_bytes sz --------------------------------------------------------------------------- -- Pushing to the update remembered set
compiler/GHC/StgToCmm/Prof.hs view
@@ -230,7 +230,7 @@ ; modl <- newByteStringCLit (bytesFS $ moduleNameFS $ moduleName $ cc_mod cc)- ; loc <- newByteStringCLit $ utf8EncodeString $+ ; loc <- newByteStringCLit $ utf8EncodeByteString $ renderWithContext ctx (ppr $! costCentreSrcSpan cc) ; let lits = [ zero platform, -- StgInt ccID,
compiler/GHC/StgToCmm/Sequel.hs view
@@ -17,7 +17,6 @@ import GHC.Cmm.BlockId import GHC.Cmm-import GHC.Cmm.Ppr() import GHC.Types.Id import GHC.Utils.Outputable
compiler/GHC/StgToCmm/TagCheck.hs view
@@ -43,17 +43,16 @@ import qualified Data.Map as M --- | Check all arguments marked as already tagged for a function--- are tagged by inserting runtime checks.+-- | Check all arguments marked as cbv for the presence of a tag *at runtime*. checkFunctionArgTags :: SDoc -> Id -> [Id] -> FCode () checkFunctionArgTags msg f args = whenCheckTags $ do onJust (return ()) (idCbvMarks_maybe f) $ \marks -> do -- Only check args marked as strict, and only lifted ones.- let cbv_args = filter (isLiftedRuntimeRep . idType) $ filterByList (map isMarkedCbv marks) args+ let cbv_args = filter (isBoxedType . idType) $ filterByList (map isMarkedCbv marks) args -- Get their (cmm) address arg_infos <- mapM getCgIdInfo cbv_args let arg_cmms = map idInfoToAmode arg_infos- mapM_ (emitTagAssertion (showPprUnsafe msg)) (arg_cmms)+ mapM_ (\(cmm,arg) -> emitTagAssertion (showPprUnsafe $ msg <+> ppr arg) cmm) (zip arg_cmms cbv_args) -- | Check all required-tagged arguments of a constructor are tagged *at compile time*. checkConArgsStatic :: SDoc -> DataCon -> [StgArg] -> FCode ()@@ -74,7 +73,7 @@ when check_tags act -- | Call barf if we failed to predict a tag correctly.--- This is immensly useful when debugging issues in tag inference+-- This is immensely useful when debugging issues in tag inference -- as it will result in a program abort when we encounter an invalid -- call/heap object, rather than leaving it be and segfaulting arbitrary -- or producing invalid results.@@ -133,7 +132,7 @@ emitArgTagCheck :: SDoc -> [CbvMark] -> [Id] -> FCode () emitArgTagCheck info marks args = whenCheckTags $ do mod <- getModuleName- let cbv_args = filter (isLiftedRuntimeRep . idType) $ filterByList (map isMarkedCbv marks) args+ let cbv_args = filter (isBoxedType . idType) $ filterByList (map isMarkedCbv marks) args arg_infos <- mapM getCgIdInfo cbv_args let arg_cmms = map idInfoToAmode arg_infos mk_msg arg = showPprUnsafe (text "Untagged arg:" <> (ppr mod) <> char ':' <> info <+> ppr arg)@@ -175,6 +174,6 @@ info <- getCgIdInfo v if taggedCgInfo info then return ()- else pprPanic "Arg not tagged as expectd" (ppr msg <+> ppr arg)+ else pprPanic "Arg not tagged as expected" (ppr msg <+> ppr arg)
compiler/GHC/StgToCmm/Ticky.hs view
@@ -154,7 +154,6 @@ import qualified Data.Char import Control.Monad ( when, unless ) import GHC.Types.Id.Info-import GHC.Utils.Trace import GHC.StgToCmm.Env (getCgInfo_maybe) import Data.Coerce (coerce) import GHC.Utils.Json@@ -363,7 +362,7 @@ Just (CgIdInfo { cg_lf = cg_lf }) | isLFThunk cg_lf -> return $! CmmLabel $ mkClosureInfoTableLabel (profilePlatform profile) tickee cg_lf- _ -> pprTraceDebug "tickyThunkUnknown" (text t <> colon <> ppr name <+> ppr (mkInfoTableLabel name NoCafRefs))+ _ -> pprTraceDebug "tickyThunkUnknown" (text t <> colon <> ppr name <+> pprDebugCLabel (profilePlatform profile) (mkInfoTableLabel name NoCafRefs)) return $! zeroCLit platform TickyLNE {} -> return $! zeroCLit platform@@ -887,7 +886,7 @@ Just (tycon, _) -> let anyOf us = getUnique tycon `elem` us in case () of- _ | anyOf [funTyConKey] -> '>'+ _ | anyOf [fUNTyConKey] -> '>' | anyOf [charTyConKey] -> 'C' | anyOf [charPrimTyConKey] -> 'c' | anyOf [doubleTyConKey] -> 'D'
compiler/GHC/StgToCmm/Utils.hs view
@@ -46,7 +46,7 @@ convertInfoProvMap, cmmInfoTableToInfoProvEnt ) where -import GHC.Prelude+import GHC.Prelude hiding ( head, init, last, tail ) import GHC.Platform import GHC.StgToCmm.Monad@@ -266,7 +266,7 @@ emitDataCon :: CLabel -> CmmInfoTable -> CostCentreStack -> [CmmLit] -> FCode () emitDataCon lbl itbl ccs payload =- emitDecl (CmmData (Section Data lbl) (CmmStatics lbl itbl ccs payload))+ emitDecl (CmmData (Section Data lbl) (CmmStatics lbl itbl ccs payload [])) ------------------------------------------------------------------------- --@@ -448,8 +448,8 @@ -> [(Literal, CmmAGraphScoped)] -- Tagged branches -> CmmAGraphScoped -- Default branch (always) -> FCode () -- Emit the code-emitCmmLitSwitch _scrut [] deflt = emit $ fst deflt-emitCmmLitSwitch scrut branches deflt = do+emitCmmLitSwitch _scrut [] deflt = emit $ fst deflt+emitCmmLitSwitch scrut branches@(branch:_) deflt = do scrut' <- assignTemp' scrut join_lbl <- newBlockId deflt_lbl <- label_code join_lbl deflt@@ -460,7 +460,7 @@ rep = typeWidth cmm_ty -- We find the necessary type information in the literals in the branches- let (signed,range) = case head branches of+ let (signed,range) = case branch of (LitNumber nt _, _) -> (signed,range) where signed = litNumIsSigned nt@@ -604,7 +604,7 @@ in InfoProvEnt cl cn "" this_mod Nothing -- | Convert source information collected about identifiers in 'GHC.STG.Debug'--- to entries suitable for placing into the info table provenenance table.+-- to entries suitable for placing into the info table provenance table. convertInfoProvMap :: [CmmInfoTable] -> Module -> InfoTableProvMap -> [InfoProvEnt] convertInfoProvMap defns this_mod (InfoTableProvMap (UniqMap dcenv) denv infoTableToSourceLocationMap) = map (\cmit ->
+ compiler/GHC/StgToJS.hs view
@@ -0,0 +1,216 @@+module GHC.StgToJS+ ( stgToJS+ )+where++import GHC.StgToJS.CodeGen+++-- Note [StgToJS design]+-- ~~~~~~~~~~~~~~~~~~~~~+--+-- StgToJS ("JS backend") is adapted from GHCJS [GHCJS2013].+--+-- Haskell to JavaScript+-- ~~~~~~~~~~~~~~~~~~~~~+-- StgToJS converts STG into a JavaScript AST (in GHC.JS) that has been adapted+-- from JMacro [JMacro].+--+-- Tail calls: translated code is tail call optimized through a trampoline,+-- since JavaScript implementations don't always support tail calls.+--+-- JavaScript ASTs are then optimized. A dataflow analysis is performed and then+-- dead code and redundant assignments are removed.+--+-- Primitives+-- ~~~~~~~~~~+-- Haskell primitives have to be represented as JavaScript values. This is done+-- as follows:+--+-- - Int#/Int32# -> number in Int32 range+-- - Int16# -> number in Int16 range+-- - Int8# -> number in Int8 range+-- - Word#/Word32# -> number in Word32 range+-- - Word16# -> number in Word16 range+-- - Word8# -> number in Word8 range+--+-- - Float#/Double# -> both represented as Javascript Double (no Float!)+--+-- - Int64# -> represented with two fields:+-- high -> number in Int32 range+-- low -> number in Word32 range+-- - Word64# -> represented with two fields: high, low+-- high -> number in Word32 range+-- low -> number in Word32 range+--+-- - Addr# -> represented with two fields: array (used as a namespace) and index+-- - StablePtr# -> similar to Addr# with array fixed to h$stablePtrBuf+--+-- - JSVal# -> any Javascript object (used to pass JS objects via FFI)+--+-- - TVar#, MVar#, etc. are represented with JS objects+--+-- Foreign JavaScript imports+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~+-- StgToJS supports inline JavaScript code. Example:+--+-- > foreign import javascript unsafe+-- > "((x,y) => x + y)"+-- > plus :: Int -> Int -> Int+--+-- Currently the JS backend only supports functions as JS imports.+--+-- In comparison, GHCJS supports JavaScript snippets with $1, $2... variables+-- as placeholders for the arguments. It requires a JavaScript parser that the+-- JS backend lacks. In GHCJS, the parser is inherited from JMacro and supports+-- local variable declarations, loops, etc. Local variables are converted to+-- hygienic names to avoid capture.+--+-- Primitives that are represented as multiple values (Int64#, Word64#, Addr#)+-- are passed to FFI functions with multiple arguments.+--+-- Interruptible convention: FFI imports with the "interruptible" calling+-- convention are passed an extra argument (usually named "$c") that is a+-- continuation function. The FFI function must call this function to return to+-- Haskell code.+--+-- Unboxed tuples: returning an unboxed tuple can be done with the predefined+-- macros RETURN_UBX_TUPn where n is the size of the tuples. Internally it uses+-- predefined "h$retN" global variables to pass additional values; the first+-- element of the tuple is returned normally.+--+-- Memory management+-- ~~~~~~~~~~~~~~~~~+-- Heap objects are represented as JavaScript values.+--+-- Most heap objects are represented generically as JavaScript "objects" (hash+-- maps). However, some Haskell heap objects can use use a more memory efficient+-- JavaScript representation: number, string... An example of a consequence of+-- this is that both Int# and Int are represented the same as a JavaScript+-- number. JavaScript introspection (e.g. typeof) is used to differentiate+-- heap object representations when necessary.+--+-- Generic representation: objects on the heap ("closures") are represented as+-- JavaScript objects with the following fields:+--+-- { f -- (function) entry function + info table+-- , d1 -- two fields of payload+-- , d2+-- , m -- GC mark+-- , cc -- optional cost-center+-- }+--+-- Payload: payload only consists of two fields (d1, d2). When more than two+-- fields of payload are required, the second field is itself an object.+-- payload [] ==> { d1 = null, d2 = null }+-- payload [a] ==> { d1 = a , d2 = null }+-- payload [a,b] ==> { d1 = a , d2 = b }+-- payload [a,b,c] ==> { d1 = a , d2 = { d1 = b, d2 = c} }+-- payload [a,b,c...] ==> { d1 = a , d2 = { d1 = b, d2 = c, ...} }+--+-- Entry function/ info tables: JavaScript functions are JavaScript objects. If+-- "f" is a function, we can:+-- - call it, e.g. "f(arg0,arg1...)"+-- - get/set its fields, e.g. "f.xyz = abc"+-- This is used to implement the equivalent of tables-next-to-code in+-- JavaScript: every heap object has an entry function "f" that also contains+-- some metadata (info table) about the Haskell object:+-- { t -- object type+-- , size -- number of fields in the payload (-1 if variable layout)+-- , i -- (array) fields layout (empty if variable layout)+-- , n -- (string) object name for easier dubugging+-- , a -- constructor tag / fun arity+-- , r -- ??+-- , s -- static references?+-- , m -- GC mark?+-- }+--+-- Payloads for each kind of heap object:+--+-- THUNK =+-- { f = returns the object reduced to WHNF+-- , m = ?+-- , d1 = ?+-- , d2 = ?+-- }+--+-- FUN =+-- { f = function itself+-- , m = ?+-- , d1 = free variable 1+-- , d2 = free variable 2+-- }+--+-- There are two different kinds of partial application:+-- - pap_r : pre-generated PAP that contains r registers+-- - pap_gen : generic PAP, contains any number of registers+--+-- PAP =+-- { f = ?+-- , m = ?+-- , d1 = function+-- , d2 =+-- { d1 & 0xff = number of args (PAP arity)+-- , d1 >> 8 = number of registers (r for h$pap_r)+-- , d2, d3... = args (r)+-- }+-- }+--+-- CON =+-- { f = entry function of the datacon worker+-- , m = 0+-- , d1 = first arg+-- , d2 = arity = 2: second arg+-- arity > 2: { d1, d2, ...} object with remaining args (starts with "d1 = x2"!)+-- }+--+-- BLACKHOLE =+-- { f = h$blackhole+-- , m = ?+-- , d1 = owning TSO+-- , d2 = waiters array+-- }+--+-- StackFrame closures are *not* represented as JS objects. Instead they are+-- "unpacked" in the stack, i.e. a stack frame occupies a few slots in the JS+-- array representing the stack ("h$stack").+--+-- When a shared thunk is entered, it is overriden with a black hole ("eager+-- blackholing") and an update frame is pushed on the stack.+--+-- Stack: the Haskell stack is implemented with a dynamically growing JavaScript+-- array ("h$stack").+-- TODO: does it shrink sometimes?+-- TODO: what are the elements of the stack? one JS object per stack frame?+--+--+-- Interaction with JavaScript's garbage collector+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+-- Using JS objects to represent Haskell heap objects means that JS's GC does+-- most of the memory management work.+--+-- However, GHC extends Haskell with features that rely on GC layer violation+-- (weak references, finalizers, etc.). To support these features, a heap scan+-- is can be performed (using TSOs, StablePtr, etc. as roots) to mark reachable+-- objects. Scanning the heap is an expensive operation, but fortunately it+-- doesn't need to happen too often and it can be disabled.+--+-- TODO: importance of eager blackholing+--+-- Concurrency+-- ~~~~~~~~~~~+-- The scheduler is implemented in JS and runs in a single JavaScript thread+-- (similarly to the C RTS not using `-threaded`).+--+-- The scheduler relies on callbacks/continuations to interact with other JS+-- codes (user interface, etc.). In particular, safe foreign import can use "$c"+-- as a continuation function to return to Haskell code.+--+-- TODO: is this still true since 2013 or are we using more recent JS features now?+-- TODO: synchronous threads+--+--+-- REFERENCES+-- * [GHCJS2013] "Demo Proposal: GHCJS, Concurrent Haskell in the Browser", Luite Stegeman,+-- 2013 (https://www.haskell.org/haskell-symposium/2013/ghcjs.pdf)+-- * [JMacro] https://hackage.haskell.org/package/jmacro
+ compiler/GHC/StgToJS/Apply.hs view
@@ -0,0 +1,1152 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE BlockArguments #-}++-----------------------------------------------------------------------------+-- |+-- Module : GHC.StgToJS.Apply+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Sylvain Henry <sylvain.henry@iohk.io>+-- Josh Meredith <josh.meredith@iohk.io>+-- Stability : experimental+--+--+-- Module that deals with expression application in JavaScript. In some cases we+-- rely on pre-generated functions that are bundled with the RTS (see rtsApply).+-----------------------------------------------------------------------------++module GHC.StgToJS.Apply+ ( genApp+ , rtsApply+ )+where++import GHC.Prelude hiding ((.|.))++import GHC.JS.Syntax+import GHC.JS.Make++import GHC.StgToJS.Arg+import GHC.StgToJS.Closure+import GHC.StgToJS.DataCon+import GHC.StgToJS.ExprCtx+import GHC.StgToJS.Heap+import GHC.StgToJS.Monad+import GHC.StgToJS.Types+import GHC.StgToJS.Profiling+import GHC.StgToJS.Regs+import GHC.StgToJS.CoreUtils+import GHC.StgToJS.Utils+import GHC.StgToJS.Rts.Types+import GHC.StgToJS.Stack+import GHC.StgToJS.Ids++import GHC.Types.Literal+import GHC.Types.Id+import GHC.Types.Id.Info+import GHC.Types.CostCentre++import GHC.Stg.Syntax++import GHC.Builtin.Names++import GHC.Core.TyCon+import GHC.Core.DataCon+import GHC.Core.Type hiding (typeSize)++import GHC.Utils.Encoding+import GHC.Utils.Misc+import GHC.Utils.Monad+import GHC.Utils.Panic+import GHC.Utils.Outputable (vcat, ppr)+import GHC.Data.FastString++import qualified Data.Bits as Bits+import Data.Monoid+import Data.Array++-- | Pre-generated functions for fast Apply.+-- These are bundled with the RTS.+rtsApply :: StgToJSConfig -> JStat+rtsApply cfg = BlockStat $+ map (specApply cfg) applySpec+ ++ map (pap cfg) specPap+ ++ [ mkApplyArr+ , genericStackApply cfg+ , genericFastApply cfg+ , zeroApply cfg+ , updates cfg+ , papGen cfg+ , moveRegs2+ , selectors cfg+ ]+++-- | Generate an application of some args to an Id.+--+-- The case where args is null is common as it's used to generate the evaluation+-- code for an Id.+genApp+ :: HasDebugCallStack+ => ExprCtx+ -> Id+ -> [StgArg]+ -> G (JStat, ExprResult)+genApp ctx i args++ -- Case: unpackCStringAppend# "some string"# str+ --+ -- Generates h$appendToHsStringA(str, "some string"), which has a faster+ -- decoding loop.+ | [StgLitArg (LitString bs), x] <- args+ , [top] <- concatMap typex_expr (ctxTarget ctx)+ , getUnique i == unpackCStringAppendIdKey+ , d <- utf8DecodeByteString bs+ = do+ prof <- csProf <$> getSettings+ let profArg = if prof then [jCafCCS] else []+ a <- genArg x+ return ( top |= app "h$appendToHsStringA" ([toJExpr d, toJExpr a] ++ profArg)+ , ExprInline Nothing+ )++ -- let-no-escape+ | Just n <- ctxLneBindingStackSize ctx i+ = do+ as' <- concatMapM genArg args+ ei <- varForEntryId i+ let ra = mconcat . reverse $+ zipWith (\r a -> toJExpr r |= a) [R1 ..] as'+ p <- pushLneFrame n ctx+ a <- adjSp 1 -- for the header (which will only be written when the thread is suspended)+ return (ra <> p <> a <> returnS ei, ExprCont)++ -- proxy#+ | [] <- args+ , getUnique i == proxyHashKey+ , [top] <- concatMap typex_expr (ctxTarget ctx)+ = return (top |= null_, ExprInline Nothing)++ -- unboxed tuple or strict type: return fields individually+ | [] <- args+ , isUnboxedTupleType (idType i) || isStrictType (idType i)+ = do+ a <- storeIdFields i (ctxTarget ctx)+ return (a, ExprInline Nothing)++ -- Handle alternative heap object representation: in some cases, a heap+ -- object is not represented as a JS object but directly as a number or a+ -- string. I.e. only the payload is stored because the box isn't useful.+ -- It happens for "Int Int#" for example: no need to box the Int# in JS.+ --+ -- We must check that:+ -- - the object is subject to the optimization (cf isUnboxable predicate)+ -- - we know that it is already evaluated (cf ctxIsEvaluated), otherwise we+ -- need to evaluate it properly first.+ --+ -- In which case we generate a dynamic check (using isObject) that either:+ -- - returns the payload of the heap object, if it uses the generic heap+ -- object representation+ -- - returns the object directly, otherwise+ | [] <- args+ , [vt] <- idVt i+ , isUnboxable vt+ , ctxIsEvaluated ctx i+ = do+ let c = head (concatMap typex_expr $ ctxTarget ctx)+ is <- varsForId i+ case is of+ [i'] ->+ return ( c |= if_ (isObject i') (closureField1 i') i'+ , ExprInline Nothing+ )+ _ -> panic "genApp: invalid size"++ -- case of Id without args and known to be already evaluated: return fields+ -- individually+ | [] <- args+ , ctxIsEvaluated ctx i || isStrictType (idType i)+ = do+ a <- storeIdFields i (ctxTarget ctx)+ -- optional runtime assert for detecting unexpected thunks (unevaluated)+ settings <- getSettings+ let ww = case concatMap typex_expr (ctxTarget ctx) of+ [t] | csAssertRts settings ->+ ifS (isObject t .&&. isThunk t)+ (appS "throw" [String "unexpected thunk"]) -- yuck+ mempty+ _ -> mempty+ return (a `mappend` ww, ExprInline Nothing)+++ -- Case: "newtype" datacon wrapper+ --+ -- If the wrapped argument is known to be already evaluated, then we don't+ -- need to enter it.+ | DataConWrapId dc <- idDetails i+ , isNewTyCon (dataConTyCon dc)+ = do+ as <- concatMapM genArg args+ case as of+ [ai] -> do+ let t = head (concatMap typex_expr (ctxTarget ctx))+ a' = case args of+ [StgVarArg a'] -> a'+ _ -> panic "genApp: unexpected arg"+ if isStrictId a' || ctxIsEvaluated ctx a'+ then return (t |= ai, ExprInline Nothing)+ else return (returnS (app "h$e" [ai]), ExprCont)+ _ -> panic "genApp: invalid size"++ -- no args and Id can't be a function: just enter it+ | [] <- args+ , idFunRepArity i == 0+ , not (might_be_a_function (idType i))+ = do+ enter_id <- genIdArg i >>=+ \case+ [x] -> return x+ xs -> pprPanic "genApp: unexpected multi-var argument"+ (vcat [ppr (length xs), ppr i])+ return (returnS (app "h$e" [enter_id]), ExprCont)++ -- fully saturated global function:+ -- - deals with arguments+ -- - jumps into the function+ | n <- length args+ , n /= 0+ , idFunRepArity i == n+ , not (isLocalId i)+ , isStrictId i+ = do+ as' <- concatMapM genArg args+ is <- assignAll jsRegsFromR1 <$> varsForId i+ jmp <- jumpToII i as' is+ return (jmp, ExprCont)++ -- oversaturated function:+ -- - push continuation with extra args+ -- - deals with arguments+ -- - jumps into the function+ | idFunRepArity i < length args+ , isStrictId i+ , idFunRepArity i > 0+ = do+ let (reg,over) = splitAt (idFunRepArity i) args+ reg' <- concatMapM genArg reg+ pc <- pushCont over+ is <- assignAll jsRegsFromR1 <$> varsForId i+ jmp <- jumpToII i reg' is+ return (pc <> jmp, ExprCont)++ -- generic apply:+ -- - try to find a pre-generated apply function that matches+ -- - use it if any+ -- - otherwise use generic apply function h$ap_gen_fast+ | otherwise+ = do+ is <- assignAll jsRegsFromR1 <$> varsForId i+ jmp <- jumpToFast args is+ return (jmp, ExprCont)++-- avoid one indirection for global ids+-- fixme in many cases we can also jump directly to the entry for local?+jumpToII :: Id -> [JExpr] -> JStat -> G JStat+jumpToII i vars load_app_in_r1+ | isLocalId i = do+ ii <- varForId i+ return $ mconcat+ [ assignAllReverseOrder jsRegsFromR2 vars+ , load_app_in_r1+ , returnS (closureEntry ii)+ ]+ | otherwise = do+ ei <- varForEntryId i+ return $ mconcat+ [ assignAllReverseOrder jsRegsFromR2 vars+ , load_app_in_r1+ , returnS ei+ ]++-- | Try to use a specialized pre-generated application function.+-- If there is none, use h$ap_gen_fast instead+jumpToFast :: HasDebugCallStack => [StgArg] -> JStat -> G JStat+jumpToFast args load_app_in_r1 = do+ -- get JS expressions for every argument+ -- Arguments may have more than one expression (e.g. Word64#)+ vars <- concatMapM genArg args+ -- try to find a specialized apply function+ let spec = mkApplySpec RegsConv args vars+ ap_fun <- selectApply spec+ pure $ mconcat+ [ assignAllReverseOrder jsRegsFromR2 vars+ , load_app_in_r1+ , case ap_fun of+ -- specialized apply: no tag+ Right fun -> returnS (ApplExpr fun [])+ -- generic apply: pass a tag indicating number of args/slots+ Left fun -> returnS (ApplExpr fun [specTagExpr spec])+ ]++-- | Calling convention for an apply function+data ApplyConv+ = RegsConv -- ^ Fast calling convention: use registers+ | StackConv -- ^ Slow calling convention: use the stack+ deriving (Show,Eq,Ord)++-- | Name of the generic apply function+genericApplyName :: ApplyConv -> FastString+genericApplyName = \case+ RegsConv -> "h$ap_gen_fast"+ StackConv -> "h$ap_gen"++-- | Expr of the generic apply function+genericApplyExpr :: ApplyConv -> JExpr+genericApplyExpr conv = var (genericApplyName conv)+++-- | Return the name of the specialized apply function for the given number of+-- args, number of arg variables, and calling convention.+specApplyName :: ApplySpec -> FastString+specApplyName = \case+ -- specialize a few for compiler performance (avoid building FastStrings over+ -- and over for common cases)+ ApplySpec RegsConv 0 0 -> "h$ap_0_0_fast"+ ApplySpec StackConv 0 0 -> "h$ap_0_0"+ ApplySpec RegsConv 1 0 -> "h$ap_1_0_fast"+ ApplySpec StackConv 1 0 -> "h$ap_1_0"+ ApplySpec RegsConv 1 1 -> "h$ap_1_1_fast"+ ApplySpec StackConv 1 1 -> "h$ap_1_1"+ ApplySpec RegsConv 1 2 -> "h$ap_1_2_fast"+ ApplySpec StackConv 1 2 -> "h$ap_1_2"+ ApplySpec RegsConv 2 1 -> "h$ap_2_1_fast"+ ApplySpec StackConv 2 1 -> "h$ap_2_1"+ ApplySpec RegsConv 2 2 -> "h$ap_2_2_fast"+ ApplySpec StackConv 2 2 -> "h$ap_2_2"+ ApplySpec RegsConv 2 3 -> "h$ap_2_3_fast"+ ApplySpec StackConv 2 3 -> "h$ap_2_3"+ ApplySpec conv nargs nvars -> mkFastString $ mconcat+ [ "h$ap_", show nargs+ , "_" , show nvars+ , case conv of+ RegsConv -> "_fast"+ StackConv -> ""+ ]++-- | Return the expression of the specialized apply function for the given+-- number of args, number of arg variables, and calling convention.+--+-- Warning: the returned function may not be generated! Use specApplyExprMaybe+-- if you want to ensure that it exists.+specApplyExpr :: ApplySpec -> JExpr+specApplyExpr spec = var (specApplyName spec)++-- | Return the expression of the specialized apply function for the given+-- number of args, number of arg variables, and calling convention.+-- Return Nothing if it isn't generated.+specApplyExprMaybe :: ApplySpec -> Maybe JExpr+specApplyExprMaybe spec =+ if spec `elem` applySpec+ then Just (specApplyExpr spec)+ else Nothing++-- | Make an ApplySpec from a calling convention, a list of Haskell args, and a+-- list of corresponding JS variables+mkApplySpec :: ApplyConv -> [StgArg] -> [JExpr] -> ApplySpec+mkApplySpec conv args vars = ApplySpec+ { specConv = conv+ , specArgs = length args+ , specVars = length vars+ }++-- | Find a specialized application function if there is one+selectApply+ :: ApplySpec+ -> G (Either JExpr JExpr) -- ^ the function to call (Left for generic, Right for specialized)+selectApply spec =+ case specApplyExprMaybe spec of+ Just e -> return (Right e)+ Nothing -> return (Left (genericApplyExpr (specConv spec)))+++-- | Apply specification+data ApplySpec = ApplySpec+ { specConv :: !ApplyConv -- ^ Calling convention+ , specArgs :: !Int -- ^ number of Haskell arguments+ , specVars :: !Int -- ^ number of JavaScript variables for the arguments+ }+ deriving (Show,Eq,Ord)++-- | List of specialized apply function templates+applySpec :: [ApplySpec]+applySpec = [ ApplySpec conv nargs nvars+ | conv <- [RegsConv, StackConv]+ , nargs <- [0..4]+ , nvars <- [max 0 (nargs-1)..(nargs*2)]+ ]++-- | Generate a tag for the given ApplySpec+--+-- Warning: tag doesn't take into account the calling convention+specTag :: ApplySpec -> Int+specTag spec = Bits.shiftL (specVars spec) 8 Bits..|. (specArgs spec)++-- | Generate a tag expression for the given ApplySpec+specTagExpr :: ApplySpec -> JExpr+specTagExpr = toJExpr . specTag++-- | Build arrays to quickly lookup apply functions+--+-- h$apply[r << 8 | n] = function application for r regs, n args+-- h$paps[r] = partial application for r registers (number of args is in the object)+mkApplyArr :: JStat+mkApplyArr = mconcat+ [ TxtI "h$apply" ||= toJExpr (JList [])+ , TxtI "h$paps" ||= toJExpr (JList [])+ , ApplStat (var "h$initStatic" .^ "push")+ [ ValExpr $ JFunc [] $ jVar \i -> mconcat+ [ i |= zero_+ , WhileStat False (i .<. Int 65536) $ mconcat+ [ var "h$apply" .! i |= var "h$ap_gen"+ , preIncrS i+ ]+ , i |= zero_+ , WhileStat False (i .<. Int 128) $ mconcat+ [ var "h$paps" .! i |= var "h$pap_gen"+ , preIncrS i+ ]+ , mconcat (map assignSpec applySpec)+ , mconcat (map assignPap specPap)+ ]+ ]+ ]+ where+ assignSpec :: ApplySpec -> JStat+ assignSpec spec = case specConv spec of+ -- both fast/slow (regs/stack) specialized apply functions have the same+ -- tags. We store the stack ones in the array because they are used as+ -- continuation stack frames.+ StackConv -> var "h$apply" .! specTagExpr spec |= specApplyExpr spec+ RegsConv -> mempty++ assignPap :: Int -> JStat+ assignPap p = var "h$paps" .! toJExpr p |=+ (var (mkFastString $ ("h$pap_" ++ show p)))++-- | Push a continuation on the stack+--+-- First push the given args, then push an apply function (specialized if+-- possible, otherwise the generic h$ap_gen function).+pushCont :: HasDebugCallStack+ => [StgArg]+ -> G JStat+pushCont args = do+ vars <- concatMapM genArg args+ let spec = mkApplySpec StackConv args vars+ selectApply spec >>= \case+ Right app -> push $ reverse $ app : vars+ Left app -> push $ reverse $ app : specTagExpr spec : vars++-- | Generic stack apply function (h$ap_gen) that can do everything, but less+-- efficiently than other more specialized functions.+--+-- Stack layout:+-- -3: ...+-- -2: args+-- -1: tag (number of arg slots << 8 | number of args)+--+-- Regs:+-- R1 = applied closure+--+genericStackApply :: StgToJSConfig -> JStat+genericStackApply cfg = closure info body+ where+ -- h$ap_gen body+ body = jVar \cf ->+ [ traceRts cfg (jString "h$ap_gen")+ , cf |= closureEntry r1+ -- switch on closure type+ , SwitchStat (entryClosureType cf)+ [ (toJExpr Thunk , thunk_case cfg cf)+ , (toJExpr Fun , fun_case cf (funArity' cf))+ , (toJExpr Pap , fun_case cf (papArity r1))+ , (toJExpr Blackhole, blackhole_case cfg)+ ]+ (default_case cf)+ ]++ -- info table for h$ap_gen+ info = ClosureInfo+ { ciVar = TxtI "h$ap_gen"+ , ciRegs = CIRegs 0 [PtrV] -- closure to apply to+ , ciName = "h$ap_gen"+ , ciLayout = CILayoutVariable+ , ciType = CIStackFrame+ , ciStatic = mempty+ }++ default_case cf = appS "throw" [jString "h$ap_gen: unexpected closure type "+ + (entryClosureType cf)]++ thunk_case cfg cf = mconcat+ [ profStat cfg pushRestoreCCS+ , returnS cf+ ]++ blackhole_case cfg = mconcat+ [ push' cfg [r1, var "h$return"]+ , returnS (app "h$blockOnBlackhole" [r1])+ ]++ fun_case c arity = jVar \tag needed_args needed_regs given_args given_regs newTag newAp p dat ->+ [ tag |= stack .! (sp - 1) -- tag on the stack+ , given_args |= mask8 tag -- indicates the number of passed args+ , given_regs |= tag .>>. 8 -- and the number of passed values for registers+ , needed_args |= mask8 arity+ , needed_regs |= arity .>>. 8+ , traceRts cfg (jString "h$ap_gen: args: " + given_args+ + jString " regs: " + given_regs)+ , ifBlockS (given_args .===. needed_args)+ --------------------------------+ -- exactly saturated application+ --------------------------------+ [ traceRts cfg (jString "h$ap_gen: exact")+ -- Set registers to register values on the stack+ , loop 0 (.<. given_regs) \i -> mconcat+ [ appS "h$setReg" [i+2, stack .! (sp-2-i)]+ , postIncrS i+ ]+ -- drop register values from the stack+ , sp |= sp - given_regs - 2+ -- enter closure in R1+ , returnS c+ ]+ [ ifBlockS (given_args .>. needed_args)+ ----------------------------+ -- oversaturated application+ ----------------------------+ [ traceRts cfg (jString "h$ap_gen: oversat: arity: " + needed_args+ + jString " regs: " + needed_regs)+ -- load needed register values+ , loop 0 (.<. needed_regs) \i -> mconcat+ [ traceRts cfg (jString "h$ap_gen: loading register: " + i)+ , appS "h$setReg" [i+2, stack .! (sp-2-i)]+ , postIncrS i+ ]+ -- compute new tag with consumed register values and args removed+ , newTag |= ((given_regs-needed_regs).<<.8) .|. (given_args - needed_args)+ -- find application function for the remaining regs/args+ , newAp |= var "h$apply" .! newTag+ , traceRts cfg (jString "h$ap_gen: next: " + (newAp .^ "n"))++ -- Drop used registers from the stack.+ -- Test if the application function needs a tag and push it.+ , ifS (newAp .===. var "h$ap_gen")+ ((sp |= sp - needed_regs) <> (stack .! (sp - 1) |= newTag))+ (sp |= sp - needed_regs - 1)++ -- Push generic application function as continuation+ , stack .! sp |= newAp++ -- Push "current thread CCS restore" function as continuation+ , profStat cfg pushRestoreCCS++ -- enter closure in R1+ , returnS c+ ]++ -----------------------------+ -- undersaturated application+ -----------------------------+ [ traceRts cfg (jString "h$ap_gen: undersat")+ -- find PAP entry function corresponding to given_regs count+ , p |= var "h$paps" .! given_regs++ -- build PAP payload: R1 + tag + given register values+ , newTag |= ((needed_regs-given_regs) .<<. 8) .|. (needed_args-given_args)+ , dat |= toJExpr [r1, newTag]+ , loop 0 (.<. given_regs) \i -> mconcat+ [ (dat .^ "push") `ApplStat` [stack .! (sp - i - 2)]+ , postIncrS i+ ]++ -- remove register values from the stack.+ , sp |= sp - given_regs - 2++ -- alloc PAP closure, store reference to it in R1.+ , r1 |= initClosure cfg p dat jCurrentCCS++ -- return to the continuation on the stack+ , returnStack+ ]+ ]+ ]++-- | Generic fast apply function (h$ap_gen_fast) that can do everything, but less+-- efficiently than other more specialized functions.+--+-- Signature tag in argument. Tag: (regs << 8 | arity)+--+-- Regs:+-- R1 = closure to apply to+--+genericFastApply :: StgToJSConfig -> JStat+genericFastApply s =+ TxtI "h$ap_gen_fast" ||= jLam \tag -> jVar \c ->+ [traceRts s (jString "h$ap_gen_fast: " + tag)+ , c |= closureEntry r1+ , SwitchStat (entryClosureType c)+ [ (toJExpr Thunk, traceRts s (jString "h$ap_gen_fast: thunk")+ <> pushStackApply c tag+ <> returnS c)+ , (toJExpr Fun, jVar \farity ->+ [ farity |= funArity' c+ , traceRts s (jString "h$ap_gen_fast: fun " + farity)+ , funCase c tag farity+ ])+ , (toJExpr Pap, jVar \parity ->+ [ parity |= papArity r1+ , traceRts s (jString "h$ap_gen_fast: pap " + parity)+ , funCase c tag parity+ ])+ , (toJExpr Con, traceRts s (jString "h$ap_gen_fast: con")+ <> jwhenS (tag .!=. 0)+ (appS "throw" [jString "h$ap_gen_fast: invalid apply"])+ <> returnS c)+ , (toJExpr Blackhole, traceRts s (jString "h$ap_gen_fast: blackhole")+ <> pushStackApply c tag+ <> push' s [r1, var "h$return"]+ <> returnS (app "h$blockOnBlackhole" [r1]))+ ] $ appS "throw" [jString "h$ap_gen_fast: unexpected closure type: " + entryClosureType c]+ ]++ where+ -- thunk: push everything to stack frame, enter thunk first+ pushStackApply :: JExpr -> JExpr -> JStat+ pushStackApply _c tag =+ jVar \ap ->+ [ pushAllRegs tag+ , ap |= var "h$apply" .! tag+ , ifS (ap .===. var "h$ap_gen")+ ((sp |= sp + 2) <> (stack .! (sp-1) |= tag))+ (sp |= sp + 1)+ , stack .! sp |= ap+ , profStat s pushRestoreCCS+ ]++ funCase :: JExpr -> JExpr -> JExpr -> JStat+ funCase c tag arity =+ jVar \ar myAr myRegs regsStart newTag newAp dat p ->+ [ ar |= mask8 arity+ , myAr |= mask8 tag+ , myRegs |= tag .>>. 8+ , traceRts s (jString "h$ap_gen_fast: args: " + myAr+ + jString " regs: " + myRegs)+ , ifS (myAr .===. ar)+ -- call the function directly+ (traceRts s (jString "h$ap_gen_fast: exact") <> returnS c)+ (ifBlockS (myAr .>. ar)+ -- push stack frame with remaining args, then call fun+ [ traceRts s (jString "h$ap_gen_fast: oversat " + sp)+ , regsStart |= (arity .>>. 8) + 1+ , sp |= sp + myRegs - regsStart + 1+ , traceRts s (jString "h$ap_gen_fast: oversat " + sp)+ , pushArgs regsStart myRegs+ , newTag |= ((myRegs-( arity.>>.8)).<<.8).|.myAr-ar+ , newAp |= var "h$apply" .! newTag+ , ifS (newAp .===. var "h$ap_gen")+ ((sp |= sp + 2) <> (stack .! (sp - 1) |= newTag))+ (sp |= sp + 1)+ , stack .! sp |= newAp+ , profStat s pushRestoreCCS+ , returnS c+ ]+ -- else+ [traceRts s (jString "h$ap_gen_fast: undersat: " + myRegs + jString " " + tag)+ , jwhenS (tag .!=. 0) $ mconcat+ [ p |= var "h$paps" .! myRegs+ , dat |= toJExpr [r1, ((arity .>>. 8)-myRegs)*256+ar-myAr]+ , loop 0 (.<. myRegs)+ (\i -> (dat .^ "push")+ `ApplStat` [app "h$getReg" [i+2]] <> postIncrS i)+ , r1 |= initClosure s p dat jCurrentCCS+ ]+ , returnStack+ ])+ ]+++ pushAllRegs :: JExpr -> JStat+ pushAllRegs tag =+ jVar \regs ->+ [ regs |= tag .>>. 8+ , sp |= sp + regs+ , SwitchStat regs (map pushReg [65,64..2]) mempty+ ]+ where+ pushReg :: Int -> (JExpr, JStat)+ pushReg r = (toJExpr (r-1), stack .! (sp - toJExpr (r - 2)) |= jsReg r)++ pushArgs :: JExpr -> JExpr -> JStat+ pushArgs start end =+ loop end (.>=.start) (\i -> traceRts s (jString "pushing register: " + i)+ <> (stack .! (sp + start - i) |= app "h$getReg" [i+1])+ <> postDecrS i+ )++-- | Make specialized apply function for the given ApplySpec+specApply :: StgToJSConfig -> ApplySpec -> JStat+specApply cfg spec@(ApplySpec conv nargs nvars) =+ let fun_name = specApplyName spec+ in case conv of+ RegsConv -> fastApply cfg fun_name nargs nvars+ StackConv -> stackApply cfg fun_name nargs nvars++-- | Make specialized apply function with Stack calling convention+stackApply+ :: StgToJSConfig+ -> FastString+ -> Int+ -> Int+ -> JStat+stackApply s fun_name nargs nvars =+ -- special case for h$ap_0_0+ if nargs == 0 && nvars == 0+ then closure info0 body0+ else closure info body+ where+ info = ClosureInfo (TxtI fun_name) (CIRegs 0 [PtrV]) fun_name (CILayoutUnknown nvars) CIStackFrame mempty+ info0 = ClosureInfo (TxtI fun_name) (CIRegs 0 [PtrV]) fun_name (CILayoutFixed 0 []) CIStackFrame mempty++ body0 = adjSpN' 1 <> enter s r1++ body = jVar \c ->+ [ c |= closureEntry r1+ , traceRts s (toJExpr fun_name+ + jString " "+ + (c .^ "n")+ + jString " sp: " + sp+ + jString " a: " + (c .^ "a"))+ , SwitchStat (entryClosureType c)+ [ (toJExpr Thunk, traceRts s (toJExpr $ fun_name <> ": thunk") <> profStat s pushRestoreCCS <> returnS c)+ , (toJExpr Fun, traceRts s (toJExpr $ fun_name <> ": fun") <> funCase c)+ , (toJExpr Pap, traceRts s (toJExpr $ fun_name <> ": pap") <> papCase c)+ , (toJExpr Blackhole, push' s [r1, var "h$return"] <> returnS (app "h$blockOnBlackhole" [r1]))+ ] (appS "throw" [toJExpr ("panic: " <> fun_name <> ", unexpected closure type: ") + (entryClosureType c)])+ ]++ funExact c = popSkip 1 (reverse $ take nvars jsRegsFromR2) <> returnS c+ stackArgs = map (\x -> stack .! (sp - toJExpr x)) [1..nvars]++ papCase :: JExpr -> JStat+ papCase c = jVar \expr arity0 arity ->+ case expr of+ ValExpr (JVar pap) -> [ arity0 |= papArity r1+ , arity |= mask8 arity0+ , traceRts s (toJExpr (fun_name <> ": found pap, arity: ") + arity)+ , ifS (toJExpr nargs .===. arity)+ --then+ (traceRts s (toJExpr (fun_name <> ": exact")) <> funExact c)+ -- else+ (ifS (toJExpr nargs .>. arity)+ (traceRts s (toJExpr (fun_name <> ": oversat")) <> oversatCase c arity0 arity)+ (traceRts s (toJExpr (fun_name <> ": undersat"))+ <> mkPap s pap r1 (toJExpr nargs) stackArgs+ <> (sp |= sp - toJExpr (nvars + 1))+ <> (r1 |= toJExpr pap)+ <> returnStack))+ ]+ _ -> mempty+++ funCase :: JExpr -> JStat+ funCase c = jVar \expr ar0 ar ->+ case expr of+ ValExpr (JVar pap) -> [ ar0 |= funArity' c+ , ar |= mask8 ar0+ , ifS (toJExpr nargs .===. ar)+ (traceRts s (toJExpr (fun_name <> ": exact")) <> funExact c)+ (ifS (toJExpr nargs .>. ar)+ (traceRts s (toJExpr (fun_name <> ": oversat"))+ <> oversatCase c ar0 ar)+ (traceRts s (toJExpr (fun_name <> ": undersat"))+ <> mkPap s pap (toJExpr R1) (toJExpr nargs) stackArgs+ <> (sp |= sp - toJExpr (nvars+1))+ <> (r1 |= toJExpr pap)+ <> returnStack))+ ]+ _ -> mempty+++ -- oversat: call the function but keep enough on the stack for the next+ oversatCase :: JExpr -- function+ -> JExpr -- the arity tag+ -> JExpr -- real arity (arity & 0xff)+ -> JStat+ oversatCase c arity arity0 =+ jVar \rs newAp ->+ [ rs |= (arity .>>. 8)+ , loadRegs rs+ , sp |= sp - rs+ , newAp |= (var "h$apply" .! ((toJExpr nargs-arity0).|.((toJExpr nvars-rs).<<.8)))+ , stack .! sp |= newAp+ , profStat s pushRestoreCCS+ , traceRts s (toJExpr (fun_name <> ": new stack frame: ") + (newAp .^ "n"))+ , returnS c+ ]+ where+ loadRegs rs = SwitchStat rs switchAlts mempty+ where+ switchAlts = map (\x -> (toJExpr x, jsReg (x+1) |= stack .! (sp - toJExpr x))) [nvars,nvars-1..1]++-- | Make specialized apply function with Regs calling convention+--+-- h$ap_n_r_fast is entered if a function of unknown arity is called, n+-- arguments are already in r registers+fastApply :: StgToJSConfig -> FastString -> Int -> Int -> JStat+fastApply s fun_name nargs nvars = func ||= body0+ where+ -- special case for h$ap_0_0_fast+ body0 = if nargs == 0 && nvars == 0+ then jLam (enter s r1)+ else toJExpr (JFunc myFunArgs body)++ func = TxtI fun_name++ myFunArgs = []++ regArgs = take nvars jsRegsFromR2++ mkAp :: Int -> Int -> [JExpr]+ mkAp n' r' = [ specApplyExpr (ApplySpec StackConv n' r') ]++ body =+ jVar \c farity arity ->+ [ c |= closureEntry r1+ , traceRts s (toJExpr (fun_name <> ": sp ") + sp)+ , SwitchStat (entryClosureType c)+ [(toJExpr Fun, traceRts s (toJExpr (fun_name <> ": ")+ + clName c+ + jString " (arity: " + (c .^ "a") + jString ")")+ <> (farity |= funArity' c)+ <> funCase c farity)+ ,(toJExpr Pap, traceRts s (toJExpr (fun_name <> ": pap")) <> (arity |= papArity r1) <> funCase c arity)+ ,(toJExpr Thunk, traceRts s (toJExpr (fun_name <> ": thunk")) <> push' s (reverse regArgs ++ mkAp nargs nvars) <> profStat s pushRestoreCCS <> returnS c)+ ,(toJExpr Blackhole, traceRts s (toJExpr (fun_name <> ": blackhole")) <> push' s (reverse regArgs ++ mkAp nargs nvars) <> push' s [r1, var "h$return"] <> returnS (app "h$blockOnBlackhole" [r1]))]+ (appS "throw" [toJExpr (fun_name <> ": unexpected closure type: ") + entryClosureType c])+ ]++ funCase :: JExpr -> JExpr -> JStat+ funCase c arity = jVar \arg ar -> case arg of+ ValExpr (JVar pap) -> [ ar |= mask8 arity+ , ifS (toJExpr nargs .===. ar)+ -- then+ (traceRts s (toJExpr (fun_name <> ": exact")) <> returnS c)+ -- else+ (ifS (toJExpr nargs .>. ar)+ --then+ (traceRts s (toJExpr (fun_name <> ": oversat")) <> oversatCase c arity)+ -- else+ (traceRts s (toJExpr (fun_name <> ": undersat"))+ <> mkPap s pap r1 (toJExpr nargs) regArgs+ <> (r1 |= toJExpr pap)+ <> returnStack))+ ]+ _ -> mempty++ oversatCase :: JExpr -> JExpr -> JStat+ oversatCase c arity =+ jVar \rs rsRemain ->+ [ rs |= arity .>>. 8+ , rsRemain |= toJExpr nvars - rs+ , traceRts s (toJExpr+ (fun_name <> " regs oversat ")+ + rs+ + jString " remain: "+ + rsRemain)+ , saveRegs rs+ , sp |= sp + rsRemain + 1+ , stack .! sp |= var "h$apply" .! ((rsRemain.<<.8).|. (toJExpr nargs - mask8 arity))+ , profStat s pushRestoreCCS+ , returnS c+ ]+ where+ saveRegs n = SwitchStat n switchAlts mempty+ where+ switchAlts = map (\x -> (toJExpr x, stack .! (sp + toJExpr (nvars-x)) |= jsReg (x+2))) [0..nvars-1]++zeroApply :: StgToJSConfig -> JStat+zeroApply s = mconcat+ [ TxtI "h$e" ||= jLam (\c -> (r1 |= c) <> enter s c)+ ]++-- carefully enter a closure that might be a thunk or a function++-- ex may be a local var, but must've been copied to R1 before calling this+enter :: StgToJSConfig -> JExpr -> JStat+enter s ex = jVar \c ->+ [ jwhenS (app "typeof" [ex] .!==. jTyObject) returnStack+ , c |= closureEntry ex+ , jwhenS (c .===. var "h$unbox_e") ((r1 |= closureField1 ex) <> returnStack)+ , SwitchStat (entryClosureType c)+ [ (toJExpr Con, mempty)+ , (toJExpr Fun, mempty)+ , (toJExpr Pap, returnStack)+ , (toJExpr Blackhole, push' s [var "h$ap_0_0", ex, var "h$return"]+ <> returnS (app "h$blockOnBlackhole" [ex]))+ ] (returnS c)+ ]++updates :: StgToJSConfig -> JStat+updates s = BlockStat+ [ closure+ (ClosureInfo (TxtI "h$upd_frame") (CIRegs 0 [PtrV]) "h$upd_frame" (CILayoutFixed 1 [PtrV]) CIStackFrame mempty)+ $ jVar \updatee waiters ss si sir ->+ let unbox_closure = Closure+ { clEntry = var "h$unbox_e"+ , clField1 = sir+ , clField2 = null_+ , clMeta = 0+ , clCC = Nothing+ }+ updateCC updatee = closureCC updatee |= jCurrentCCS+ in [ updatee |= stack .! (sp - 1)+ , traceRts s (jString "h$upd_frame updatee alloc: " + updatee .^ "alloc")+ , -- wake up threads blocked on blackhole+ waiters |= closureField2 updatee+ , jwhenS (waiters .!==. null_)+ (loop 0 (.<. waiters .^ "length")+ (\i -> appS "h$wakeupThread" [waiters .! i] <> postIncrS i))+ , -- update selectors+ jwhenS ((app "typeof" [closureMeta updatee] .===. jTyObject) .&&. (closureMeta updatee .^ "sel"))+ ((ss |= closureMeta updatee .^ "sel")+ <> loop 0 (.<. ss .^ "length") \i -> mconcat+ [ si |= ss .! i+ , sir |= (closureField2 si) `ApplExpr` [r1]+ , ifS (app "typeof" [sir] .===. jTyObject)+ (copyClosure DontCopyCC si sir)+ (assignClosure si unbox_closure)+ , postIncrS i+ ])+ , -- overwrite the object+ ifS (app "typeof" [r1] .===. jTyObject)+ (mconcat [ traceRts s (jString "$upd_frame: boxed: " + ((closureEntry r1) .^ "n"))+ , copyClosure DontCopyCC updatee r1+ ])+ -- the heap object is represented by another type of value+ -- (e.g. a JS number or string) so the unboxing closure+ -- will simply return it.+ (assignClosure updatee (unbox_closure { clField1 = r1 }))+ , profStat s (updateCC updatee)+ , adjSpN' 2+ , traceRts s (jString "h$upd_frame: updating: "+ + updatee+ + jString " -> "+ + r1)+ , returnStack+ ]++ , closure+ (ClosureInfo (TxtI "h$upd_frame_lne") (CIRegs 0 [PtrV]) "h$upd_frame_lne" (CILayoutFixed 1 [PtrV]) CIStackFrame mempty)+ $ jVar \updateePos ->+ [ updateePos |= stack .! (sp - 1)+ , (stack .! updateePos |= r1)+ , adjSpN' 2+ , traceRts s (jString "h$upd_frame_lne: updating: "+ + updateePos+ + jString " -> "+ + r1)+ , returnStack+ ]+ ]++selectors :: StgToJSConfig -> JStat+selectors s =+ mkSel "1" closureField1+ <> mkSel "2a" closureField2+ <> mkSel "2b" (closureField1 . closureField2)+ <> mconcat (map mkSelN [3..16])+ where+ mkSelN :: Int -> JStat+ mkSelN x = mkSel (mkFastString $ show x)+ (\e -> SelExpr (closureField2 (toJExpr e))+ (TxtI $ mkFastString ("d" ++ show (x-1))))+++ mkSel :: FastString -> (JExpr -> JExpr) -> JStat+ mkSel name sel = mconcat+ [TxtI createName ||= jLam \r -> mconcat+ [ traceRts s (toJExpr ("selector create: " <> name <> " for ") + (r .^ "alloc"))+ , ifS (isThunk r .||. isBlackhole r)+ (returnS (app "h$mkSelThunk" [r, toJExpr (v entryName), toJExpr (v resName)]))+ (returnS (sel r))+ ]+ , TxtI resName ||= jLam \r -> mconcat+ [ traceRts s (toJExpr ("selector result: " <> name <> " for ") + (r .^ "alloc"))+ , returnS (sel r)+ ]+ , closure+ (ClosureInfo (TxtI entryName) (CIRegs 0 [PtrV]) ("select " <> name) (CILayoutFixed 1 [PtrV]) CIThunk mempty)+ (jVar \tgt ->+ [ tgt |= closureField1 r1+ , traceRts s (toJExpr ("selector entry: " <> name <> " for ") + (tgt .^ "alloc"))+ , ifS (isThunk tgt .||. isBlackhole tgt)+ (preIncrS sp+ <> (stack .! sp |= var frameName)+ <> returnS (app "h$e" [tgt]))+ (returnS (app "h$e" [sel tgt]))+ ])+ , closure+ (ClosureInfo (TxtI frameName) (CIRegs 0 [PtrV]) ("select " <> name <> " frame") (CILayoutFixed 0 []) CIStackFrame mempty)+ $ mconcat [ traceRts s (toJExpr ("selector frame: " <> name))+ , postDecrS sp+ , returnS (app "h$e" [sel r1])+ ]+ ]++ where+ v x = JVar (TxtI x)+ n ext = "h$c_sel_" <> name <> ext+ createName = n ""+ resName = n "_res"+ entryName = n "_e"+ frameName = n "_frame_e"+++-- arity is the remaining arity after our supplied arguments are applied+mkPap :: StgToJSConfig+ -> Ident -- ^ id of the pap object+ -> JExpr -- ^ the function that's called (can be a second pap)+ -> JExpr -- ^ number of arguments in pap+ -> [JExpr] -- ^ values for the supplied arguments+ -> JStat+mkPap s tgt fun n values =+ traceRts s (toJExpr $ "making pap with: " ++ show (length values) ++ " items")+ `mappend`+ allocDynamic s True tgt (toJExpr entry) (fun:papAr:map toJExpr values')+ (if csProf s then Just jCurrentCCS else Nothing)+ where+ papAr = funOrPapArity fun Nothing - toJExpr (length values * 256) - n++ values' | GHC.Prelude.null values = [null_]+ | otherwise = values+ entry | length values > numSpecPap = TxtI "h$pap_gen"+ | otherwise = specPapIdents ! length values++-- | Number of specialized PAPs (pre-generated for a given number of args)+numSpecPap :: Int+numSpecPap = 6++-- specialized (faster) pap generated for [0..numSpecPap]+-- others use h$pap_gen+specPap :: [Int]+specPap = [0..numSpecPap]++-- | Cache of specialized PAP idents+specPapIdents :: Array Int Ident+specPapIdents = listArray (0,numSpecPap) $ map (TxtI . mkFastString . ("h$pap_"++) . show) specPap++pap :: StgToJSConfig+ -> Int+ -> JStat+pap s r = closure (ClosureInfo funcIdent CIRegsUnknown funcName (CILayoutUnknown (r+2)) CIPap mempty) body+ where+ funcIdent = TxtI funcName+ funcName = mkFastString ("h$pap_" ++ show r)++ body = jVar \c d f extra ->+ [ c |= closureField1 r1+ , d |= closureField2 r1+ , f |= closureEntry c+ , assertRts s (isFun' f .||. isPap' f) (funcName <> ": expected function or pap")+ , profStat s (enterCostCentreFun currentCCS)+ , extra |= (funOrPapArity c (Just f) .>>. 8) - toJExpr r+ , traceRts s (toJExpr (funcName <> ": pap extra args moving: ") + extra)+ , moveBy extra+ , loadOwnArgs d+ , r1 |= c+ , returnS f+ ]+ moveBy extra = SwitchStat extra+ (reverse $ map moveCase [1..maxReg-r-1]) mempty+ moveCase m = (toJExpr m, jsReg (m+r+1) |= jsReg (m+1))+ loadOwnArgs d = mconcat $ map (\r ->+ jsReg (r+1) |= dField d (r+2)) [1..r]+ dField d n = SelExpr d (TxtI . mkFastString $ ('d':show (n-1)))++-- Construct a generic PAP+papGen :: StgToJSConfig -> JStat+papGen cfg =+ closure (ClosureInfo funcIdent CIRegsUnknown funcName CILayoutVariable CIPap mempty)+ (jVar \c f d pr or r ->+ [ c |= closureField1 r1+ , d |= closureField2 r1+ , f |= closureEntry c+ , pr |= funOrPapArity c (Just f) .>>. 8+ , or |= papArity r1 .>>. 8+ , r |= pr - or+ , assertRts cfg+ (isFun' f .||. isPap' f)+ (jString "h$pap_gen: expected function or pap")+ , profStat cfg (enterCostCentreFun currentCCS)+ , traceRts cfg (jString "h$pap_gen: generic pap extra args moving: " + or)+ , appS "h$moveRegs2" [or, r]+ , loadOwnArgs d r+ , r1 |= c+ , returnS f+ ])+++ where+ funcIdent = TxtI funcName+ funcName = "h$pap_gen"+ loadOwnArgs d r =+ let prop n = d .^ ("d" <> mkFastString (show $ n+1))+ loadOwnArg n = (toJExpr n, jsReg (n+1) |= prop n)+ in SwitchStat r (map loadOwnArg [127,126..1]) mempty++-- general utilities+-- move the first n registers, starting at R2, m places up (do not use with negative m)+moveRegs2 :: JStat+moveRegs2 = TxtI "h$moveRegs2" ||= jLam moveSwitch+ where+ moveSwitch n m = SwitchStat ((n .<<. 8) .|. m) switchCases (defaultCase n m)+ -- fast cases+ switchCases = [switchCase n m | n <- [1..5], m <- [1..4]]+ switchCase :: Int -> Int -> (JExpr, JStat)+ switchCase n m = (toJExpr $+ (n `Bits.shiftL` 8) Bits..|. m+ , mconcat (map (`moveRegFast` m) [n+1,n..2])+ <> BreakStat Nothing {-[j| break; |]-})+ moveRegFast n m = jsReg (n+m) |= jsReg n+ -- fallback+ defaultCase n m =+ loop n (.>.0) (\i -> appS "h$setReg" [i+1+m, app "h$getReg" [i+1]] `mappend` postDecrS i)+++-- Initalize a variable sized object from an array of values+initClosure :: StgToJSConfig -> JExpr -> JExpr -> JExpr -> JExpr+initClosure cfg entry values ccs = app "h$init_closure"+ [ newClosure $ Closure+ { clEntry = entry+ , clField1 = null_+ , clField2 = null_+ , clMeta = 0+ , clCC = if csProf cfg then Just ccs else Nothing+ }+ , values+ ]++-- | Return an expression for every field of the given Id+getIdFields :: Id -> G [TypedExpr]+getIdFields i = assocIdExprs i <$> varsForId i++-- | Store fields of Id into the given target expressions+storeIdFields :: Id -> [TypedExpr] -> G JStat+storeIdFields i dst = do+ fields <- getIdFields i+ pure (assignCoerce1 dst fields)
+ compiler/GHC/StgToJS/Arg.hs view
@@ -0,0 +1,285 @@+{-# LANGUAGE LambdaCase #-}++-----------------------------------------------------------------------------+-- |+-- Module : GHC.StgToJS.Args+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Sylvain Henry <sylvain.henry@iohk.io>+-- Josh Meredith <josh.meredith@iohk.io>+-- Stability : experimental+--+-- Code generation of application arguments+-----------------------------------------------------------------------------++module GHC.StgToJS.Arg+ ( genArg+ , genIdArg+ , genIdArgI+ , genIdStackArgI+ , allocConStatic+ , allocUnboxedConStatic+ , allocateStaticList+ , jsStaticArg+ , jsStaticArgs+ )+where++import GHC.Prelude++import GHC.JS.Syntax+import GHC.JS.Make++import GHC.StgToJS.DataCon+import GHC.StgToJS.Types+import GHC.StgToJS.Monad+import GHC.StgToJS.Literal+import GHC.StgToJS.CoreUtils+import GHC.StgToJS.Profiling+import GHC.StgToJS.Ids++import GHC.Builtin.Types+import GHC.Stg.Syntax+import GHC.Core.DataCon++import GHC.Types.CostCentre+import GHC.Types.Unique.FM+import GHC.Types.Id++import GHC.Utils.Misc+import GHC.Utils.Outputable+import GHC.Utils.Panic+import qualified Control.Monad.Trans.State.Strict as State++{-+Note [ Unboxable Literals Optimization ]+~~~~~~~~~~~~~~~~~~++Boxable types in the JS backend are represented as heap objects. See Note+[StgToJS design] in GHC.StgToJS.hs for more details. Some types, such as Int8+do not benefit from not being wrapped in an object in the JS runtime. This optimization+detects such types and changes the code generator to generate a more efficient+representation. The change is minor and saves one level on indirection. Instead+of generating a wrapper object with a field for the value's payload, such as:++// a JS object for an Int8+var anInt8 = { d1 = <Int8# payload>+ , f : entry function which would scrutinize the payload+ }++we instead generate:++// notice, no wrapper object. This representation is essentially an Int8# in the JS backend+var anInt8 = <Int8# payload>++This optimization fires when the follow invariants hold:+ 1. The value in question has a Type which has a single data constructor+ 2. The data constructor holds a single field that is monomorphic+ 3. The value in question is distinguishable from a THUNK using the JavaScript typeof operator.++From the haskell perspective this means that:+ 1. An Int8# is always a JavaScript 'number', never a JavaScript object.+ 2. An Int8 is either a JavaScript 'number' _or_ a JavaScript object depending on+ its use case and this optimization.++How is this sound?+~~~~~~~~~~~~~~~~~~++Normally this optimization would violate the guarantees of call-by-need, however+we are able to statically detect whether the type in question will be a THUNK or+not during code gen because the JS backend is consuming STG and we can check+during runtime with the typeof operator. Similarly we can check at runtime using+JavaScript's introspection operator `typeof`. Thus, when we know the value in+question will not be a THUNK we can safely elide the wrapping object, which+unboxes the value in the JS runtime. For example, an Int8 contains an Int8#+which has the JavaScript type 'number'. A THUNK of type Int8 would have a+JavaScript type 'object', so using 'typeof' allows us to check if we have+something that is definitely evaluated (i.e., a 'number') or something else. If+it is an 'object' then we may need to enter it to begin its evaluation. Consider+a type which has a 'ThreadId#' field; such as type would not be subject to this+optimization because it has to be represented as a JavaScript 'object' and thus+cannot be unboxed in this way. Another (edge) case is Int64#. Int64# is+similarly not unboxable in this way because Int64# does not fit in one+JavaScript variable and thus requires an 'object' for its representation in the+JavaScript runtime.++-}++-- | Generate JS code for static arguments+genStaticArg :: HasDebugCallStack => StgArg -> G [StaticArg]+genStaticArg a = case a of+ StgLitArg l -> map StaticLitArg <$> genStaticLit l+ StgVarArg i -> do+ unFloat <- State.gets gsUnfloated+ case lookupUFM unFloat i of+ Nothing -> reg+ Just expr -> unfloated expr+ where+ r = uTypeVt . stgArgType $ a+ reg+ | isVoid r =+ return []+ | i == trueDataConId =+ return [StaticLitArg (BoolLit True)]+ | i == falseDataConId =+ return [StaticLitArg (BoolLit False)]+ | isMultiVar r =+ map (\(TxtI t) -> StaticObjArg t) <$> mapM (identForIdN i) [1..varSize r] -- this seems wrong, not an obj?+ | otherwise = (\(TxtI it) -> [StaticObjArg it]) <$> identForId i++ unfloated :: CgStgExpr -> G [StaticArg]+ unfloated (StgLit l) = map StaticLitArg <$> genStaticLit l+ unfloated (StgConApp dc _n args _)+ | isBoolDataCon dc || isUnboxableCon dc =+ (:[]) . allocUnboxedConStatic dc . concat <$> mapM genStaticArg args -- fixme what is allocunboxedcon?+ | null args = (\(TxtI t) -> [StaticObjArg t]) <$> identForId (dataConWorkId dc)+ | otherwise = do+ as <- concat <$> mapM genStaticArg args+ (TxtI e) <- identForDataConWorker dc+ return [StaticConArg e as]+ unfloated x = pprPanic "genArg: unexpected unfloated expression" (pprStgExpr panicStgPprOpts x)++-- | Generate JS code for an StgArg+genArg :: HasDebugCallStack => StgArg -> G [JExpr]+genArg a = case a of+ StgLitArg l -> genLit l+ StgVarArg i -> do+ unFloat <- State.gets gsUnfloated+ case lookupUFM unFloat i of+ Just expr -> unfloated expr+ Nothing+ | isVoid r -> return []+ | i == trueDataConId -> return [true_]+ | i == falseDataConId -> return [false_]+ | isMultiVar r -> mapM (varForIdN i) [1..varSize r]+ | otherwise -> (:[]) <$> varForId i++ where+ -- if our argument is a joinid, it can be an unboxed tuple+ r :: HasDebugCallStack => VarType+ r = uTypeVt . stgArgType $ a++ unfloated :: HasDebugCallStack => CgStgExpr -> G [JExpr]+ unfloated = \case+ StgLit l -> genLit l+ StgConApp dc _n args _+ | isBoolDataCon dc || isUnboxableCon dc+ -> (:[]) . allocUnboxedCon dc . concat <$> mapM genArg args+ | null args -> (:[]) <$> varForId (dataConWorkId dc)+ | otherwise -> do+ as <- concat <$> mapM genArg args+ e <- varForDataConWorker dc+ inl_alloc <- csInlineAlloc <$> getSettings+ return [allocDynamicE inl_alloc e as Nothing]+ x -> pprPanic "genArg: unexpected unfloated expression" (pprStgExpr panicStgPprOpts x)++-- | Generate a Var as JExpr+genIdArg :: HasDebugCallStack => Id -> G [JExpr]+genIdArg i = genArg (StgVarArg i)++-- | Generate an Id as an Ident+genIdArgI :: HasDebugCallStack => Id -> G [Ident]+genIdArgI i+ | isVoid r = return []+ | isMultiVar r = mapM (identForIdN i) [1..varSize r]+ | otherwise = (:[]) <$> identForId i+ where+ r = uTypeVt . idType $ i++-- | Generate IDs for stack arguments. See 'StgToJS.Expr.loadRetArgs' for use case+genIdStackArgI :: HasDebugCallStack => Id -> G [(Ident,StackSlot)]+genIdStackArgI i = zipWith f [1..] <$> genIdArgI i+ where+ f :: Int -> Ident -> (Ident,StackSlot)+ f n ident = (ident, SlotId i n)++-- | Allocate Static Constructors+allocConStatic :: HasDebugCallStack => Ident -> CostCentreStack -> DataCon -> [StgArg] -> G ()+allocConStatic (TxtI to) cc con args = do+ as <- mapM genStaticArg args+ cc' <- costCentreStackLbl cc+ allocConStatic' cc' (concat as)+ where+ -- see Note [ Unboxable Literals Optimization ] for the purpose of these+ -- checks+ allocConStatic' :: HasDebugCallStack => Maybe Ident -> [StaticArg] -> G ()+ allocConStatic' cc' []+ | isBoolDataCon con && dataConTag con == 1 =+ emitStatic to (StaticUnboxed $ StaticUnboxedBool False) cc'+ | isBoolDataCon con && dataConTag con == 2 =+ emitStatic to (StaticUnboxed $ StaticUnboxedBool True) cc'+ | otherwise = do+ (TxtI e) <- identForDataConWorker con+ emitStatic to (StaticData e []) cc'+ allocConStatic' cc' [x]+ | isUnboxableCon con =+ case x of+ StaticLitArg (IntLit i) ->+ emitStatic to (StaticUnboxed $ StaticUnboxedInt i) cc'+ StaticLitArg (BoolLit b) ->+ emitStatic to (StaticUnboxed $ StaticUnboxedBool b) cc'+ StaticLitArg (DoubleLit d) ->+ emitStatic to (StaticUnboxed $ StaticUnboxedDouble d) cc'+ _ ->+ pprPanic "allocConStatic: invalid unboxed literal" (ppr x)+ allocConStatic' cc' xs =+ if con == consDataCon+ then case args of+ (a0:a1:_) -> flip (emitStatic to) cc' =<< allocateStaticList [a0] a1+ _ -> panic "allocConStatic: invalid args for consDataCon"+ else do+ (TxtI e) <- identForDataConWorker con+ emitStatic to (StaticData e xs) cc'++-- | Allocate unboxed constructors+allocUnboxedConStatic :: DataCon -> [StaticArg] -> StaticArg+allocUnboxedConStatic con = \case+ []+ | isBoolDataCon con && dataConTag con == 1+ -> StaticLitArg (BoolLit False)+ | isBoolDataCon con && dataConTag con == 2+ -> StaticLitArg (BoolLit True)+ [a@(StaticLitArg (IntLit _i))] -> a+ [a@(StaticLitArg (DoubleLit _d))] -> a+ _ -> pprPanic "allocUnboxedConStatic: not an unboxed constructor" (ppr con)+++-- | Allocate Static list+allocateStaticList :: [StgArg] -> StgArg -> G StaticVal+allocateStaticList xs a@(StgVarArg i)+ | isDataConId_maybe i == Just nilDataCon = listAlloc xs Nothing+ | otherwise = do+ unFloat <- State.gets gsUnfloated+ case lookupUFM unFloat i of+ Just (StgConApp dc _n [h,t] _)+ | dc == consDataCon -> allocateStaticList (h:xs) t+ _ -> listAlloc xs (Just a)+ where+ listAlloc :: [StgArg] -> Maybe StgArg -> G StaticVal+ listAlloc xs Nothing = do+ as <- concat . reverse <$> mapM genStaticArg xs+ return (StaticList as Nothing)+ listAlloc xs (Just r) = do+ as <- concat . reverse <$> mapM genStaticArg xs+ r' <- genStaticArg r+ case r' of+ [StaticObjArg ri] -> return (StaticList as (Just ri))+ _ ->+ pprPanic "allocateStaticList: invalid argument (tail)" (ppr (xs, r))+allocateStaticList _ _ = panic "allocateStaticList: unexpected literal in list"++-- | Generate JS code corresponding to a static arg+jsStaticArg :: StaticArg -> JExpr+jsStaticArg = \case+ StaticLitArg l -> toJExpr l+ StaticObjArg t -> ValExpr (JVar (TxtI t))+ StaticConArg c args ->+ allocDynamicE False (ValExpr . JVar . TxtI $ c) (map jsStaticArg args) Nothing++-- | Generate JS code corresponding to a list of static args+jsStaticArgs :: [StaticArg] -> JExpr+jsStaticArgs = ValExpr . JList . map jsStaticArg+
+ compiler/GHC/StgToJS/Closure.hs view
@@ -0,0 +1,156 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module GHC.StgToJS.Closure+ ( closureInfoStat+ , closure+ , conClosure+ , Closure (..)+ , newClosure+ , assignClosure+ , CopyCC (..)+ , copyClosure+ )+where++import GHC.Prelude+import GHC.Data.FastString++import GHC.StgToJS.Heap+import GHC.StgToJS.Types+import GHC.StgToJS.CoreUtils+import GHC.StgToJS.Regs (stack,sp)++import GHC.JS.Make+import GHC.JS.Syntax++import Data.Monoid+import qualified Data.Bits as Bits++closureInfoStat :: Bool -> ClosureInfo -> JStat+closureInfoStat debug (ClosureInfo obj rs name layout ctype srefs)+ = setObjInfoL debug obj rs layout ty name tag srefs+ where+ !ty = case ctype of+ CIThunk -> Thunk+ CIFun {} -> Fun+ CICon {} -> Con+ CIBlackhole -> Blackhole+ CIPap -> Pap+ CIStackFrame -> StackFrame+ !tag = case ctype of+ CIThunk -> 0+ CIFun arity nregs -> mkArityTag arity nregs+ CICon con -> con+ CIBlackhole -> 0+ CIPap -> 0+ CIStackFrame -> 0+++setObjInfoL :: Bool -- ^ debug: output symbol names+ -> Ident -- ^ the object name+ -> CIRegs -- ^ things in registers+ -> CILayout -- ^ layout of the object+ -> ClosureType -- ^ closure type+ -> FastString -- ^ object name, for printing+ -> Int -- ^ `a' argument, depends on type (arity, conid)+ -> CIStatic -- ^ static refs+ -> JStat+setObjInfoL debug obj rs layout t n a+ = setObjInfo debug obj t n field_types a size rs+ where+ size = case layout of+ CILayoutVariable -> (-1)+ CILayoutUnknown sz -> sz+ CILayoutFixed sz _ -> sz+ field_types = case layout of+ CILayoutVariable -> []+ CILayoutUnknown size -> toTypeList (replicate size ObjV)+ CILayoutFixed _ fs -> toTypeList fs++setObjInfo :: Bool -- ^ debug: output all symbol names+ -> Ident -- ^ the thing to modify+ -> ClosureType -- ^ closure type+ -> FastString -- ^ object name, for printing+ -> [Int] -- ^ list of item types in the object, if known (free variables, datacon fields)+ -> Int -- ^ extra 'a' parameter, for constructor tag or arity+ -> Int -- ^ object size, -1 (number of vars) for unknown+ -> CIRegs -- ^ things in registers+ -> CIStatic -- ^ static refs+ -> JStat+setObjInfo debug obj t name fields a size regs static+ | debug = appS "h$setObjInfo" [ toJExpr obj+ , toJExpr t+ , toJExpr name+ , toJExpr fields+ , toJExpr a+ , toJExpr size+ , toJExpr (regTag regs)+ , toJExpr static+ ]+ | otherwise = appS "h$o" [ toJExpr obj+ , toJExpr t+ , toJExpr a+ , toJExpr size+ , toJExpr (regTag regs)+ , toJExpr static+ ]+ where+ regTag CIRegsUnknown = -1+ regTag (CIRegs skip types) =+ let nregs = sum $ map varSize types+ in skip + (nregs `Bits.shiftL` 8)++closure :: ClosureInfo -- ^ object being info'd see @ciVar@ in @ClosureInfo@+ -> JStat -- ^ rhs+ -> JStat+closure ci body = (ciVar ci ||= jLam body) `mappend` closureInfoStat False ci++conClosure :: Ident -> FastString -> CILayout -> Int -> JStat+conClosure symbol name layout constr =+ closure (ClosureInfo symbol (CIRegs 0 [PtrV]) name layout (CICon constr) mempty)+ (returnS (stack .! sp))++-- | Used to pass arguments to newClosure with some safety+data Closure = Closure+ { clEntry :: JExpr+ , clField1 :: JExpr+ , clField2 :: JExpr+ , clMeta :: JExpr+ , clCC :: Maybe JExpr+ }++newClosure :: Closure -> JExpr+newClosure Closure{..} =+ let xs = [ (closureEntry_ , clEntry)+ , (closureField1_, clField1)+ , (closureField2_, clField2)+ , (closureMeta_ , clMeta)+ ]+ in case clCC of+ -- CC field is optional (probably to minimize code size as we could assign+ -- null_, but we get the same effect implicitly)+ Nothing -> ValExpr (jhFromList xs)+ Just cc -> ValExpr (jhFromList $ (closureCC_,cc) : xs)++assignClosure :: JExpr -> Closure -> JStat+assignClosure t Closure{..} = BlockStat+ [ closureEntry t |= clEntry+ , closureField1 t |= clField1+ , closureField2 t |= clField2+ , closureMeta t |= clMeta+ ] <> case clCC of+ Nothing -> mempty+ Just cc -> closureCC t |= cc++data CopyCC = CopyCC | DontCopyCC++copyClosure :: CopyCC -> JExpr -> JExpr -> JStat+copyClosure copy_cc t s = BlockStat+ [ closureEntry t |= closureEntry s+ , closureField1 t |= closureField1 s+ , closureField2 t |= closureField2 s+ , closureMeta t |= closureMeta s+ ] <> case copy_cc of+ DontCopyCC -> mempty+ CopyCC -> closureCC t |= closureCC s
+ compiler/GHC/StgToJS/CodeGen.hs view
@@ -0,0 +1,367 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE LambdaCase #-}++-- | JavaScript code generator+module GHC.StgToJS.CodeGen+ ( stgToJS+ )+where++import GHC.Prelude++import GHC.Driver.Flags (DumpFlag (Opt_D_dump_js))++import GHC.JS.Ppr+import GHC.JS.Syntax+import GHC.JS.Make+import GHC.JS.Transform++import GHC.StgToJS.Arg+import GHC.StgToJS.Sinker+import GHC.StgToJS.Types+import qualified GHC.StgToJS.Object as Object+import GHC.StgToJS.StgUtils+import GHC.StgToJS.CoreUtils+import GHC.StgToJS.Deps+import GHC.StgToJS.Expr+import GHC.StgToJS.ExprCtx+import GHC.StgToJS.Monad+import GHC.StgToJS.Profiling+import GHC.StgToJS.Regs+import GHC.StgToJS.StaticPtr+import GHC.StgToJS.Symbols+import GHC.StgToJS.Stack+import GHC.StgToJS.Ids++import GHC.Stg.Syntax+import GHC.Core.DataCon+import GHC.Core.TyCo.Rep (scaledThing)++import GHC.Unit.Module+import GHC.Linker.Types (SptEntry (..))++import GHC.Types.CostCentre+import GHC.Types.ForeignStubs (ForeignStubs (..), getCHeader, getCStub)+import GHC.Types.RepType+import GHC.Types.Id+import GHC.Types.Unique++import GHC.Data.FastString+import GHC.Utils.Encoding+import GHC.Utils.Logger+import GHC.Utils.Panic+import GHC.Utils.Misc+import GHC.Utils.Binary+import qualified Control.Monad.Trans.State.Strict as State+import GHC.Utils.Outputable hiding ((<>))++import qualified Data.Set as S+import Data.Monoid+import Control.Monad+import System.Directory+import System.FilePath++-- | Code generator for JavaScript+stgToJS+ :: Logger+ -> StgToJSConfig+ -> [CgStgTopBinding]+ -> Module+ -> [SptEntry]+ -> ForeignStubs+ -> CollectedCCs+ -> FilePath -- ^ Output file name+ -> IO ()+stgToJS logger config stg_binds0 this_mod spt_entries foreign_stubs cccs output_fn = do++ let (unfloated_binds, stg_binds) = sinkPgm this_mod stg_binds0+ -- TODO: avoid top level lifting in core-2-core when the JS backend is+ -- enabled instead of undoing it here++ -- TODO: add dump pass for optimized STG ast for JS++ (deps,lus) <- runG config this_mod unfloated_binds $ do+ ifProfilingM $ initCostCentres cccs+ lus <- genUnits this_mod stg_binds spt_entries foreign_stubs+ deps <- genDependencyData this_mod lus+ pure (deps,lus)++ -- Doc to dump when -ddump-js is enabled+ when (logHasDumpFlag logger Opt_D_dump_js) $ do+ putDumpFileMaybe logger Opt_D_dump_js "JavaScript code" FormatJS+ $ vcat (fmap (docToSDoc . jsToDoc . oiStat . luObjUnit) lus)++ -- Write the object file+ bh <- openBinMem (4 * 1024 * 1000) -- a bit less than 4kB+ Object.putObject bh (moduleName this_mod) deps (map luObjUnit lus)++ createDirectoryIfMissing True (takeDirectory output_fn)+ writeBinMem bh output_fn++++-- | Generate the ingredients for the linkable units for this module+genUnits :: HasDebugCallStack+ => Module+ -> [CgStgTopBinding]+ -> [SptEntry]+ -> ForeignStubs+ -> G [LinkableUnit] -- ^ the linkable units+genUnits m ss spt_entries foreign_stubs = do+ gbl <- generateGlobalBlock+ exports <- generateExportsBlock+ others <- go 2 ss+ pure (gbl:exports:others)+ where+ go :: HasDebugCallStack+ => Int -- the block we're generating (block 0 is the global unit for the module)+ -> [CgStgTopBinding]+ -> G [LinkableUnit]+ go !n = \case+ [] -> pure []+ (x:xs) -> do+ mlu <- generateBlock x n+ lus <- go (n+1) xs+ return (maybe lus (:lus) mlu)++ -- Generate the global unit that all other blocks in the module depend on+ -- used for cost centres and static initializers+ -- the global unit has no dependencies, exports the moduleGlobalSymbol+ generateGlobalBlock :: HasDebugCallStack => G LinkableUnit+ generateGlobalBlock = do+ glbl <- State.gets gsGlobal+ staticInit <-+ initStaticPtrs spt_entries+ let stat = ( -- O.optimize .+ jsSaturate (Just $ modulePrefix m 1)+ $ mconcat (reverse glbl) <> staticInit)+ let syms = [moduleGlobalSymbol m]+ let oi = ObjUnit+ { oiSymbols = syms+ , oiClInfo = []+ , oiStatic = []+ , oiStat = stat+ , oiRaw = mempty+ , oiFExports = []+ , oiFImports = []+ }+ let lu = LinkableUnit+ { luObjUnit = oi+ , luIdExports = []+ , luOtherExports = syms+ , luIdDeps = []+ , luPseudoIdDeps = []+ , luOtherDeps = []+ , luRequired = False+ , luForeignRefs = []+ }+ pure lu++ generateExportsBlock :: HasDebugCallStack => G LinkableUnit+ generateExportsBlock = do+ let (f_hdr, f_c) = case foreign_stubs of+ NoStubs -> (empty, empty)+ ForeignStubs hdr c -> (getCHeader hdr, getCStub c)+ unique_deps = map mkUniqueDep (lines $ renderWithContext defaultSDocContext f_hdr)+ mkUniqueDep (tag:xs) = mkUnique tag (read xs)+ mkUniqueDep [] = panic "mkUniqueDep"++ let syms = [moduleExportsSymbol m]+ let raw = utf8EncodeByteString $ renderWithContext defaultSDocContext f_c+ let oi = ObjUnit+ { oiSymbols = syms+ , oiClInfo = []+ , oiStatic = []+ , oiStat = mempty+ , oiRaw = raw+ , oiFExports = []+ , oiFImports = []+ }+ let lu = LinkableUnit+ { luObjUnit = oi+ , luIdExports = []+ , luOtherExports = syms+ , luIdDeps = []+ , luPseudoIdDeps = unique_deps+ , luOtherDeps = []+ , luRequired = True+ , luForeignRefs = []+ }+ pure lu++ -- Generate the linkable unit for one binding or group of+ -- mutually recursive bindings+ generateBlock :: HasDebugCallStack+ => CgStgTopBinding+ -> Int+ -> G (Maybe LinkableUnit)+ generateBlock top_bind n = case top_bind of+ StgTopStringLit bnd str -> do+ bids <- identsForId bnd+ case bids of+ [(TxtI b1t),(TxtI b2t)] -> do+ -- [e1,e2] <- genLit (MachStr str)+ emitStatic b1t (StaticUnboxed (StaticUnboxedString str)) Nothing+ emitStatic b2t (StaticUnboxed (StaticUnboxedStringOffset str)) Nothing+ _extraTl <- State.gets (ggsToplevelStats . gsGroup)+ si <- State.gets (ggsStatic . gsGroup)+ let body = mempty -- mconcat (reverse extraTl) <> b1 ||= e1 <> b2 ||= e2+ let stat = jsSaturate (Just $ modulePrefix m n) body+ let ids = [bnd]+ syms <- (\(TxtI i) -> [i]) <$> identForId bnd+ let oi = ObjUnit+ { oiSymbols = syms+ , oiClInfo = []+ , oiStatic = si+ , oiStat = stat+ , oiRaw = ""+ , oiFExports = []+ , oiFImports = []+ }+ let lu = LinkableUnit+ { luObjUnit = oi+ , luIdExports = ids+ , luOtherExports = []+ , luIdDeps = []+ , luPseudoIdDeps = []+ , luOtherDeps = []+ , luRequired = False+ , luForeignRefs = []+ }+ pure (Just lu)+ _ -> panic "generateBlock: invalid size"++ StgTopLifted decl -> do+ tl <- genToplevel decl+ extraTl <- State.gets (ggsToplevelStats . gsGroup)+ ci <- State.gets (ggsClosureInfo . gsGroup)+ si <- State.gets (ggsStatic . gsGroup)+ unf <- State.gets gsUnfloated+ extraDeps <- State.gets (ggsExtraDeps . gsGroup)+ fRefs <- State.gets (ggsForeignRefs . gsGroup)+ resetGroup+ let allDeps = collectIds unf decl+ topDeps = collectTopIds decl+ required = hasExport decl+ stat = -- Opt.optimize .+ jsSaturate (Just $ modulePrefix m n)+ $ mconcat (reverse extraTl) <> tl+ syms <- mapM (fmap (\(TxtI i) -> i) . identForId) topDeps+ let oi = ObjUnit+ { oiSymbols = syms+ , oiClInfo = ci+ , oiStatic = si+ , oiStat = stat+ , oiRaw = ""+ , oiFExports = []+ , oiFImports = fRefs+ }+ let lu = LinkableUnit+ { luObjUnit = oi+ , luIdExports = topDeps+ , luOtherExports = []+ , luIdDeps = allDeps+ , luPseudoIdDeps = []+ , luOtherDeps = S.toList extraDeps+ , luRequired = required+ , luForeignRefs = fRefs+ }+ pure $! seqList topDeps `seq` seqList allDeps `seq` Just lu++-- | variable prefix for the nth block in module+modulePrefix :: Module -> Int -> FastString+modulePrefix m n =+ let encMod = zEncodeString . moduleNameString . moduleName $ m+ in mkFastString $ "h$" ++ encMod ++ "_id_" ++ show n++genToplevel :: CgStgBinding -> G JStat+genToplevel (StgNonRec bndr rhs) = genToplevelDecl bndr rhs+genToplevel (StgRec bs) =+ mconcat <$> mapM (\(bndr, rhs) -> genToplevelDecl bndr rhs) bs++genToplevelDecl :: Id -> CgStgRhs -> G JStat+genToplevelDecl i rhs = do+ s1 <- resetSlots (genToplevelConEntry i rhs)+ s2 <- resetSlots (genToplevelRhs i rhs)+ return (s1 <> s2)++genToplevelConEntry :: Id -> CgStgRhs -> G JStat+genToplevelConEntry i rhs = case rhs of+ StgRhsCon _cc con _mu _ts _args+ | isDataConWorkId i+ -> genSetConInfo i con (stgRhsLive rhs) -- NoSRT+ StgRhsClosure _ _cc _upd_flag _args _body+ | Just dc <- isDataConWorkId_maybe i+ -> genSetConInfo i dc (stgRhsLive rhs) -- srt+ _ -> pure mempty++genSetConInfo :: HasDebugCallStack => Id -> DataCon -> LiveVars -> G JStat+genSetConInfo i d l {- srt -} = do+ ei <- identForDataConEntryId i+ sr <- genStaticRefs l+ emitClosureInfo $ ClosureInfo ei+ (CIRegs 0 [PtrV])+ (mkFastString $ renderWithContext defaultSDocContext (ppr d))+ (fixedLayout $ map uTypeVt fields)+ (CICon $ dataConTag d)+ sr+ return (ei ||= mkDataEntry)+ where+ -- dataConRepArgTys sometimes returns unboxed tuples. is that a bug?+ fields = concatMap (map primRepToType . typePrimRep . unwrapType . scaledThing)+ (dataConRepArgTys d)+ -- concatMap (map slotTyToType . repTypeSlots . repType) (dataConRepArgTys d)++mkDataEntry :: JExpr+mkDataEntry = ValExpr $ JFunc [] returnStack++genToplevelRhs :: Id -> CgStgRhs -> G JStat+-- general cases:+genToplevelRhs i rhs = case rhs of+ StgRhsCon cc con _mu _tys args -> do+ ii <- identForId i+ allocConStatic ii cc con args+ return mempty+ StgRhsClosure _ext cc _upd_flag {- srt -} args body -> do+ {-+ algorithm:+ - collect all Id refs that are in the global id cache+ - count usage in body for each ref+ - order by increasing use+ - prepend loading lives var to body: body can stay the same+ -}+ eid@(TxtI eidt) <- identForEntryId i+ (TxtI idt) <- identForId i+ body <- genBody (initExprCtx i) i R2 args body+ global_occs <- globalOccs (jsSaturate (Just "ghcjs_tmp_sat_") body)+ let lidents = map global_ident global_occs+ let lids = map global_id global_occs+ let lidents' = map identFS lidents+ CIStaticRefs sr0 <- genStaticRefsRhs rhs+ let sri = filter (`notElem` lidents') sr0+ sr = CIStaticRefs sri+ et <- genEntryType args+ ll <- loadLiveFun lids+ (static, regs, upd) <-+ if et == CIThunk+ then do+ r <- updateThunk+ pure (StaticThunk (Just (eidt, map StaticObjArg lidents')), CIRegs 0 [PtrV],r)+ else return (StaticFun eidt (map StaticObjArg lidents'),+ (if null lidents then CIRegs 1 (concatMap idVt args)+ else CIRegs 0 (PtrV : concatMap idVt args))+ , mempty)+ setcc <- ifProfiling $+ if et == CIThunk+ then enterCostCentreThunk+ else enterCostCentreFun cc+ emitClosureInfo (ClosureInfo eid+ regs+ idt+ (fixedLayout $ map (uTypeVt . idType) lids)+ et+ sr)+ ccId <- costCentreStackLbl cc+ emitStatic idt static ccId+ return $ (eid ||= toJExpr (JFunc [] (ll <> upd <> setcc <> body)))
+ compiler/GHC/StgToJS/CoreUtils.hs view
@@ -0,0 +1,282 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}++-- | Core utils+module GHC.StgToJS.CoreUtils where++import GHC.Prelude++import GHC.JS.Syntax++import GHC.StgToJS.Types++import GHC.Stg.Syntax++import GHC.Tc.Utils.TcType++import GHC.Builtin.Types+import GHC.Builtin.Types.Prim++import GHC.Core.DataCon+import GHC.Core.TyCo.Rep+import GHC.Core.TyCon+import GHC.Core.Type++import GHC.Types.RepType+import GHC.Types.Var+import GHC.Types.Id++import GHC.Utils.Misc+import GHC.Utils.Outputable+import GHC.Utils.Panic++import qualified Data.Bits as Bits++-- | can we unbox C x to x, only if x is represented as a Number+isUnboxableCon :: DataCon -> Bool+isUnboxableCon dc+ | [t] <- dataConRepArgTys dc+ , [t1] <- typeVt (scaledThing t)+ = isUnboxable t1 &&+ dataConTag dc == 1 &&+ length (tyConDataCons $ dataConTyCon dc) == 1+ | otherwise = False++-- | one-constructor types with one primitive field represented as a JS Number+-- can be unboxed+isUnboxable :: VarType -> Bool+isUnboxable DoubleV = True+isUnboxable IntV = True -- includes Char#+isUnboxable _ = False++-- | Number of slots occupied by a PrimRep+data SlotCount+ = NoSlot+ | OneSlot+ | TwoSlots+ deriving (Show,Eq,Ord)++instance Outputable SlotCount where+ ppr = text . show++-- | Return SlotCount as an Int+slotCount :: SlotCount -> Int+slotCount = \case+ NoSlot -> 0+ OneSlot -> 1+ TwoSlots -> 2+++-- | Number of slots occupied by a value with the given VarType+varSize :: VarType -> Int+varSize = slotCount . varSlotCount++varSlotCount :: VarType -> SlotCount+varSlotCount VoidV = NoSlot+varSlotCount LongV = TwoSlots -- hi, low+varSlotCount AddrV = TwoSlots -- obj/array, offset+varSlotCount _ = OneSlot++typeSize :: Type -> Int+typeSize t = sum . map varSize . typeVt $ t++isVoid :: VarType -> Bool+isVoid VoidV = True+isVoid _ = False++isPtr :: VarType -> Bool+isPtr PtrV = True+isPtr _ = False++isSingleVar :: VarType -> Bool+isSingleVar v = varSlotCount v == OneSlot++isMultiVar :: VarType -> Bool+isMultiVar v = case varSlotCount v of+ NoSlot -> False+ OneSlot -> False+ TwoSlots -> True++-- | can we pattern match on these values in a case?+isMatchable :: [VarType] -> Bool+isMatchable [DoubleV] = True+isMatchable [IntV] = True+isMatchable _ = False++tyConVt :: HasDebugCallStack => TyCon -> [VarType]+tyConVt = typeVt . mkTyConTy++idVt :: HasDebugCallStack => Id -> [VarType]+idVt = typeVt . idType++typeVt :: HasDebugCallStack => Type -> [VarType]+typeVt t | isRuntimeRepKindedTy t = []+typeVt t = map primRepVt (typePrimRep t)-- map uTypeVt (repTypeArgs t)++-- only use if you know it's not an unboxed tuple+uTypeVt :: HasDebugCallStack => UnaryType -> VarType+uTypeVt ut+ | isRuntimeRepKindedTy ut = VoidV+-- | isRuntimeRepTy ut = VoidV+ -- GHC panics on this otherwise+ | Just (tc, ty_args) <- splitTyConApp_maybe ut+ , length ty_args /= tyConArity tc = PtrV+ | isPrimitiveType ut = (primTypeVt ut)+ | otherwise =+ case typePrimRep' ut of+ [] -> VoidV+ [pt] -> primRepVt pt+ _ -> pprPanic "uTypeVt: not unary" (ppr ut)++primRepVt :: HasDebugCallStack => PrimRep -> VarType+primRepVt VoidRep = VoidV+primRepVt LiftedRep = PtrV -- fixme does ByteArray# ever map to this?+primRepVt UnliftedRep = RtsObjV+primRepVt IntRep = IntV+primRepVt Int8Rep = IntV+primRepVt Int16Rep = IntV+primRepVt Int32Rep = IntV+primRepVt WordRep = IntV+primRepVt Word8Rep = IntV+primRepVt Word16Rep = IntV+primRepVt Word32Rep = IntV+primRepVt Int64Rep = LongV+primRepVt Word64Rep = LongV+primRepVt AddrRep = AddrV+primRepVt FloatRep = DoubleV+primRepVt DoubleRep = DoubleV+primRepVt (VecRep{}) = error "uTypeVt: vector types are unsupported"++typePrimRep' :: HasDebugCallStack => UnaryType -> [PrimRep]+typePrimRep' ty = kindPrimRep' empty (typeKind ty)++-- | Find the primitive representation of a 'TyCon'. Defined here to+-- avoid module loops. Call this only on unlifted tycons.+tyConPrimRep' :: HasDebugCallStack => TyCon -> [PrimRep]+tyConPrimRep' tc = kindPrimRep' empty res_kind+ where+ res_kind = tyConResKind tc++-- | Take a kind (of shape @TYPE rr@) and produce the 'PrimRep's+-- of values of types of this kind.+kindPrimRep' :: HasDebugCallStack => SDoc -> Kind -> [PrimRep]+kindPrimRep' doc ki+ | Just ki' <- coreView ki+ = kindPrimRep' doc ki'+kindPrimRep' doc (TyConApp _typ [runtime_rep])+ = -- ASSERT( typ `hasKey` tYPETyConKey )+ runtimeRepPrimRep doc runtime_rep+kindPrimRep' doc ki+ = pprPanic "kindPrimRep'" (ppr ki $$ doc)++primTypeVt :: HasDebugCallStack => Type -> VarType+primTypeVt t = case tyConAppTyCon_maybe (unwrapType t) of+ Nothing -> error "primTypeVt: not a TyCon"+ Just tc+ | tc == charPrimTyCon -> IntV+ | tc == intPrimTyCon -> IntV+ | tc == wordPrimTyCon -> IntV+ | tc == floatPrimTyCon -> DoubleV+ | tc == doublePrimTyCon -> DoubleV+ | tc == int8PrimTyCon -> IntV+ | tc == word8PrimTyCon -> IntV+ | tc == int16PrimTyCon -> IntV+ | tc == word16PrimTyCon -> IntV+ | tc == int32PrimTyCon -> IntV+ | tc == word32PrimTyCon -> IntV+ | tc == int64PrimTyCon -> LongV+ | tc == word64PrimTyCon -> LongV+ | tc == addrPrimTyCon -> AddrV+ | tc == stablePtrPrimTyCon -> AddrV+ | tc == stableNamePrimTyCon -> RtsObjV+ | tc == statePrimTyCon -> VoidV+ | tc == proxyPrimTyCon -> VoidV+ | tc == realWorldTyCon -> VoidV+ | tc == threadIdPrimTyCon -> RtsObjV+ | tc == weakPrimTyCon -> RtsObjV+ | tc == arrayPrimTyCon -> ArrV+ | tc == smallArrayPrimTyCon -> ArrV+ | tc == byteArrayPrimTyCon -> ObjV -- can contain any JS reference, used for JSVal+ | tc == mutableArrayPrimTyCon -> ArrV+ | tc == smallMutableArrayPrimTyCon -> ArrV+ | tc == mutableByteArrayPrimTyCon -> ObjV -- can contain any JS reference, used for JSVal+ | tc == mutVarPrimTyCon -> RtsObjV+ | tc == mVarPrimTyCon -> RtsObjV+ | tc == tVarPrimTyCon -> RtsObjV+ | tc == bcoPrimTyCon -> RtsObjV -- unsupported?+ | tc == stackSnapshotPrimTyCon -> RtsObjV+ | tc == ioPortPrimTyCon -> RtsObjV -- unsupported?+ | tc == anyTyCon -> PtrV+ | tc == compactPrimTyCon -> ObjV -- unsupported?+ | tc == eqPrimTyCon -> VoidV -- coercion token?+ | tc == eqReprPrimTyCon -> VoidV -- role+ | tc == unboxedUnitTyCon -> VoidV -- Void#+ | otherwise -> PtrV -- anything else must be some boxed thing++argVt :: StgArg -> VarType+argVt a = uTypeVt . stgArgType $ a++dataConType :: DataCon -> Type+dataConType dc = idType (dataConWrapId dc)++isBoolDataCon :: DataCon -> Bool+isBoolDataCon dc = isBoolTy (dataConType dc)++-- standard fixed layout: payload types+-- payload starts at .d1 for heap objects, entry closest to Sp for stack frames+fixedLayout :: [VarType] -> CILayout+fixedLayout vts = CILayoutFixed (sum (map varSize vts)) vts++-- 2-var values might have been moved around separately, use DoubleV as substitute+-- ObjV is 1 var, so this is no problem for implicit metadata+stackSlotType :: Id -> VarType+stackSlotType i+ | OneSlot <- varSlotCount otype = otype+ | otherwise = DoubleV+ where otype = uTypeVt (idType i)++idPrimReps :: Id -> [PrimRep]+idPrimReps = typePrimReps . idType++typePrimReps :: Type -> [PrimRep]+typePrimReps = typePrimRep . unwrapType++primRepSize :: PrimRep -> SlotCount+primRepSize p = varSlotCount (primRepVt p)++-- | Associate the given values to each RrimRep in the given order, taking into+-- account the number of slots per PrimRep+assocPrimReps :: Outputable a => [PrimRep] -> [a] -> [(PrimRep, [a])]+assocPrimReps [] _ = []+assocPrimReps (r:rs) vs = case (primRepSize r,vs) of+ (NoSlot, xs) -> (r,[]) : assocPrimReps rs xs+ (OneSlot, x:xs) -> (r,[x]) : assocPrimReps rs xs+ (TwoSlots, x:y:xs) -> (r,[x,y]) : assocPrimReps rs xs+ err -> pprPanic "assocPrimReps" (ppr err)++-- | Associate the given values to the Id's PrimReps, taking into account the+-- number of slots per PrimRep+assocIdPrimReps :: Outputable a => Id -> [a] -> [(PrimRep, [a])]+assocIdPrimReps i = assocPrimReps (idPrimReps i)++-- | Associate the given JExpr to the Id's PrimReps, taking into account the+-- number of slots per PrimRep+assocIdExprs :: Id -> [JExpr] -> [TypedExpr]+assocIdExprs i es = fmap (uncurry TypedExpr) (assocIdPrimReps i es)++-- | Return False only if we are *sure* it's a data type+-- Look through newtypes etc as much as possible+might_be_a_function :: HasDebugCallStack => Type -> Bool+might_be_a_function ty+ | [LiftedRep] <- typePrimRep ty+ , Just tc <- tyConAppTyCon_maybe (unwrapType ty)+ , isDataTyCon tc+ = False+ | otherwise+ = True++mkArityTag :: Int -> Int -> Int+mkArityTag arity registers = arity Bits..|. (registers `Bits.shiftL` 8)++toTypeList :: [VarType] -> [Int]+toTypeList = concatMap (\x -> replicate (varSize x) (fromEnum x))
+ compiler/GHC/StgToJS/DataCon.hs view
@@ -0,0 +1,124 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}++-----------------------------------------------------------------------------+-- |+-- Module : GHC.StgToJS.DataCon+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Sylvain Henry <sylvain.henry@iohk.io>+-- Josh Meredith <josh.meredith@iohk.io>+-- Stability : experimental+--+-- Code generation of data constructors+-----------------------------------------------------------------------------++module GHC.StgToJS.DataCon+ ( genCon+ , allocCon+ , allocUnboxedCon+ , allocDynamicE+ , allocDynamic+ )+where++import GHC.Prelude++import GHC.JS.Syntax+import GHC.JS.Make++import GHC.StgToJS.Closure+import GHC.StgToJS.ExprCtx+import GHC.StgToJS.Types+import GHC.StgToJS.Monad+import GHC.StgToJS.CoreUtils+import GHC.StgToJS.Profiling+import GHC.StgToJS.Utils+import GHC.StgToJS.Ids++import GHC.Core.DataCon++import GHC.Types.CostCentre+import GHC.Types.Unique.Map++import GHC.Utils.Outputable+import GHC.Utils.Panic+import GHC.Data.FastString++import Data.Maybe++-- | Generate a data constructor. Special handling for unboxed tuples+genCon :: ExprCtx -> DataCon -> [JExpr] -> G JStat+genCon ctx con args+ | isUnboxedTupleDataCon con+ = return $ assignToExprCtx ctx args++ | [ValExpr (JVar ctxi)] <- concatMap typex_expr (ctxTarget ctx)+ = allocCon ctxi con currentCCS args++ | xs <- concatMap typex_expr (ctxTarget ctx)+ = pprPanic "genCon: unhandled DataCon" (ppr (con, args, xs))++-- | Allocate a data constructor. Allocate in this context means bind the data+-- constructor to 'to'+allocCon :: Ident -> DataCon -> CostCentreStack -> [JExpr] -> G JStat+allocCon to con cc xs+ | isBoolDataCon con || isUnboxableCon con =+ return (toJExpr to |= allocUnboxedCon con xs)+{- | null xs = do+ i <- varForId (dataConWorkId con)+ return (assignj to i) -}+ | otherwise = do+ e <- varForDataConWorker con+ cs <- getSettings+ prof <- profiling+ ccsJ <- if prof then ccsVarJ cc else return Nothing+ return $ allocDynamic cs False to e xs ccsJ++-- | Allocate an unboxed data constructor. If we have a bool we calculate the+-- right value. If not then we expect a singleton list and unbox by converting+-- ''C x' to 'x'. NB. This function may panic.+allocUnboxedCon :: DataCon -> [JExpr] -> JExpr+allocUnboxedCon con = \case+ []+ | isBoolDataCon con && dataConTag con == 1 -> false_+ | isBoolDataCon con && dataConTag con == 2 -> true_+ [x]+ | isUnboxableCon con -> x+ xs -> pprPanic "allocUnboxedCon: not an unboxed constructor" (ppr (con,xs))++-- | Allocate an entry function. See 'GHC.StgToJS.hs' for the object layout.+allocDynamicE :: Bool -- ^ csInlineAlloc from StgToJSConfig+ -> JExpr+ -> [JExpr]+ -> Maybe JExpr+ -> JExpr+allocDynamicE inline_alloc entry free cc+ | inline_alloc || length free > 24 = newClosure $ Closure+ { clEntry = entry+ , clField1 = fillObj1+ , clField2 = fillObj2+ , clMeta = ValExpr (JInt 0)+ , clCC = cc+ }+ | otherwise = ApplExpr allocFun (toJExpr entry : free ++ maybeToList cc)+ where+ allocFun = allocClsA (length free)+ (fillObj1,fillObj2)+ = case free of+ [] -> (null_, null_)+ [x] -> (x,null_)+ [x,y] -> (x,y)+ (x:xs) -> (x,toJExpr (JHash $ listToUniqMap (zip dataFields xs)))+ dataFields = map (mkFastString . ('d':) . show) [(1::Int)..]++-- | Allocate a dynamic object+allocDynamic :: StgToJSConfig -> Bool -> Ident -> JExpr -> [JExpr] -> Maybe JExpr -> JStat+allocDynamic s need_decl to entry free cc+ | need_decl = DeclStat to (Just value)+ | otherwise = toJExpr to |= value+ where+ value = allocDynamicE (csInlineAlloc s) entry free cc
+ compiler/GHC/StgToJS/Deps.hs view
@@ -0,0 +1,191 @@+{-# LANGUAGE TupleSections #-}++-----------------------------------------------------------------------------+-- |+-- Module : GHC.StgToJS.Deps+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Sylvain Henry <sylvain.henry@iohk.io>+-- Josh Meredith <josh.meredith@iohk.io>+-- Stability : experimental+--+-- Module to calculate the transitive dependencies of a module+-----------------------------------------------------------------------------++module GHC.StgToJS.Deps+ ( genDependencyData+ )+where++import GHC.Prelude++import GHC.StgToJS.Object as Object+import GHC.StgToJS.Types+import GHC.StgToJS.Ids++import GHC.JS.Syntax++import GHC.Types.Id+import GHC.Types.Unique+import GHC.Types.Unique.FM+import GHC.Types.Name++import GHC.Unit.Module++import GHC.Utils.Misc+import GHC.Utils.Outputable+import GHC.Utils.Panic++import GHC.Data.FastString++import Data.Map (Map)+import qualified Data.Map as M+import qualified Data.Set as S+import qualified Data.IntSet as IS+import qualified Data.IntMap as IM+import Data.IntMap (IntMap)+import Data.Array+import Data.Either+import Control.Monad++import Control.Monad.Trans.Class+import Control.Monad.Trans.State++data DependencyDataCache = DDC+ { ddcModule :: !(IntMap Unit) -- ^ Unique Module -> Unit+ , ddcId :: !(IntMap Object.ExportedFun) -- ^ Unique Id -> Object.ExportedFun (only to other modules)+ , ddcOther :: !(Map OtherSymb Object.ExportedFun)+ }++-- | Generate module dependency data+--+-- Generate the object's dependency data, taking care that package and module names+-- are only stored once+genDependencyData+ :: HasDebugCallStack+ => Module+ -> [LinkableUnit]+ -> G Object.Deps+genDependencyData mod units = do+ -- [(blockindex, blockdeps, required, exported)]+ ds <- evalStateT (mapM (uncurry oneDep) blocks)+ (DDC IM.empty IM.empty M.empty)+ return $ Object.Deps+ { depsModule = mod+ , depsRequired = IS.fromList [ n | (n, _, True, _) <- ds ]+ , depsHaskellExported = M.fromList $ (\(n,_,_,es) -> map (,n) es) =<< ds+ , depsBlocks = listArray (0, length blocks-1) (map (\(_,deps,_,_) -> deps) ds)+ }+ where+ -- Id -> Block+ unitIdExports :: UniqFM Id Int+ unitIdExports = listToUFM $+ concatMap (\(u,n) -> map (,n) (luIdExports u)) blocks++ -- OtherSymb -> Block+ unitOtherExports :: Map OtherSymb Int+ unitOtherExports = M.fromList $+ concatMap (\(u,n) -> map (,n)+ (map (OtherSymb mod)+ (luOtherExports u)))+ blocks++ blocks :: [(LinkableUnit, Int)]+ blocks = zip units [0..]++ -- generate the list of exports and set of dependencies for one unit+ oneDep :: LinkableUnit+ -> Int+ -> StateT DependencyDataCache G (Int, Object.BlockDeps, Bool, [Object.ExportedFun])+ oneDep (LinkableUnit _ idExports otherExports idDeps pseudoIdDeps otherDeps req _frefs) n = do+ (edi, bdi) <- partitionEithers <$> mapM (lookupIdFun n) idDeps+ (edo, bdo) <- partitionEithers <$> mapM lookupOtherFun otherDeps+ (edp, bdp) <- partitionEithers <$> mapM (lookupPseudoIdFun n) pseudoIdDeps+ expi <- mapM lookupExportedId (filter isExportedId idExports)+ expo <- mapM lookupExportedOther otherExports+ -- fixme thin deps, remove all transitive dependencies!+ let bdeps = Object.BlockDeps+ (IS.toList . IS.fromList . filter (/=n) $ bdi++bdo++bdp)+ (S.toList . S.fromList $ edi++edo++edp)+ return (n, bdeps, req, expi++expo)++ idModule :: Id -> Maybe Module+ idModule i = nameModule_maybe (getName i) >>= \m ->+ guard (m /= mod) >> return m++ lookupPseudoIdFun :: Int -> Unique+ -> StateT DependencyDataCache G (Either Object.ExportedFun Int)+ lookupPseudoIdFun _n u =+ case lookupUFM_Directly unitIdExports u of+ Just k -> return (Right k)+ _ -> panic "lookupPseudoIdFun"++ -- get the function for an Id from the cache, add it if necessary+ -- result: Left Object.ExportedFun if function refers to another module+ -- Right blockNumber if function refers to current module+ --+ -- assumes function is internal to the current block if it's+ -- from teh current module and not in the unitIdExports map.+ lookupIdFun :: Int -> Id+ -> StateT DependencyDataCache G (Either Object.ExportedFun Int)+ lookupIdFun n i = case lookupUFM unitIdExports i of+ Just k -> return (Right k)+ Nothing -> case idModule i of+ Nothing -> return (Right n)+ Just m ->+ let k = getKey . getUnique $ i+ addEntry :: StateT DependencyDataCache G Object.ExportedFun+ addEntry = do+ (TxtI idTxt) <- lift (identForId i)+ lookupExternalFun (Just k) (OtherSymb m idTxt)+ in if m == mod+ then pprPanic "local id not found" (ppr m)+ else Left <$> do+ mr <- gets (IM.lookup k . ddcId)+ maybe addEntry return mr++ -- get the function for an OtherSymb from the cache, add it if necessary+ lookupOtherFun :: OtherSymb+ -> StateT DependencyDataCache G (Either Object.ExportedFun Int)+ lookupOtherFun od@(OtherSymb m idTxt) =+ case M.lookup od unitOtherExports of+ Just n -> return (Right n)+ Nothing | m == mod -> panic ("genDependencyData.lookupOtherFun: unknown local other id: " ++ unpackFS idTxt)+ Nothing -> Left <$> (maybe (lookupExternalFun Nothing od) return =<<+ gets (M.lookup od . ddcOther))++ lookupExportedId :: Id -> StateT DependencyDataCache G Object.ExportedFun+ lookupExportedId i = do+ (TxtI idTxt) <- lift (identForId i)+ lookupExternalFun (Just . getKey . getUnique $ i) (OtherSymb mod idTxt)++ lookupExportedOther :: FastString -> StateT DependencyDataCache G Object.ExportedFun+ lookupExportedOther = lookupExternalFun Nothing . OtherSymb mod++ -- lookup a dependency to another module, add to the id cache if there's+ -- an id key, otherwise add to other cache+ lookupExternalFun :: Maybe Int+ -> OtherSymb -> StateT DependencyDataCache G Object.ExportedFun+ lookupExternalFun mbIdKey od@(OtherSymb m idTxt) = do+ let mk = getKey . getUnique $ m+ mpk = moduleUnit m+ exp_fun = Object.ExportedFun m (LexicalFastString idTxt)+ addCache = do+ ms <- gets ddcModule+ let !cache' = IM.insert mk mpk ms+ modify (\s -> s { ddcModule = cache'})+ pure exp_fun+ f <- do+ mbm <- gets (IM.member mk . ddcModule)+ case mbm of+ False -> addCache+ True -> pure exp_fun++ case mbIdKey of+ Nothing -> modify (\s -> s { ddcOther = M.insert od f (ddcOther s) })+ Just k -> modify (\s -> s { ddcId = IM.insert k f (ddcId s) })++ return f
+ compiler/GHC/StgToJS/Expr.hs view
@@ -0,0 +1,1045 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE DeriveFunctor #-}++-----------------------------------------------------------------------------+-- |+-- Module : GHC.StgToJS.Expr+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Sylvain Henry <sylvain.henry@iohk.io>+-- Josh Meredith <josh.meredith@iohk.io>+-- Stability : experimental+--+-- Code generation of Expressions+-----------------------------------------------------------------------------++module GHC.StgToJS.Expr+ ( genExpr+ , genEntryType+ , loadLiveFun+ , genStaticRefsRhs+ , genStaticRefs+ , genBody+ )+where++import GHC.Prelude++import GHC.JS.Syntax+import GHC.JS.Make++import GHC.StgToJS.Apply+import GHC.StgToJS.Arg+import GHC.StgToJS.ExprCtx+import GHC.StgToJS.FFI+import GHC.StgToJS.Heap+import GHC.StgToJS.Monad+import GHC.StgToJS.DataCon+import GHC.StgToJS.Types+import GHC.StgToJS.Literal+import GHC.StgToJS.Prim+import GHC.StgToJS.Profiling+import GHC.StgToJS.Regs+import GHC.StgToJS.StgUtils+import GHC.StgToJS.CoreUtils+import GHC.StgToJS.Utils+import GHC.StgToJS.Stack+import GHC.StgToJS.Ids++import GHC.Types.Basic+import GHC.Types.CostCentre+import GHC.Types.Tickish+import GHC.Types.Var.Set+import GHC.Types.Id+import GHC.Types.Unique.FM+import GHC.Types.RepType++import GHC.Stg.Syntax+import GHC.Stg.Utils++import GHC.Builtin.PrimOps++import GHC.Core+import GHC.Core.TyCon+import GHC.Core.DataCon+import GHC.Core.Opt.Arity (isOneShotBndr)+import GHC.Core.Type hiding (typeSize)++import GHC.Utils.Misc+import GHC.Utils.Monad+import GHC.Utils.Panic+import GHC.Utils.Panic.Plain+import GHC.Utils.Outputable (ppr, renderWithContext, defaultSDocContext)+import qualified Control.Monad.Trans.State.Strict as State+import GHC.Data.FastString+import qualified GHC.Data.List.SetOps as ListSetOps++import Data.Monoid+import Data.Maybe+import Data.Function+import Data.Either+import qualified Data.List as L+import qualified Data.Set as S+import qualified Data.Map as M+import Control.Monad+import Control.Arrow ((&&&))++-- | Evaluate an expression in the given expression context (continuation)+genExpr :: HasDebugCallStack => ExprCtx -> CgStgExpr -> G (JStat, ExprResult)+genExpr ctx stg = case stg of+ StgApp f args -> genApp ctx f args+ StgLit l -> do+ ls <- genLit l+ let r = assignToExprCtx ctx ls+ pure (r,ExprInline Nothing)+ StgConApp con _n args _ -> do+ as <- concatMapM genArg args+ c <- genCon ctx con as+ return (c, ExprInline (Just as))+ StgOpApp (StgFCallOp f _) args t+ -> genForeignCall ctx f t (concatMap typex_expr $ ctxTarget ctx) args+ StgOpApp (StgPrimOp op) args t+ -> genPrimOp ctx op args t+ StgOpApp (StgPrimCallOp c) args t+ -> genPrimCall ctx c args t+ StgCase e b at alts+ -> genCase ctx b e at alts (liveVars $ stgExprLive False stg)+ StgLet _ b e -> do+ (b',ctx') <- genBind ctx b+ (s,r) <- genExpr ctx' e+ return (b' <> s, r)+ StgLetNoEscape _ b e -> do+ (b', ctx') <- genBindLne ctx b+ (s, r) <- genExpr ctx' e+ return (b' <> s, r)+ StgTick (ProfNote cc count scope) e -> do+ setSCCstats <- ifProfilingM $ setCC cc count scope+ (stats, result) <- genExpr ctx e+ return (setSCCstats <> stats, result)+ StgTick (SourceNote span _sname) e+ -> genExpr (ctxSetSrcSpan span ctx) e+ StgTick _m e+ -> genExpr ctx e++-- | regular let binding: allocate heap object+genBind :: HasDebugCallStack+ => ExprCtx+ -> CgStgBinding+ -> G (JStat, ExprCtx)+genBind ctx bndr =+ case bndr of+ StgNonRec b r -> do+ j <- assign b r >>= \case+ Just ja -> return ja+ Nothing -> allocCls Nothing [(b,r)]+ return (j, addEvalRhs ctx [(b,r)])+ StgRec bs -> do+ jas <- mapM (uncurry assign) bs -- fixme these might depend on parts initialized by allocCls+ let m = if null jas then Nothing else Just (mconcat $ catMaybes jas)+ j <- allocCls m . map snd . filter (isNothing . fst) $ zip jas bs+ return (j, addEvalRhs ctx bs)+ where+ ctx' = ctxClearLneFrame ctx++ assign :: Id -> CgStgRhs -> G (Maybe JStat)+ assign b (StgRhsClosure _ _ccs {-[the_fv]-} _upd [] expr)+ | let strip = snd . stripStgTicksTop (not . tickishIsCode)+ , StgCase (StgApp scrutinee []) _ (AlgAlt _) [GenStgAlt (DataAlt _) params sel_expr] <- strip expr+ , StgApp selectee [] <- strip sel_expr+ , let params_w_offsets = zip params (L.scanl' (+) 1 $ map (typeSize . idType) params)+ , let total_size = sum (map (typeSize . idType) params)+ -- , the_fv == scrutinee -- fixme check+ , Just the_offset <- ListSetOps.assocMaybe params_w_offsets selectee+ , the_offset <= 16 -- fixme make this some configurable constant+ = do+ let the_fv = scrutinee -- error "the_fv" -- fixme+ let sel_tag | the_offset == 2 = if total_size == 2 then "2a"+ else "2b"+ | otherwise = show the_offset+ tgts <- identsForId b+ the_fvjs <- varsForId the_fv+ case (tgts, the_fvjs) of+ ([tgt], [the_fvj]) -> return $ Just+ (tgt ||= ApplExpr (var ("h$c_sel_" <> mkFastString sel_tag)) [the_fvj])+ _ -> panic "genBind.assign: invalid size"+ assign b (StgRhsClosure _ext _ccs _upd [] expr)+ | snd (isInlineExpr (ctxEvaluatedIds ctx) expr) = do+ d <- declVarsForId b+ tgt <- varsForId b+ let ctx' = ctx { ctxTarget = assocIdExprs b tgt }+ (j, _) <- genExpr ctx' expr+ return (Just (d <> j))+ assign _b StgRhsCon{} = return Nothing+ assign b r = genEntry ctx' b r >> return Nothing++ addEvalRhs c [] = c+ addEvalRhs c ((b,r):xs)+ | StgRhsCon{} <- r = addEvalRhs (ctxAssertEvaluated b c) xs+ | (StgRhsClosure _ _ ReEntrant _ _) <- r = addEvalRhs (ctxAssertEvaluated b c) xs+ | otherwise = addEvalRhs c xs++genBindLne :: HasDebugCallStack+ => ExprCtx+ -> CgStgBinding+ -> G (JStat, ExprCtx)+genBindLne ctx bndr = do+ -- compute live variables and the offsets where they will be stored in the+ -- stack+ vis <- map (\(x,y,_) -> (x,y)) <$>+ optimizeFree oldFrameSize (newLvs++map fst updBinds)+ -- initialize updatable bindings to null_+ declUpds <- mconcat <$> mapM (fmap (||= null_) . identForId . fst) updBinds+ -- update expression context to include the updated LNE frame+ let ctx' = ctxUpdateLneFrame vis bound ctx+ mapM_ (uncurry $ genEntryLne ctx') binds+ return (declUpds, ctx')+ where+ oldFrameSize = ctxLneFrameSize ctx+ isOldLv i = ctxIsLneBinding ctx i ||+ ctxIsLneLiveVar ctx i+ live = liveVars $ mkDVarSet $ stgLneLive' bndr+ newLvs = filter (not . isOldLv) (dVarSetElems live)+ binds = case bndr of+ StgNonRec b e -> [(b,e)]+ StgRec bs -> bs+ bound = map fst binds+ (updBinds, _nonUpdBinds) = L.partition (isUpdatableRhs . snd) binds++-- | Generate let-no-escape entry+--+-- Let-no-escape entries live on the stack. There is no heap object associated with them.+--+-- A let-no-escape entry is called like a normal stack frame, although as an optimization,+-- `Stack`[`Sp`] is not set when making the call. This is done later if the+-- thread needs to be suspended.+--+-- Updatable let-no-escape binders have one 'private' slot in the stack frame. This slot+-- is initially set to null, changed to h$blackhole when the thunk is being evaluated.+--+genEntryLne :: HasDebugCallStack => ExprCtx -> Id -> CgStgRhs -> G ()+genEntryLne ctx i rhs@(StgRhsClosure _ext _cc update args body) =+ resetSlots $ do+ let payloadSize = ctxLneFrameSize ctx+ vars = ctxLneFrameVars ctx+ myOffset =+ maybe (panic "genEntryLne: updatable binder not found in let-no-escape frame")+ ((payloadSize-) . fst)+ (L.find ((==i) . fst . snd) (zip [0..] vars))+ bh | isUpdatable update =+ jVar (\x -> mconcat+ [ x |= ApplExpr (var "h$bh_lne") [Sub sp (toJExpr myOffset), toJExpr (payloadSize+1)]+ , IfStat x (ReturnStat x) mempty+ ])+ | otherwise = mempty+ lvs <- popLneFrame True payloadSize ctx+ body <- genBody ctx i R1 args body+ ei@(TxtI eii) <- identForEntryId i+ sr <- genStaticRefsRhs rhs+ let f = JFunc [] (bh <> lvs <> body)+ emitClosureInfo $+ ClosureInfo ei+ (CIRegs 0 $ concatMap idVt args)+ (eii <> ", " <> mkFastString (renderWithContext defaultSDocContext (ppr i)))+ (fixedLayout . reverse $+ map (stackSlotType . fst) (ctxLneFrameVars ctx))+ CIStackFrame+ sr+ emitToplevel (ei ||= toJExpr f)+genEntryLne ctx i (StgRhsCon cc con _mu _ticks args) = resetSlots $ do+ let payloadSize = ctxLneFrameSize ctx+ ei@(TxtI _eii) <- identForEntryId i+ -- di <- varForDataConWorker con+ ii <- freshIdent+ p <- popLneFrame True payloadSize ctx+ args' <- concatMapM genArg args+ ac <- allocCon ii con cc args'+ emitToplevel (ei ||= toJExpr (JFunc []+ (mconcat [decl ii, p, ac, r1 |= toJExpr ii, returnStack])))++-- | Generate the entry function for a local closure+genEntry :: HasDebugCallStack => ExprCtx -> Id -> CgStgRhs -> G ()+genEntry _ _i StgRhsCon {} = return ()+genEntry ctx i rhs@(StgRhsClosure _ext cc {-_bi live-} upd_flag args body) = resetSlots $ do+ let live = stgLneLiveExpr rhs -- error "fixme" -- probably find live vars in body+ ll <- loadLiveFun live+ llv <- verifyRuntimeReps live+ upd <- genUpdFrame upd_flag i+ body <- genBody entryCtx i R2 args body+ ei@(TxtI eii) <- identForEntryId i+ et <- genEntryType args+ setcc <- ifProfiling $+ if et == CIThunk+ then enterCostCentreThunk+ else enterCostCentreFun cc+ sr <- genStaticRefsRhs rhs+ emitClosureInfo $ ClosureInfo ei+ (CIRegs 0 $ PtrV : concatMap idVt args)+ (eii <> ", " <> mkFastString (renderWithContext defaultSDocContext (ppr i)))+ (fixedLayout $ map (uTypeVt . idType) live)+ et+ sr+ emitToplevel (ei ||= toJExpr (JFunc [] (mconcat [ll, llv, upd, setcc, body])))+ where+ entryCtx = ctxSetTarget [] (ctxClearLneFrame ctx)++-- | Generate the entry function types for identifiers. Note that this only+-- returns either 'CIThunk' or 'CIFun'. Everything else (PAP Blackhole etc.) is+-- filtered as not a RuntimeRepKinded type.+genEntryType :: HasDebugCallStack => [Id] -> G CIType+genEntryType [] = return CIThunk+genEntryType args0 = do+ args' <- mapM genIdArg args+ return $ CIFun (length args) (length $ concat args')+ where+ args = filter (not . isRuntimeRepKindedTy . idType) args0++-- | Generate the body of an object+genBody :: HasDebugCallStack+ => ExprCtx+ -> Id+ -> StgReg+ -> [Id]+ -> CgStgExpr+ -> G JStat+genBody ctx i startReg args e = do+ -- load arguments into local variables+ la <- do+ args' <- concatMapM genIdArgI args+ return (declAssignAll args' (fmap toJExpr [startReg..]))++ -- assert that arguments have valid runtime reps+ lav <- verifyRuntimeReps args++ -- compute PrimReps and their number of slots required to return the result of+ -- i applied to args.+ let res_vars = resultSize args i++ -- compute typed expressions for each slot and assign registers+ let go_var regs = \case+ [] -> []+ ((rep,size):rs) ->+ let !(regs0,regs1) = splitAt size regs+ !ts = go_var regs1 rs+ in TypedExpr rep regs0 : ts++ let tgt = go_var jsRegsFromR1 res_vars+ let !ctx' = ctx { ctxTarget = tgt }++ -- generate code for the expression+ (e, _r) <- genExpr ctx' e++ return $ la <> lav <> e <> returnStack++-- | Find the result type after applying the function to the arguments+--+-- It's trickier than it looks because:+--+-- 1. we don't have the Arity of the Id. The following functions return+-- different values in some cases:+-- - idArity+-- - typeArity . idType+-- - idFunRepArity+-- - typeArity . unwrapType . idType+-- Moreover the number of args may be different than all of these arities+--+-- 2. sometimes the type is Any, perhaps after some unwrapping. For example+-- HappyAbsSyn is a newtype around HappyAny which is Any or (forall a. a).+--+-- Se we're left to use the applied arguments to peel the type (unwrapped) one+-- arg at a time. But passed args are args after unarisation so we need to+-- unarise every argument type that we peel (using typePrimRepArgs) to get the+-- number of passed args consumed by each type arg.+--+-- In case of failure to determine the type, we default to LiftedRep as it's+-- probably what it is.+--+resultSize :: HasDebugCallStack => [Id] -> Id -> [(PrimRep, Int)]+resultSize args i = result+ where+ result = result_reps `zip` result_slots+ result_slots = fmap (slotCount . primRepSize) result_reps+ result_reps = trim_args (unwrapType (idType i)) (length args)++ trim_args t 0 = typePrimRep t+ trim_args t n+ | Just (_af, _mult, arg, res) <- splitFunTy_maybe t+ , nargs <- length (typePrimRepArgs arg)+ , assert (n >= nargs) True+ = trim_args (unwrapType res) (n - nargs)+ | otherwise+ = pprTrace "result_type: not a function type, assume LiftedRep" (ppr t)+ [LiftedRep]++-- | Ensure that the set of identifiers has valid 'RuntimeRep's. This function+-- returns a no-op when 'csRuntimeAssert' in 'StgToJSConfig' is False.+verifyRuntimeReps :: HasDebugCallStack => [Id] -> G JStat+verifyRuntimeReps xs = do+ runtime_assert <- csRuntimeAssert <$> getSettings+ if not runtime_assert+ then pure mempty+ else mconcat <$> mapM verifyRuntimeRep xs+ where+ verifyRuntimeRep i = do+ i' <- varsForId i+ pure $ go i' (idVt i)+ go js (VoidV:vs) = go js vs+ go (j1:j2:js) (LongV:vs) = v "h$verify_rep_long" [j1,j2] <> go js vs+ go (j1:j2:js) (AddrV:vs) = v "h$verify_rep_addr" [j1,j2] <> go js vs+ go (j:js) (v:vs) = ver j v <> go js vs+ go [] [] = mempty+ go _ _ = pprPanic "verifyRuntimeReps: inconsistent sizes" (ppr xs)+ ver j PtrV = v "h$verify_rep_heapobj" [j]+ ver j IntV = v "h$verify_rep_int" [j]+ ver j RtsObjV = v "h$verify_rep_rtsobj" [j]+ ver j DoubleV = v "h$verify_rep_double" [j]+ ver j ArrV = v "h$verify_rep_arr" [j]+ ver _ _ = mempty+ v f as = ApplStat (var f) as++-- | Given a set of 'Id's, bind each 'Id' to the appropriate data fields in N+-- registers. This assumes these data fields have already been populated in the+-- registers. For the empty, singleton, and binary case use register 1, for any+-- more use as many registers as necessary.+loadLiveFun :: [Id] -> G JStat+loadLiveFun l = do+ l' <- concat <$> mapM identsForId l+ case l' of+ [] -> return mempty+ -- set the ident to d1 field of register 1+ [v] -> return (v ||= r1 .^ closureField1_)+ -- set the idents to d1 and d2 fields of register 1+ [v1,v2] -> return $ mconcat+ [ v1 ||= r1 .^ closureField1_+ , v2 ||= r1 .^ closureField2_+ ]+ -- and so on+ (v:vs) -> do+ d <- freshIdent+ let l'' = mconcat . zipWith (loadLiveVar $ toJExpr d) [(1::Int)..] $ vs+ return $ mconcat+ [ v ||= r1 .^ closureField1_+ , d ||= r1 .^ closureField2_+ , l''+ ]+ where+ loadLiveVar d n v = let ident = TxtI (dataFieldName n)+ in v ||= SelExpr d ident++-- | Pop a let-no-escape frame off the stack+popLneFrame :: Bool -> Int -> ExprCtx -> G JStat+popLneFrame inEntry size ctx = do+ -- calculate the new stack size+ let ctx' = ctxLneShrinkStack ctx size++ let gen_id_slot (i,n) = do+ ids <- identsForId i+ let !id_n = ids !! (n-1)+ pure (id_n, SlotId i n)++ is <- mapM gen_id_slot (ctxLneFrameVars ctx')++ let skip = if inEntry then 1 else 0 -- pop the frame header+ popSkipI skip is++-- | Generate an updated given an 'Id'+genUpdFrame :: UpdateFlag -> Id -> G JStat+genUpdFrame u i+ | isReEntrant u = pure mempty+ | isOneShotBndr i = maybeBh+ | isUpdatable u = updateThunk+ | otherwise = maybeBh+ where+ isReEntrant ReEntrant = True+ isReEntrant _ = False+ maybeBh = do+ settings <- getSettings+ assertRtsStat (return $ bhSingleEntry settings)++-- | Blackhole single entry+--+-- Overwrite a single entry object with a special thunk that behaves like a+-- black hole (throws a JS exception when entered) but pretends to be a thunk.+-- Useful for making sure that the object is not accidentally entered multiple+-- times+--+bhSingleEntry :: StgToJSConfig -> JStat+bhSingleEntry _settings = mconcat+ [ r1 .^ closureEntry_ |= var "h$blackholeTrap"+ , r1 .^ closureField1_ |= undefined_+ , r1 .^ closureField2_ |= undefined_+ ]++genStaticRefsRhs :: CgStgRhs -> G CIStatic+genStaticRefsRhs lv = genStaticRefs (stgRhsLive lv)++-- fixme, update to new way to compute static refs dynamically+genStaticRefs :: LiveVars -> G CIStatic+genStaticRefs lv+ | isEmptyDVarSet sv = return (CIStaticRefs [])+ | otherwise = do+ unfloated <- State.gets gsUnfloated+ let xs = filter (\x -> not (elemUFM x unfloated ||+ typeLevity_maybe (idType x) == Just Unlifted))+ (dVarSetElems sv)+ CIStaticRefs . catMaybes <$> mapM getStaticRef xs+ where+ sv = liveStatic lv++ getStaticRef :: Id -> G (Maybe FastString)+ getStaticRef = fmap (fmap itxt . listToMaybe) . identsForId++-- | Reorder the things we need to push to reuse existing stack values as much+-- as possible True if already on the stack at that location+optimizeFree+ :: HasDebugCallStack+ => Int+ -> [Id]+ -> G [(Id,Int,Bool)] -- ^ A list of stack slots.+ -- -- Id: stored on the slot+ -- -- Int: the part of the value that is stored+ -- -- Bool: True when the slot already contains a value+optimizeFree offset ids = do+ -- this line goes wrong vvvvvvv+ let -- ids' = concat $ map (\i -> map (i,) [1..varSize . uTypeVt . idType $ i]) ids+ idSize :: Id -> Int+ idSize i = sum $ map varSize (typeVt . idType $ i)+ ids' = concatMap (\i -> map (i,) [1..idSize i]) ids+ -- 1..varSize] . uTypeVt . idType $ i]) (typeVt ids)+ l = length ids'+ slots <- drop offset . take l . (++repeat SlotUnknown) <$> getSlots+ let slm = M.fromList (zip slots [0..])+ (remaining, fixed) = partitionEithers $+ map (\inp@(i,n) -> maybe (Left inp) (\j -> Right (i,n,j,True))+ (M.lookup (SlotId i n) slm)) ids'+ takenSlots = S.fromList (fmap (\(_,_,x,_) -> x) fixed)+ freeSlots = filter (`S.notMember` takenSlots) [0..l-1]+ remaining' = zipWith (\(i,n) j -> (i,n,j,False)) remaining freeSlots+ allSlots = L.sortBy (compare `on` \(_,_,x,_) -> x) (fixed ++ remaining')+ return $ map (\(i,n,_,b) -> (i,n,b)) allSlots++-- | Allocate local closures+allocCls :: Maybe JStat -> [(Id, CgStgRhs)] -> G JStat+allocCls dynMiddle xs = do+ (stat, dyn) <- partitionEithers <$> mapM toCl xs+ ac <- allocDynAll True dynMiddle dyn+ pure (mconcat stat <> ac)+ where+ -- left = static, right = dynamic+ toCl :: (Id, CgStgRhs)+ -> G (Either JStat (Ident,JExpr,[JExpr],CostCentreStack))+ -- statics+ {- making zero-arg constructors static is problematic, see #646+ proper candidates for this optimization should have been floated+ already+ toCl (i, StgRhsCon cc con []) = do+ ii <- identForId i+ Left <$> (return (decl ii) <> allocCon ii con cc []) -}+ toCl (i, StgRhsCon cc con _mui _ticjs [a]) | isUnboxableCon con = do+ ii <- identForId i+ ac <- allocCon ii con cc =<< genArg a+ pure (Left (decl ii <> ac))++ -- dynamics+ toCl (i, StgRhsCon cc con _mu _ticks ar) =+ -- fixme do we need to handle unboxed?+ Right <$> ((,,,) <$> identForId i+ <*> varForDataConWorker con+ <*> concatMapM genArg ar+ <*> pure cc)+ toCl (i, cl@(StgRhsClosure _ext cc _upd_flag _args _body)) =+ let live = stgLneLiveExpr cl+ in Right <$> ((,,,) <$> identForId i+ <*> varForEntryId i+ <*> concatMapM varsForId live+ <*> pure cc)++-- fixme CgCase has a reps_compatible check here+-- | Consume Stg case statement and generate a case statement. See also+-- 'genAlts'+genCase :: HasDebugCallStack+ => ExprCtx+ -> Id+ -> CgStgExpr+ -> AltType+ -> [CgStgAlt]+ -> LiveVars+ -> G (JStat, ExprResult)+genCase ctx bnd e at alts l+ | snd (isInlineExpr (ctxEvaluatedIds ctx) e) = do+ bndi <- identsForId bnd+ let ctx' = ctxSetTop bnd+ $ ctxSetTarget (assocIdExprs bnd (map toJExpr bndi))+ $ ctx+ (ej, r) <- genExpr ctx' e+ let d = case r of+ ExprInline d0 -> d0+ ExprCont -> pprPanic "genCase: expression was not inline"+ (pprStgExpr panicStgPprOpts e)++ (aj, ar) <- genAlts (ctxAssertEvaluated bnd ctx) bnd at d alts+ (saveCCS,restoreCCS) <- ifProfilingM $ do+ ccsVar <- freshIdent+ pure ( ccsVar ||= toJExpr jCurrentCCS+ , toJExpr jCurrentCCS |= toJExpr ccsVar+ )+ return ( mconcat+ [ mconcat (map decl bndi)+ , saveCCS+ , ej+ , restoreCCS+ , aj+ ]+ , ar+ )+ | otherwise = do+ rj <- genRet (ctxAssertEvaluated bnd ctx) bnd at alts l+ let ctx' = ctxSetTop bnd+ $ ctxSetTarget (assocIdExprs bnd (map toJExpr [R1 ..]))+ $ ctx+ (ej, _r) <- genExpr ctx' e+ return (rj <> ej, ExprCont)++genRet :: HasDebugCallStack+ => ExprCtx+ -> Id+ -> AltType+ -> [CgStgAlt]+ -> LiveVars+ -> G JStat+genRet ctx e at as l = freshIdent >>= f+ where+ allRefs :: [Id]+ allRefs = S.toList . S.unions $ fmap (exprRefs emptyUFM . alt_rhs) as+ lneLive :: Int+ lneLive = maximum $ 0 : catMaybes (map (ctxLneBindingStackSize ctx) allRefs)+ ctx' = ctxLneShrinkStack ctx lneLive+ lneVars = map fst $ ctxLneFrameVars ctx'+ isLne i = ctxIsLneBinding ctx i || ctxIsLneLiveVar ctx' i+ nonLne = filter (not . isLne) (dVarSetElems l)++ f :: Ident -> G JStat+ f r@(TxtI ri) = do+ pushLne <- pushLneFrame lneLive ctx+ saveCCS <- ifProfilingM $ push [jCurrentCCS]+ free <- optimizeFree 0 nonLne+ pushRet <- pushRetArgs free (toJExpr r)+ fun' <- fun free+ sr <- genStaticRefs l -- srt+ prof <- profiling+ emitClosureInfo $+ ClosureInfo r+ (CIRegs 0 altRegs)+ ri+ (fixedLayout . reverse $+ map (stackSlotType . fst3) free+ ++ if prof then [ObjV] else map stackSlotType lneVars)+ CIStackFrame+ sr+ emitToplevel $ r ||= toJExpr (JFunc [] fun')+ return (pushLne <> saveCCS <> pushRet)+ fst3 ~(x,_,_) = x++ altRegs :: HasDebugCallStack => [VarType]+ altRegs = case at of+ PrimAlt ptc -> [primRepVt ptc]+ MultiValAlt _n -> idVt e+ _ -> [PtrV]++ -- special case for popping CCS but preserving stack size+ pop_handle_CCS :: [(JExpr, StackSlot)] -> G JStat+ pop_handle_CCS [] = return mempty+ pop_handle_CCS xs = do+ -- grab the slots from 'xs' and push+ addSlots (map snd xs)+ -- move the stack pointer into the stack by ''length xs + n'+ a <- adjSpN (length xs)+ -- now load from the top of the stack+ return (loadSkip 0 (map fst xs) <> a)++ fun free = resetSlots $ do+ decs <- declVarsForId e+ load <- flip assignAll (map toJExpr [R1 ..]) . map toJExpr <$> identsForId e+ loadv <- verifyRuntimeReps [e]+ ras <- loadRetArgs free+ rasv <- verifyRuntimeReps (map (\(x,_,_)->x) free)+ restoreCCS <- ifProfilingM . pop_handle_CCS $ pure (jCurrentCCS, SlotUnknown)+ rlne <- popLneFrame False lneLive ctx'+ rlnev <- verifyRuntimeReps lneVars+ (alts, _altr) <- genAlts ctx' e at Nothing as+ return $ decs <> load <> loadv <> ras <> rasv <> restoreCCS <> rlne <> rlnev <> alts <>+ returnStack++-- | Consume an Stg case alternative and generate the corresponding alternative+-- in JS land. If one alternative is a continuation then we must normalize the+-- other alternatives. See 'Branch' and 'normalizeBranches'.+genAlts :: HasDebugCallStack+ => ExprCtx -- ^ lhs to assign expression result to+ -> Id -- ^ id being matched+ -> AltType -- ^ type+ -> Maybe [JExpr] -- ^ if known, fields in datacon from earlier expression+ -> [CgStgAlt] -- ^ the alternatives+ -> G (JStat, ExprResult)+genAlts ctx e at me alts = do+ (st, er) <- case at of++ PolyAlt -> case alts of+ [alt] -> (branch_stat &&& branch_result) <$> mkAlgBranch ctx e alt+ _ -> panic "genAlts: multiple polyalt"++ PrimAlt _tc+ | [GenStgAlt _ bs expr] <- alts+ -> do+ ie <- varsForId e+ dids <- mconcat <$> mapM declVarsForId bs+ bss <- concatMapM varsForId bs+ (ej, er) <- genExpr ctx expr+ return (dids <> assignAll bss ie <> ej, er)++ PrimAlt tc+ -> do+ ie <- varsForId e+ (r, bss) <- normalizeBranches ctx <$>+ mapM (isolateSlots . mkPrimIfBranch ctx [primRepVt tc]) alts+ setSlots []+ return (mkSw ie bss, r)++ MultiValAlt n+ | [GenStgAlt _ bs expr] <- alts+ -> do+ eids <- varsForId e+ l <- loadUbxTup eids bs n+ (ej, er) <- genExpr ctx expr+ return (l <> ej, er)++ AlgAlt tc+ | [_alt] <- alts+ , isUnboxedTupleTyCon tc+ -> panic "genAlts: unexpected unboxed tuple"++ AlgAlt _tc+ | Just es <- me+ , [GenStgAlt (DataAlt dc) bs expr] <- alts+ , not (isUnboxableCon dc)+ -> do+ bsi <- mapM identsForId bs+ (ej, er) <- genExpr ctx expr+ return (declAssignAll (concat bsi) es <> ej, er)++ AlgAlt _tc+ | [alt] <- alts+ -> do+ Branch _ s r <- mkAlgBranch ctx e alt+ return (s, r)++ AlgAlt _tc+ | [alt,_] <- alts+ , DataAlt dc <- alt_con alt+ , isBoolDataCon dc+ -> do+ i <- varForId e+ nbs <- normalizeBranches ctx <$>+ mapM (isolateSlots . mkAlgBranch ctx e) alts+ case nbs of+ (r, [Branch _ s1 _, Branch _ s2 _]) -> do+ let s = if dataConTag dc == 2+ then IfStat i s1 s2+ else IfStat i s2 s1+ setSlots []+ return (s, r)+ _ -> error "genAlts: invalid branches for Bool"++ AlgAlt _tc -> do+ ei <- varForId e+ (r, brs) <- normalizeBranches ctx <$>+ mapM (isolateSlots . mkAlgBranch ctx e) alts+ setSlots []+ return (mkSwitch (ei .^ "f" .^ "a") brs, r)++ _ -> pprPanic "genAlts: unhandled case variant" (ppr (at, length alts))++ ver <- verifyMatchRep e at+ pure (ver <> st, er)++-- | If 'StgToJSConfig.csRuntimeAssert' is set, then generate an assertion that+-- asserts the pattern match is valid, e.g., the match is attempted on a+-- Boolean, a Data Constructor, or some number.+verifyMatchRep :: HasDebugCallStack => Id -> AltType -> G JStat+verifyMatchRep x alt = do+ runtime_assert <- csRuntimeAssert <$> getSettings+ if not runtime_assert+ then pure mempty+ else case alt of+ AlgAlt tc -> do+ ix <- varsForId x+ pure $ ApplStat (var "h$verify_match_alg") (ValExpr(JStr(mkFastString (renderWithContext defaultSDocContext (ppr tc)))):ix)+ _ -> pure mempty++-- | A 'Branch' represents a possible branching path of an Stg case statement,+-- i.e., a possible code path from an 'StgAlt'+data Branch a = Branch+ { branch_expr :: a+ , branch_stat :: JStat+ , branch_result :: ExprResult+ }+ deriving (Eq,Functor)++-- | If one branch ends in a continuation but another is inline, we need to+-- adjust the inline branch to use the continuation convention+normalizeBranches :: ExprCtx+ -> [Branch a]+ -> (ExprResult, [Branch a])+normalizeBranches ctx brs+ | all (==ExprCont) (fmap branch_result brs) =+ (ExprCont, brs)+ | branchResult (fmap branch_result brs) == ExprCont =+ (ExprCont, map mkCont brs)+ | otherwise =+ (ExprInline Nothing, brs)+ where+ mkCont b = case branch_result b of+ ExprInline{} -> b { branch_stat = branch_stat b <> assignAll jsRegsFromR1+ (concatMap typex_expr $ ctxTarget ctx)+ , branch_result = ExprCont+ }+ _ -> b++-- | Load an unboxed tuple. "Loading" means getting all 'Idents' from the input+-- ID's, declaring them as variables in JS land and binding them, in order, to+-- 'es'.+loadUbxTup :: [JExpr] -> [Id] -> Int -> G JStat+loadUbxTup es bs _n = do+ bs' <- concatMapM identsForId bs+ return $ declAssignAll bs' es++mkSw :: [JExpr] -> [Branch (Maybe [JExpr])] -> JStat+mkSw [e] cases = mkSwitch e (fmap (fmap (fmap head)) cases)+mkSw es cases = mkIfElse es cases++-- | Switch for pattern matching on constructors or prims+mkSwitch :: JExpr -> [Branch (Maybe JExpr)] -> JStat+mkSwitch e cases+ | [Branch (Just c1) s1 _] <- n+ , [Branch _ s2 _] <- d+ = IfStat (InfixExpr StrictEqOp e c1) s1 s2++ | [Branch (Just c1) s1 _, Branch _ s2 _] <- n+ , null d+ = IfStat (InfixExpr StrictEqOp e c1) s1 s2++ | null d+ = SwitchStat e (map addBreak (init n)) (branch_stat (last n))++ | [Branch _ d0 _] <- d+ = SwitchStat e (map addBreak n) d0++ | otherwise = panic "mkSwitch: multiple default cases"+ where+ addBreak (Branch (Just c) s _) = (c, mconcat [s, BreakStat Nothing])+ addBreak _ = panic "mkSwitch: addBreak"+ (n,d) = L.partition (isJust . branch_expr) cases++-- | if/else for pattern matching on things that js cannot switch on+-- the list of branches is expected to have the default alternative+-- first, if it exists+mkIfElse :: [JExpr] -> [Branch (Maybe [JExpr])] -> JStat+mkIfElse e s = go (L.reverse s)+ where+ go = \case+ [Branch _ s _] -> s -- only one 'nothing' allowed+ (Branch (Just e0) s _ : xs) -> IfStat (mkEq e e0) s (go xs)+ [] -> panic "mkIfElse: empty expression list"+ _ -> panic "mkIfElse: multiple DEFAULT cases"++-- | Wrapper to contruct sequences of (===), e.g.,+--+-- > mkEq [l0,l1,l2] [r0,r1,r2] = (l0 === r0) && (l1 === r1) && (l2 === r2)+--+mkEq :: [JExpr] -> [JExpr] -> JExpr+mkEq es1 es2+ | length es1 == length es2 = foldl1 (InfixExpr LAndOp) (zipWith (InfixExpr StrictEqOp) es1 es2)+ | otherwise = panic "mkEq: incompatible expressions"++mkAlgBranch :: ExprCtx -- ^ toplevel id for the result+ -> Id -- ^ datacon to match+ -> CgStgAlt -- ^ match alternative with binders+ -> G (Branch (Maybe JExpr))+mkAlgBranch top d alt+ | DataAlt dc <- alt_con alt+ , isUnboxableCon dc+ , [b] <- alt_bndrs alt+ = do+ idd <- varForId d+ fldx <- identsForId b+ case fldx of+ [fld] -> do+ (ej, er) <- genExpr top (alt_rhs alt)+ return (Branch Nothing (mconcat [fld ||= idd, ej]) er)+ _ -> panic "mkAlgBranch: invalid size"++ | otherwise+ = do+ cc <- caseCond (alt_con alt)+ idd <- varForId d+ b <- loadParams idd (alt_bndrs alt)+ (ej, er) <- genExpr top (alt_rhs alt)+ return (Branch cc (b <> ej) er)++-- | Generate a primitive If-expression+mkPrimIfBranch :: ExprCtx+ -> [VarType]+ -> CgStgAlt+ -> G (Branch (Maybe [JExpr]))+mkPrimIfBranch top _vt alt =+ (\ic (ej,er) -> Branch ic ej er) <$> ifCond (alt_con alt) <*> genExpr top (alt_rhs alt)++-- fixme are bool things always checked correctly here?+ifCond :: AltCon -> G (Maybe [JExpr])+ifCond = \case+ DataAlt da -> return $ Just [toJExpr (dataConTag da)]+ LitAlt l -> Just <$> genLit l+ DEFAULT -> return Nothing++caseCond :: AltCon -> G (Maybe JExpr)+caseCond = \case+ DEFAULT -> return Nothing+ DataAlt da -> return $ Just (toJExpr $ dataConTag da)+ LitAlt l -> genLit l >>= \case+ [e] -> pure (Just e)+ es -> pprPanic "caseCond: expected single-variable literal" (ppr es)++-- fixme use single tmp var for all branches+-- | Load parameters from constructor+loadParams :: JExpr -> [Id] -> G JStat+loadParams from args = do+ as <- concat <$> zipWithM (\a u -> map (,u) <$> identsForId a) args use+ return $ case as of+ [] -> mempty+ [(x,u)] -> loadIfUsed (from .^ closureField1_) x u+ [(x1,u1),(x2,u2)] -> mconcat+ [ loadIfUsed (from .^ closureField1_) x1 u1+ , loadIfUsed (from .^ closureField2_) x2 u2+ ]+ ((x,u):xs) -> mconcat+ [ loadIfUsed (from .^ closureField1_) x u+ , jVar (\d -> mconcat [ d |= from .^ closureField2_+ , loadConVarsIfUsed d xs+ ])+ ]+ where+ use = repeat True -- fixme clean up+ loadIfUsed fr tgt True = tgt ||= fr+ loadIfUsed _ _ _ = mempty++ loadConVarsIfUsed fr cs = mconcat $ zipWith f cs [(1::Int)..]+ where f (x,u) n = loadIfUsed (SelExpr fr (TxtI (dataFieldName n))) x u++-- | Determine if a branch will end in a continuation or not. If not the inline+-- branch must be normalized. See 'normalizeBranches'+-- NB. not a Monoid+branchResult :: HasDebugCallStack => [ExprResult] -> ExprResult+branchResult = \case+ [] -> panic "branchResult: empty list"+ [e] -> e+ (ExprCont:_) -> ExprCont+ (_:es)+ | elem ExprCont es -> ExprCont+ | otherwise -> ExprInline Nothing++-- | Push return arguments onto the stack. The 'Bool' tracks whether the value+-- is already on the stack or not, used in 'StgToJS.Stack.pushOptimized'.+pushRetArgs :: HasDebugCallStack => [(Id,Int,Bool)] -> JExpr -> G JStat+pushRetArgs free fun = do+ rs <- mapM (\(i,n,b) -> (\es->(es!!(n-1),b)) <$> genIdArg i) free+ pushOptimized (rs++[(fun,False)])++-- | Load the return arguments then pop the stack frame+loadRetArgs :: HasDebugCallStack => [(Id,Int,Bool)] -> G JStat+loadRetArgs free = do+ ids <- mapM (\(i,n,_b) -> (!! (n-1)) <$> genIdStackArgI i) free+ popSkipI 1 ids++-- | allocate multiple, possibly mutually recursive, closures+allocDynAll :: Bool -> Maybe JStat -> [(Ident,JExpr,[JExpr],CostCentreStack)] -> G JStat+{-+XXX remove use of template and enable in-place init again+allocDynAll haveDecl middle [(to,entry,free,cc)]+ | isNothing middle && to `notElem` (free ^.. template) = do+ ccs <- ccsVarJ cc+ return $ allocDynamic s haveDecl to entry free ccs -}+allocDynAll haveDecl middle cls = do+ settings <- getSettings+ let+ middle' = fromMaybe mempty middle++ decl_maybe i e+ | haveDecl = toJExpr i |= e+ | otherwise = i ||= e++ makeObjs :: G JStat+ makeObjs =+ fmap mconcat $ forM cls $ \(i,f,_,cc) -> do+ ccs <- maybeToList <$> costCentreStackLbl cc+ pure $ mconcat+ [ decl_maybe i $ if csInlineAlloc settings+ then ValExpr (jhFromList $ [ (closureEntry_ , f)+ , (closureField1_, null_)+ , (closureField2_, null_)+ , (closureMeta_ , zero_)+ ]+ ++ fmap (\cid -> ("cc", ValExpr (JVar cid))) ccs)+ else ApplExpr (var "h$c") (f : fmap (ValExpr . JVar) ccs)+ ]++ fillObjs = mconcat $ map fillObj cls+ fillObj (i,_,es,_)+ | csInlineAlloc settings || length es > 24 =+ case es of+ [] -> mempty+ [ex] -> toJExpr i .^ closureField1_ |= toJExpr ex+ [e1,e2] -> mconcat+ [ toJExpr i .^ closureField1_ |= toJExpr e1+ , toJExpr i .^ closureField2_ |= toJExpr e2+ ]+ (ex:es) -> mconcat+ [ toJExpr i .^ closureField1_ |= toJExpr ex+ , toJExpr i .^ closureField2_ |= toJExpr (jhFromList (zip dataFieldNames es))+ ]+ | otherwise = case es of+ [] -> mempty+ [ex] -> toJExpr i .^ closureField1_ |= ex+ [e1,e2] -> mconcat+ [ toJExpr i .^ closureField1_ |= e1+ , toJExpr i .^ closureField2_ |= e2+ ]+ (ex:es) -> mconcat+ [ toJExpr i .^ closureField1_ |= ex+ , toJExpr i .^ closureField2_ |= fillFun es+ ]++ fillFun [] = null_+ fillFun es = ApplExpr (allocData (length es)) es++ checkObjs | csAssertRts settings = mconcat $+ map (\(i,_,_,_) -> ApplStat (ValExpr (JVar (TxtI "h$checkObj"))) [toJExpr i]) cls+ | otherwise = mempty++ objs <- makeObjs+ pure $ mconcat [objs, middle', fillObjs, checkObjs]++-- | Generate a primop. This function wraps around the real generator+-- 'GHC.StgToJS.genPrim', handling the 'ExprCtx' and all arguments before+-- generating the primop.+genPrimOp :: ExprCtx -> PrimOp -> [StgArg] -> Type -> G (JStat, ExprResult)+genPrimOp ctx op args t = do+ as <- concatMapM genArg args+ prof <- csProf <$> getSettings+ bound <- csBoundsCheck <$> getSettings+ -- fixme: should we preserve/check the primreps?+ return $ case genPrim prof bound t op (concatMap typex_expr $ ctxTarget ctx) as of+ PrimInline s -> (s, ExprInline Nothing)+ PRPrimCall s -> (s, ExprCont)
+ compiler/GHC/StgToJS/ExprCtx.hs view
@@ -0,0 +1,172 @@+{-# LANGUAGE TupleSections #-}++-----------------------------------------------------------------------------+-- |+-- Module : GHC.StgToJS.ExprCtx+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Sylvain Henry <sylvain.henry@iohk.io>+-- Josh Meredith <josh.meredith@iohk.io>+-- Stability : experimental+--+-- TODO: Write my description!+-----------------------------------------------------------------------------++module GHC.StgToJS.ExprCtx+ ( ExprCtx+ , initExprCtx+ , ctxAssertEvaluated+ , ctxIsEvaluated+ , ctxSetSrcSpan+ , ctxSrcSpan+ , ctxSetTop+ , ctxTarget+ , ctxSetTarget+ , ctxEvaluatedIds+ -- * Let-no-escape+ , ctxClearLneFrame+ , ctxUpdateLneFrame+ , ctxLneFrameVars+ , ctxLneFrameSize+ , ctxIsLneBinding+ , ctxIsLneLiveVar+ , ctxLneBindingStackSize+ , ctxLneShrinkStack+ )+where++import GHC.Prelude++import GHC.StgToJS.Types++import GHC.Types.Unique.FM+import GHC.Types.Unique.Set+import GHC.Types.Var+import GHC.Types.SrcLoc++import GHC.Utils.Outputable+import GHC.Utils.Panic++import Data.Maybe+++-- | Context into which an expression is evaluated+data ExprCtx = ExprCtx+ { ctxTop :: Id+ -- ^ Top-level binding Id++ , ctxTarget :: [TypedExpr]+ -- ^ Target variables for the evaluated expression++ , ctxEvaluatedIds :: UniqSet Id+ -- ^ Ids that we know to be evaluated (e.g. case binders when the expression+ -- to evaluate is in an alternative)++ , ctxSrcSpan :: Maybe RealSrcSpan+ -- ^ Source location++ ----------------------------+ -- Handling of let-no-escape++ , ctxLneFrameBs :: UniqFM Id Int+ -- ^ LNE bindings with their expected stack size.+ --+ -- The Int is the size of the stack when the LNE binding was defined.+ -- We need to shrink the stack back to this size when we enter one of the+ -- associated binder rhs: it expects its free variables at certain offsets+ -- in the stack.++ , ctxLneFrameVars :: [(Id,Int)]+ -- ^ Contents of current LNE frame+ --+ -- Variables and their index on the stack++ , ctxLneFrameSize :: {-# UNPACK #-} !Int+ -- ^ Cache the length of `ctxLneFrameVars`++ }++-- | Initialize an expression context in the context of the given top-level+-- binding Id+initExprCtx :: Id -> ExprCtx+initExprCtx i = ExprCtx+ { ctxTop = i+ , ctxTarget = []+ , ctxEvaluatedIds = emptyUniqSet+ , ctxLneFrameBs = emptyUFM+ , ctxLneFrameVars = []+ , ctxLneFrameSize = 0+ , ctxSrcSpan = Nothing+ }++-- | Set target+ctxSetTarget :: [TypedExpr] -> ExprCtx -> ExprCtx+ctxSetTarget t ctx = ctx { ctxTarget = t }++-- | Set top-level binding Id+ctxSetTop :: Id -> ExprCtx -> ExprCtx+ctxSetTop i ctx = ctx { ctxTop = i }++-- | Add an Id to the known-evaluated set+ctxAssertEvaluated :: Id -> ExprCtx -> ExprCtx+ctxAssertEvaluated i ctx = ctx { ctxEvaluatedIds = addOneToUniqSet (ctxEvaluatedIds ctx) i }++-- | Set source location+ctxSetSrcSpan :: RealSrcSpan -> ExprCtx -> ExprCtx+ctxSetSrcSpan span ctx = ctx { ctxSrcSpan = Just span }++-- | Update let-no-escape frame+ctxUpdateLneFrame :: [(Id,Int)] -> [Id] -> ExprCtx -> ExprCtx+ctxUpdateLneFrame new_spilled_vars new_lne_ids ctx =+ let old_frame_size = ctxLneFrameSize ctx+ new_frame_size = old_frame_size + length new_spilled_vars+ in ctx+ { ctxLneFrameBs = addListToUFM (ctxLneFrameBs ctx) (map (,new_frame_size) new_lne_ids)+ , ctxLneFrameSize = new_frame_size+ , ctxLneFrameVars = ctxLneFrameVars ctx ++ new_spilled_vars+ }++-- | Remove information about the current LNE frame+ctxClearLneFrame :: ExprCtx -> ExprCtx+ctxClearLneFrame ctx =+ ctx+ { ctxLneFrameBs = emptyUFM+ , ctxLneFrameVars = []+ , ctxLneFrameSize = 0+ }++-- | Predicate: do we know for sure that the given Id is evaluated?+ctxIsEvaluated :: ExprCtx -> Id -> Bool+ctxIsEvaluated ctx i = i `elementOfUniqSet` ctxEvaluatedIds ctx++-- | Does the given Id correspond to a LNE binding+ctxIsLneBinding :: ExprCtx -> Id -> Bool+ctxIsLneBinding ctx i = isJust (ctxLneBindingStackSize ctx i)++-- | Does the given Id correspond to a LNE live var on the stack+ctxIsLneLiveVar :: ExprCtx -> Id -> Bool+ctxIsLneLiveVar ctx i = i `elem` map fst (ctxLneFrameVars ctx)++-- | Return the LNE stack size associated to the given Id.+-- Return Nothing when the Id doesn't correspond to a LNE binding.+ctxLneBindingStackSize :: ExprCtx -> Id -> Maybe Int+ctxLneBindingStackSize ctx i = lookupUFM (ctxLneFrameBs ctx) i++-- | Shrink the LNE stack to the given size+ctxLneShrinkStack :: ExprCtx -> Int -> ExprCtx+ctxLneShrinkStack ctx n =+ let l = ctxLneFrameSize ctx+ in assertPpr+ (l >= n)+ (vcat [ text "ctxLneShrinkStack: let-no-escape stack too short:"+ , ppr l+ , text " < "+ , ppr n+ ])+ (ctx { ctxLneFrameVars = take n (ctxLneFrameVars ctx)+ , ctxLneFrameSize = n+ }+ )
+ compiler/GHC/StgToJS/FFI.hs view
@@ -0,0 +1,352 @@+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE OverloadedStrings #-}++module GHC.StgToJS.FFI+ ( genPrimCall+ , genForeignCall+ , saturateFFI+ )+where++import GHC.Prelude++import GHC.JS.Syntax+import GHC.JS.Make+import GHC.JS.Transform++import GHC.StgToJS.Arg+import GHC.StgToJS.ExprCtx+import GHC.StgToJS.Monad+import GHC.StgToJS.Types+import GHC.StgToJS.Literal+import GHC.StgToJS.Regs+import GHC.StgToJS.CoreUtils+import GHC.StgToJS.Ids++import GHC.Types.RepType+import GHC.Types.ForeignCall+import GHC.Types.Unique.Map+import GHC.Types.Unique.FM++import GHC.Stg.Syntax++import GHC.Builtin.PrimOps+import GHC.Builtin.Types.Prim++import GHC.Core.Type hiding (typeSize)++import GHC.Utils.Misc+import GHC.Utils.Panic+import GHC.Utils.Outputable (renderWithContext, defaultSDocContext, ppr, vcat, text)+import GHC.Data.FastString++import Data.Char+import Data.Monoid+import Data.Maybe+import qualified Data.List as L+import Control.Monad+import Control.Applicative+import qualified Text.ParserCombinators.ReadP as P++genPrimCall :: ExprCtx -> PrimCall -> [StgArg] -> Type -> G (JStat, ExprResult)+genPrimCall ctx (PrimCall lbl _) args t = do+ j <- parseFFIPattern False False False ("h$" ++ unpackFS lbl) t (concatMap typex_expr $ ctxTarget ctx) args+ return (j, ExprInline Nothing)++-- | generate the actual call+{-+ parse FFI patterns:+ "&value -> value+ 1. "function" -> ret = function(...)+ 2. "$r = $1.f($2) -> r1 = a1.f(a2)++ arguments, $1, $2, $3 unary arguments+ $1_1, $1_2, for a binary argument++ return type examples+ 1. $r unary return+ 2. $r1, $r2 binary return+ 3. $r1, $r2, $r3_1, $r3_2 unboxed tuple return+ -}+parseFFIPattern :: Bool -- ^ catch exception and convert them to haskell exceptions+ -> Bool -- ^ async (only valid with javascript calling conv)+ -> Bool -- ^ using javascript calling convention+ -> String+ -> Type+ -> [JExpr]+ -> [StgArg]+ -> G JStat+parseFFIPattern catchExcep async jscc pat t es as+ | catchExcep = do+ c <- parseFFIPatternA async jscc pat t es as+ -- Generate:+ -- try {+ -- `c`;+ -- } catch(except) {+ -- return h$throwJSException(except);+ -- }+ let ex = TxtI "except"+ return (TryStat c ex (ReturnStat (ApplExpr (var "h$throwJSException") [toJExpr ex])) mempty)+ | otherwise = parseFFIPatternA async jscc pat t es as++parseFFIPatternA :: Bool -- ^ async+ -> Bool -- ^ using JavaScript calling conv+ -> String+ -> Type+ -> [JExpr]+ -> [StgArg]+ -> G JStat+-- async calls get an extra callback argument+-- call it with the result+parseFFIPatternA True True pat t es as = do+ cb <- freshIdent+ x <- freshIdent+ d <- freshIdent+ stat <- parseFFIPattern' (Just (toJExpr cb)) True pat t es as+ return $ mconcat+ [ x ||= (toJExpr (jhFromList [("mv", null_)]))+ , cb ||= ApplExpr (var "h$mkForeignCallback") [toJExpr x]+ , stat+ , IfStat (InfixExpr StrictEqOp (toJExpr x .^ "mv") null_)+ (mconcat+ [ toJExpr x .^ "mv" |= UOpExpr NewOp (ApplExpr (var "h$MVar") [])+ , sp |= Add sp one_+ , (IdxExpr stack sp) |= var "h$unboxFFIResult"+ , ReturnStat $ ApplExpr (var "h$takeMVar") [toJExpr x .^ "mv"]+ ])+ (mconcat+ [ d ||= toJExpr x .^ "mv"+ , copyResult (toJExpr d)+ ])+ ]+ where nrst = typeSize t+ copyResult d = assignAllEqual es (map (IdxExpr d . toJExpr) [0..nrst-1])+parseFFIPatternA _async javascriptCc pat t es as =+ parseFFIPattern' Nothing javascriptCc pat t es as++-- parseFFIPatternA _ _ _ _ _ _ = error "parseFFIPattern: non-JavaScript pattern must be synchronous"++parseFFIPattern' :: Maybe JExpr -- ^ Nothing for sync, Just callback for async+ -> Bool -- ^ javascript calling convention used+ -> String -- ^ pattern called+ -> Type -- ^ return type+ -> [JExpr] -- ^ expressions to return in (may be more than necessary)+ -> [StgArg] -- ^ arguments+ -> G JStat+parseFFIPattern' callback javascriptCc pat t ret args+ | not javascriptCc = mkApply pat+ | otherwise =+ if True+ then mkApply pat+ else do+ u <- freshUnique+ case parseFfiJME pat u of+ Right (ValExpr (JVar (TxtI _ident))) -> mkApply pat+ Right expr | not async && length tgt < 2 -> do+ (statPre, ap) <- argPlaceholders javascriptCc args+ let rp = resultPlaceholders async t ret+ env = addListToUFM emptyUFM (rp ++ ap)+ if length tgt == 1+ then return $ statPre <> (mapStatIdent (replaceIdent env) (var "$r" |= expr))+ else return $ statPre <> (mapStatIdent (replaceIdent env) (toStat expr))+ Right _ -> p $ "invalid expression FFI pattern. Expression FFI patterns can only be used for synchronous FFI " +++ " imports with result size 0 or 1.\n" ++ pat+ Left _ -> case parseFfiJM pat u of+ Left err -> p (show err)+ Right stat -> do+ let rp = resultPlaceholders async t ret+ let cp = callbackPlaceholders callback+ (statPre, ap) <- argPlaceholders javascriptCc args+ let env = addListToUFM emptyUFM (rp ++ ap ++ cp)+ return $ statPre <> (mapStatIdent (replaceIdent env) stat) -- fixme trace?+ where+ async = isJust callback+ tgt = take (typeSize t) ret+ -- automatic apply, build call and result copy+ mkApply f+ | Just cb <- callback = do+ (stats, as) <- unzip <$> mapM (genFFIArg javascriptCc) args+ cs <- getSettings+ return $ traceCall cs as <> mconcat stats <> ApplStat f' (concat as++[cb])+ | {-ts@-}+ (t:ts') <- tgt = do+ (stats, as) <- unzip <$> mapM (genFFIArg javascriptCc) args+ cs <- getSettings+ return $ traceCall cs as+ <> mconcat stats+ <> (t |= ApplExpr f' (concat as) )+ <> copyResult ts'+ -- _ -> error "mkApply: empty list"+ | otherwise = do+ (stats, as) <- unzip <$> mapM (genFFIArg javascriptCc) args+ cs <- getSettings+ return $ traceCall cs as <> mconcat stats <> ApplStat f' (concat as)+ where f' = toJExpr (TxtI $ mkFastString f)+ copyResult rs = mconcat $ zipWith (\t r -> toJExpr r |= toJExpr t) (enumFrom Ret1) rs+ p e = error ("Parse error in FFI pattern: " ++ pat ++ "\n" ++ e)++ replaceIdent :: UniqFM Ident JExpr -> Ident -> JExpr+ replaceIdent env i+ | isFFIPlaceholder i = fromMaybe err (lookupUFM env i)+ | otherwise = ValExpr (JVar i)+ where+ (TxtI i') = i+ err = pprPanic "parseFFIPattern': invalid placeholder, check function type"+ (vcat [text pat, ppr i', ppr args, ppr t])+ traceCall cs as+ | csTraceForeign cs = ApplStat (var "h$traceForeign") [toJExpr pat, toJExpr as]+ | otherwise = mempty++-- ident is $N, $N_R, $rN, $rN_R or $r or $c+isFFIPlaceholder :: Ident -> Bool+isFFIPlaceholder (TxtI x) = not (null (P.readP_to_S parser (unpackFS x)))+ where+ digit = P.satisfy (`elem` ("0123456789" :: String))+ parser = void (P.string "$r" >> P.eof) <|>+ void (P.string "$c" >> P.eof) <|> do+ _ <- P.char '$'+ P.optional (P.char 'r')+ _ <- P.many1 digit+ P.optional (P.char '_' >> P.many1 digit)+ P.eof++-- generate arg to be passed to FFI call, with marshalling JStat to be run+-- before the call+genFFIArg :: Bool -> StgArg -> G (JStat, [JExpr])+genFFIArg _isJavaScriptCc (StgLitArg l) = (mempty,) <$> genLit l+genFFIArg isJavaScriptCc a@(StgVarArg i)+ | not isJavaScriptCc &&+ (tycon == byteArrayPrimTyCon || tycon == mutableByteArrayPrimTyCon) =+ (\x -> (mempty,[x, zero_])) <$> varForId i+ | isVoid r = return (mempty, [])+-- | Just x <- marshalFFIArg a = x+ | isMultiVar r = (mempty,) <$> mapM (varForIdN i) [1..varSize r]+ | otherwise = (\x -> (mempty,[x])) <$> varForId i+ where+ tycon = tyConAppTyCon (unwrapType arg_ty)+ arg_ty = stgArgType a+ r = uTypeVt arg_ty++-- $1, $2, $3 for single, $1_1, $1_2 etc for dual+-- void args not counted+argPlaceholders :: Bool -> [StgArg] -> G (JStat, [(Ident,JExpr)])+argPlaceholders isJavaScriptCc args = do+ (stats, idents0) <- unzip <$> mapM (genFFIArg isJavaScriptCc) args+ let idents = filter (not . null) idents0+ return $ (mconcat stats, concat+ (zipWith (\is n -> mkPlaceholder True ("$"++show n) is) idents [(1::Int)..]))++mkPlaceholder :: Bool -> String -> [JExpr] -> [(Ident, JExpr)]+mkPlaceholder undersc prefix aids =+ case aids of+ [] -> []+ [x] -> [(TxtI . mkFastString $ prefix, x)]+ xs@(x:_) -> (TxtI . mkFastString $ prefix, x) :+ zipWith (\x m -> (TxtI . mkFastString $ prefix ++ u ++ show m,x)) xs [(1::Int)..]+ where u = if undersc then "_" else ""++-- $r for single, $r1,$r2 for dual+-- $r1, $r2, etc for ubx tup, void args not counted+resultPlaceholders :: Bool -> Type -> [JExpr] -> [(Ident,JExpr)] -- ident, replacement+resultPlaceholders True _ _ = [] -- async has no direct resuls, use callback+resultPlaceholders False t rs =+ case typeVt (unwrapType t) of+ [t'] -> mkUnary (varSize t')+ uts ->+ let sizes = filter (>0) (map varSize uts)+ f _ 0 = []+ f n 1 = [["$r" ++ show n]]+ f n k = ["$r" ++ sn, "$r" ++ sn ++ "_1"] : map (\x -> ["$r" ++ sn ++ "_" ++ show x]) [2..k]+ where sn = show n+ phs = zipWith (\size n -> f n size) sizes [(1::Int)..]+ in case sizes of+ [n] -> mkUnary n+ _ -> concat $ zipWith (\phs' r -> map (\i -> (TxtI (mkFastString i), r)) phs') (concat phs) rs+ where+ mkUnary 0 = []+ mkUnary 1 = [(TxtI "$r",head rs)] -- single+ mkUnary n = [(TxtI "$r",head rs),(TxtI "$r1", head rs)] +++ zipWith (\n r -> (TxtI . mkFastString $ "$r" ++ show n, toJExpr r)) [2..n] (tail rs)++callbackPlaceholders :: Maybe JExpr -> [(Ident,JExpr)]+callbackPlaceholders Nothing = []+callbackPlaceholders (Just e) = [((TxtI "$c"), e)]++parseFfiJME :: String -> Int -> Either String JExpr+parseFfiJME _xs _u = Left "parseFfiJME not yet implemented"++parseFfiJM :: String -> Int -> Either String JStat+parseFfiJM _xs _u = Left "parseFfiJM not yet implemented"++saturateFFI :: JMacro a => Int -> a -> a+saturateFFI u = jsSaturate (Just . mkFastString $ "ghcjs_ffi_sat_" ++ show u)++genForeignCall :: HasDebugCallStack+ => ExprCtx+ -> ForeignCall+ -> Type+ -> [JExpr]+ -> [StgArg]+ -> G (JStat, ExprResult)+genForeignCall _ctx+ (CCall (CCallSpec (StaticTarget _ tgt Nothing True)+ JavaScriptCallConv+ PlayRisky))+ _t+ [obj]+ args+ | tgt == fsLit "h$buildObject"+ , Just pairs <- getObjectKeyValuePairs args = do+ pairs' <- mapM (\(k,v) -> genArg v >>= \vs -> return (k, head vs)) pairs+ return ( (|=) obj (ValExpr (JHash $ listToUniqMap pairs'))+ , ExprInline Nothing+ )++genForeignCall ctx (CCall (CCallSpec ccTarget cconv safety)) t tgt args = do+ emitForeign (ctxSrcSpan ctx) (mkFastString lbl) safety cconv (map showArgType args) (showType t)+ (,exprResult) <$> parseFFIPattern catchExcep async isJsCc lbl t tgt' args+ where+ isJsCc = cconv == JavaScriptCallConv++ lbl | (StaticTarget _ clbl _mpkg _isFunPtr) <- ccTarget+ = let clbl' = unpackFS clbl+ in if | isJsCc -> clbl'+ | wrapperPrefix `L.isPrefixOf` clbl' ->+ ("h$" ++ (drop 2 $ dropWhile isDigit $ drop (length wrapperPrefix) clbl'))+ | otherwise -> "h$" ++ clbl'+ | otherwise = "h$callDynamic"++ exprResult | async = ExprCont+ | otherwise = ExprInline Nothing++ catchExcep = (cconv == JavaScriptCallConv) &&+ playSafe safety || playInterruptible safety++ async | isJsCc = playInterruptible safety+ | otherwise = playInterruptible safety || playSafe safety++ tgt' | async = take (length tgt) jsRegsFromR1+ | otherwise = tgt++ wrapperPrefix = "ghczuwrapperZC"++getObjectKeyValuePairs :: [StgArg] -> Maybe [(FastString, StgArg)]+getObjectKeyValuePairs [] = Just []+getObjectKeyValuePairs (k:v:xs)+ | Just t <- argJSStringLitUnfolding k =+ fmap ((t,v):) (getObjectKeyValuePairs xs)+getObjectKeyValuePairs _ = Nothing++argJSStringLitUnfolding :: StgArg -> Maybe FastString+argJSStringLitUnfolding (StgVarArg _v) = Nothing -- fixme+argJSStringLitUnfolding _ = Nothing++showArgType :: StgArg -> FastString+showArgType a = showType (stgArgType a)++showType :: Type -> FastString+showType t+ | Just tc <- tyConAppTyCon_maybe (unwrapType t) =+ mkFastString (renderWithContext defaultSDocContext (ppr tc))+ | otherwise = "<unknown>"
+ compiler/GHC/StgToJS/Heap.hs view
@@ -0,0 +1,155 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE LambdaCase #-}++module GHC.StgToJS.Heap+ ( closureType+ , entryClosureType+ , isObject+ , isThunk+ , isThunk'+ , isBlackhole+ , isFun+ , isFun'+ , isPap+ , isPap'+ , isCon+ , isCon'+ , conTag+ , conTag'+ , closureEntry+ , closureMeta+ , closureField1+ , closureField2+ , closureCC+ , funArity+ , funArity'+ , papArity+ , funOrPapArity+ -- * Field names+ , closureEntry_+ , closureMeta_+ , closureCC_+ , closureField1_+ , closureField2_+ -- * Javascript Type literals+ , jTyObject+ )+where++import GHC.Prelude++import GHC.JS.Syntax+import GHC.JS.Make+import GHC.StgToJS.Types+import GHC.Data.FastString++closureEntry_ :: FastString+closureEntry_ = "f"++closureField1_ :: FastString+closureField1_ = "d1"++closureField2_ :: FastString+closureField2_ = "d2"++closureMeta_ :: FastString+closureMeta_ = "m"++closureCC_ :: FastString+closureCC_ = "cc"++entryClosureType_ :: FastString+entryClosureType_ = "t"++entryConTag_ :: FastString+entryConTag_ = "a"++entryFunArity_ :: FastString+entryFunArity_ = "a"++jTyObject :: JExpr+jTyObject = jString "object"++closureType :: JExpr -> JExpr+closureType = entryClosureType . closureEntry++entryClosureType :: JExpr -> JExpr+entryClosureType f = f .^ entryClosureType_++isObject :: JExpr -> JExpr+isObject c = typeof c .===. String "object"++isThunk :: JExpr -> JExpr+isThunk c = closureType c .===. toJExpr Thunk++isThunk' :: JExpr -> JExpr+isThunk' f = entryClosureType f .===. toJExpr Thunk++isBlackhole :: JExpr -> JExpr+isBlackhole c = closureType c .===. toJExpr Blackhole++isFun :: JExpr -> JExpr+isFun c = closureType c .===. toJExpr Fun++isFun' :: JExpr -> JExpr+isFun' f = entryClosureType f .===. toJExpr Fun++isPap :: JExpr -> JExpr+isPap c = closureType c .===. toJExpr Pap++isPap' :: JExpr -> JExpr+isPap' f = entryClosureType f .===. toJExpr Pap++isCon :: JExpr -> JExpr+isCon c = closureType c .===. toJExpr Con++isCon' :: JExpr -> JExpr+isCon' f = entryClosureType f .===. toJExpr Con++conTag :: JExpr -> JExpr+conTag = conTag' . closureEntry++conTag' :: JExpr -> JExpr+conTag' f = f .^ entryConTag_++-- | Get closure entry function+closureEntry :: JExpr -> JExpr+closureEntry p = p .^ closureEntry_++-- | Get closure metadata+closureMeta :: JExpr -> JExpr+closureMeta p = p .^ closureMeta_++-- | Get closure cost-center+closureCC :: JExpr -> JExpr+closureCC p = p .^ closureCC_++-- | Get closure extra field 1+closureField1 :: JExpr -> JExpr+closureField1 p = p .^ closureField1_++-- | Get closure extra field 2+closureField2 :: JExpr -> JExpr+closureField2 p = p .^ closureField2_++-- number of arguments (arity & 0xff = arguments, arity >> 8 = number of registers)+funArity :: JExpr -> JExpr+funArity = funArity' . closureEntry++-- function arity with raw reference to the entry+funArity' :: JExpr -> JExpr+funArity' f = f .^ entryFunArity_++-- arity of a partial application+papArity :: JExpr -> JExpr+papArity cp = closureField1 (closureField2 cp)++funOrPapArity+ :: JExpr -- ^ heap object+ -> Maybe JExpr -- ^ reference to entry, if you have one already (saves a c.f lookup twice)+ -> JExpr -- ^ arity tag (tag >> 8 = registers, tag & 0xff = arguments)+funOrPapArity c = \case+ Nothing -> ((IfExpr (toJExpr (isFun c))) (toJExpr (funArity c)))+ (toJExpr (papArity c))+ Just f -> ((IfExpr (toJExpr (isFun' f))) (toJExpr (funArity' f)))+ (toJExpr (papArity c))
+ compiler/GHC/StgToJS/Ids.hs view
@@ -0,0 +1,238 @@+-----------------------------------------------------------------------------+-- |+-- Module : GHC.StgToJS.Ids+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Sylvain Henry <sylvain.henry@iohk.io>+-- Josh Meredith <josh.meredith@iohk.io>+-- Stability : experimental+--+-- Module to deal with JS identifiers+-----------------------------------------------------------------------------++module GHC.StgToJS.Ids+ ( freshUnique+ , freshIdent+ , makeIdentForId+ , cachedIdentForId+ -- * Helpers for Idents+ , identForId+ , identForIdN+ , identsForId+ , identForEntryId+ , identForDataConEntryId+ , identForDataConWorker+ -- * Helpers for variables+ , varForId+ , varForIdN+ , varsForId+ , varForEntryId+ , varForDataConEntryId+ , varForDataConWorker+ , declVarsForId+ )+where++import GHC.Prelude++import GHC.StgToJS.Types+import GHC.StgToJS.Monad+import GHC.StgToJS.CoreUtils+import GHC.StgToJS.Symbols++import GHC.JS.Syntax+import GHC.JS.Make++import GHC.Core.DataCon+import GHC.Types.Id+import GHC.Types.Unique+import GHC.Types.Unique.FM+import GHC.Types.Name+import GHC.Unit.Module+import GHC.Data.FastString+import GHC.Data.FastMutInt++import Control.Monad+import Control.Monad.IO.Class+import qualified Control.Monad.Trans.State.Strict as State+import qualified Data.Map as M+import Data.Maybe+import qualified Data.ByteString.Char8 as BSC++-- | Get fresh unique number+freshUnique :: G Int+freshUnique = do+ id_gen <- State.gets gsId+ liftIO $ do+ -- no need for atomicFetchAdd as we don't use threads in G+ v <- readFastMutInt id_gen+ writeFastMutInt id_gen (v+1)+ pure v++-- | Get fresh local Ident of the form: h$$unit:module_uniq+freshIdent :: G Ident+freshIdent = do+ i <- freshUnique+ mod <- State.gets gsModule+ let !name = mkFreshJsSymbol mod i+ return (TxtI name)+++-- | Generate unique Ident for the given ID (uncached!)+--+-- The ident has the following forms:+--+-- global Id: h$unit:module.name[_num][_type_suffix]+-- local Id: h$$unit:module.name[_num][_type_suffix]_uniq+--+-- Note that the string is z-encoded except for "_" delimiters.+--+-- Optional "_type_suffix" can be:+-- - "_e" for IdEntry+-- - "_con_e" for IdConEntry+--+-- Optional "_num" is passed as an argument to this function. It is used for+-- Haskell Ids that require several JS variables: e.g. 64-bit numbers (Word64#,+-- Int64#), Addr#, StablePtr#, unboxed tuples, etc.+--+makeIdentForId :: Id -> Maybe Int -> IdType -> Module -> Ident+makeIdentForId i num id_type current_module = TxtI ident+ where+ exported = isExportedId i+ name = getName i+ mod+ | exported+ , Just m <- nameModule_maybe name+ = m+ | otherwise+ = current_module++ !ident = mkFastStringByteString $ mconcat+ [ mkJsSymbolBS exported mod (occNameFS (nameOccName name))++ -------------+ -- suffixes++ -- suffix for Ids represented with more than one JS var ("_0", "_1", etc.)+ , case num of+ Nothing -> mempty+ Just v -> mconcat [BSC.pack "_", intBS v]++ -- suffix for entry and constructor entry+ , case id_type of+ IdPlain -> mempty+ IdEntry -> BSC.pack "_e"+ IdConEntry -> BSC.pack "_con_e"++ -- unique suffix for non-exported Ids+ , if exported+ then mempty+ else let (c,u) = unpkUnique (getUnique i)+ in mconcat [BSC.pack ['_',c,'_'], intBS u]+ ]++-- | Retrieve the cached Ident for the given Id if there is one. Otherwise make+-- a new one with 'makeIdentForId' and cache it.+cachedIdentForId :: Id -> Maybe Int -> IdType -> G Ident+cachedIdentForId i mi id_type = do++ -- compute key+ let !key = IdKey (getKey . getUnique $ i) (fromMaybe 0 mi) id_type++ -- lookup Ident in the Ident cache+ IdCache cache <- State.gets gsIdents+ ident <- case M.lookup key cache of+ Just ident -> pure ident+ Nothing -> do+ mod <- State.gets gsModule+ let !ident = makeIdentForId i mi id_type mod+ let !cache' = IdCache (M.insert key ident cache)+ State.modify (\s -> s { gsIdents = cache' })+ pure ident++ -- Now update the GlobalId cache, if required++ let update_global_cache = isGlobalId i && isNothing mi && id_type == IdPlain+ -- fixme also allow caching entries for lifting?++ when (update_global_cache) $ do+ GlobalIdCache gidc <- getGlobalIdCache+ case elemUFM ident gidc of+ False -> setGlobalIdCache $ GlobalIdCache (addToUFM gidc ident (key, i))+ True -> pure ()++ pure ident++-- | Retrieve default Ident for the given Id+identForId :: Id -> G Ident+identForId i = cachedIdentForId i Nothing IdPlain++-- | Retrieve default Ident for the given Id with sub index+--+-- Some types, Word64, Addr#, unboxed tuple have more than one corresponding JS+-- var, hence we use the sub index to identify each subpart / JS variable.+identForIdN :: Id -> Int -> G Ident+identForIdN i n = cachedIdentForId i (Just n) IdPlain++-- | Retrieve all the idents for the given Id.+identsForId :: Id -> G [Ident]+identsForId i = case typeSize (idType i) of+ 0 -> pure mempty+ 1 -> (:[]) <$> identForId i+ s -> mapM (identForIdN i) [1..s]+++-- | Retrieve entry Ident for the given Id+identForEntryId :: Id -> G Ident+identForEntryId i = cachedIdentForId i Nothing IdEntry++-- | Retrieve datacon entry Ident for the given Id+--+-- Different name than the datacon wrapper.+identForDataConEntryId :: Id -> G Ident+identForDataConEntryId i = cachedIdentForId i Nothing IdConEntry+++-- | Retrieve default variable name for the given Id+varForId :: Id -> G JExpr+varForId i = toJExpr <$> identForId i++-- | Retrieve default variable name for the given Id with sub index+varForIdN :: Id -> Int -> G JExpr+varForIdN i n = toJExpr <$> identForIdN i n++-- | Retrieve all the JS vars for the given Id+varsForId :: Id -> G [JExpr]+varsForId i = case typeSize (idType i) of+ 0 -> pure mempty+ 1 -> (:[]) <$> varForId i+ s -> mapM (varForIdN i) [1..s]+++-- | Retrieve entry variable name for the given Id+varForEntryId :: Id -> G JExpr+varForEntryId i = toJExpr <$> identForEntryId i++-- | Retrieve datacon entry variable name for the given Id+varForDataConEntryId :: Id -> G JExpr+varForDataConEntryId i = ValExpr . JVar <$> identForDataConEntryId i+++-- | Retrieve datacon worker entry variable name for the given datacon+identForDataConWorker :: DataCon -> G Ident+identForDataConWorker d = identForDataConEntryId (dataConWorkId d)++-- | Retrieve datacon worker entry variable name for the given datacon+varForDataConWorker :: DataCon -> G JExpr+varForDataConWorker d = varForDataConEntryId (dataConWorkId d)++-- | Declare all js vars for the id+declVarsForId :: Id -> G JStat+declVarsForId i = case typeSize (idType i) of+ 0 -> return mempty+ 1 -> decl <$> identForId i+ s -> mconcat <$> mapM (\n -> decl <$> identForIdN i n) [1..s]+
+ compiler/GHC/StgToJS/Linker/Linker.hs view
@@ -0,0 +1,945 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE LambdaCase #-}++-----------------------------------------------------------------------------+-- |+-- Module : GHC.StgToJS.Linker.Linker+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Sylvain Henry <sylvain.henry@iohk.io>+-- Josh Meredith <josh.meredith@iohk.io>+-- Stability : experimental+--+-- GHCJS linker, collects dependencies from the object files+-- which contain linkable units with dependency information+--+-----------------------------------------------------------------------------++module GHC.StgToJS.Linker.Linker+ ( jsLinkBinary+ , embedJsFile+ )+where++import Prelude++import GHC.Platform.Host (hostPlatformArchOS)++import GHC.JS.Make+import GHC.JS.Syntax++import GHC.Driver.Session (DynFlags(..))+import Language.Haskell.Syntax.Module.Name+import GHC.SysTools.Cpp+import GHC.SysTools++import GHC.Linker.Static.Utils (exeFileName)++import GHC.StgToJS.Linker.Types+import GHC.StgToJS.Linker.Utils+import GHC.StgToJS.Rts.Rts+import GHC.StgToJS.Object+import GHC.StgToJS.Types hiding (LinkableUnit)+import GHC.StgToJS.Symbols+import GHC.StgToJS.Printer+import GHC.StgToJS.Arg+import GHC.StgToJS.Closure++import GHC.Unit.State+import GHC.Unit.Env+import GHC.Unit.Home+import GHC.Unit.Types+import GHC.Unit.Module (moduleStableString)++import GHC.Utils.Outputable hiding ((<>))+import GHC.Utils.Panic+import GHC.Utils.Error+import GHC.Utils.Logger (Logger, logVerbAtLeast)+import GHC.Utils.Binary+import qualified GHC.Utils.Ppr as Ppr+import GHC.Utils.Monad+import GHC.Utils.TmpFs++import GHC.Types.Unique.Set++import qualified GHC.SysTools.Ar as Ar++import qualified GHC.Data.ShortText as ST+import GHC.Data.FastString++import Control.Concurrent.MVar+import Control.Monad++import Data.Array+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC+import qualified Data.ByteString.Lazy.Char8 as BLC+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString as BS+import Data.Function (on)+import Data.IntSet (IntSet)+import qualified Data.IntSet as IS+import Data.IORef+import Data.List ( partition, nub, intercalate, group, sort+ , groupBy, intersperse,+ )+import Data.Map.Strict (Map)+import qualified Data.Map.Strict as M+import Data.Maybe+import Data.Set (Set)+import qualified Data.Set as S+import Data.Word++import System.IO+import System.FilePath ((<.>), (</>), dropExtension, takeDirectory)+import System.Directory ( createDirectoryIfMissing+ , doesFileExist+ , getCurrentDirectory+ , Permissions(..)+ , setPermissions+ , getPermissions+ )++data LinkerStats = LinkerStats+ { bytesPerModule :: !(Map Module Word64) -- ^ number of bytes linked per module+ , packedMetaDataSize :: !Word64 -- ^ number of bytes for metadata+ }++newtype ArchiveState = ArchiveState { loadedArchives :: IORef (Map FilePath Ar.Archive) }++emptyArchiveState :: IO ArchiveState+emptyArchiveState = ArchiveState <$> newIORef M.empty++jsLinkBinary+ :: JSLinkConfig+ -> StgToJSConfig+ -> [FilePath]+ -> Logger+ -> DynFlags+ -> UnitEnv+ -> [FilePath]+ -> [UnitId]+ -> IO ()+jsLinkBinary lc_cfg cfg js_srcs logger dflags u_env objs dep_pkgs+ | lcNoJSExecutables lc_cfg = return ()+ | otherwise = do+ -- additional objects to link are passed as FileOption ldInputs...+ let cmdline_objs = [ f | FileOption _ f <- ldInputs dflags ]+ -- discriminate JavaScript sources from real object files.+ (cmdline_js_srcs, cmdline_js_objs) <- partitionM isJsFile cmdline_objs+ let+ objs' = map ObjFile (objs ++ cmdline_js_objs)+ js_srcs' = js_srcs ++ cmdline_js_srcs+ isRoot _ = True+ exe = jsExeFileName dflags++ void $ link lc_cfg cfg logger u_env exe mempty dep_pkgs objs' js_srcs' isRoot mempty++-- | link and write result to disk (jsexe directory)+link :: JSLinkConfig+ -> StgToJSConfig+ -> Logger+ -> UnitEnv+ -> FilePath -- ^ output file/directory+ -> [FilePath] -- ^ include path for home package+ -> [UnitId] -- ^ packages to link+ -> [LinkedObj] -- ^ the object files we're linking+ -> [FilePath] -- ^ extra js files to include+ -> (ExportedFun -> Bool) -- ^ functions from the objects to use as roots (include all their deps)+ -> Set ExportedFun -- ^ extra symbols to link in+ -> IO ()+link lc_cfg cfg logger unit_env out _include units objFiles jsFiles isRootFun extraStaticDeps = do++ -- create output directory+ createDirectoryIfMissing False out++ -------------------------------------------------------------+ -- link all Haskell code (program + dependencies) into out.js++ -- compute dependencies+ (dep_map, dep_units, all_deps, _rts_wired_functions, dep_archives)+ <- computeLinkDependencies cfg logger out unit_env units objFiles extraStaticDeps isRootFun++ -- retrieve code for dependencies+ mods <- collectDeps dep_map dep_units all_deps++ -- LTO + rendering of JS code+ link_stats <- withBinaryFile (out </> "out.js") WriteMode $ \h ->+ renderLinker h mods jsFiles++ -------------------------------------------------------------++ -- dump foreign references file (.frefs)+ unless (lcOnlyOut lc_cfg) $ do+ let frefsFile = "out.frefs"+ -- frefs = concatMap mc_frefs mods+ jsonFrefs = mempty -- FIXME: toJson frefs++ BL.writeFile (out </> frefsFile <.> "json") jsonFrefs+ BL.writeFile (out </> frefsFile <.> "js")+ ("h$checkForeignRefs(" <> jsonFrefs <> ");")++ -- dump stats+ unless (lcNoStats lc_cfg) $ do+ let statsFile = "out.stats"+ writeFile (out </> statsFile) (renderLinkerStats link_stats)++ -- link generated RTS parts into rts.js+ unless (lcNoRts lc_cfg) $ do+ BL.writeFile (out </> "rts.js") ( BLC.pack rtsDeclsText+ <> BLC.pack (rtsText cfg))++ -- link dependencies' JS files into lib.js+ withBinaryFile (out </> "lib.js") WriteMode $ \h -> do+ forM_ dep_archives $ \archive_file -> do+ Ar.Archive entries <- Ar.loadAr archive_file+ forM_ entries $ \entry -> do+ case getJsArchiveEntry entry of+ Nothing -> return ()+ Just bs -> do+ B.hPut h bs+ hPutChar h '\n'++ -- link everything together into all.js+ when (generateAllJs lc_cfg) $ do+ _ <- combineFiles lc_cfg out+ writeHtml out+ writeRunMain out+ writeRunner lc_cfg out+ writeExterns out+++computeLinkDependencies+ :: StgToJSConfig+ -> Logger+ -> String+ -> UnitEnv+ -> [UnitId]+ -> [LinkedObj]+ -> Set ExportedFun+ -> (ExportedFun -> Bool)+ -> IO (Map Module (Deps, DepsLocation), [UnitId], Set LinkableUnit, Set ExportedFun, [FilePath])+computeLinkDependencies cfg logger target unit_env units objFiles extraStaticDeps isRootFun = do++ (objDepsMap, objRequiredUnits) <- loadObjDeps objFiles++ let roots = S.fromList . filter isRootFun $ concatMap (M.keys . depsHaskellExported . fst) (M.elems objDepsMap)+ rootMods = map (moduleNameString . moduleName . head) . group . sort . map funModule . S.toList $ roots+ objPkgs = map moduleUnitId $ nub (M.keys objDepsMap)++ when (logVerbAtLeast logger 2) $ void $ do+ compilationProgressMsg logger $ hcat+ [ text "Linking ", text target, text " (", text (intercalate "," rootMods), char ')' ]+ compilationProgressMsg logger $ hcat+ [ text "objDepsMap ", ppr objDepsMap ]+ compilationProgressMsg logger $ hcat+ [ text "objFiles ", ppr objFiles ]++ let (rts_wired_units, rts_wired_functions) = rtsDeps units++ -- all the units we want to link together, without their dependencies+ let root_units = filter (/= mainUnitId)+ $ nub+ $ rts_wired_units ++ reverse objPkgs ++ reverse units++ -- all the units we want to link together, including their dependencies,+ -- preload units, and backpack instantiations+ all_units_infos <- mayThrowUnitErr (preloadUnitsInfo' unit_env root_units)++ let all_units = fmap unitId all_units_infos++ dep_archives <- getPackageArchives cfg unit_env all_units+ env <- newGhcjsEnv+ (archsDepsMap, archsRequiredUnits) <- loadArchiveDeps env dep_archives++ when (logVerbAtLeast logger 2) $+ logInfo logger $ hang (text "Linking with archives:") 2 (vcat (fmap text dep_archives))++ -- compute dependencies+ let dep_units = all_units ++ [homeUnitId (ue_unsafeHomeUnit $ unit_env)]+ dep_map = objDepsMap `M.union` archsDepsMap+ excluded_units = S.empty+ dep_fun_roots = roots `S.union` rts_wired_functions `S.union` extraStaticDeps+ dep_unit_roots = archsRequiredUnits ++ objRequiredUnits++ all_deps <- getDeps (fmap fst dep_map) excluded_units dep_fun_roots dep_unit_roots++ when (logVerbAtLeast logger 2) $+ logInfo logger $ hang (text "Units to link:") 2 (vcat (fmap ppr dep_units))+ -- logInfo logger $ hang (text "All deps:") 2 (vcat (fmap ppr (S.toList all_deps)))++ return (dep_map, dep_units, all_deps, rts_wired_functions, dep_archives)+++-- | Compiled module+data ModuleCode = ModuleCode+ { mc_module :: !Module+ , mc_js_code :: !JStat+ , mc_exports :: !B.ByteString -- ^ rendered exports+ , mc_closures :: ![ClosureInfo]+ , mc_statics :: ![StaticInfo]+ , mc_frefs :: ![ForeignJSRef]+ }++-- | ModuleCode after link with other modules.+--+-- It contains less information than ModuleCode because they have been commoned+-- up into global "metadata" for the whole link.+data CompactedModuleCode = CompactedModuleCode+ { cmc_module :: !Module+ , cmc_js_code :: !JStat+ , cmc_exports :: !B.ByteString -- ^ rendered exports+ }++-- | Link modules and pretty-print them into the given Handle+renderLinker+ :: Handle+ -> [ModuleCode] -- ^ linked code per module+ -> [FilePath] -- ^ additional JS files+ -> IO LinkerStats+renderLinker h mods jsFiles = do++ -- link modules+ let (compacted_mods, meta) = linkModules mods++ let+ putBS = B.hPut h+ putJS x = do+ before <- hTell h+ Ppr.printLeftRender h (pretty x)+ hPutChar h '\n'+ after <- hTell h+ pure $! (after - before)++ ---------------------------------------------------------+ -- Pretty-print JavaScript code for all the dependencies.+ --+ -- We have to pretty-print at link time because we want to be able to perform+ -- global link-time optimisations (e.g. renamings) on the whole generated JS+ -- file.++ -- modules themselves+ mod_sizes <- forM compacted_mods $ \m -> do+ !mod_size <- fromIntegral <$> putJS (cmc_js_code m)+ let !mod_mod = cmc_module m+ pure (mod_mod, mod_size)++ -- commoned up metadata+ !meta_length <- fromIntegral <$> putJS meta++ -- module exports+ mapM_ (putBS . cmc_exports) compacted_mods++ -- explicit additional JS files+ mapM_ (\i -> B.readFile i >>= putBS) jsFiles++ -- stats+ let link_stats = LinkerStats+ { bytesPerModule = M.fromList mod_sizes+ , packedMetaDataSize = meta_length+ }++ pure link_stats++-- | Render linker stats+renderLinkerStats :: LinkerStats -> String+renderLinkerStats s =+ intercalate "\n\n" [meta_stats, package_stats, module_stats] <> "\n\n"+ where+ meta = packedMetaDataSize s+ meta_stats = "number of modules: " <> show (length bytes_per_mod)+ <> "\npacked metadata: " <> show meta++ bytes_per_mod = M.toList $ bytesPerModule s++ show_unit (UnitId fs) = unpackFS fs++ ps :: Map UnitId Word64+ ps = M.fromListWith (+) . map (\(m,s) -> (moduleUnitId m,s)) $ bytes_per_mod++ pad :: Int -> String -> String+ pad n t = let l = length t+ in if l < n then t <> replicate (n-l) ' ' else t++ pkgMods :: [[(Module,Word64)]]+ pkgMods = groupBy ((==) `on` (moduleUnitId . fst)) bytes_per_mod++ showMod :: (Module, Word64) -> String+ showMod (m,s) = pad 40 (" " <> moduleStableString m <> ":") <> show s <> "\n"++ package_stats :: String+ package_stats = "code size summary per package (in bytes):\n\n"+ <> concatMap (\(p,s) -> pad 25 (show_unit p <> ":") <> show s <> "\n") (M.toList ps)++ module_stats :: String+ module_stats = "code size per module (in bytes):\n\n" <> unlines (map (concatMap showMod) pkgMods)+++getPackageArchives :: StgToJSConfig -> UnitEnv -> [UnitId] -> IO [FilePath]+getPackageArchives cfg unit_env units =+ filterM doesFileExist [ ST.unpack p </> "lib" ++ ST.unpack l ++ profSuff <.> "a"+ | u <- units+ , p <- getInstalledPackageLibDirs ue_state u+ , l <- getInstalledPackageHsLibs ue_state u+ ]+ where+ ue_state = ue_units unit_env++ -- XXX the profiling library name is probably wrong now+ profSuff | csProf cfg = "_p"+ | otherwise = ""+++-- | Combine rts.js, lib.js, out.js to all.js that can be run+-- directly with node.js or SpiderMonkey jsshell+combineFiles :: JSLinkConfig+ -> FilePath+ -> IO ()+combineFiles cfg fp = do+ let files = map (fp </>) ["rts.js", "lib.js", "out.js"]+ withBinaryFile (fp </> "all.js") WriteMode $ \h -> do+ let cpy i = B.readFile i >>= B.hPut h+ mapM_ cpy files+ unless (lcNoHsMain cfg) $ do+ B.hPut h runMainJS++-- | write the index.html file that loads the program if it does not exit+writeHtml+ :: FilePath -- ^ output directory+ -> IO ()+writeHtml out = do+ let htmlFile = out </> "index.html"+ e <- doesFileExist htmlFile+ unless e $+ B.writeFile htmlFile templateHtml+++templateHtml :: B.ByteString+templateHtml =+ "<!DOCTYPE html>\n\+ \<html>\n\+ \ <head>\n\+ \ </head>\n\+ \ <body>\n\+ \ </body>\n\+ \ <script language=\"javascript\" src=\"all.js\" defer></script>\n\+ \</html>"++-- | write the runmain.js file that will be run with defer so that it runs after+-- index.html is loaded+writeRunMain+ :: FilePath -- ^ output directory+ -> IO ()+writeRunMain out = do+ let runMainFile = out </> "runmain.js"+ e <- doesFileExist runMainFile+ unless e $+ B.writeFile runMainFile runMainJS++runMainJS :: B.ByteString+runMainJS = "h$main(h$mainZCZCMainzimain);\n"++writeRunner :: JSLinkConfig -- ^ Settings+ -> FilePath -- ^ Output directory+ -> IO ()+writeRunner _settings out = do+ cd <- getCurrentDirectory+ let arch_os = hostPlatformArchOS+ let runner = cd </> exeFileName arch_os False (Just (dropExtension out))+ srcFile = out </> "all" <.> "js"+ nodePgm :: B.ByteString+ nodePgm = "node"+ src <- B.readFile (cd </> srcFile)+ B.writeFile runner ("#!/usr/bin/env " <> nodePgm <> "\n" <> src)+ perms <- getPermissions runner+ setPermissions runner (perms {executable = True})++rtsExterns :: FastString+rtsExterns =+ "// GHCJS RTS externs for closure compiler ADVANCED_OPTIMIZATIONS\n\n" <>+ mconcat (map (\x -> "/** @type {*} */\nObject.d" <> mkFastString (show x) <> ";\n")+ [(7::Int)..16384])++writeExterns :: FilePath -> IO ()+writeExterns out = writeFile (out </> "all.js.externs")+ $ unpackFS rtsExterns++-- | get all dependencies for a given set of roots+getDeps :: Map Module Deps -- ^ loaded deps+ -> Set LinkableUnit -- ^ don't link these blocks+ -> Set ExportedFun -- ^ start here+ -> [LinkableUnit] -- ^ and also link these+ -> IO (Set LinkableUnit)+getDeps loaded_deps base fun startlu = go' S.empty (S.fromList startlu) (S.toList fun)+ where+ go :: Set LinkableUnit+ -> Set LinkableUnit+ -> IO (Set LinkableUnit)+ go result open = case S.minView open of+ Nothing -> return result+ Just (lu@(lmod,n), open') ->+ case M.lookup lmod loaded_deps of+ Nothing -> pprPanic "getDeps.go: object file not loaded for: " (pprModule lmod)+ Just (Deps _ _ _ b) ->+ let block = b!n+ result' = S.insert lu result+ in go' result'+ (addOpen result' open' $+ map (lmod,) (blockBlockDeps block)) (blockFunDeps block)++ go' :: Set LinkableUnit+ -> Set LinkableUnit+ -> [ExportedFun]+ -> IO (Set LinkableUnit)+ go' result open [] = go result open+ go' result open (f:fs) =+ let key = funModule f+ in case M.lookup key loaded_deps of+ Nothing -> pprPanic "getDeps.go': object file not loaded for: " $ pprModule key+ Just (Deps _m _r e _b) ->+ let lun :: Int+ lun = fromMaybe (pprPanic "exported function not found: " $ ppr f)+ (M.lookup f e)+ lu = (key, lun)+ in go' result (addOpen result open [lu]) fs++ addOpen :: Set LinkableUnit -> Set LinkableUnit -> [LinkableUnit]+ -> Set LinkableUnit+ addOpen result open newUnits =+ let alreadyLinked s = S.member s result ||+ S.member s open ||+ S.member s base+ in open `S.union` S.fromList (filter (not . alreadyLinked) newUnits)++-- | collect dependencies for a set of roots+collectDeps :: Map Module (Deps, DepsLocation) -- ^ Dependency map+ -> [UnitId] -- ^ packages, code linked in this order+ -> Set LinkableUnit -- ^ All dependencides+ -> IO [ModuleCode]+collectDeps mod_deps packages all_deps = do++ -- read ghc-prim first, since we depend on that for static initialization+ let packages' = uncurry (++) $ partition (== primUnitId) (nub packages)++ units_by_module :: Map Module IntSet+ units_by_module = M.fromListWith IS.union $+ map (\(m,n) -> (m, IS.singleton n)) (S.toList all_deps)++ mod_deps_bypkg :: Map UnitId [(Deps, DepsLocation)]+ mod_deps_bypkg = M.fromListWith (++)+ (map (\(m,v) -> (moduleUnitId m,[v])) (M.toList mod_deps))++ ar_state <- emptyArchiveState+ fmap (catMaybes . concat) . forM packages' $ \pkg ->+ mapM (uncurry $ extractDeps ar_state units_by_module)+ (fromMaybe [] $ M.lookup pkg mod_deps_bypkg)++extractDeps :: ArchiveState+ -> Map Module IntSet+ -> Deps+ -> DepsLocation+ -> IO (Maybe ModuleCode)+extractDeps ar_state units deps loc =+ case M.lookup mod units of+ Nothing -> return Nothing+ Just mod_units -> Just <$> do+ let selector n _ = fromIntegral n `IS.member` mod_units || isGlobalUnit (fromIntegral n)+ case loc of+ ObjectFile fp -> do+ us <- readObjectUnits fp selector+ pure (collectCode us)+ ArchiveFile a -> do+ obj <- readArObject ar_state mod a+ us <- getObjectUnits obj selector+ pure (collectCode us)+ InMemory _n obj -> do+ us <- getObjectUnits obj selector+ pure (collectCode us)+ where+ mod = depsModule deps+ newline = BC.pack "\n"+ mk_exports = mconcat . intersperse newline . filter (not . BS.null) . map oiRaw+ mk_js_code = mconcat . map oiStat+ collectCode l = ModuleCode+ { mc_module = mod+ , mc_js_code = mk_js_code l+ , mc_exports = mk_exports l+ , mc_closures = concatMap oiClInfo l+ , mc_statics = concatMap oiStatic l+ , mc_frefs = concatMap oiFImports l+ }++readArObject :: ArchiveState -> Module -> FilePath -> IO Object+readArObject ar_state mod ar_file = do+ loaded_ars <- readIORef (loadedArchives ar_state)+ (Ar.Archive entries) <- case M.lookup ar_file loaded_ars of+ Just a -> pure a+ Nothing -> do+ a <- Ar.loadAr ar_file+ modifyIORef (loadedArchives ar_state) (M.insert ar_file a)+ pure a++ -- look for the right object in archive+ let go_entries = \case+ -- XXX this shouldn't be an exception probably+ [] -> panic $ "could not find object for module "+ ++ moduleNameString (moduleName mod)+ ++ " in "+ ++ ar_file++ (e:es) -> do+ let bs = Ar.filedata e+ bh <- unsafeUnpackBinBuffer bs+ getObjectHeader bh >>= \case+ Left _ -> go_entries es -- not a valid object entry+ Right mod_name+ | mod_name /= moduleName mod+ -> go_entries es -- not the module we're looking for+ | otherwise+ -> getObjectBody bh mod_name -- found it++ go_entries entries+++-- | A helper function to read system dependencies that are hardcoded+diffDeps+ :: [UnitId] -- ^ Packages that are already Linked+ -> ([UnitId], Set ExportedFun) -- ^ New units and functions to link+ -> ([UnitId], Set ExportedFun) -- ^ Diff+diffDeps pkgs (deps_pkgs,deps_funs) =+ ( filter linked_pkg deps_pkgs+ , S.filter linked_fun deps_funs+ )+ where+ linked_fun f = moduleUnitId (funModule f) `S.member` linked_pkgs+ linked_pkg p = S.member p linked_pkgs+ linked_pkgs = S.fromList pkgs++-- | dependencies for the RTS, these need to be always linked+rtsDeps :: [UnitId] -> ([UnitId], Set ExportedFun)+rtsDeps pkgs = diffDeps pkgs $+ ( [baseUnitId, primUnitId]+ , S.fromList $ concat+ [ mkBaseFuns "GHC.Conc.Sync"+ ["reportError"]+ , mkBaseFuns "Control.Exception.Base"+ ["nonTermination"]+ , mkBaseFuns "GHC.Exception.Type"+ [ "SomeException"+ , "underflowException"+ , "overflowException"+ , "divZeroException"+ ]+ , mkBaseFuns "GHC.TopHandler"+ [ "runMainIO"+ , "topHandler"+ ]+ , mkBaseFuns "GHC.Base"+ ["$fMonadIO"]+ , mkBaseFuns "GHC.Maybe"+ [ "Nothing"+ , "Just"+ ]+ , mkBaseFuns "GHC.Ptr"+ ["Ptr"]+ , mkBaseFuns "GHC.JS.Prim"+ [ "JSVal"+ , "JSException"+ , "$fShowJSException"+ , "$fExceptionJSException"+ , "resolve"+ , "resolveIO"+ , "toIO"+ ]+ , mkBaseFuns "GHC.JS.Prim.Internal"+ [ "wouldBlock"+ , "blockedIndefinitelyOnMVar"+ , "blockedIndefinitelyOnSTM"+ , "ignoreException"+ , "setCurrentThreadResultException"+ , "setCurrentThreadResultValue"+ ]+ , mkPrimFuns "GHC.Types"+ [ ":"+ , "[]"+ ]+ , mkPrimFuns "GHC.Tuple.Prim"+ [ "(,)"+ , "(,,)"+ , "(,,,)"+ , "(,,,,)"+ , "(,,,,,)"+ , "(,,,,,,)"+ , "(,,,,,,,)"+ , "(,,,,,,,,)"+ , "(,,,,,,,,,)"+ ]+ ]+ )++-- | Export the functions in base+mkBaseFuns :: FastString -> [FastString] -> [ExportedFun]+mkBaseFuns = mkExportedFuns baseUnitId++-- | Export the Prim functions+mkPrimFuns :: FastString -> [FastString] -> [ExportedFun]+mkPrimFuns = mkExportedFuns primUnitId++-- | Given a @UnitId@, a module name, and a set of symbols in the module,+-- package these into an @ExportedFun@.+mkExportedFuns :: UnitId -> FastString -> [FastString] -> [ExportedFun]+mkExportedFuns uid mod_name symbols = map mk_fun symbols+ where+ mod = mkModule (RealUnit (Definite uid)) (mkModuleNameFS mod_name)+ mk_fun sym = ExportedFun mod (LexicalFastString (mkJsSymbol True mod sym))++-- | read all dependency data from the to-be-linked files+loadObjDeps :: [LinkedObj] -- ^ object files to link+ -> IO (Map Module (Deps, DepsLocation), [LinkableUnit])+loadObjDeps objs = (prepareLoadedDeps . catMaybes) <$> mapM readDepsFromObj objs++-- | Load dependencies for the Linker from Ar+loadArchiveDeps :: GhcjsEnv+ -> [FilePath]+ -> IO ( Map Module (Deps, DepsLocation)+ , [LinkableUnit]+ )+loadArchiveDeps env archives = modifyMVar (linkerArchiveDeps env) $ \m ->+ case M.lookup archives' m of+ Just r -> return (m, r)+ Nothing -> loadArchiveDeps' archives >>= \r -> return (M.insert archives' r m, r)+ where+ archives' = S.fromList archives++loadArchiveDeps' :: [FilePath]+ -> IO ( Map Module (Deps, DepsLocation)+ , [LinkableUnit]+ )+loadArchiveDeps' archives = do+ archDeps <- forM archives $ \file -> do+ (Ar.Archive entries) <- Ar.loadAr file+ catMaybes <$> mapM (readEntry file) entries+ return (prepareLoadedDeps $ concat archDeps)+ where+ readEntry :: FilePath -> Ar.ArchiveEntry -> IO (Maybe (Deps, DepsLocation))+ readEntry ar_file ar_entry = do+ let bs = Ar.filedata ar_entry+ bh <- unsafeUnpackBinBuffer bs+ getObjectHeader bh >>= \case+ Left _ -> pure Nothing -- not a valid object entry+ Right mod_name -> do+ obj <- getObjectBody bh mod_name+ let !deps = objDeps obj+ pure $ Just (deps, ArchiveFile ar_file)++-- | Predicate to check that an entry in Ar is a JS source+-- and to return it without its header+getJsArchiveEntry :: Ar.ArchiveEntry -> Maybe B.ByteString+getJsArchiveEntry entry = getJsBS (Ar.filedata entry)++-- | Predicate to check that a file is a JS source+isJsFile :: FilePath -> IO Bool+isJsFile fp = withBinaryFile fp ReadMode $ \h -> do+ bs <- B.hGet h jsHeaderLength+ pure (isJsBS bs)++isJsBS :: B.ByteString -> Bool+isJsBS bs = isJust (getJsBS bs)++-- | Get JS source with its header (if it's one)+getJsBS :: B.ByteString -> Maybe B.ByteString+getJsBS bs = B.stripPrefix jsHeader bs++-- Header added to JS sources to discriminate them from other object files.+-- They all have .o extension but JS sources have this header.+jsHeader :: B.ByteString+jsHeader = "//JavaScript"++jsHeaderLength :: Int+jsHeaderLength = B.length jsHeader++++prepareLoadedDeps :: [(Deps, DepsLocation)]+ -> ( Map Module (Deps, DepsLocation)+ , [LinkableUnit]+ )+prepareLoadedDeps deps =+ let req = concatMap (requiredUnits . fst) deps+ depsMap = M.fromList $ map (\d -> (depsModule (fst d), d)) deps+ in (depsMap, req)++requiredUnits :: Deps -> [LinkableUnit]+requiredUnits d = map (depsModule d,) (IS.toList $ depsRequired d)++-- | read dependencies from an object that might have already been into memory+-- pulls in all Deps from an archive+readDepsFromObj :: LinkedObj -> IO (Maybe (Deps, DepsLocation))+readDepsFromObj = \case+ ObjLoaded name obj -> do+ let !deps = objDeps obj+ pure $ Just (deps,InMemory name obj)+ ObjFile file -> do+ readObjectDeps file >>= \case+ Nothing -> pure Nothing+ Just deps -> pure $ Just (deps,ObjectFile file)+++-- | Embed a JS file into a .o file+--+-- The JS file is merely copied into a .o file with an additional header+-- ("//Javascript") in order to be recognized later on.+--+-- JS files may contain option pragmas of the form: //#OPTIONS:+-- For now, only the CPP option is supported. If the CPP option is set, we+-- append some common CPP definitions to the file and call cpp on it.+embedJsFile :: Logger -> DynFlags -> TmpFs -> UnitEnv -> FilePath -> FilePath -> IO ()+embedJsFile logger dflags tmpfs unit_env input_fn output_fn = do+ let profiling = False -- FIXME: add support for profiling way++ createDirectoryIfMissing True (takeDirectory output_fn)++ -- the header lets the linker recognize processed JavaScript files+ -- But don't add JavaScript header to object files!++ -- header appended to JS files stored as .o to recognize them.+ let header = "//JavaScript\n"+ jsFileNeedsCpp input_fn >>= \case+ False -> copyWithHeader header input_fn output_fn+ True -> do++ -- append common CPP definitions to the .js file.+ -- They define macros that avoid directly wiring zencoded names+ -- in RTS JS files+ pp_fn <- newTempName logger tmpfs (tmpDir dflags) TFL_CurrentModule "js"+ payload <- B.readFile input_fn+ B.writeFile pp_fn (commonCppDefs profiling <> payload)++ -- run CPP on the input JS file+ js_fn <- newTempName logger tmpfs (tmpDir dflags) TFL_CurrentModule "js"+ let+ cpp_opts = CppOpts+ { cppUseCc = True+ , cppLinePragmas = False -- LINE pragmas aren't JS compatible+ }+ doCpp logger+ tmpfs+ dflags+ unit_env+ cpp_opts+ pp_fn+ js_fn+ -- add header to recognize the object as a JS file+ copyWithHeader header js_fn output_fn++jsFileNeedsCpp :: FilePath -> IO Bool+jsFileNeedsCpp fn = do+ opts <- getOptionsFromJsFile fn+ pure (CPP `elem` opts)++-- | Link module codes.+--+-- Performs link time optimizations and produces one JStat per module plus some+-- commoned up initialization code.+linkModules :: [ModuleCode] -> ([CompactedModuleCode], JStat)+linkModules mods = (compact_mods, meta)+ where+ compact_mods = map compact mods++ -- here GHCJS used to:+ -- - deduplicate declarations+ -- - rename local variables into shorter ones+ -- - compress initialization data+ -- but we haven't ported it (yet).+ compact m = CompactedModuleCode+ { cmc_js_code = mc_js_code m+ , cmc_module = mc_module m+ , cmc_exports = mc_exports m+ }++ -- common up statics: different bindings may reference the same statics, we+ -- filter them here to initialize them once+ statics = nubStaticInfo (concatMap mc_statics mods)++ infos = concatMap mc_closures mods+ debug = False -- TODO: this could be enabled in a debug build.+ -- It adds debug info to heap objects+ meta = mconcat+ -- render metadata as individual statements+ [ mconcat (map staticDeclStat statics)+ , mconcat (map staticInitStat statics)+ , mconcat (map (closureInfoStat debug) infos)+ ]++-- | Only keep a single StaticInfo with a given name+nubStaticInfo :: [StaticInfo] -> [StaticInfo]+nubStaticInfo = go emptyUniqSet+ where+ go us = \case+ [] -> []+ (x:xs) ->+ -- only match on siVar. There is no reason for the initializing value to+ -- be different for the same global name.+ let name = siVar x+ in if elementOfUniqSet name us+ then go us xs+ else x : go (addOneToUniqSet us name) xs++-- | Initialize a global object.+--+-- All global objects have to be declared (staticInfoDecl) first.+staticInitStat :: StaticInfo -> JStat+staticInitStat (StaticInfo i sv mcc) =+ case sv of+ StaticData con args -> appS "h$sti" $ add_cc_arg+ [ var i+ , var con+ , jsStaticArgs args+ ]+ StaticFun f args -> appS "h$sti" $ add_cc_arg+ [ var i+ , var f+ , jsStaticArgs args+ ]+ StaticList args mt -> appS "h$stl" $ add_cc_arg+ [ var i+ , jsStaticArgs args+ , toJExpr $ maybe null_ (toJExpr . TxtI) mt+ ]+ StaticThunk (Just (f,args)) -> appS "h$stc" $ add_cc_arg+ [ var i+ , var f+ , jsStaticArgs args+ ]+ _ -> mempty+ where+ -- add optional cost-center argument+ add_cc_arg as = case mcc of+ Nothing -> as+ Just cc -> as ++ [toJExpr cc]++-- | declare and do first-pass init of a global object (create JS object for heap objects)+staticDeclStat :: StaticInfo -> JStat+staticDeclStat (StaticInfo global_name static_value _) = decl+ where+ global_ident = TxtI global_name+ decl_init v = global_ident ||= v+ decl_no_init = appS "h$di" [toJExpr global_ident]++ decl = case static_value of+ StaticUnboxed u -> decl_init (unboxed_expr u)+ StaticThunk Nothing -> decl_no_init -- CAF initialized in an alternative way+ _ -> decl_init (app "h$d" [])++ unboxed_expr = \case+ StaticUnboxedBool b -> app "h$p" [toJExpr b]+ StaticUnboxedInt i -> app "h$p" [toJExpr i]+ StaticUnboxedDouble d -> app "h$p" [toJExpr (unSaneDouble d)]+ StaticUnboxedString str -> app "h$rawStringData" [ValExpr (to_byte_list str)]+ StaticUnboxedStringOffset {} -> 0++ to_byte_list = JList . map (Int . fromIntegral) . BS.unpack
+ compiler/GHC/StgToJS/Linker/Types.hs view
@@ -0,0 +1,101 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PartialTypeSignatures #-}+{-# LANGUAGE LambdaCase #-}++{-# OPTIONS_GHC -Wno-orphans #-} -- for Ident's Binary instance++-----------------------------------------------------------------------------+-- |+-- Module : GHC.StgToJS.Linker.Types+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Sylvain Henry <sylvain.henry@iohk.io>+-- Josh Meredith <josh.meredith@iohk.io>+-- Stability : experimental+--+-----------------------------------------------------------------------------++module GHC.StgToJS.Linker.Types+ ( GhcjsEnv (..)+ , newGhcjsEnv+ , JSLinkConfig (..)+ , defaultJSLinkConfig+ , generateAllJs+ , LinkedObj (..)+ , LinkableUnit+ )+where++import GHC.StgToJS.Object++import GHC.Unit.Types+import GHC.Utils.Outputable (hsep,Outputable(..),text,ppr)++import Data.Map.Strict (Map)+import qualified Data.Map.Strict as M+import Data.Set (Set)++import Control.Concurrent.MVar++import System.IO++import Prelude++--------------------------------------------------------------------------------+-- Linker Config+--------------------------------------------------------------------------------++data JSLinkConfig = JSLinkConfig+ { lcNoJSExecutables :: Bool+ , lcNoHsMain :: Bool+ , lcOnlyOut :: Bool+ , lcNoRts :: Bool+ , lcNoStats :: Bool+ }++-- | we generate a runnable all.js only if we link a complete application,+-- no incremental linking and no skipped parts+generateAllJs :: JSLinkConfig -> Bool+generateAllJs s = not (lcOnlyOut s) && not (lcNoRts s)++defaultJSLinkConfig :: JSLinkConfig+defaultJSLinkConfig = JSLinkConfig+ { lcNoJSExecutables = False+ , lcNoHsMain = False+ , lcOnlyOut = False+ , lcNoRts = False+ , lcNoStats = False+ }++--------------------------------------------------------------------------------+-- Linker Environment+--------------------------------------------------------------------------------++-- | A @LinkableUnit@ is a pair of a module and the index of the block in the+-- object file+type LinkableUnit = (Module, Int)++-- | An object file that's either already in memory (with name) or on disk+data LinkedObj+ = ObjFile FilePath -- ^ load from this file+ | ObjLoaded String Object -- ^ already loaded: description and payload++instance Outputable LinkedObj where+ ppr = \case+ ObjFile fp -> hsep [text "ObjFile", text fp]+ ObjLoaded s o -> hsep [text "ObjLoaded", text s, ppr (objModuleName o)]++data GhcjsEnv = GhcjsEnv+ { linkerArchiveDeps :: MVar (Map (Set FilePath)+ (Map Module (Deps, DepsLocation)+ , [LinkableUnit]+ )+ )+ }++-- | return a fresh @GhcjsEnv@+newGhcjsEnv :: IO GhcjsEnv+newGhcjsEnv = GhcjsEnv <$> newMVar M.empty
+ compiler/GHC/StgToJS/Linker/Utils.hs view
@@ -0,0 +1,308 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE MultiWayIf #-}+-----------------------------------------------------------------------------+-- |+-- Module : GHC.StgToJS.Linker.Utils+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Sylvain Henry <sylvain.henry@iohk.io>+-- Josh Meredith <josh.meredith@iohk.io>+-- Stability : experimental+--+-- Various utilies used in the JS Linker+--+-----------------------------------------------------------------------------++module GHC.StgToJS.Linker.Utils+ ( getOptionsFromJsFile+ , JSOption(..)+ , jsExeFileName+ , getInstalledPackageLibDirs+ , getInstalledPackageHsLibs+ , commonCppDefs+ )+where++import System.FilePath+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as Char8+import Data.ByteString (ByteString)++import GHC.Driver.Session++import GHC.Data.ShortText+import GHC.Unit.State+import GHC.Unit.Types++import GHC.StgToJS.Types++import Prelude+import GHC.Platform+import Data.List (isPrefixOf)+import System.IO+import Data.Char (isSpace)+import qualified Control.Exception as Exception++-- | Retrieve library directories provided by the @UnitId@ in @UnitState@+getInstalledPackageLibDirs :: UnitState -> UnitId -> [ShortText]+getInstalledPackageLibDirs us = maybe mempty unitLibraryDirs . lookupUnitId us++-- | Retrieve the names of the libraries provided by @UnitId@+getInstalledPackageHsLibs :: UnitState -> UnitId -> [ShortText]+getInstalledPackageHsLibs us = maybe mempty unitLibraries . lookupUnitId us++-- | A constant holding the JavaScript executable Filename extension+jsexeExtension :: String+jsexeExtension = "jsexe"++-- | CPP definitions that are inserted into every .pp file+commonCppDefs :: Bool -> ByteString+commonCppDefs profiling = case profiling of+ True -> commonCppDefs_profiled+ False -> commonCppDefs_vanilla++-- | CPP definitions for normal operation and profiling. Use CAFs for+-- commonCppDefs_* so that they are shared for every CPP file+commonCppDefs_vanilla, commonCppDefs_profiled :: ByteString+commonCppDefs_vanilla = genCommonCppDefs False+commonCppDefs_profiled = genCommonCppDefs True++-- | Generate CPP Definitions depending on a profiled or normal build. This+-- occurs at link time.+genCommonCppDefs :: Bool -> ByteString+genCommonCppDefs profiling = mconcat+ [+ -- constants+ let mk_int_def n v = "#define " <> Char8.pack n <> " (" <> Char8.pack (show v) <> ")\n"+ -- generate "#define CLOSURE_TYPE_xyz (num)" defines+ mk_closure_def t = mk_int_def (ctJsName t) (ctNum t)+ closure_defs = map mk_closure_def [minBound..maxBound]+ -- generate "#define THREAD_xyz_xyz (num)" defines+ mk_thread_def t = mk_int_def (threadStatusJsName t) (threadStatusNum t)+ thread_defs = map mk_thread_def [minBound..maxBound]+ in mconcat (closure_defs ++ thread_defs)++ -- low-level heap object manipulation macros+ , if profiling+ then mconcat+ [ "#define MK_TUP2(x1,x2) (h$c2(h$ghczmprimZCGHCziTupleziPrimziZLz2cUZR_con_e,(x1),(x2),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"+ , "#define MK_TUP3(x1,x2,x3) (h$c3(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUZR_con_e,(x1),(x2),(x3),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"+ , "#define MK_TUP4(x1,x2,x3,x4) (h$c4(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"+ , "#define MK_TUP5(x1,x2,x3,x4,x5) (h$c5(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"+ , "#define MK_TUP6(x1,x2,x3,x4,x5,x6) (h$c6(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"+ , "#define MK_TUP7(x1,x2,x3,x4,x5,x6,x7) (h$c7(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),(x7),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"+ , "#define MK_TUP8(x1,x2,x3,x4,x5,x6,x7,x8) (h$c8(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"+ , "#define MK_TUP9(x1,x2,x3,x4,x5,x6,x7,x8,x9) (h$c9(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(x9),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"+ , "#define MK_TUP10(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10) (h$c10(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(x9),(x10),h$currentThread?h$currentThread.ccs:h$CCS_SYSTEM))\n"+ ]+ else mconcat+ [ "#define MK_TUP2(x1,x2) (h$c2(h$ghczmprimZCGHCziTupleziPrimziZLz2cUZR_con_e,(x1),(x2)))\n"+ , "#define MK_TUP3(x1,x2,x3) (h$c3(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUZR_con_e,(x1),(x2),(x3)))\n"+ , "#define MK_TUP4(x1,x2,x3,x4) (h$c4(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4)))\n"+ , "#define MK_TUP5(x1,x2,x3,x4,x5) (h$c5(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5)))\n"+ , "#define MK_TUP6(x1,x2,x3,x4,x5,x6) (h$c6(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6)))\n"+ , "#define MK_TUP7(x1,x2,x3,x4,x5,x6,x7) (h$c7(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),(x7)))\n"+ , "#define MK_TUP8(x1,x2,x3,x4,x5,x6,x7,x8) (h$c8(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8)))\n"+ , "#define MK_TUP9(x1,x2,x3,x4,x5,x6,x7,x8,x9) (h$c9(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(x9)))\n"+ , "#define MK_TUP10(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10) (h$c10(h$ghczmprimZCGHCziTupleziPrimziZLz2cUz2cUz2cUz2cUz2cUz2cUz2cUz2cUz2cUZR_con_e,(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(x9),(x10)))\n"+ ]++ , "#define TUP2_1(x) ((x).d1)\n"+ , "#define TUP2_2(x) ((x).d2)\n"++ -- GHCJS.Prim.JSVal+ , if profiling+ then "#define MK_JSVAL(x) (h$baseZCGHCziJSziPrimziJSVal_con_e, (x), h$CCS_SYSTEM)\n"+ else "#define MK_JSVAL(x) (h$baseZCGHCziJSziPrimziJSVal_con_e, (x))\n"+ , "#define JSVAL_VAL(x) ((x).d1)\n"++ -- GHCJS.Prim.JSException+ , if profiling+ then "#define MK_JSEXCEPTION(msg,hsMsg) (h$c2(h$baseZCGHCziJSziPrimziJSException_con_e,(msg),(hsMsg),h$CCS_SYSTEM))\n"+ else "#define MK_JSEXCEPTION(msg,hsMsg) (h$c2(h$baseZCGHCziJSziPrimziJSException_con_e,(msg),(hsMsg)))\n"++ -- Exception dictionary for JSException+ , "#define HS_JSEXCEPTION_EXCEPTION h$baseZCGHCziJSziPrimzizdfExceptionJSException\n"++ -- SomeException+ , if profiling+ then "#define MK_SOMEEXCEPTION(dict,except) (h$c2(h$baseZCGHCziExceptionziTypeziSomeException_con_e,(dict),(except),h$CCS_SYSTEM))\n"+ else "#define MK_SOMEEXCEPTION(dict,except) (h$c2(h$baseZCGHCziExceptionziTypeziSomeException_con_e,(dict),(except)))\n"++ -- GHC.Ptr.Ptr+ , if profiling+ then "#define MK_PTR(val,offset) (h$c2(h$baseZCGHCziPtrziPtr_con_e, (val), (offset), h$CCS_SYSTEM))\n"+ else "#define MK_PTR(val,offset) (h$c2(h$baseZCGHCziPtrziPtr_con_e, (val), (offset)))\n"++ -- Data.Maybe.Maybe+ , "#define HS_NOTHING h$baseZCGHCziMaybeziNothing\n"+ , "#define IS_NOTHING(cl) ((cl).f === h$baseZCGHCziMaybeziNothing_con_e)\n"+ , "#define IS_JUST(cl) ((cl).f === h$baseZCGHCziMaybeziJust_con_e)\n"+ , "#define JUST_VAL(jj) ((jj).d1)\n"+ -- "#define HS_NOTHING h$nothing\n"+ , if profiling+ then "#define MK_JUST(val) (h$c1(h$baseZCGHCziMaybeziJust_con_e, (val), h$CCS_SYSTEM))\n"+ else "#define MK_JUST(val) (h$c1(h$baseZCGHCziMaybeziJust_con_e, (val)))\n"++ -- Data.List+ , "#define HS_NIL h$ghczmprimZCGHCziTypesziZMZN\n"+ , "#define HS_NIL_CON h$ghczmprimZCGHCziTypesziZMZN_con_e\n"+ , "#define IS_CONS(cl) ((cl).f === h$ghczmprimZCGHCziTypesziZC_con_e)\n"+ , "#define IS_NIL(cl) ((cl).f === h$ghczmprimZCGHCziTypesziZMZN_con_e)\n"+ , "#define CONS_HEAD(cl) ((cl).d1)\n"+ , "#define CONS_TAIL(cl) ((cl).d2)\n"+ , if profiling+ then mconcat+ [ "#define MK_CONS(head,tail) (h$c2(h$ghczmprimZCGHCziTypesziZC_con_e, (head), (tail), h$CCS_SYSTEM))\n"+ , "#define MK_CONS_CC(head,tail,cc) (h$c2(h$ghczmprimZCGHCziTypesziZC_con_e, (head), (tail), (cc)))\n"+ ]+ else mconcat+ [ "#define MK_CONS(head,tail) (h$c2(h$ghczmprimZCGHCziTypesziZC_con_e, (head), (tail)))\n"+ , "#define MK_CONS_CC(head,tail,cc) (h$c2(h$ghczmprimZCGHCziTypesziZC_con_e, (head), (tail)))\n"+ ]++ -- Data.Text+ , "#define DATA_TEXT_ARRAY(x) ((x).d1)\n"+ , "#define DATA_TEXT_OFFSET(x) ((x).d2.d1)\n"+ , "#define DATA_TEXT_LENGTH(x) ((x).d2.d2)\n"++ -- Data.Text.Lazy+ , "#define LAZY_TEXT_IS_CHUNK(x) ((x).f.a === 2)\n"+ , "#define LAZY_TEXT_IS_NIL(x) ((x).f.a === 1)\n"+ , "#define LAZY_TEXT_CHUNK_HEAD(x) ((x))\n"+ , "#define LAZY_TEXT_CHUNK_TAIL(x) ((x).d2.d3)\n"++ -- black holes+ -- can we skip the indirection for black holes?+ , "#define IS_BLACKHOLE(x) (typeof (x) === 'object' && (x) && (x).f && (x).f.t === CLOSURE_TYPE_BLACKHOLE)\n"+ , "#define BLACKHOLE_TID(bh) ((bh).d1)\n"+ , "#define SET_BLACKHOLE_TID(bh,tid) ((bh).d1 = (tid))\n"+ , "#define BLACKHOLE_QUEUE(bh) ((bh).d2)\n"+ , "#define SET_BLACKHOLE_QUEUE(bh,val) ((bh).d2 = (val))\n"++ -- resumable thunks+ , "#define MAKE_RESUMABLE(closure,stack) { (closure).f = h$resume_e; (closure).d1 = (stack), (closure).d2 = null; }\n"++ -- general deconstruction+ , "#define IS_THUNK(x) ((x).f.t === CLOSURE_TYPE_THUNK)\n"+ , "#define CONSTR_TAG(x) ((x).f.a)\n"++ -- retrieve a numeric value that's possibly stored as an indirection+ , "#define IS_WRAPPED_NUMBER(val) ((typeof(val)==='object')&&(val).f === h$unbox_e)\n"+ , "#define UNWRAP_NUMBER(val) ((typeof(val) === 'number')?(val):(val).d1)\n"++ -- generic lazy values+ , if profiling+ then mconcat+ [ "#define MK_LAZY(fun) (h$c1(h$lazy_e, (fun), h$CCS_SYSTEM))\n"+ , "#define MK_LAZY_CC(fun,cc) (h$c1(h$lazy_e, (fun), (cc)))\n"+ ]+ else mconcat+ [ "#define MK_LAZY(fun) (h$c1(h$lazy_e, (fun)))\n"+ , "#define MK_LAZY_CC(fun,cc) (h$c1(h$lazy_e, (fun)))\n"+ ]++ -- generic data constructors and selectors+ , if profiling+ then mconcat+ [ "#define MK_DATA1_1(val) (h$c1(h$data1_e, (val), h$CCS_SYSTEM))\n"+ , "#define MK_DATA1_2(val1,val2) (h$c2(h$data1_e, (val1), (val2), h$CCS_SYSTEM))\n"+ , "#define MK_DATA2_1(val) (h$c1(h$data2_e, (val), h$CCS_SYSTEM))\n"+ , "#define MK_DATA2_2(val1,val2) (h$c2(h$data1_e, (val1), (val2), h$CCS_SYSTEM))\n"+ , "#define MK_SELECT1(val) (h$c1(h$select1_e, (val), h$CCS_SYSTEM))\n"+ , "#define MK_SELECT2(val) (h$c1(h$select2_e, (val), h$CCS_SYSTEM))\n"+ , "#define MK_AP1(fun,val) (h$c2(h$ap1_e, (fun), (val), h$CCS_SYSTEM))\n"+ , "#define MK_AP2(fun,val1,val2) (h$c3(h$ap2_e, (fun), (val1), (val2), h$CCS_SYSTEM))\n"+ , "#define MK_AP3(fun,val1,val2,val3) (h$c4(h$ap3_e, (fun), (val1), (val2), (val3), h$CCS_SYSTEM))\n"+ ]+ else mconcat+ [ "#define MK_DATA1_1(val) (h$c1(h$data1_e, (val)))\n"+ , "#define MK_DATA1_2(val1,val2) (h$c2(h$data1_e, (val1), (val2)))\n"+ , "#define MK_DATA2_1(val) (h$c1(h$data2_e, (val)))\n"+ , "#define MK_DATA2_2(val1,val2) (h$c2(h$data2_e, (val1), (val2)))\n"+ , "#define MK_SELECT1(val) (h$c1(h$select1_e, (val)))\n"+ , "#define MK_SELECT2(val) (h$c1(h$select2_e, (val)))\n"+ , "#define MK_AP1(fun,val) (h$c2(h$ap1_e,(fun),(val)))\n"+ , "#define MK_AP2(fun,val1,val2) (h$c3(h$ap2_e,(fun),(val1),(val2)))\n"+ , "#define MK_AP3(fun,val1,val2,val3) (h$c4(h$ap3_e, (fun), (val1), (val2), (val3)))\n"+ ]++ -- unboxed tuple returns+ -- , "#define RETURN_UBX_TUP1(x) return x;\n"+ , "#define RETURN_UBX_TUP2(x1,x2) { h$ret1 = (x2); return (x1); }\n"+ , "#define RETURN_UBX_TUP3(x1,x2,x3) { h$ret1 = (x2); h$ret2 = (x3); return (x1); }\n"+ , "#define RETURN_UBX_TUP4(x1,x2,x3,x4) { h$ret1 = (x2); h$ret2 = (x3); h$ret3 = (x4); return (x1); }\n"+ , "#define RETURN_UBX_TUP5(x1,x2,x3,x4,x5) { h$ret1 = (x2); h$ret2 = (x3); h$ret3 = (x4); h$ret4 = (x5); return (x1); }\n"+ , "#define RETURN_UBX_TUP6(x1,x2,x3,x4,x5,x6) { h$ret1 = (x2); h$ret2 = (x3); h$ret3 = (x4); h$ret4 = (x5); h$ret5 = (x6); return (x1); }\n"+ , "#define RETURN_UBX_TUP7(x1,x2,x3,x4,x5,x6,x7) { h$ret1 = (x2); h$ret2 = (x3); h$ret3 = (x4); h$ret4 = (x5); h$ret5 = (x6); h$ret6 = (x7); return (x1); }\n"+ , "#define RETURN_UBX_TUP8(x1,x2,x3,x4,x5,x6,x7,x8) { h$ret1 = (x2); h$ret2 = (x3); h$ret3 = (x4); h$ret4 = (x5); h$ret5 = (x6); h$ret6 = (x7); h$ret7 = (x8); return (x1); }\n"+ , "#define RETURN_UBX_TUP9(x1,x2,x3,x4,x5,x6,x7,x8,x9) { h$ret1 = (x2); h$ret2 = (x3); h$ret3 = (x4); h$ret4 = (x5); h$ret5 = (x6); h$ret6 = (x7); h$ret7 = (x8); h$ret8 = (x9); return (x1); }\n"+ , "#define RETURN_UBX_TUP10(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10) { h$ret1 = (x2); h$ret2 = (x3); h$ret3 = (x4); h$ret4 = (x5); h$ret5 = (x6); h$ret6 = (x7); h$ret7 = (x8); h$ret8 = (x9); h$ret9 = (x10); return (x1); }\n"++ , "#define CALL_UBX_TUP2(r1,r2,c) { (r1) = (c); (r2) = h$ret1; }\n"+ , "#define CALL_UBX_TUP3(r1,r2,r3,c) { (r1) = (c); (r2) = h$ret1; (r3) = h$ret2; }\n"+ , "#define CALL_UBX_TUP4(r1,r2,r3,r4,c) { (r1) = (c); (r2) = h$ret1; (r3) = h$ret2; (r4) = h$ret3; }\n"+ , "#define CALL_UBX_TUP5(r1,r2,r3,r4,r5,c) { (r1) = (c); (r2) = h$ret1; (r3) = h$ret2; (r4) = h$ret3; (r5) = h$ret4; }\n"+ , "#define CALL_UBX_TUP6(r1,r2,r3,r4,r5,r6,c) { (r1) = (c); (r2) = h$ret1; (r3) = h$ret2; (r4) = h$ret3; (r5) = h$ret4; (r6) = h$ret5; }\n"+ , "#define CALL_UBX_TUP7(r1,r2,r3,r4,r5,r6,r7,c) { (r1) = (c); (r2) = h$ret1; (r3) = h$ret2; (r4) = h$ret3; (r5) = h$ret4; (r6) = h$ret5; (r7) = h$ret6; }\n"+ , "#define CALL_UBX_TUP8(r1,r2,r3,r4,r5,r6,r7,r8,c) { (r1) = (c); (r2) = h$ret1; (r3) = h$ret2; (r4) = h$ret3; (r5) = h$ret4; (r6) = h$ret5; (r7) = h$ret6; (r8) = h$ret7; }\n"+ , "#define CALL_UBX_TUP9(r1,r2,r3,r4,r5,r6,r7,r8,r9,c) { (r1) = (c); (r2) = h$ret1; (r3) = h$ret2; (r4) = h$ret3; (r5) = h$ret4; (r6) = h$ret5; (r7) = h$ret6; (r8) = h$ret7; (r9) = h$ret8; }\n"+ , "#define CALL_UBX_TUP10(r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,c) { (r1) = (c); (r2) = h$ret1; (r3) = h$ret2; (r4) = h$ret3; (r5) = h$ret4; (r6) = h$ret5; (r7) = h$ret6; (r8) = h$ret7; (r9) = h$ret8; (r10) = h$ret9; }\n"+ ]++-- | Construct the Filename for the "binary" of Haskell code compiled to+-- JavaScript.+jsExeFileName :: DynFlags -> FilePath+jsExeFileName dflags+ | Just s <- outputFile_ dflags =+ -- unmunge the extension+ let s' = dropPrefix "js_" (drop 1 $ takeExtension s)+ in if Prelude.null s'+ then dropExtension s <.> jsexeExtension+ else dropExtension s <.> s'+ | otherwise =+ if platformOS (targetPlatform dflags) == OSMinGW32+ then "main.jsexe"+ else "a.jsexe"+ where+ dropPrefix prefix xs+ | prefix `isPrefixOf` xs = drop (length prefix) xs+ | otherwise = xs+++-- | Parse option pragma in JS file+getOptionsFromJsFile :: FilePath -- ^ Input file+ -> IO [JSOption] -- ^ Parsed options, if any.+getOptionsFromJsFile filename+ = Exception.bracket+ (openBinaryFile filename ReadMode)+ hClose+ getJsOptions++data JSOption = CPP deriving (Eq, Ord)++getJsOptions :: Handle -> IO [JSOption]+getJsOptions handle = do+ hSetEncoding handle utf8+ prefix' <- B.hGet handle prefixLen+ if prefix == prefix'+ then parseJsOptions <$> hGetLine handle+ else pure []+ where+ prefix :: B.ByteString+ prefix = "//#OPTIONS:"+ prefixLen = B.length prefix++parseJsOptions :: String -> [JSOption]+parseJsOptions xs = go xs+ where+ trim = reverse . dropWhile isSpace . reverse . dropWhile isSpace+ go [] = []+ go xs = let (tok, rest) = break (== ',') xs+ tok' = trim tok+ rest' = drop 1 rest+ in if | tok' == "CPP" -> CPP : go rest'+ | otherwise -> go rest'
+ compiler/GHC/StgToJS/Literal.hs view
@@ -0,0 +1,109 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}++module GHC.StgToJS.Literal+ ( genLit+ , genStaticLit+ )+where++import GHC.Prelude++import GHC.JS.Syntax+import GHC.JS.Make++import GHC.StgToJS.Types+import GHC.StgToJS.Monad+import GHC.StgToJS.Ids+import GHC.StgToJS.Symbols++import GHC.Data.FastString+import GHC.Types.Literal+import GHC.Types.Basic+import GHC.Utils.Misc+import GHC.Utils.Panic+import GHC.Utils.Outputable+import GHC.Float++import Data.Bits as Bits+import Data.Char (ord)++-- | Generate JS expressions for a Literal+--+-- Literals represented with 2 values:+-- * Addr# (Null and Strings): array and offset+-- * 64-bit values: high 32-bit, low 32-bit+-- * labels: call to h$mkFunctionPtr and 0, or function name and 0+genLit :: HasDebugCallStack => Literal -> G [JExpr]+genLit = \case+ LitChar c -> return [ toJExpr (ord c) ]+ LitString str ->+ freshIdent >>= \strLit@(TxtI strLitT) ->+ freshIdent >>= \strOff@(TxtI strOffT) -> do+ emitStatic strLitT (StaticUnboxed (StaticUnboxedString str)) Nothing+ emitStatic strOffT (StaticUnboxed (StaticUnboxedStringOffset str)) Nothing+ return [ ValExpr (JVar strLit), ValExpr (JVar strOff) ]+ LitNullAddr -> return [ null_, ValExpr (JInt 0) ]+ LitNumber nt v -> case nt of+ LitNumInt -> return [ toJExpr v ]+ LitNumInt8 -> return [ toJExpr v ]+ LitNumInt16 -> return [ toJExpr v ]+ LitNumInt32 -> return [ toJExpr v ]+ LitNumInt64 -> return [ toJExpr (Bits.shiftR v 32), toU32Expr v ]+ LitNumWord -> return [ toU32Expr v ]+ LitNumWord8 -> return [ toU32Expr v ]+ LitNumWord16 -> return [ toU32Expr v ]+ LitNumWord32 -> return [ toU32Expr v ]+ LitNumWord64 -> return [ toU32Expr (Bits.shiftR v 32), toU32Expr v ]+ LitNumBigNat -> panic "genLit: unexpected BigNat that should have been removed in CorePrep"+ LitFloat r -> return [ toJExpr (r2f r) ]+ LitDouble r -> return [ toJExpr (r2d r) ]+ LitLabel name _size fod+ | fod == IsFunction -> return [ ApplExpr (var "h$mkFunctionPtr")+ [var (mkRawSymbol True name)]+ , ValExpr (JInt 0)+ ]+ | otherwise -> return [ toJExpr (TxtI (mkRawSymbol True name))+ , ValExpr (JInt 0)+ ]+ LitRubbish {} -> return [ null_ ]++-- | generate a literal for the static init tables+genStaticLit :: Literal -> G [StaticLit]+genStaticLit = \case+ LitChar c -> return [ IntLit (fromIntegral $ ord c) ]+ LitString str+ | True -> return [ StringLit (mkFastStringByteString str), IntLit 0]+ -- \| invalid UTF8 -> return [ BinLit str, IntLit 0]+ LitNullAddr -> return [ NullLit, IntLit 0 ]+ LitNumber nt v -> case nt of+ LitNumInt -> return [ IntLit v ]+ LitNumInt8 -> return [ IntLit v ]+ LitNumInt16 -> return [ IntLit v ]+ LitNumInt32 -> return [ IntLit v ]+ LitNumInt64 -> return [ IntLit (v `Bits.shiftR` 32), toU32Lit v ]+ LitNumWord -> return [ toU32Lit v ]+ LitNumWord8 -> return [ toU32Lit v ]+ LitNumWord16 -> return [ toU32Lit v ]+ LitNumWord32 -> return [ toU32Lit v ]+ LitNumWord64 -> return [ toU32Lit (v `Bits.shiftR` 32), toU32Lit v ]+ LitNumBigNat -> panic "genStaticLit: unexpected BigNat that should have been removed in CorePrep"+ LitFloat r -> return [ DoubleLit . SaneDouble . r2f $ r ]+ LitDouble r -> return [ DoubleLit . SaneDouble . r2d $ r ]+ LitLabel name _size fod -> return [ LabelLit (fod == IsFunction) (mkRawSymbol True name)+ , IntLit 0 ]+ l -> pprPanic "genStaticLit" (ppr l)++-- make an unsigned 32 bit number from this unsigned one, lower 32 bits+toU32Expr :: Integer -> JExpr+toU32Expr i = Int (i Bits..&. 0xFFFFFFFF) .>>>. 0++-- make an unsigned 32 bit number from this unsigned one, lower 32 bits+toU32Lit :: Integer -> StaticLit+toU32Lit i = IntLit (i Bits..&. 0xFFFFFFFF)++r2d :: Rational -> Double+r2d = realToFrac++r2f :: Rational -> Double+r2f = float2Double . realToFrac
+ compiler/GHC/StgToJS/Monad.hs view
@@ -0,0 +1,183 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE LambdaCase #-}++-- | JS codegen state monad+module GHC.StgToJS.Monad+ ( runG+ , emitGlobal+ , addDependency+ , emitToplevel+ , emitStatic+ , emitClosureInfo+ , emitForeign+ , assertRtsStat+ , getSettings+ , globalOccs+ , setGlobalIdCache+ , getGlobalIdCache+ , GlobalOcc(..)+ -- * Group+ , modifyGroup+ , resetGroup+ )+where++import GHC.Prelude++import GHC.JS.Syntax+import GHC.JS.Transform++import GHC.StgToJS.Types++import GHC.Unit.Module+import GHC.Stg.Syntax++import GHC.Types.SrcLoc+import GHC.Types.Id+import GHC.Types.Unique.FM+import GHC.Types.ForeignCall++import qualified Control.Monad.Trans.State.Strict as State+import GHC.Data.FastString+import GHC.Data.FastMutInt++import qualified Data.Map as M+import qualified Data.Set as S+import qualified Data.List as L++runG :: StgToJSConfig -> Module -> UniqFM Id CgStgExpr -> G a -> IO a+runG config m unfloat action = State.evalStateT action =<< initState config m unfloat++initState :: StgToJSConfig -> Module -> UniqFM Id CgStgExpr -> IO GenState+initState config m unfloat = do+ id_gen <- newFastMutInt 1+ pure $ GenState+ { gsSettings = config+ , gsModule = m+ , gsId = id_gen+ , gsIdents = emptyIdCache+ , gsUnfloated = unfloat+ , gsGroup = defaultGenGroupState+ , gsGlobal = []+ }+++modifyGroup :: (GenGroupState -> GenGroupState) -> G ()+modifyGroup f = State.modify mod_state+ where+ mod_state s = s { gsGroup = f (gsGroup s) }++-- | emit a global (for the current module) toplevel statement+emitGlobal :: JStat -> G ()+emitGlobal stat = State.modify (\s -> s { gsGlobal = stat : gsGlobal s })++-- | add a dependency on a particular symbol to the current group+addDependency :: OtherSymb -> G ()+addDependency symbol = modifyGroup mod_group+ where+ mod_group g = g { ggsExtraDeps = S.insert symbol (ggsExtraDeps g) }++-- | emit a top-level statement for the current binding group+emitToplevel :: JStat -> G ()+emitToplevel s = modifyGroup mod_group+ where+ mod_group g = g { ggsToplevelStats = s : ggsToplevelStats g}++-- | emit static data for the binding group+emitStatic :: FastString -> StaticVal -> Maybe Ident -> G ()+emitStatic ident val cc = modifyGroup mod_group+ where+ mod_group g = g { ggsStatic = mod_static (ggsStatic g) }+ mod_static s = StaticInfo ident val cc : s++-- | add closure info in our binding group. all heap objects must have closure info+emitClosureInfo :: ClosureInfo -> G ()+emitClosureInfo ci = modifyGroup mod_group+ where+ mod_group g = g { ggsClosureInfo = ci : ggsClosureInfo g}++emitForeign :: Maybe RealSrcSpan+ -> FastString+ -> Safety+ -> CCallConv+ -> [FastString]+ -> FastString+ -> G ()+emitForeign mbSpan pat safety cconv arg_tys res_ty = modifyGroup mod_group+ where+ mod_group g = g { ggsForeignRefs = new_ref : ggsForeignRefs g }+ new_ref = ForeignJSRef spanTxt pat safety cconv arg_tys res_ty+ spanTxt = case mbSpan of+ -- TODO: Is there a better way to concatenate FastStrings?+ Just sp -> mkFastString $+ unpackFS (srcSpanFile sp) +++ " " +++ show (srcSpanStartLine sp, srcSpanStartCol sp) +++ "-" +++ show (srcSpanEndLine sp, srcSpanEndCol sp)+ Nothing -> "<unknown>"+++++++-- | start with a new binding group+resetGroup :: G ()+resetGroup = State.modify (\s -> s { gsGroup = defaultGenGroupState })++defaultGenGroupState :: GenGroupState+defaultGenGroupState = GenGroupState [] [] [] [] 0 S.empty emptyGlobalIdCache []++emptyGlobalIdCache :: GlobalIdCache+emptyGlobalIdCache = GlobalIdCache emptyUFM++emptyIdCache :: IdCache+emptyIdCache = IdCache M.empty++++assertRtsStat :: G JStat -> G JStat+assertRtsStat stat = do+ s <- State.gets gsSettings+ if csAssertRts s then stat else pure mempty++getSettings :: G StgToJSConfig+getSettings = State.gets gsSettings++getGlobalIdCache :: G GlobalIdCache+getGlobalIdCache = State.gets (ggsGlobalIdCache . gsGroup)++setGlobalIdCache :: GlobalIdCache -> G ()+setGlobalIdCache v = State.modify (\s -> s { gsGroup = (gsGroup s) { ggsGlobalIdCache = v}})+++data GlobalOcc = GlobalOcc+ { global_ident :: !Ident+ , global_id :: !Id+ , global_count :: !Word+ }++-- | Return number of occurrences of every global id used in the given JStat.+-- Sort by increasing occurrence count.+globalOccs :: JStat -> G [GlobalOcc]+globalOccs jst = do+ GlobalIdCache gidc <- getGlobalIdCache+ -- build a map form Ident Unique to (Ident, Id, Count)+ let+ cmp_cnt g1 g2 = compare (global_count g1) (global_count g2)+ inc g1 g2 = g1 { global_count = global_count g1 + global_count g2 }+ go gids = \case+ [] -> -- return global Ids used locally sorted by increased use+ L.sortBy cmp_cnt $ nonDetEltsUFM gids+ (i:is) ->+ -- check if the Id is global+ case lookupUFM gidc i of+ Nothing -> go gids is+ Just (_k,gid) ->+ -- add it to the list of already found global ids. Increasing+ -- count by 1+ let g = GlobalOcc i gid 1+ in go (addToUFM_C inc gids i g) is++ pure $ go emptyUFM (identsS jst)
+ compiler/GHC/StgToJS/Object.hs view
@@ -0,0 +1,622 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- only for DB.Binary instances on Module+{-# OPTIONS_GHC -fno-warn-orphans #-}++-----------------------------------------------------------------------------+-- |+-- Module : GHC.StgToJS.Object+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Sylvain Henry <sylvain.henry@iohk.io>+-- Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Josh Meredith <josh.meredith@iohk.io>+-- Stability : experimental+--+-- Serialization/deserialization of binary .o files for the JavaScript backend+-- The .o files contain dependency information and generated code.+-- All strings are mapped to a central string table, which helps reduce+-- file size and gives us efficient hash consing on read+--+-- Binary intermediate JavaScript object files:+-- serialized [Text] -> ([ClosureInfo], JStat) blocks+--+-- file layout:+-- - magic "GHCJSOBJ"+-- - compiler version tag+-- - module name+-- - offsets of string table+-- - dependencies+-- - offset of the index+-- - unit infos+-- - index+-- - string table+--+-----------------------------------------------------------------------------++module GHC.StgToJS.Object+ ( putObject+ , getObjectHeader+ , getObjectBody+ , getObject+ , readObject+ , getObjectUnits+ , readObjectUnits+ , readObjectDeps+ , isGlobalUnit+ , isJsObjectFile+ , Object(..)+ , IndexEntry(..)+ , Deps (..), BlockDeps (..), DepsLocation (..)+ , ExportedFun (..)+ )+where++import GHC.Prelude++import Control.Monad++import Data.Array+import Data.Int+import Data.IntSet (IntSet)+import qualified Data.IntSet as IS+import Data.List (sortOn)+import Data.Map (Map)+import qualified Data.Map as M+import Data.Word+import Data.Char+import Foreign.Storable+import Foreign.Marshal.Array+import System.IO++import GHC.Settings.Constants (hiVersion)++import GHC.JS.Syntax+import GHC.StgToJS.Types++import GHC.Unit.Module++import GHC.Data.FastString++import GHC.Types.Unique.Map+import GHC.Float (castDoubleToWord64, castWord64ToDouble)++import GHC.Utils.Binary hiding (SymbolTable)+import GHC.Utils.Outputable (ppr, Outputable, hcat, vcat, text, hsep)+import GHC.Utils.Monad (mapMaybeM)++-- | An object file+data Object = Object+ { objModuleName :: !ModuleName+ -- ^ name of the module+ , objHandle :: !BinHandle+ -- ^ BinHandle that can be used to read the ObjUnits+ , objPayloadOffset :: !(Bin ObjUnit)+ -- ^ Offset of the payload (units)+ , objDeps :: !Deps+ -- ^ Dependencies+ , objIndex :: !Index+ -- ^ The Index, serialed unit indices and their linkable units+ }++type BlockId = Int+type BlockIds = IntSet++-- | dependencies for a single module+data Deps = Deps+ { depsModule :: !Module+ -- ^ module+ , depsRequired :: !BlockIds+ -- ^ blocks that always need to be linked when this object is loaded (e.g.+ -- everything that contains initializer code or foreign exports)+ , depsHaskellExported :: !(Map ExportedFun BlockId)+ -- ^ exported Haskell functions -> block+ , depsBlocks :: !(Array BlockId BlockDeps)+ -- ^ info about each block+ }++instance Outputable Deps where+ ppr d = vcat+ [ hcat [ text "module: ", pprModule (depsModule d) ]+ , hcat [ text "exports: ", ppr (M.keys (depsHaskellExported d)) ]+ ]++-- | Where are the dependencies+data DepsLocation+ = ObjectFile FilePath -- ^ In an object file at path+ | ArchiveFile FilePath -- ^ In a Ar file at path+ | InMemory String Object -- ^ In memory++instance Outputable DepsLocation where+ ppr = \case+ ObjectFile fp -> hsep [text "ObjectFile", text fp]+ ArchiveFile fp -> hsep [text "ArchiveFile", text fp]+ InMemory s o -> hsep [text "InMemory", text s, ppr (objModuleName o)]++data BlockDeps = BlockDeps+ { blockBlockDeps :: [Int] -- ^ dependencies on blocks in this object+ , blockFunDeps :: [ExportedFun] -- ^ dependencies on exported symbols in other objects+ -- , blockForeignExported :: [ExpFun]+ -- , blockForeignImported :: [ForeignRef]+ }++{- | we use the convention that the first unit (0) is a module-global+ unit that's always included when something from the module+ is loaded. everything in a module implicitly depends on the+ global block. the global unit itself can't have dependencies+ -}+isGlobalUnit :: Int -> Bool+isGlobalUnit n = n == 0++-- | Exported Functions+data ExportedFun = ExportedFun+ { funModule :: !Module -- ^ The module containing the function+ , funSymbol :: !LexicalFastString -- ^ The function+ } deriving (Eq, Ord)++instance Outputable ExportedFun where+ ppr (ExportedFun m f) = vcat+ [ hcat [ text "module: ", pprModule m ]+ , hcat [ text "symbol: ", ppr f ]+ ]++-- | Write an ObjUnit, except for the top level symbols which are stored in the+-- index+putObjUnit :: BinHandle -> ObjUnit -> IO ()+putObjUnit bh (ObjUnit _syms b c d e f g) = do+ put_ bh b+ put_ bh c+ lazyPut bh d+ put_ bh e+ put_ bh f+ put_ bh g++-- | Read an ObjUnit and associate it to the given symbols (that must have been+-- read from the index)+getObjUnit :: [FastString] -> BinHandle -> IO ObjUnit+getObjUnit syms bh = do+ b <- get bh+ c <- get bh+ d <- lazyGet bh+ e <- get bh+ f <- get bh+ g <- get bh+ pure $ ObjUnit+ { oiSymbols = syms+ , oiClInfo = b+ , oiStatic = c+ , oiStat = d+ , oiRaw = e+ , oiFExports = f+ , oiFImports = g+ }+++-- | A tag that determines the kind of payload in the .o file. See+-- @StgToJS.Linker.Arhive.magic@ for another kind of magic+magic :: String+magic = "GHCJSOBJ"++-- | Serialized unit indexes and their exported symbols+-- (the first unit is module-global)+type Index = [IndexEntry]+data IndexEntry = IndexEntry+ { idxSymbols :: ![FastString] -- ^ Symbols exported by a unit+ , idxOffset :: !(Bin ObjUnit) -- ^ Offset of the unit in the object file+ }+++--------------------------------------------------------------------------------+-- Essential oeprations on Objects+--------------------------------------------------------------------------------++-- | Given a handle to a Binary payload, add the module, 'mod_name', its+-- dependencies, 'deps', and its linkable units to the payload.+putObject+ :: BinHandle+ -> ModuleName -- ^ module+ -> Deps -- ^ dependencies+ -> [ObjUnit] -- ^ linkable units and their symbols+ -> IO ()+putObject bh mod_name deps os = do+ forM_ magic (putByte bh . fromIntegral . ord)+ put_ bh (show hiVersion)++ -- we store the module name as a String because we don't want to have to+ -- decode the FastString table just to decode it when we're looking for an+ -- object in an archive.+ put_ bh (moduleNameString mod_name)++ (bh_fs, _bin_dict, put_dict) <- initFSTable bh++ forwardPut_ bh (const put_dict) $ do+ put_ bh_fs deps++ -- forward put the index+ forwardPut_ bh_fs (put_ bh_fs) $ do+ idx <- forM os $ \o -> do+ p <- tellBin bh_fs+ -- write units without their symbols+ putObjUnit bh_fs o+ -- return symbols and offset to store in the index+ pure (oiSymbols o,p)+ pure idx++-- | Test if the object file is a JS object+isJsObjectFile :: FilePath -> IO Bool+isJsObjectFile fp = do+ let !n = length magic+ withBinaryFile fp ReadMode $ \hdl -> do+ allocaArray n $ \ptr -> do+ n' <- hGetBuf hdl ptr n+ if (n' /= n)+ then pure False+ else checkMagic (peekElemOff ptr)++-- | Check magic+checkMagic :: (Int -> IO Word8) -> IO Bool+checkMagic get_byte = do+ let go_magic !i = \case+ [] -> pure True+ (e:es) -> get_byte i >>= \case+ c | fromIntegral (ord e) == c -> go_magic (i+1) es+ | otherwise -> pure False+ go_magic 0 magic++-- | Parse object magic+getCheckMagic :: BinHandle -> IO Bool+getCheckMagic bh = checkMagic (const (getByte bh))++-- | Parse object header+getObjectHeader :: BinHandle -> IO (Either String ModuleName)+getObjectHeader bh = do+ is_magic <- getCheckMagic bh+ case is_magic of+ False -> pure (Left "invalid magic header")+ True -> do+ is_correct_version <- ((== hiVersion) . read) <$> get bh+ case is_correct_version of+ False -> pure (Left "invalid header version")+ True -> do+ mod_name <- get bh+ pure (Right (mkModuleName (mod_name)))+++-- | Parse object body. Must be called after a sucessful getObjectHeader+getObjectBody :: BinHandle -> ModuleName -> IO Object+getObjectBody bh0 mod_name = do+ -- Read the string table+ dict <- forwardGet bh0 (getDictionary bh0)+ let bh = setUserData bh0 $ noUserData { ud_get_fs = getDictFastString dict }++ deps <- get bh+ idx <- forwardGet bh (get bh)+ payload_pos <- tellBin bh++ pure $ Object+ { objModuleName = mod_name+ , objHandle = bh+ , objPayloadOffset = payload_pos+ , objDeps = deps+ , objIndex = idx+ }++-- | Parse object+getObject :: BinHandle -> IO (Maybe Object)+getObject bh = do+ getObjectHeader bh >>= \case+ Left _err -> pure Nothing+ Right mod_name -> Just <$> getObjectBody bh mod_name++-- | Read object from file+--+-- The object is still in memory after this (see objHandle).+readObject :: FilePath -> IO (Maybe Object)+readObject file = do+ bh <- readBinMem file+ getObject bh++-- | Reads only the part necessary to get the dependencies+readObjectDeps :: FilePath -> IO (Maybe Deps)+readObjectDeps file = do+ bh <- readBinMem file+ getObject bh >>= \case+ Just obj -> pure $! Just $! objDeps obj+ Nothing -> pure Nothing++-- | Get units in the object file, using the given filtering function+getObjectUnits :: Object -> (Word -> IndexEntry -> Bool) -> IO [ObjUnit]+getObjectUnits obj pred = mapMaybeM read_entry (zip (objIndex obj) [0..])+ where+ bh = objHandle obj+ read_entry (e@(IndexEntry syms offset),i)+ | pred i e = do+ seekBin bh offset+ Just <$> getObjUnit syms bh+ | otherwise = pure Nothing++-- | Read units in the object file, using the given filtering function+readObjectUnits :: FilePath -> (Word -> IndexEntry -> Bool) -> IO [ObjUnit]+readObjectUnits file pred = do+ readObject file >>= \case+ Nothing -> pure []+ Just obj -> getObjectUnits obj pred+++--------------------------------------------------------------------------------+-- Helper functions+--------------------------------------------------------------------------------++putEnum :: Enum a => BinHandle -> a -> IO ()+putEnum bh x | n > 65535 = error ("putEnum: out of range: " ++ show n)+ | otherwise = put_ bh n+ where n = fromIntegral $ fromEnum x :: Word16++getEnum :: Enum a => BinHandle -> IO a+getEnum bh = toEnum . fromIntegral <$> (get bh :: IO Word16)++-- | Helper to convert Int to Int32+toI32 :: Int -> Int32+toI32 = fromIntegral++-- | Helper to convert Int32 to Int+fromI32 :: Int32 -> Int+fromI32 = fromIntegral+++--------------------------------------------------------------------------------+-- Binary Instances+--------------------------------------------------------------------------------++instance Binary IndexEntry where+ put_ bh (IndexEntry a b) = put_ bh a >> put_ bh b+ get bh = IndexEntry <$> get bh <*> get bh++instance Binary Deps where+ put_ bh (Deps m r e b) = do+ put_ bh m+ put_ bh (map toI32 $ IS.toList r)+ put_ bh (map (\(x,y) -> (x, toI32 y)) $ M.toList e)+ put_ bh (elems b)+ get bh = Deps <$> get bh+ <*> (IS.fromList . map fromI32 <$> get bh)+ <*> (M.fromList . map (\(x,y) -> (x, fromI32 y)) <$> get bh)+ <*> ((\xs -> listArray (0, length xs - 1) xs) <$> get bh)++instance Binary BlockDeps where+ put_ bh (BlockDeps bbd bfd) = put_ bh bbd >> put_ bh bfd+ get bh = BlockDeps <$> get bh <*> get bh++instance Binary ForeignJSRef where+ put_ bh (ForeignJSRef span pat safety cconv arg_tys res_ty) =+ put_ bh span >> put_ bh pat >> putEnum bh safety >> putEnum bh cconv >> put_ bh arg_tys >> put_ bh res_ty+ get bh = ForeignJSRef <$> get bh <*> get bh <*> getEnum bh <*> getEnum bh <*> get bh <*> get bh++instance Binary ExpFun where+ put_ bh (ExpFun isIO args res) = put_ bh isIO >> put_ bh args >> put_ bh res+ get bh = ExpFun <$> get bh <*> get bh <*> get bh++instance Binary JStat where+ put_ bh (DeclStat i e) = putByte bh 1 >> put_ bh i >> put_ bh e+ put_ bh (ReturnStat e) = putByte bh 2 >> put_ bh e+ put_ bh (IfStat e s1 s2) = putByte bh 3 >> put_ bh e >> put_ bh s1 >> put_ bh s2+ put_ bh (WhileStat b e s) = putByte bh 4 >> put_ bh b >> put_ bh e >> put_ bh s+ put_ bh (ForInStat b i e s) = putByte bh 5 >> put_ bh b >> put_ bh i >> put_ bh e >> put_ bh s+ put_ bh (SwitchStat e ss s) = putByte bh 6 >> put_ bh e >> put_ bh ss >> put_ bh s+ put_ bh (TryStat s1 i s2 s3) = putByte bh 7 >> put_ bh s1 >> put_ bh i >> put_ bh s2 >> put_ bh s3+ put_ bh (BlockStat xs) = putByte bh 8 >> put_ bh xs+ put_ bh (ApplStat e es) = putByte bh 9 >> put_ bh e >> put_ bh es+ put_ bh (UOpStat o e) = putByte bh 10 >> put_ bh o >> put_ bh e+ put_ bh (AssignStat e1 e2) = putByte bh 11 >> put_ bh e1 >> put_ bh e2+ put_ _ (UnsatBlock {}) = error "put_ bh JStat: UnsatBlock"+ put_ bh (LabelStat l s) = putByte bh 12 >> put_ bh l >> put_ bh s+ put_ bh (BreakStat ml) = putByte bh 13 >> put_ bh ml+ put_ bh (ContinueStat ml) = putByte bh 14 >> put_ bh ml+ get bh = getByte bh >>= \case+ 1 -> DeclStat <$> get bh <*> get bh+ 2 -> ReturnStat <$> get bh+ 3 -> IfStat <$> get bh <*> get bh <*> get bh+ 4 -> WhileStat <$> get bh <*> get bh <*> get bh+ 5 -> ForInStat <$> get bh <*> get bh <*> get bh <*> get bh+ 6 -> SwitchStat <$> get bh <*> get bh <*> get bh+ 7 -> TryStat <$> get bh <*> get bh <*> get bh <*> get bh+ 8 -> BlockStat <$> get bh+ 9 -> ApplStat <$> get bh <*> get bh+ 10 -> UOpStat <$> get bh <*> get bh+ 11 -> AssignStat <$> get bh <*> get bh+ 12 -> LabelStat <$> get bh <*> get bh+ 13 -> BreakStat <$> get bh+ 14 -> ContinueStat <$> get bh+ n -> error ("Binary get bh JStat: invalid tag: " ++ show n)++instance Binary JExpr where+ put_ bh (ValExpr v) = putByte bh 1 >> put_ bh v+ put_ bh (SelExpr e i) = putByte bh 2 >> put_ bh e >> put_ bh i+ put_ bh (IdxExpr e1 e2) = putByte bh 3 >> put_ bh e1 >> put_ bh e2+ put_ bh (InfixExpr o e1 e2) = putByte bh 4 >> put_ bh o >> put_ bh e1 >> put_ bh e2+ put_ bh (UOpExpr o e) = putByte bh 5 >> put_ bh o >> put_ bh e+ put_ bh (IfExpr e1 e2 e3) = putByte bh 6 >> put_ bh e1 >> put_ bh e2 >> put_ bh e3+ put_ bh (ApplExpr e es) = putByte bh 7 >> put_ bh e >> put_ bh es+ put_ _ (UnsatExpr {}) = error "put_ bh JExpr: UnsatExpr"+ get bh = getByte bh >>= \case+ 1 -> ValExpr <$> get bh+ 2 -> SelExpr <$> get bh <*> get bh+ 3 -> IdxExpr <$> get bh <*> get bh+ 4 -> InfixExpr <$> get bh <*> get bh <*> get bh+ 5 -> UOpExpr <$> get bh <*> get bh+ 6 -> IfExpr <$> get bh <*> get bh <*> get bh+ 7 -> ApplExpr <$> get bh <*> get bh+ n -> error ("Binary get bh JExpr: invalid tag: " ++ show n)++instance Binary JVal where+ put_ bh (JVar i) = putByte bh 1 >> put_ bh i+ put_ bh (JList es) = putByte bh 2 >> put_ bh es+ put_ bh (JDouble d) = putByte bh 3 >> put_ bh d+ put_ bh (JInt i) = putByte bh 4 >> put_ bh i+ put_ bh (JStr xs) = putByte bh 5 >> put_ bh xs+ put_ bh (JRegEx xs) = putByte bh 6 >> put_ bh xs+ put_ bh (JHash m) = putByte bh 7 >> put_ bh (sortOn (LexicalFastString . fst) $ nonDetEltsUniqMap m)+ put_ bh (JFunc is s) = putByte bh 8 >> put_ bh is >> put_ bh s+ put_ _ (UnsatVal {}) = error "put_ bh JVal: UnsatVal"+ get bh = getByte bh >>= \case+ 1 -> JVar <$> get bh+ 2 -> JList <$> get bh+ 3 -> JDouble <$> get bh+ 4 -> JInt <$> get bh+ 5 -> JStr <$> get bh+ 6 -> JRegEx <$> get bh+ 7 -> JHash . listToUniqMap <$> get bh+ 8 -> JFunc <$> get bh <*> get bh+ n -> error ("Binary get bh JVal: invalid tag: " ++ show n)++instance Binary Ident where+ put_ bh (TxtI xs) = put_ bh xs+ get bh = TxtI <$> get bh++-- we need to preserve NaN and infinities, unfortunately the Binary instance for Double does not do this+instance Binary SaneDouble where+ put_ bh (SaneDouble d)+ | isNaN d = putByte bh 1+ | isInfinite d && d > 0 = putByte bh 2+ | isInfinite d && d < 0 = putByte bh 3+ | isNegativeZero d = putByte bh 4+ | otherwise = putByte bh 5 >> put_ bh (castDoubleToWord64 d)+ get bh = getByte bh >>= \case+ 1 -> pure $ SaneDouble (0 / 0)+ 2 -> pure $ SaneDouble (1 / 0)+ 3 -> pure $ SaneDouble ((-1) / 0)+ 4 -> pure $ SaneDouble (-0)+ 5 -> SaneDouble . castWord64ToDouble <$> get bh+ n -> error ("Binary get bh SaneDouble: invalid tag: " ++ show n)++instance Binary ClosureInfo where+ put_ bh (ClosureInfo v regs name layo typ static) = do+ put_ bh v >> put_ bh regs >> put_ bh name >> put_ bh layo >> put_ bh typ >> put_ bh static+ get bh = ClosureInfo <$> get bh <*> get bh <*> get bh <*> get bh <*> get bh <*> get bh++instance Binary JSFFIType where+ put_ bh = putEnum bh+ get bh = getEnum bh++instance Binary VarType where+ put_ bh = putEnum bh+ get bh = getEnum bh++instance Binary CIRegs where+ put_ bh CIRegsUnknown = putByte bh 1+ put_ bh (CIRegs skip types) = putByte bh 2 >> put_ bh skip >> put_ bh types+ get bh = getByte bh >>= \case+ 1 -> pure CIRegsUnknown+ 2 -> CIRegs <$> get bh <*> get bh+ n -> error ("Binary get bh CIRegs: invalid tag: " ++ show n)++instance Binary JOp where+ put_ bh = putEnum bh+ get bh = getEnum bh++instance Binary JUOp where+ put_ bh = putEnum bh+ get bh = getEnum bh++-- 16 bit sizes should be enough...+instance Binary CILayout where+ put_ bh CILayoutVariable = putByte bh 1+ put_ bh (CILayoutUnknown size) = putByte bh 2 >> put_ bh size+ put_ bh (CILayoutFixed size types) = putByte bh 3 >> put_ bh size >> put_ bh types+ get bh = getByte bh >>= \case+ 1 -> pure CILayoutVariable+ 2 -> CILayoutUnknown <$> get bh+ 3 -> CILayoutFixed <$> get bh <*> get bh+ n -> error ("Binary get bh CILayout: invalid tag: " ++ show n)++instance Binary CIStatic where+ put_ bh (CIStaticRefs refs) = putByte bh 1 >> put_ bh refs+ get bh = getByte bh >>= \case+ 1 -> CIStaticRefs <$> get bh+ n -> error ("Binary get bh CIStatic: invalid tag: " ++ show n)++instance Binary CIType where+ put_ bh (CIFun arity regs) = putByte bh 1 >> put_ bh arity >> put_ bh regs+ put_ bh CIThunk = putByte bh 2+ put_ bh (CICon conTag) = putByte bh 3 >> put_ bh conTag+ put_ bh CIPap = putByte bh 4+ put_ bh CIBlackhole = putByte bh 5+ put_ bh CIStackFrame = putByte bh 6+ get bh = getByte bh >>= \case+ 1 -> CIFun <$> get bh <*> get bh+ 2 -> pure CIThunk+ 3 -> CICon <$> get bh+ 4 -> pure CIPap+ 5 -> pure CIBlackhole+ 6 -> pure CIStackFrame+ n -> error ("Binary get bh CIType: invalid tag: " ++ show n)++instance Binary ExportedFun where+ put_ bh (ExportedFun modu symb) = put_ bh modu >> put_ bh symb+ get bh = ExportedFun <$> get bh <*> get bh++instance Binary StaticInfo where+ put_ bh (StaticInfo ident val cc) = put_ bh ident >> put_ bh val >> put_ bh cc+ get bh = StaticInfo <$> get bh <*> get bh <*> get bh++instance Binary StaticVal where+ put_ bh (StaticFun f args) = putByte bh 1 >> put_ bh f >> put_ bh args+ put_ bh (StaticThunk t) = putByte bh 2 >> put_ bh t+ put_ bh (StaticUnboxed u) = putByte bh 3 >> put_ bh u+ put_ bh (StaticData dc args) = putByte bh 4 >> put_ bh dc >> put_ bh args+ put_ bh (StaticList xs t) = putByte bh 5 >> put_ bh xs >> put_ bh t+ get bh = getByte bh >>= \case+ 1 -> StaticFun <$> get bh <*> get bh+ 2 -> StaticThunk <$> get bh+ 3 -> StaticUnboxed <$> get bh+ 4 -> StaticData <$> get bh <*> get bh+ 5 -> StaticList <$> get bh <*> get bh+ n -> error ("Binary get bh StaticVal: invalid tag " ++ show n)++instance Binary StaticUnboxed where+ put_ bh (StaticUnboxedBool b) = putByte bh 1 >> put_ bh b+ put_ bh (StaticUnboxedInt i) = putByte bh 2 >> put_ bh i+ put_ bh (StaticUnboxedDouble d) = putByte bh 3 >> put_ bh d+ put_ bh (StaticUnboxedString str) = putByte bh 4 >> put_ bh str+ put_ bh (StaticUnboxedStringOffset str) = putByte bh 5 >> put_ bh str+ get bh = getByte bh >>= \case+ 1 -> StaticUnboxedBool <$> get bh+ 2 -> StaticUnboxedInt <$> get bh+ 3 -> StaticUnboxedDouble <$> get bh+ 4 -> StaticUnboxedString <$> get bh+ 5 -> StaticUnboxedStringOffset <$> get bh+ n -> error ("Binary get bh StaticUnboxed: invalid tag " ++ show n)++instance Binary StaticArg where+ put_ bh (StaticObjArg i) = putByte bh 1 >> put_ bh i+ put_ bh (StaticLitArg p) = putByte bh 2 >> put_ bh p+ put_ bh (StaticConArg c args) = putByte bh 3 >> put_ bh c >> put_ bh args+ get bh = getByte bh >>= \case+ 1 -> StaticObjArg <$> get bh+ 2 -> StaticLitArg <$> get bh+ 3 -> StaticConArg <$> get bh <*> get bh+ n -> error ("Binary get bh StaticArg: invalid tag " ++ show n)++instance Binary StaticLit where+ put_ bh (BoolLit b) = putByte bh 1 >> put_ bh b+ put_ bh (IntLit i) = putByte bh 2 >> put_ bh i+ put_ bh NullLit = putByte bh 3+ put_ bh (DoubleLit d) = putByte bh 4 >> put_ bh d+ put_ bh (StringLit t) = putByte bh 5 >> put_ bh t+ put_ bh (BinLit b) = putByte bh 6 >> put_ bh b+ put_ bh (LabelLit b t) = putByte bh 7 >> put_ bh b >> put_ bh t+ get bh = getByte bh >>= \case+ 1 -> BoolLit <$> get bh+ 2 -> IntLit <$> get bh+ 3 -> pure NullLit+ 4 -> DoubleLit <$> get bh+ 5 -> StringLit <$> get bh+ 6 -> BinLit <$> get bh+ 7 -> LabelLit <$> get bh <*> get bh+ n -> error ("Binary get bh StaticLit: invalid tag " ++ show n)
+ compiler/GHC/StgToJS/Prim.hs view
@@ -0,0 +1,1502 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE MultiWayIf #-}++-- disable this warning because of all the lambdas matching on primops'+-- arguments.+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}++module GHC.StgToJS.Prim+ ( genPrim+ )+where++import GHC.Prelude++import GHC.JS.Syntax hiding (JUOp (..))+import GHC.JS.Make++import GHC.StgToJS.Heap+import GHC.StgToJS.Types+import GHC.StgToJS.Profiling+import GHC.StgToJS.Regs++import GHC.Core.Type++import GHC.Builtin.PrimOps+import GHC.Tc.Utils.TcType (isBoolTy)+import GHC.Utils.Encoding (zEncodeString)++import GHC.Data.FastString+import GHC.Utils.Outputable (renderWithContext, defaultSDocContext, ppr)+import Data.Maybe+++genPrim :: Bool -- ^ Profiling (cost-centres) enabled+ -> Bool -- ^ Array bounds-checking enabled+ -> Type+ -> PrimOp -- ^ the primitive operation+ -> [JExpr] -- ^ where to store the result+ -> [JExpr] -- ^ arguments+ -> PrimRes+genPrim prof bound ty op = case op of+ CharGtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>. y)+ CharGeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>=. y)+ CharEqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)+ CharNeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .!==. y)+ CharLtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<. y)+ CharLeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<=. y)+ OrdOp -> \[r] [x] -> PrimInline $ r |= x++ Int8ToWord8Op -> \[r] [x] -> PrimInline $ r |= mask8 x+ Word8ToInt8Op -> \[r] [x] -> PrimInline $ r |= signExtend8 x+ Int16ToWord16Op -> \[r] [x] -> PrimInline $ r |= mask16 x+ Word16ToInt16Op -> \[r] [x] -> PrimInline $ r |= signExtend16 x+ Int32ToWord32Op -> \[r] [x] -> PrimInline $ r |= x .>>>. zero_+ Word32ToInt32Op -> \[r] [x] -> PrimInline $ r |= toI32 x++------------------------------ Int ----------------------------------------------++ IntAddOp -> \[r] [x,y] -> PrimInline $ r |= toI32 (Add x y)+ IntSubOp -> \[r] [x,y] -> PrimInline $ r |= toI32 (Sub x y)+ IntMulOp -> \[r] [x,y] -> PrimInline $ r |= app "h$mulInt32" [x, y]+ IntMul2Op -> \[c,hr,lr] [x,y] -> PrimInline $ appT [c,hr,lr] "h$hs_timesInt2" [x, y]+ IntMulMayOfloOp -> \[r] [x,y] -> PrimInline $ jVar \tmp -> mconcat+ [ tmp |= Mul x y+ , r |= if01 (tmp .===. toI32 tmp)+ ]+ IntQuotOp -> \[r] [x,y] -> PrimInline $ r |= toI32 (Div x y)+ IntRemOp -> \[r] [x,y] -> PrimInline $ r |= Mod x y+ IntQuotRemOp -> \[q,r] [x,y] -> PrimInline $ mconcat+ [ q |= toI32 (Div x y)+ , r |= x `Sub` (Mul y q)+ ]+ IntAndOp -> \[r] [x,y] -> PrimInline $ r |= BAnd x y+ IntOrOp -> \[r] [x,y] -> PrimInline $ r |= BOr x y+ IntXorOp -> \[r] [x,y] -> PrimInline $ r |= BXor x y+ IntNotOp -> \[r] [x] -> PrimInline $ r |= BNot x++ IntNegOp -> \[r] [x] -> PrimInline $ r |= toI32 (Negate x)+-- add with carry: overflow == 0 iff no overflow+ IntAddCOp -> \[r,overf] [x,y] ->+ PrimInline $ jVar \rt -> mconcat+ [ rt |= Add x y+ , r |= toI32 rt+ , overf |= if10 (r .!=. rt)+ ]+ IntSubCOp -> \[r,overf] [x,y] ->+ PrimInline $ jVar \rt -> mconcat+ [ rt |= Sub x y+ , r |= toI32 rt+ , overf |= if10 (r .!=. rt)+ ]+ IntGtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>. y)+ IntGeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>=. y)+ IntEqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)+ IntNeOp -> \[r] [x,y] -> PrimInline $ r |= if10(x .!==. y)+ IntLtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<. y)+ IntLeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<=. y)+ ChrOp -> \[r] [x] -> PrimInline $ r |= x+ IntToWordOp -> \[r] [x] -> PrimInline $ r |= x .>>>. 0+ IntToFloatOp -> \[r] [x] -> PrimInline $ r |= x+ IntToDoubleOp -> \[r] [x] -> PrimInline $ r |= x+ IntSllOp -> \[r] [x,y] -> PrimInline $ r |= x .<<. y+ IntSraOp -> \[r] [x,y] -> PrimInline $ r |= x .>>. y+ IntSrlOp -> \[r] [x,y] -> PrimInline $ r |= toI32 (x .>>>. y)++------------------------------ Int8 ---------------------------------------------++ Int8ToIntOp -> \[r] [x] -> PrimInline $ r |= x+ IntToInt8Op -> \[r] [x] -> PrimInline $ r |= signExtend8 x+ Int8NegOp -> \[r] [x] -> PrimInline $ r |= signExtend8 (Negate x)+ Int8AddOp -> \[r] [x,y] -> PrimInline $ r |= signExtend8 (Add x y)+ Int8SubOp -> \[r] [x,y] -> PrimInline $ r |= signExtend8 (Sub x y)+ Int8MulOp -> \[r] [x,y] -> PrimInline $ r |= signExtend8 (Mul x y)+ Int8QuotOp -> \[r] [x,y] -> PrimInline $ r |= signExtend8 (quotShortInt 8 x y)+ Int8RemOp -> \[r] [x,y] -> PrimInline $ r |= signExtend8 (remShortInt 8 x y)+ Int8QuotRemOp -> \[r1,r2] [x,y] -> PrimInline $ mconcat+ [ r1 |= signExtend8 (quotShortInt 8 x y)+ , r2 |= signExtend8 (remShortInt 8 x y)+ ]+ Int8EqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)+ Int8GeOp -> \[r] [x,y] -> PrimInline $ r |= if10 ((x .<<. (Int 24)) .>=. (y .<<. (Int 24)))+ Int8GtOp -> \[r] [x,y] -> PrimInline $ r |= if10 ((x .<<. (Int 24)) .>. (y .<<. (Int 24)))+ Int8LeOp -> \[r] [x,y] -> PrimInline $ r |= if10 ((x .<<. (Int 24)) .<=. (y .<<. (Int 24)))+ Int8LtOp -> \[r] [x,y] -> PrimInline $ r |= if10 ((x .<<. (Int 24)) .<. (y .<<. (Int 24)))+ Int8NeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .!==. y)++ Int8SraOp -> \[r] [x,i] -> PrimInline $ r |= x .>>. i+ Int8SrlOp -> \[r] [x,i] -> PrimInline $ r |= signExtend8 (mask8 x .>>>. i)+ Int8SllOp -> \[r] [x,i] -> PrimInline $ r |= signExtend8 (mask8 (x .<<. i))++------------------------------ Word8 --------------------------------------------++ Word8ToWordOp -> \[r] [x] -> PrimInline $ r |= mask8 x+ WordToWord8Op -> \[r] [x] -> PrimInline $ r |= mask8 x++ Word8AddOp -> \[r] [x,y] -> PrimInline $ r |= mask8 (Add x y)+ Word8SubOp -> \[r] [x,y] -> PrimInline $ r |= mask8 (Sub x y)+ Word8MulOp -> \[r] [x,y] -> PrimInline $ r |= mask8 (Mul x y)+ Word8QuotOp -> \[r] [x,y] -> PrimInline $ r |= mask8 (Div x y)+ Word8RemOp -> \[r] [x,y] -> PrimInline $ r |= Mod x y+ Word8QuotRemOp -> \[r1,r2] [x,y] -> PrimInline $ mconcat+ [ r1 |= toI32 (Div x y)+ , r2 |= Mod x y+ ]+ Word8EqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)+ Word8GeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>=. y)+ Word8GtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>. y)+ Word8LeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<=. y)+ Word8LtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<. y)+ Word8NeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .!==. y)++ Word8AndOp -> \[r] [x,y] -> PrimInline $ r |= BAnd x y+ Word8OrOp -> \[r] [x,y] -> PrimInline $ r |= BOr x y+ Word8XorOp -> \[r] [x,y] -> PrimInline $ r |= BXor x y+ Word8NotOp -> \[r] [x] -> PrimInline $ r |= BXor x (Int 0xff)++ Word8SllOp -> \[r] [x,i] -> PrimInline $ r |= mask8 (x .<<. i)+ Word8SrlOp -> \[r] [x,i] -> PrimInline $ r |= x .>>>. i++------------------------------ Int16 -------------------------------------------++ Int16ToIntOp -> \[r] [x] -> PrimInline $ r |= x+ IntToInt16Op -> \[r] [x] -> PrimInline $ r |= signExtend16 x++ Int16NegOp -> \[r] [x] -> PrimInline $ r |= signExtend16 (Negate x)+ Int16AddOp -> \[r] [x,y] -> PrimInline $ r |= signExtend16 (Add x y)+ Int16SubOp -> \[r] [x,y] -> PrimInline $ r |= signExtend16 (Sub x y)+ Int16MulOp -> \[r] [x,y] -> PrimInline $ r |= signExtend16 (Mul x y)+ Int16QuotOp -> \[r] [x,y] -> PrimInline $ r |= signExtend16 (quotShortInt 16 x y)+ Int16RemOp -> \[r] [x,y] -> PrimInline $ r |= signExtend16 (remShortInt 16 x y)+ Int16QuotRemOp -> \[r1,r2] [x,y] -> PrimInline $ mconcat+ [ r1 |= signExtend16 (quotShortInt 16 x y)+ , r2 |= signExtend16 (remShortInt 16 x y)+ ]+ Int16EqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)+ Int16GeOp -> \[r] [x,y] -> PrimInline $ r |= if10 ((x .<<. (Int 16)) .>=. (y .<<. (Int 16)))+ Int16GtOp -> \[r] [x,y] -> PrimInline $ r |= if10 ((x .<<. (Int 16)) .>. (y .<<. (Int 16)))+ Int16LeOp -> \[r] [x,y] -> PrimInline $ r |= if10 ((x .<<. (Int 16)) .<=. (y .<<. (Int 16)))+ Int16LtOp -> \[r] [x,y] -> PrimInline $ r |= if10 ((x .<<. (Int 16)) .<. (y .<<. (Int 16)))+ Int16NeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .!==. y)++ Int16SraOp -> \[r] [x,i] -> PrimInline $ r |= x .>>. i+ Int16SrlOp -> \[r] [x,i] -> PrimInline $ r |= signExtend16 (mask16 x .>>>. i)+ Int16SllOp -> \[r] [x,i] -> PrimInline $ r |= signExtend16 (x .<<. i)++------------------------------ Word16 ------------------------------------------++ Word16ToWordOp -> \[r] [x] -> PrimInline $ r |= x+ WordToWord16Op -> \[r] [x] -> PrimInline $ r |= mask16 x++ Word16AddOp -> \[r] [x,y] -> PrimInline $ r |= mask16 (Add x y)+ Word16SubOp -> \[r] [x,y] -> PrimInline $ r |= mask16 (Sub x y)+ Word16MulOp -> \[r] [x,y] -> PrimInline $ r |= mask16 (Mul x y)+ Word16QuotOp -> \[r] [x,y] -> PrimInline $ r |= mask16 (Div x y)+ Word16RemOp -> \[r] [x,y] -> PrimInline $ r |= Mod x y+ Word16QuotRemOp -> \[r1,r2] [x,y] -> PrimInline $ mconcat+ [ r1 |= toI32 (Div x y)+ , r2 |= Mod x y+ ]+ Word16EqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)+ Word16GeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>=. y)+ Word16GtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>. y)+ Word16LeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<=. y)+ Word16LtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<. y)+ Word16NeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .!==. y)++ Word16AndOp -> \[r] [x,y] -> PrimInline $ r |= BAnd x y+ Word16OrOp -> \[r] [x,y] -> PrimInline $ r |= BOr x y+ Word16XorOp -> \[r] [x,y] -> PrimInline $ r |= BXor x y+ Word16NotOp -> \[r] [x] -> PrimInline $ r |= BXor x (Int 0xffff)++ Word16SllOp -> \[r] [x,i] -> PrimInline $ r |= mask16 (x .<<. i)+ Word16SrlOp -> \[r] [x,i] -> PrimInline $ r |= x .>>>. i++------------------------------ Int32 --------------------------------------------++ Int32ToIntOp -> \[r] [x] -> PrimInline $ r |= x+ IntToInt32Op -> \[r] [x] -> PrimInline $ r |= x++ Int32NegOp -> \rs xs -> genPrim prof bound ty IntNegOp rs xs+ Int32AddOp -> \rs xs -> genPrim prof bound ty IntAddOp rs xs+ Int32SubOp -> \rs xs -> genPrim prof bound ty IntSubOp rs xs+ Int32MulOp -> \rs xs -> genPrim prof bound ty IntMulOp rs xs+ Int32QuotOp -> \rs xs -> genPrim prof bound ty IntQuotOp rs xs+ Int32RemOp -> \rs xs -> genPrim prof bound ty IntRemOp rs xs+ Int32QuotRemOp -> \rs xs -> genPrim prof bound ty IntQuotRemOp rs xs++ Int32EqOp -> \rs xs -> genPrim prof bound ty IntEqOp rs xs+ Int32GeOp -> \rs xs -> genPrim prof bound ty IntGeOp rs xs+ Int32GtOp -> \rs xs -> genPrim prof bound ty IntGtOp rs xs+ Int32LeOp -> \rs xs -> genPrim prof bound ty IntLeOp rs xs+ Int32LtOp -> \rs xs -> genPrim prof bound ty IntLtOp rs xs+ Int32NeOp -> \rs xs -> genPrim prof bound ty IntNeOp rs xs++ Int32SraOp -> \rs xs -> genPrim prof bound ty IntSraOp rs xs+ Int32SrlOp -> \rs xs -> genPrim prof bound ty IntSrlOp rs xs+ Int32SllOp -> \rs xs -> genPrim prof bound ty IntSllOp rs xs++------------------------------ Word32 -------------------------------------------++ Word32ToWordOp -> \[r] [x] -> PrimInline $ r |= x+ WordToWord32Op -> \[r] [x] -> PrimInline $ r |= x++ Word32AddOp -> \rs xs -> genPrim prof bound ty WordAddOp rs xs+ Word32SubOp -> \rs xs -> genPrim prof bound ty WordSubOp rs xs+ Word32MulOp -> \rs xs -> genPrim prof bound ty WordMulOp rs xs+ Word32QuotOp -> \rs xs -> genPrim prof bound ty WordQuotOp rs xs+ Word32RemOp -> \rs xs -> genPrim prof bound ty WordRemOp rs xs+ Word32QuotRemOp -> \rs xs -> genPrim prof bound ty WordQuotRemOp rs xs++ Word32EqOp -> \rs xs -> genPrim prof bound ty WordEqOp rs xs+ Word32GeOp -> \rs xs -> genPrim prof bound ty WordGeOp rs xs+ Word32GtOp -> \rs xs -> genPrim prof bound ty WordGtOp rs xs+ Word32LeOp -> \rs xs -> genPrim prof bound ty WordLeOp rs xs+ Word32LtOp -> \rs xs -> genPrim prof bound ty WordLtOp rs xs+ Word32NeOp -> \rs xs -> genPrim prof bound ty WordNeOp rs xs++ Word32AndOp -> \rs xs -> genPrim prof bound ty WordAndOp rs xs+ Word32OrOp -> \rs xs -> genPrim prof bound ty WordOrOp rs xs+ Word32XorOp -> \rs xs -> genPrim prof bound ty WordXorOp rs xs+ Word32NotOp -> \rs xs -> genPrim prof bound ty WordNotOp rs xs++ Word32SllOp -> \rs xs -> genPrim prof bound ty WordSllOp rs xs+ Word32SrlOp -> \rs xs -> genPrim prof bound ty WordSrlOp rs xs++------------------------------ Int64 --------------------------------------------++ Int64ToIntOp -> \[r] [_h,l] -> PrimInline $ r |= toI32 l++ Int64NegOp -> \[r_h,r_l] [h,l] ->+ PrimInline $ mconcat+ [ r_l |= toU32 (BNot l + 1)+ , r_h |= toI32 (BNot h + Not r_l)+ ]++ Int64AddOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_plusInt64" [h0,l0,h1,l1]+ Int64SubOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_minusInt64" [h0,l0,h1,l1]+ Int64MulOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_timesInt64" [h0,l0,h1,l1]+ Int64QuotOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_quotInt64" [h0,l0,h1,l1]+ Int64RemOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_remInt64" [h0,l0,h1,l1]++ Int64SllOp -> \[hr,lr] [h,l,n] -> PrimInline $ appT [hr,lr] "h$hs_uncheckedShiftLLInt64" [h,l,n]+ Int64SraOp -> \[hr,lr] [h,l,n] -> PrimInline $ appT [hr,lr] "h$hs_uncheckedShiftRAInt64" [h,l,n]+ Int64SrlOp -> \[hr,lr] [h,l,n] -> PrimInline $ appT [hr,lr] "h$hs_uncheckedShiftRLInt64" [h,l,n]++ Int64ToWord64Op -> \[r1,r2] [x1,x2] ->+ PrimInline $ mconcat+ [ r1 |= toU32 x1+ , r2 |= x2+ ]+ IntToInt64Op -> \[r1,r2] [x] ->+ PrimInline $ mconcat+ [ r1 |= if_ (x .<. 0) (-1) 0 -- sign-extension+ , r2 |= toU32 x+ ]++ Int64EqOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LAnd (l0 .===. l1) (h0 .===. h1))+ Int64NeOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (l0 .!==. l1) (h0 .!==. h1))+ Int64GeOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (h0 .>. h1) (LAnd (h0 .===. h1) (l0 .>=. l1)))+ Int64GtOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (h0 .>. h1) (LAnd (h0 .===. h1) (l0 .>. l1)))+ Int64LeOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (h0 .<. h1) (LAnd (h0 .===. h1) (l0 .<=. l1)))+ Int64LtOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (h0 .<. h1) (LAnd (h0 .===. h1) (l0 .<. l1)))++------------------------------ Word64 -------------------------------------------++ Word64ToWordOp -> \[r] [_x1,x2] -> PrimInline $ r |= x2++ WordToWord64Op -> \[rh,rl] [x] ->+ PrimInline $ mconcat+ [ rh |= 0+ , rl |= x+ ]++ Word64ToInt64Op -> \[r1,r2] [x1,x2] ->+ PrimInline $ mconcat+ [ r1 |= toI32 x1+ , r2 |= x2+ ]++ Word64EqOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LAnd (l0 .===. l1) (h0 .===. h1))+ Word64NeOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (l0 .!==. l1) (h0 .!==. h1))+ Word64GeOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (h0 .>. h1) (LAnd (h0 .===. h1) (l0 .>=. l1)))+ Word64GtOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (h0 .>. h1) (LAnd (h0 .===. h1) (l0 .>. l1)))+ Word64LeOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (h0 .<. h1) (LAnd (h0 .===. h1) (l0 .<=. l1)))+ Word64LtOp -> \[r] [h0,l0,h1,l1] -> PrimInline $ r |= if10 (LOr (h0 .<. h1) (LAnd (h0 .===. h1) (l0 .<. l1)))++ Word64SllOp -> \[hr,lr] [h,l,n] -> PrimInline $ appT [hr,lr] "h$hs_uncheckedShiftLWord64" [h,l,n]+ Word64SrlOp -> \[hr,lr] [h,l,n] -> PrimInline $ appT [hr,lr] "h$hs_uncheckedShiftRWord64" [h,l,n]++ Word64OrOp -> \[hr,hl] [h0, l0, h1, l1] ->+ PrimInline $ mconcat+ [ hr |= toU32 (BOr h0 h1)+ , hl |= toU32 (BOr l0 l1)+ ]++ Word64AndOp -> \[hr,hl] [h0, l0, h1, l1] ->+ PrimInline $ mconcat+ [ hr |= toU32 (BAnd h0 h1)+ , hl |= toU32 (BAnd l0 l1)+ ]++ Word64XorOp -> \[hr,hl] [h0, l0, h1, l1] ->+ PrimInline $ mconcat+ [ hr |= toU32 (BXor h0 h1)+ , hl |= toU32 (BXor l0 l1)+ ]++ Word64NotOp -> \[hr,hl] [h, l] ->+ PrimInline $ mconcat+ [ hr |= toU32 (BNot h)+ , hl |= toU32 (BNot l)+ ]++ Word64AddOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_plusWord64" [h0,l0,h1,l1]+ Word64SubOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_minusWord64" [h0,l0,h1,l1]+ Word64MulOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_timesWord64" [h0,l0,h1,l1]+ Word64QuotOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_quotWord64" [h0,l0,h1,l1]+ Word64RemOp -> \[hr,lr] [h0,l0,h1,l1] -> PrimInline $ appT [hr,lr] "h$hs_remWord64" [h0,l0,h1,l1]++------------------------------ Word ---------------------------------------------++ WordAddOp -> \[r] [x,y] -> PrimInline $ r |= (x `Add` y) .>>>. zero_+ WordAddCOp -> \[r,c] [x,y] -> PrimInline $+ jVar \t -> mconcat+ [ t |= x `Add` y+ , r |= toU32 t+ , c |= if10 (t .!==. r)+ ]+ WordSubCOp -> \[r,c] [x,y] ->+ PrimInline $ mconcat+ [ r |= toU32 (Sub x y)+ , c |= if10 (y .>. x)+ ]+ WordAdd2Op -> \[h,l] [x,y] -> PrimInline $ appT [h,l] "h$wordAdd2" [x,y]+ WordSubOp -> \ [r] [x,y] -> PrimInline $ r |= toU32 (Sub x y)+ WordMulOp -> \ [r] [x,y] -> PrimInline $ r |= app "h$mulWord32" [x, y]+ WordMul2Op -> \[h,l] [x,y] -> PrimInline $ appT [h,l] "h$mul2Word32" [x,y]+ WordQuotOp -> \ [q] [x,y] -> PrimInline $ q |= app "h$quotWord32" [x,y]+ WordRemOp -> \ [r] [x,y] -> PrimInline $ r |= app "h$remWord32" [x,y]+ WordQuotRemOp -> \[q,r] [x,y] -> PrimInline $ appT [q,r] "h$quotRemWord32" [x,y]+ WordQuotRem2Op -> \[q,r] [xh,xl,y] -> PrimInline $ appT [q,r] "h$quotRem2Word32" [xh,xl,y]+ WordAndOp -> \[r] [x,y] -> PrimInline $ r |= toU32 (BAnd x y)+ WordOrOp -> \[r] [x,y] -> PrimInline $ r |= toU32 (BOr x y)+ WordXorOp -> \[r] [x,y] -> PrimInline $ r |= toU32 (BXor x y)+ WordNotOp -> \[r] [x] -> PrimInline $ r |= toU32 (BNot x)+ WordSllOp -> \[r] [x,y] -> PrimInline $ r |= toU32 (x .<<. y)+ WordSrlOp -> \[r] [x,y] -> PrimInline $ r |= x .>>>. y+ WordToIntOp -> \[r] [x] -> PrimInline $ r |= toI32 x+ WordGtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>. y)+ WordGeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>=. y)+ WordEqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)+ WordNeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .!==. y)+ WordLtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<. y)+ WordLeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<=. y)+ WordToDoubleOp -> \[r] [x] -> PrimInline $ r |= x+ WordToFloatOp -> \[r] [x] -> PrimInline $ r |= math_fround [x]+ PopCnt8Op -> \[r] [x] -> PrimInline $ r |= var "h$popCntTab" .! (mask8 x)+ PopCnt16Op -> \[r] [x] -> PrimInline $ r |= Add (var "h$popCntTab" .! (mask8 x))+ (var "h$popCntTab" .! (mask8 (x .>>>. Int 8)))++ PopCnt32Op -> \[r] [x] -> PrimInline $ r |= app "h$popCnt32" [x]+ PopCnt64Op -> \[r] [x1,x2] -> PrimInline $ r |= app "h$popCnt64" [x1,x2]+ PopCntOp -> \[r] [x] -> genPrim prof bound ty PopCnt32Op [r] [x]+ Pdep8Op -> \[r] [s,m] -> PrimInline $ r |= app "h$pdep8" [s,m]+ Pdep16Op -> \[r] [s,m] -> PrimInline $ r |= app "h$pdep16" [s,m]+ Pdep32Op -> \[r] [s,m] -> PrimInline $ r |= app "h$pdep32" [s,m]+ Pdep64Op -> \[ra,rb] [sa,sb,ma,mb] -> PrimInline $ appT [ra,rb] "h$pdep64" [sa,sb,ma,mb]+ PdepOp -> \rs xs -> genPrim prof bound ty Pdep32Op rs xs+ Pext8Op -> \[r] [s,m] -> PrimInline $ r |= app "h$pext8" [s,m]+ Pext16Op -> \[r] [s,m] -> PrimInline $ r |= app "h$pext16" [s,m]+ Pext32Op -> \[r] [s,m] -> PrimInline $ r |= app "h$pext32" [s,m]+ Pext64Op -> \[ra,rb] [sa,sb,ma,mb] -> PrimInline $ appT [ra,rb] "h$pext64" [sa,sb,ma,mb]+ PextOp -> \rs xs -> genPrim prof bound ty Pext32Op rs xs++ ClzOp -> \[r] [x] -> PrimInline $ r |= app "h$clz32" [x]+ Clz8Op -> \[r] [x] -> PrimInline $ r |= app "h$clz8" [x]+ Clz16Op -> \[r] [x] -> PrimInline $ r |= app "h$clz16" [x]+ Clz32Op -> \[r] [x] -> PrimInline $ r |= app "h$clz32" [x]+ Clz64Op -> \[r] [x1,x2] -> PrimInline $ r |= app "h$clz64" [x1,x2]+ CtzOp -> \[r] [x] -> PrimInline $ r |= app "h$ctz32" [x]+ Ctz8Op -> \[r] [x] -> PrimInline $ r |= app "h$ctz8" [x]+ Ctz16Op -> \[r] [x] -> PrimInline $ r |= app "h$ctz16" [x]+ Ctz32Op -> \[r] [x] -> PrimInline $ r |= app "h$ctz32" [x]+ Ctz64Op -> \[r] [x1,x2] -> PrimInline $ r |= app "h$ctz64" [x1,x2]++ BSwap16Op -> \[r] [x] -> PrimInline $+ r |= BOr ((mask8 x) .<<. (Int 8))+ (mask8 (x .>>>. (Int 8)))+ BSwap32Op -> \[r] [x] -> PrimInline $+ r |= toU32 ((x .<<. (Int 24))+ `BOr` ((BAnd x (Int 0xFF00)) .<<. (Int 8))+ `BOr` ((BAnd x (Int 0xFF0000)) .>>. (Int 8))+ `BOr` (x .>>>. (Int 24)))+ BSwap64Op -> \[r1,r2] [x,y] -> PrimInline $ appT [r1,r2] "h$bswap64" [x,y]+ BSwapOp -> \[r] [x] -> genPrim prof bound ty BSwap32Op [r] [x]++ BRevOp -> \[r] [w] -> genPrim prof bound ty BRev32Op [r] [w]+ BRev8Op -> \[r] [w] -> PrimInline $ r |= (app "h$reverseWord" [w] .>>>. 24)+ BRev16Op -> \[r] [w] -> PrimInline $ r |= (app "h$reverseWord" [w] .>>>. 16)+ BRev32Op -> \[r] [w] -> PrimInline $ r |= app "h$reverseWord" [w]+ BRev64Op -> \[rh,rl] [h,l] -> PrimInline $ mconcat [ rl |= app "h$reverseWord" [h]+ , rh |= app "h$reverseWord" [l]+ ]++------------------------------ Narrow -------------------------------------------++ Narrow8IntOp -> \[r] [x] -> PrimInline $ r |= signExtend8 x+ Narrow16IntOp -> \[r] [x] -> PrimInline $ r |= signExtend16 x+ Narrow32IntOp -> \[r] [x] -> PrimInline $ r |= toI32 x+ Narrow8WordOp -> \[r] [x] -> PrimInline $ r |= mask8 x+ Narrow16WordOp -> \[r] [x] -> PrimInline $ r |= mask16 x+ Narrow32WordOp -> \[r] [x] -> PrimInline $ r |= toU32 x++------------------------------ Double -------------------------------------------++ DoubleGtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>. y)+ DoubleGeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>=. y)+ DoubleEqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)+ DoubleNeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .!==. y)+ DoubleLtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<. y)+ DoubleLeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<=. y)+ DoubleAddOp -> \[r] [x,y] -> PrimInline $ r |= Add x y+ DoubleSubOp -> \[r] [x,y] -> PrimInline $ r |= Sub x y+ DoubleMulOp -> \[r] [x,y] -> PrimInline $ r |= Mul x y+ DoubleDivOp -> \[r] [x,y] -> PrimInline $ r |= Div x y+ DoubleNegOp -> \[r] [x] -> PrimInline $ r |= Negate x+ DoubleFabsOp -> \[r] [x] -> PrimInline $ r |= math_abs [x]+ DoubleToIntOp -> \[r] [x] -> PrimInline $ r |= toI32 x+ DoubleToFloatOp -> \[r] [x] -> PrimInline $ r |= math_fround [x]+ DoubleExpOp -> \[r] [x] -> PrimInline $ r |= math_exp [x]+ DoubleExpM1Op -> \[r] [x] -> PrimInline $ r |= math_expm1 [x]+ DoubleLogOp -> \[r] [x] -> PrimInline $ r |= math_log [x]+ DoubleLog1POp -> \[r] [x] -> PrimInline $ r |= math_log1p [x]+ DoubleSqrtOp -> \[r] [x] -> PrimInline $ r |= math_sqrt [x]+ DoubleSinOp -> \[r] [x] -> PrimInline $ r |= math_sin [x]+ DoubleCosOp -> \[r] [x] -> PrimInline $ r |= math_cos [x]+ DoubleTanOp -> \[r] [x] -> PrimInline $ r |= math_tan [x]+ DoubleAsinOp -> \[r] [x] -> PrimInline $ r |= math_asin [x]+ DoubleAcosOp -> \[r] [x] -> PrimInline $ r |= math_acos [x]+ DoubleAtanOp -> \[r] [x] -> PrimInline $ r |= math_atan [x]+ DoubleSinhOp -> \[r] [x] -> PrimInline $ r |= math_sinh [x]+ DoubleCoshOp -> \[r] [x] -> PrimInline $ r |= math_cosh [x]+ DoubleTanhOp -> \[r] [x] -> PrimInline $ r |= math_tanh [x]+ DoubleAsinhOp -> \[r] [x] -> PrimInline $ r |= math_asinh [x]+ DoubleAcoshOp -> \[r] [x] -> PrimInline $ r |= math_acosh [x]+ DoubleAtanhOp -> \[r] [x] -> PrimInline $ r |= math_atanh [x]+ DoublePowerOp -> \[r] [x,y] -> PrimInline $ r |= math_pow [x,y]+ DoubleDecode_2IntOp -> \[s,h,l,e] [x] -> PrimInline $ appT [s,h,l,e] "h$decodeDouble2Int" [x]+ DoubleDecode_Int64Op -> \[s1,s2,e] [d] -> PrimInline $ appT [e,s1,s2] "h$decodeDoubleInt64" [d]++------------------------------ Float --------------------------------------------++ FloatGtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>. y)+ FloatGeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .>=. y)+ FloatEqOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .===. y)+ FloatNeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .!==. y)+ FloatLtOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<. y)+ FloatLeOp -> \[r] [x,y] -> PrimInline $ r |= if10 (x .<=. y)+ FloatAddOp -> \[r] [x,y] -> PrimInline $ r |= math_fround [Add x y]+ FloatSubOp -> \[r] [x,y] -> PrimInline $ r |= math_fround [Sub x y]+ FloatMulOp -> \[r] [x,y] -> PrimInline $ r |= math_fround [Mul x y]+ FloatDivOp -> \[r] [x,y] -> PrimInline $ r |= math_fround [Div x y]+ FloatNegOp -> \[r] [x] -> PrimInline $ r |= Negate x+ FloatFabsOp -> \[r] [x] -> PrimInline $ r |= math_abs [x]+ FloatToIntOp -> \[r] [x] -> PrimInline $ r |= toI32 x+ FloatExpOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_exp [x]]+ FloatExpM1Op -> \[r] [x] -> PrimInline $ r |= math_fround [math_expm1 [x]]+ FloatLogOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_log [x]]+ FloatLog1POp -> \[r] [x] -> PrimInline $ r |= math_fround [math_log1p [x]]+ FloatSqrtOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_sqrt [x]]+ FloatSinOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_sin [x]]+ FloatCosOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_cos [x]]+ FloatTanOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_tan [x]]+ FloatAsinOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_asin [x]]+ FloatAcosOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_acos [x]]+ FloatAtanOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_atan [x]]+ FloatSinhOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_sinh [x]]+ FloatCoshOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_cosh [x]]+ FloatTanhOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_tanh [x]]+ FloatAsinhOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_asinh [x]]+ FloatAcoshOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_acosh [x]]+ FloatAtanhOp -> \[r] [x] -> PrimInline $ r |= math_fround [math_atanh [x]]+ FloatPowerOp -> \[r] [x,y] -> PrimInline $ r |= math_fround [math_pow [x,y]]+ FloatToDoubleOp -> \[r] [x] -> PrimInline $ r |= x+ FloatDecode_IntOp -> \[s,e] [x] -> PrimInline $ appT [s,e] "h$decodeFloatInt" [x]++------------------------------ Arrays -------------------------------------------++ NewArrayOp -> \[r] [l,e] -> PrimInline (newArray r l e)+ ReadArrayOp -> \[r] [a,i] -> PrimInline $ boundsChecked bound a i (r |= a .! i)+ WriteArrayOp -> \[] [a,i,v] -> PrimInline $ boundsChecked bound a i (a .! i |= v)+ SizeofArrayOp -> \[r] [a] -> PrimInline $ r |= a .^ "length"+ SizeofMutableArrayOp -> \[r] [a] -> PrimInline $ r |= a .^ "length"+ IndexArrayOp -> \[r] [a,i] -> PrimInline $ boundsChecked bound a i (r |= a .! i)+ UnsafeFreezeArrayOp -> \[r] [a] -> PrimInline $ r |= a+ UnsafeThawArrayOp -> \[r] [a] -> PrimInline $ r |= a+ CopyArrayOp -> \[] [a,o1,ma,o2,n] ->+ PrimInline $ loopBlockS (Int 0) (.<. n) \i ->+ [ ma .! (Add i o2) |= a .! (Add i o1)+ , preIncrS i+ ]+ CopyMutableArrayOp -> \[] [a1,o1,a2,o2,n] -> PrimInline $ appS "h$copyMutableArray" [a1,o1,a2,o2,n]+ CloneArrayOp -> \[r] [a,start,n] -> PrimInline $ r |= app "h$sliceArray" [a,start,n]+ CloneMutableArrayOp -> \[r] [a,start,n] -> genPrim prof bound ty CloneArrayOp [r] [a,start,n]+ FreezeArrayOp -> \[r] [a,start,n] -> PrimInline $ r |= app "h$sliceArray" [a,start,n]+ ThawArrayOp -> \[r] [a,start,n] -> PrimInline $ r |= app "h$sliceArray" [a,start,n]+ CasArrayOp -> \[s,o] [a,i,old,new] -> PrimInline $+ jVar \x -> mconcat+ [ x |= a .! i+ , ifBlockS (x .===. old)+ [ o |= new+ , a .! i |= new+ , s |= zero_+ ]+ [ s |= one_+ , o |= x+ ]+ ]++------------------------------ Small Arrays -------------------------------------++ NewSmallArrayOp -> \[a] [n,e] -> PrimInline $ a |= app "h$newArray" [n,e]+ ReadSmallArrayOp -> \[r] [a,i] -> PrimInline $ boundsChecked bound a i (r |= a .! i)+ WriteSmallArrayOp -> \[] [a,i,e] -> PrimInline $ boundsChecked bound a i (a .! i |= e)+ SizeofSmallArrayOp -> \[r] [a] -> PrimInline $ r |= a .^ "length"+ SizeofSmallMutableArrayOp -> \[r] [a] -> PrimInline $ r |= a .^ "length"+ IndexSmallArrayOp -> \[r] [a,i] -> PrimInline $ boundsChecked bound a i (r |= a .! i)+ UnsafeFreezeSmallArrayOp -> \[r] [a] -> PrimInline $ r |= a+ UnsafeThawSmallArrayOp -> \[r] [a] -> PrimInline $ r |= a+ CopySmallArrayOp -> \[] [s,si,d,di,n] -> PrimInline $+ loopBlockS (Sub n one_) (.>=. zero_) \i ->+ [ d .! (Add di i) |= s .! (Add si i)+ , postDecrS i+ ]+ CopySmallMutableArrayOp -> \[] [s,si,d,di,n] -> PrimInline $ appS "h$copyMutableArray" [s,si,d,di,n]+ CloneSmallArrayOp -> \[r] [a,o,n] -> PrimInline $ cloneArray r a (Just o) n+ CloneSmallMutableArrayOp -> \[r] [a,o,n] -> PrimInline $ cloneArray r a (Just o) n+ FreezeSmallArrayOp -> \[r] [a,o,n] -> PrimInline $ cloneArray r a (Just o) n+ ThawSmallArrayOp -> \[r] [a,o,n] -> PrimInline $ cloneArray r a (Just o) n+ CasSmallArrayOp -> \[s,o] [a,i,old,new] -> PrimInline $ jVar \x -> mconcat+ [ x |= a .! i+ , ifBlockS (x .===. old)+ [ o |= new+ , a .! i |= new+ , s |= zero_+ ]+ [ s |= one_+ , o |= x+ ]+ ]++------------------------------- Byte Arrays -------------------------------------++ NewByteArrayOp_Char -> \[r] [l] -> PrimInline (newByteArray r l)+ NewPinnedByteArrayOp_Char -> \[r] [l] -> PrimInline (newByteArray r l)+ NewAlignedPinnedByteArrayOp_Char -> \[r] [l,_align] -> PrimInline (newByteArray r l)+ MutableByteArrayIsPinnedOp -> \[r] [_] -> PrimInline $ r |= one_+ ByteArrayIsPinnedOp -> \[r] [_] -> PrimInline $ r |= one_+ ByteArrayContents_Char -> \[a,o] [b] -> PrimInline $ mconcat [a |= b, o |= zero_]+ MutableByteArrayContents_Char -> \[a,o] [b] -> PrimInline $ mconcat [a |= b, o |= zero_]+ ShrinkMutableByteArrayOp_Char -> \[] [a,n] -> PrimInline $ appS "h$shrinkMutableByteArray" [a,n]+ ResizeMutableByteArrayOp_Char -> \[r] [a,n] -> PrimInline $ r |= app "h$resizeMutableByteArray" [a,n]+ UnsafeFreezeByteArrayOp -> \[a] [b] -> PrimInline $ a |= b+ SizeofByteArrayOp -> \[r] [a] -> PrimInline $ r |= a .^ "len"+ SizeofMutableByteArrayOp -> \[r] [a] -> PrimInline $ r |= a .^ "len"+ GetSizeofMutableByteArrayOp -> \[r] [a] -> PrimInline $ r |= a .^ "len"+ IndexByteArrayOp_Char -> \[r] [a,i] -> PrimInline . boundsChecked bound a i $ r |= read_u8 a i+ IndexByteArrayOp_WideChar -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_i32 a i+ IndexByteArrayOp_Int -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_i32 a i+ IndexByteArrayOp_Word -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_u32 a i+ IndexByteArrayOp_Addr -> \[r1,r2] [a,i] ->+ PrimInline . boundsChecked bound a i $ jVar \t -> mconcat+ [ t |= a .^ "arr"+ , ifBlockS (t .&&. t .! (i .<<. two_))+ [ r1 |= t .! (i .<<. two_) .! zero_+ , r2 |= t .! (i .<<. two_) .! one_+ ]+ [ r1 |= null_+ , r2 |= zero_+ ]+ ]++ IndexByteArrayOp_Float -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_f32 a i+ IndexByteArrayOp_Double -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 7) $ r |= read_f64 a i+ IndexByteArrayOp_StablePtr -> \[r1,r2] [a,i] ->+ PrimInline . boundsChecked bound a (Add i 3) $ mconcat+ [ r1 |= var "h$stablePtrBuf"+ , r2 |= read_i32 a i+ ]+ IndexByteArrayOp_Int8 -> \[r] [a,i] -> PrimInline . boundsChecked bound a i $ r |= read_i8 a i+ IndexByteArrayOp_Int16 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 1) $ r |= read_i16 a i+ IndexByteArrayOp_Int32 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_i32 a i+ IndexByteArrayOp_Int64 -> \[h,l] [a,i] -> PrimInline . boundsChecked bound a (Add i 7) $ mconcat+ [ h |= read_i32 a (Add (i .<<. one_) one_)+ , l |= read_u32 a (i .<<. one_)+ ]+ IndexByteArrayOp_Word8 -> \[r] [a,i] -> PrimInline . boundsChecked bound a i $ r |= read_u8 a i+ IndexByteArrayOp_Word16 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 1) $ r |= read_u16 a i+ IndexByteArrayOp_Word32 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_u32 a i+ IndexByteArrayOp_Word64 -> \[h,l] [a,i] -> PrimInline . boundsChecked bound a (Add i 7) $ mconcat+ [ h |= read_u32 a (Add (i .<<. one_) one_)+ , l |= read_u32 a (i .<<. one_)+ ]+ ReadByteArrayOp_Char -> \[r] [a,i] -> PrimInline . boundsChecked bound a i $ r |= read_u8 a i+ ReadByteArrayOp_WideChar -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_i32 a i+ ReadByteArrayOp_Int -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_i32 a i+ ReadByteArrayOp_Word -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_u32 a i+ ReadByteArrayOp_Addr -> \[r1,r2] [a,i] ->+ PrimInline $ jVar \x -> mconcat+ [ x |= i .<<. two_+ , ifS (a .^ "arr" .&&. a .^ "arr" .! x)+ (mconcat [ r1 |= a .^ "arr" .! x .! zero_+ , r2 |= a .^ "arr" .! x .! one_+ ])+ (mconcat [r1 |= null_, r2 |= one_])+ ]+ ReadByteArrayOp_Float -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_f32 a i+ ReadByteArrayOp_Double -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 7) $ r |= read_f64 a i+ ReadByteArrayOp_StablePtr -> \[r1,r2] [a,i] ->+ PrimInline . boundsChecked bound a (Add i 3) $ mconcat+ [ r1 |= var "h$stablePtrBuf"+ , r2 |= read_i32 a i+ ]+ ReadByteArrayOp_Int8 -> \[r] [a,i] -> PrimInline . boundsChecked bound a i $ r |= read_i8 a i+ ReadByteArrayOp_Int16 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 1) $ r |= read_i16 a i+ ReadByteArrayOp_Int32 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_i32 a i+ ReadByteArrayOp_Int64 -> \[h,l] [a,i] ->+ PrimInline . boundsChecked bound a (Add i 7) $ mconcat+ [ h |= read_i32 a (Add (i .<<. one_) one_)+ , l |= read_u32 a (i .<<. one_)+ ]+ ReadByteArrayOp_Word8 -> \[r] [a,i] -> PrimInline . boundsChecked bound a i $ r |= read_u8 a i+ ReadByteArrayOp_Word16 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 1) $ r |= read_u16 a i+ ReadByteArrayOp_Word32 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_u32 a i+ ReadByteArrayOp_Word64 -> \[h,l] [a,i] ->+ PrimInline . boundsChecked bound a (Add i 7) $ mconcat+ [ h |= read_u32 a (Add (i .<<. one_) one_)+ , l |= read_u32 a (i .<<. one_)+ ]+ WriteByteArrayOp_Char -> \[] [a,i,e] -> PrimInline . boundsChecked bound a i $ write_u8 a i e+ WriteByteArrayOp_WideChar -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_i32 a i e+ WriteByteArrayOp_Int -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_i32 a i e+ WriteByteArrayOp_Word -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_u32 a i e+ WriteByteArrayOp_Addr -> \[] [a,i,e1,e2] ->+ PrimInline $ mconcat+ [ ifS (Not (a .^ "arr")) (a .^ "arr" |= ValExpr (JList [])) mempty+ , a .^ "arr" .! (i .<<. two_) |= ValExpr (JList [e1, e2])+ ]+ WriteByteArrayOp_Float -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_f32 a i e+ WriteByteArrayOp_Double -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 7) $ write_f64 a i e+ WriteByteArrayOp_StablePtr -> \[] [a,i,_e1,e2] -> PrimInline . boundsChecked bound a (Add i 3) $ write_i32 a i e2++ WriteByteArrayOp_Int8 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a i $ write_i8 a i e+ WriteByteArrayOp_Int16 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 1) $ write_i16 a i e+ WriteByteArrayOp_Int32 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_i32 a i e+ WriteByteArrayOp_Int64 -> \[] [a,i,e1,e2] ->+ PrimInline . boundsChecked bound a (Add i 7) $ mconcat+ [ write_i32 a (Add (i .<<. one_) one_) e1+ , write_u32 a (i .<<. one_) e2+ ]+ WriteByteArrayOp_Word8 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a i $ write_u8 a i e+ WriteByteArrayOp_Word16 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 1) $ write_u16 a i e+ WriteByteArrayOp_Word32 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_u32 a i e+ WriteByteArrayOp_Word64 -> \[] [a,i,h,l] ->+ PrimInline . boundsChecked bound a (Add i 7) $ mconcat+ [ write_u32 a (Add (i .<<. one_) one_) h+ , write_u32 a (i .<<. one_) l+ ]+ CompareByteArraysOp -> \[r] [a1,o1,a2,o2,n] ->+ PrimInline . boundsChecked bound a1 (Add o1 (Sub n 1))+ . boundsChecked bound a2 (Add o2 (Sub n 1))+ $ r |= app "h$compareByteArrays" [a1,o1,a2,o2,n]++ CopyByteArrayOp -> \[] [a1,o1,a2,o2,n] ->+ PrimInline . boundsChecked bound a1 (Add o1 (Sub n 1))+ . boundsChecked bound a2 (Add o2 (Sub n 1))+ $ appS "h$copyMutableByteArray" [a1,o1,a2,o2,n]+ CopyMutableByteArrayOp -> \[] xs@[_a1,_o1,_a2,_o2,_n] -> genPrim prof bound ty CopyByteArrayOp [] xs+ CopyByteArrayToAddrOp -> \[] xs@[_a1,_o1,_a2,_o2,_n] -> genPrim prof bound ty CopyByteArrayOp [] xs+ CopyMutableByteArrayToAddrOp -> \[] xs@[_a1,_o1,_a2,_o2,_n] -> genPrim prof bound ty CopyByteArrayOp [] xs+ CopyAddrToByteArrayOp -> \[] xs@[_ba,_bo,_aa,_ao,_n] -> genPrim prof bound ty CopyByteArrayOp [] xs++ SetByteArrayOp -> \[] [a,o,n,v] ->+ PrimInline . boundsChecked bound a (Add o (Sub n 1)) $ loopBlockS zero_ (.<. n) \i ->+ [ write_u8 a (Add o i) v+ , postIncrS i+ ]++ AtomicReadByteArrayOp_Int -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_i32 a i+ AtomicWriteByteArrayOp_Int -> \[] [a,i,v] -> PrimInline . boundsChecked bound a (Add i 3) $ write_i32 a i v+ FetchAddByteArrayOp_Int -> \[r] [a,i,v] -> PrimInline . boundsChecked bound a (Add i 3) $ fetchOpByteArray Add r a i v+ FetchSubByteArrayOp_Int -> \[r] [a,i,v] -> PrimInline . boundsChecked bound a (Add i 3) $ fetchOpByteArray Sub r a i v+ FetchAndByteArrayOp_Int -> \[r] [a,i,v] -> PrimInline . boundsChecked bound a (Add i 3) $ fetchOpByteArray BAnd r a i v+ FetchOrByteArrayOp_Int -> \[r] [a,i,v] -> PrimInline . boundsChecked bound a (Add i 3) $ fetchOpByteArray BOr r a i v+ FetchNandByteArrayOp_Int -> \[r] [a,i,v] -> PrimInline . boundsChecked bound a (Add i 3) $ fetchOpByteArray (\x y -> BNot (BAnd x y)) r a i v+ FetchXorByteArrayOp_Int -> \[r] [a,i,v] -> PrimInline . boundsChecked bound a (Add i 3) $ fetchOpByteArray BXor r a i v++------------------------------- Addr# ------------------------------------------++ AddrAddOp -> \[a',o'] [a,o,i] -> PrimInline $ mconcat [a' |= a, o' |= Add o i]+ AddrSubOp -> \[i] [_a1,o1,_a2,o2] -> PrimInline $ i |= Sub o1 o2+ AddrRemOp -> \[r] [_a,o,i] -> PrimInline $ r |= Mod o i+ AddrToIntOp -> \[i] [_a,o] -> PrimInline $ i |= o -- only usable for comparisons within one range+ IntToAddrOp -> \[a,o] [i] -> PrimInline $ mconcat [a |= null_, o |= i]+ AddrGtOp -> \[r] [a1,o1,a2,o2] -> PrimInline $ r |= if10 (app "h$comparePointer" [a1,o1,a2,o2] .>. zero_)+ AddrGeOp -> \[r] [a1,o1,a2,o2] -> PrimInline $ r |= if10 (app "h$comparePointer" [a1,o1,a2,o2] .>=. zero_)+ AddrEqOp -> \[r] [a1,o1,a2,o2] -> PrimInline $ r |= if10 (app "h$comparePointer" [a1,o1,a2,o2] .===. zero_)+ AddrNeOp -> \[r] [a1,o1,a2,o2] -> PrimInline $ r |= if10 (app "h$comparePointer" [a1,o1,a2,o2] .!==. zero_)+ AddrLtOp -> \[r] [a1,o1,a2,o2] -> PrimInline $ r |= if10 (app "h$comparePointer" [a1,o1,a2,o2] .<. zero_)+ AddrLeOp -> \[r] [a1,o1,a2,o2] -> PrimInline $ r |= if10 (app "h$comparePointer" [a1,o1,a2,o2] .<=. zero_)++------------------------------- Addr Indexing: Unboxed Arrays -------------------++ IndexOffAddrOp_Char -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off8 o i) $ c |= read_boff_u8 a (off8 o i)+ IndexOffAddrOp_WideChar -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_i32 a (off32 o i)+ IndexOffAddrOp_Int -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_i32 a (off32 o i)+ IndexOffAddrOp_Word -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_u32 a (off32 o i)+ IndexOffAddrOp_Addr -> \[ca,co] [a,o,i] ->+ PrimInline . boundsChecked bound (a .^ "arr") (off32 o i)+ $ ifBlockS (a .^ "arr " .&&. a .^ "arr" .! (i .<<. two_))+ [ ca |= a .^ "arr" .! (off32 o i) .! zero_+ , co |= a .^ "arr" .! (off32 o i) .! one_+ ]+ [ ca |= null_+ , co |= zero_+ ]+ IndexOffAddrOp_Float -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_f32 a (off32 o i)+ IndexOffAddrOp_Double -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off64 o i) $ c |= read_boff_f64 a (off64 o i)+ IndexOffAddrOp_StablePtr -> \[c1,c2] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ mconcat+ [ c1 |= var "h$stablePtrBuf"+ , c2 |= read_boff_i32 a (off32 o i)+ ]+ IndexOffAddrOp_Int8 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off8 o i) $ c |= read_boff_i8 a (off8 o i)+ IndexOffAddrOp_Int16 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off16 o i) $ c |= read_boff_i16 a (off16 o i)+ IndexOffAddrOp_Int32 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_i32 a (off32 o i)+ IndexOffAddrOp_Int64 -> \[h,l] [a,o,i] ->+ PrimInline $ mconcat+ [ h |= read_boff_i32 a (Add (off64 o i) (Int 4))+ , l |= read_boff_u32 a (off64 o i)+ ]+ IndexOffAddrOp_Word8 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off8 o i) $ c |= read_boff_u8 a (off8 o i)+ IndexOffAddrOp_Word16 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off16 o i) $ c |= read_boff_u16 a (off16 o i)+ IndexOffAddrOp_Word32 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_u32 a (off32 o i)+ IndexOffAddrOp_Word64 -> \[h,l] [a,o,i] ->+ PrimInline $ mconcat+ [ h |= read_boff_u32 a (Add (off64 o i) (Int 4))+ , l |= read_boff_u32 a (off64 o i)+ ]+ ReadOffAddrOp_Char -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off8 o i) $ c |= read_boff_u8 a (off8 o i)+ ReadOffAddrOp_WideChar -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_i32 a (off32 o i)+ ReadOffAddrOp_Int -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_i32 a (off32 o i)+ ReadOffAddrOp_Word -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_u32 a (off32 o i)+ ReadOffAddrOp_Addr -> \[c1,c2] [a,o,i] ->+ PrimInline $ jVar \x -> mconcat+ [ x |= i .<<. two_+ , boundsChecked bound (a .^ "arr") (Add o x) $+ ifBlockS (a .^ "arr" .&&. a .^ "arr" .! (Add o x))+ [ c1 |= a .^ "arr" .! (Add o x) .! zero_+ , c2 |= a .^ "arr" .! (Add o x) .! one_+ ]+ [ c1 |= null_+ , c2 |= zero_+ ]+ ]+ ReadOffAddrOp_Float -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ c |= read_boff_f32 a (off32 o i)+ ReadOffAddrOp_Double -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off64 o i) $ c |= read_boff_f64 a (off64 o i)+ ReadOffAddrOp_StablePtr -> \[c1,c2] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ mconcat+ [ c1 |= var "h$stablePtrBuf"+ , c2 |= read_boff_u32 a (off32 o i)+ ]+ ReadOffAddrOp_Int8 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off8 o i) $ AssignStat c $ read_boff_i8 a (off8 o i)+ ReadOffAddrOp_Int16 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off16 o i) $ AssignStat c $ read_boff_i16 a (off16 o i)+ ReadOffAddrOp_Int32 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ AssignStat c $ read_boff_i32 a (off32 o i)+ ReadOffAddrOp_Int64 -> \[h,l] [a,o,i] ->+ PrimInline $ mconcat+ [ h |= read_i32 a (Add (off64 o i) (Int 4))+ , l |= read_u32 a (off64 o i)+ ]+ ReadOffAddrOp_Word8 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off8 o i) $ AssignStat c $ read_boff_u8 a (off8 o i)+ ReadOffAddrOp_Word16 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off16 o i) $ AssignStat c $ read_boff_u16 a (off16 o i)+ ReadOffAddrOp_Word32 -> \[c] [a,o,i] -> PrimInline . boundsChecked bound a (off32 o i) $ AssignStat c $ read_boff_u32 a (off32 o i)+ ReadOffAddrOp_Word64 -> \[c1,c2] [a,o,i] ->+ PrimInline $ mconcat+ [ c1 |= read_boff_u32 a (Add (off64 o i) (Int 4))+ , c2 |= read_boff_u32 a (off64 o i)+ ]+ WriteOffAddrOp_Char -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off8 o i) $ write_boff_u8 a (off8 o i) v+ WriteOffAddrOp_WideChar -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off32 o i) $ write_boff_i32 a (off32 o i) v+ WriteOffAddrOp_Int -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off32 o i) $ write_boff_i32 a (off32 o i) v+ WriteOffAddrOp_Word -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off32 o i) $ write_boff_u32 a (off32 o i) v+ WriteOffAddrOp_Addr -> \[] [a,o,i,va,vo] ->+ PrimInline $ mconcat+ [ ifS (Not (a .^ "arr")) (a .^ "arr" |= ValExpr (JList [])) mempty+ , boundsChecked bound (a .^ "arr") (off32 o i) $+ AssignStat (a .^ "arr" .! (off32 o i)) $ ValExpr (JList [va, vo])+ ]+ WriteOffAddrOp_Float -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off32 o i) $ write_boff_f32 a (off32 o i) v+ WriteOffAddrOp_Double -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off64 o i) $ write_boff_f64 a (off64 o i) v+ WriteOffAddrOp_StablePtr -> \[] [a,o,i,_v1,v2] -> PrimInline . boundsChecked bound a (off32 o i) $ write_boff_u32 a (off32 o i) v2+ WriteOffAddrOp_Int8 -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off8 o i) $ write_boff_i8 a (off8 o i) v+ WriteOffAddrOp_Int16 -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off16 o i) $ write_boff_i16 a (off16 o i) v+ WriteOffAddrOp_Int32 -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off32 o i) $ write_boff_i32 a (off32 o i) v+ WriteOffAddrOp_Int64 -> \[] [a,o,i,v1,v2] -> PrimInline . boundsChecked bound a (off64 o i) $ mconcat+ [ write_boff_i32 a (Add (off64 o i) (Int 4)) v1+ , write_boff_u32 a (off64 o i) v2+ ]+ WriteOffAddrOp_Word8 -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off8 o i) $ write_boff_u8 a (off8 o i) v+ WriteOffAddrOp_Word16 -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off16 o i) $ write_boff_u16 a (off16 o i) v+ WriteOffAddrOp_Word32 -> \[] [a,o,i,v] -> PrimInline . boundsChecked bound a (off32 o i) $ write_boff_u32 a (off32 o i) v+ WriteOffAddrOp_Word64 -> \[] [a,o,i,v1,v2] -> PrimInline . boundsChecked bound a (off64 o i) $ mconcat+ [ write_boff_u32 a (Add (off64 o i) (Int 4)) v1+ , write_boff_u32 a (off64 o i) v2+ ]+-- Mutable variables+ NewMutVarOp -> \[r] [x] -> PrimInline $ r |= New (app "h$MutVar" [x])+ ReadMutVarOp -> \[r] [m] -> PrimInline $ r |= m .^ "val"+ WriteMutVarOp -> \[] [m,x] -> PrimInline $ m .^ "val" |= x+ AtomicModifyMutVar2Op -> \[r1,r2] [m,f] -> PrimInline $ appT [r1,r2] "h$atomicModifyMutVar2" [m,f]+ AtomicModifyMutVar_Op -> \[r1,r2] [m,f] -> PrimInline $ appT [r1,r2] "h$atomicModifyMutVar" [m,f]++ CasMutVarOp -> \[status,r] [mv,o,n] -> PrimInline $ ifS (mv .^ "val" .===. o)+ (mconcat [status |= zero_, r |= n, mv .^ "val" |= n])+ (mconcat [status |= one_ , r |= mv .^ "val"])++------------------------------- Exceptions --------------------------------------++ CatchOp -> \[_r] [a,handler] -> PRPrimCall $ returnS (app "h$catch" [a, handler])++ -- fully ignore the result arity as it can use 1 or 2+ -- slots, depending on the return type.+ RaiseOp -> \_r [a] -> PRPrimCall $ returnS (app "h$throw" [a, false_])+ RaiseIOOp -> \_r [a] -> PRPrimCall $ returnS (app "h$throw" [a, false_])+ RaiseUnderflowOp -> \_r [] -> PRPrimCall $ returnS (app "h$throw" [var "h$baseZCGHCziExceptionziTypeziunderflowException", false_])+ RaiseOverflowOp -> \_r [] -> PRPrimCall $ returnS (app "h$throw" [var "h$baseZCGHCziExceptionziTypezioverflowException", false_])+ RaiseDivZeroOp -> \_r [] -> PRPrimCall $ returnS (app "h$throw" [var "h$baseZCGHCziExceptionziTypezidivZZeroException", false_])+ MaskAsyncExceptionsOp -> \_r [a] -> PRPrimCall $ returnS (app "h$maskAsync" [a])+ MaskUninterruptibleOp -> \_r [a] -> PRPrimCall $ returnS (app "h$maskUnintAsync" [a])+ UnmaskAsyncExceptionsOp -> \_r [a] -> PRPrimCall $ returnS (app "h$unmaskAsync" [a])++ MaskStatus -> \[r] [] -> PrimInline $ r |= app "h$maskStatus" []++------------------------------- STM-accessible Mutable Variables --------------++ AtomicallyOp -> \[_r] [a] -> PRPrimCall $ returnS (app "h$atomically" [a])+ RetryOp -> \_r [] -> PRPrimCall $ returnS (app "h$stmRetry" [])+ CatchRetryOp -> \[_r] [a,b] -> PRPrimCall $ returnS (app "h$stmCatchRetry" [a,b])+ CatchSTMOp -> \[_r] [a,h] -> PRPrimCall $ returnS (app "h$catchStm" [a,h])+ NewTVarOp -> \[tv] [v] -> PrimInline $ tv |= app "h$newTVar" [v]+ ReadTVarOp -> \[r] [tv] -> PrimInline $ r |= app "h$readTVar" [tv]+ ReadTVarIOOp -> \[r] [tv] -> PrimInline $ r |= app "h$readTVarIO" [tv]+ WriteTVarOp -> \[] [tv,v] -> PrimInline $ appS "h$writeTVar" [tv,v]++------------------------------- Synchronized Mutable Variables ------------------++ NewMVarOp -> \[r] [] -> PrimInline $ r |= New (app "h$MVar" [])+ TakeMVarOp -> \[_r] [m] -> PRPrimCall $ returnS (app "h$takeMVar" [m])+ TryTakeMVarOp -> \[r,v] [m] -> PrimInline $ appT [r,v] "h$tryTakeMVar" [m]+ PutMVarOp -> \[] [m,v] -> PRPrimCall $ returnS (app "h$putMVar" [m,v])+ TryPutMVarOp -> \[r] [m,v] -> PrimInline $ r |= app "h$tryPutMVar" [m,v]+ ReadMVarOp -> \[_r] [m] -> PRPrimCall $ returnS (app "h$readMVar" [m])+ TryReadMVarOp -> \[r,v] [m] -> PrimInline $ mconcat+ [ v |= m .^ "val"+ , r |= if01 (v .===. null_)+ ]+ IsEmptyMVarOp -> \[r] [m] -> PrimInline $ r |= if10 (m .^ "val" .===. null_)++------------------------------- Delay/Wait Ops ---------------------------------++ DelayOp -> \[] [t] -> PRPrimCall $ returnS (app "h$delayThread" [t])+ WaitReadOp -> \[] [fd] -> PRPrimCall $ returnS (app "h$waidRead" [fd])+ WaitWriteOp -> \[] [fd] -> PRPrimCall $ returnS (app "h$waitWrite" [fd])++------------------------------- Concurrency Primitives -------------------------++ ForkOp -> \[_tid] [x] -> PRPrimCall $ returnS (app "h$fork" [x, true_])+ ForkOnOp -> \[_tid] [_p,x] -> PRPrimCall $ returnS (app "h$fork" [x, true_]) -- ignore processor argument+ KillThreadOp -> \[] [tid,ex] -> PRPrimCall $ returnS (app "h$killThread" [tid,ex])+ YieldOp -> \[] [] -> PRPrimCall $ returnS (app "h$yield" [])+ MyThreadIdOp -> \[r] [] -> PrimInline $ r |= var "h$currentThread"+ IsCurrentThreadBoundOp -> \[r] [] -> PrimInline $ r |= one_+ NoDuplicateOp -> \[] [] -> PrimInline mempty -- don't need to do anything as long as we have eager blackholing+ ThreadStatusOp -> \[stat,cap,locked] [tid] -> PrimInline $ appT [stat, cap, locked] "h$threadStatus" [tid]+ ListThreadsOp -> \[r] [] -> PrimInline $ r |= var "h$threads"+ GetThreadLabelOp -> \[r1, r2] [t] -> PrimInline $ appT [r1, r2] "h$getThreadLabel" [t]+ LabelThreadOp -> \[] [t,l] -> PrimInline $ t .^ "label" |= l++------------------------------- Weak Pointers -----------------------------------++ MkWeakOp -> \[r] [o,b,c] -> PrimInline $ r |= app "h$makeWeak" [o,b,c]+ MkWeakNoFinalizerOp -> \[r] [o,b] -> PrimInline $ r |= app "h$makeWeakNoFinalizer" [o,b]+ AddCFinalizerToWeakOp -> \[r] [_a1,_a1o,_a2,_a2o,_i,_a3,_a3o,_w] -> PrimInline $ r |= one_+ DeRefWeakOp -> \[f,v] [w] -> PrimInline $ mconcat+ [ v |= w .^ "val"+ , f |= if01 (v .===. null_)+ ]+ FinalizeWeakOp -> \[fl,fin] [w] -> PrimInline $ appT [fin, fl] "h$finalizeWeak" [w]+ TouchOp -> \[] [_e] -> PrimInline mempty+ KeepAliveOp -> \[_r] [x, f] -> PRPrimCall $ ReturnStat (app "h$keepAlive" [x, f])+++------------------------------ Stable pointers and names ------------------------++ MakeStablePtrOp -> \[s1,s2] [a] -> PrimInline $ mconcat+ [ s1 |= var "h$stablePtrBuf"+ , s2 |= app "h$makeStablePtr" [a]+ ]+ DeRefStablePtrOp -> \[r] [_s1,s2] -> PrimInline $ r |= app "h$deRefStablePtr" [s2]+ EqStablePtrOp -> \[r] [_sa1,sa2,_sb1,sb2] -> PrimInline $ r |= if10 (sa2 .===. sb2)++ MakeStableNameOp -> \[r] [a] -> PrimInline $ r |= app "h$makeStableName" [a]+ StableNameToIntOp -> \[r] [s] -> PrimInline $ r |= app "h$stableNameInt" [s]++------------------------------ Compact normal form -----------------------------++ CompactNewOp -> \[c] [s] -> PrimInline $ c |= app "h$compactNew" [s]+ CompactResizeOp -> \[] [c,s] -> PrimInline $ appS "h$compactResize" [c,s]+ CompactContainsOp -> \[r] [c,v] -> PrimInline $ r |= app "h$compactContains" [c,v]+ CompactContainsAnyOp -> \[r] [v] -> PrimInline $ r |= app "h$compactContainsAny" [v]+ CompactGetFirstBlockOp -> \[ra,ro,s] [c] ->+ PrimInline $ appT [ra,ro,s] "h$compactGetFirstBlock" [c]+ CompactGetNextBlockOp -> \[ra,ro,s] [c,a,o] ->+ PrimInline $ appT [ra,ro,s] "h$compactGetNextBlock" [c,a,o]+ CompactAllocateBlockOp -> \[ra,ro] [size,sa,so] ->+ PrimInline $ appT [ra,ro] "h$compactAllocateBlock" [size,sa,so]+ CompactFixupPointersOp -> \[c,newroota, newrooto] [blocka,blocko,roota,rooto] ->+ PrimInline $ appT [c,newroota,newrooto] "h$compactFixupPointers" [blocka,blocko,roota,rooto]+ CompactAdd -> \[_r] [c,o] ->+ PRPrimCall $ returnS (app "h$compactAdd" [c,o])+ CompactAddWithSharing -> \[_r] [c,o] ->+ PRPrimCall $ returnS (app "h$compactAddWithSharing" [c,o])+ CompactSize -> \[s] [c] ->+ PrimInline $ s |= app "h$compactSize" [c]++------------------------------ Unsafe pointer equality --------------------------++ ReallyUnsafePtrEqualityOp -> \[r] [p1,p2] -> PrimInline $ r |= if10 (p1 .===. p2)++------------------------------ Parallelism --------------------------------------++ ParOp -> \[r] [_a] -> PrimInline $ r |= zero_+ SparkOp -> \[r] [a] -> PrimInline $ r |= a+ SeqOp -> \[_r] [e] -> PRPrimCall $ returnS (app "h$e" [e])+ NumSparks -> \[r] [] -> PrimInline $ r |= zero_++------------------------------ Tag to enum stuff --------------------------------++ DataToTagOp -> \[_r] [d] -> PRPrimCall $ mconcat+ [ stack .! PreInc sp |= var "h$dataToTag_e"+ , returnS (app "h$e" [d])+ ]+ TagToEnumOp -> \[r] [tag] -> if+ | isBoolTy ty -> PrimInline $ r |= IfExpr tag true_ false_+ | otherwise -> PrimInline $ r |= app "h$tagToEnum" [tag]++------------------------------ Bytecode operations ------------------------------++ AddrToAnyOp -> \[r] [d,_o] -> PrimInline $ r |= d++------------------------------ Profiling (CCS) ------------------------------++ GetCCSOfOp -> \[a, o] [obj] -> if+ | prof -> PrimInline $ mconcat+ [ a |= if_ (isObject obj)+ (app "h$buildCCSPtr" [obj .^ "cc"])+ null_+ , o |= zero_+ ]+ | otherwise -> PrimInline $ mconcat+ [ a |= null_+ , o |= zero_+ ]++ GetCurrentCCSOp -> \[a, o] [_dummy_arg] ->+ let ptr = if prof then app "h$buildCCSPtr" [jCurrentCCS]+ else null_+ in PrimInline $ mconcat+ [ a |= ptr+ , o |= zero_+ ]++ ClearCCSOp -> \[_r] [x] -> PRPrimCall $ ReturnStat (app "h$clearCCS" [x])++------------------------------ Eventlog -------------------++ TraceEventOp -> \[] [ed,eo] -> PrimInline $ appS "h$traceEvent" [ed,eo]+ TraceEventBinaryOp -> \[] [ed,eo,len] -> PrimInline $ appS "h$traceEventBinary" [ed,eo,len]+ TraceMarkerOp -> \[] [ed,eo] -> PrimInline $ appS "h$traceMarker" [ed,eo]++ IndexByteArrayOp_Word8AsChar -> \[r] [a,i] -> PrimInline . boundsChecked bound a i $ r |= read_boff_u8 a i+ IndexByteArrayOp_Word8AsWideChar -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_i32 a i+ IndexByteArrayOp_Word8AsAddr -> \[r1,r2] [a,i] ->+ PrimInline $ jVar \x -> mconcat+ [ x |= i .<<. two_+ , boundsChecked bound (a .^ "arr") x $+ ifS (a .^ "arr" .&&. a .^ "arr" .! x)+ (mconcat [ r1 |= a .^ "arr" .! x .! zero_+ , r2 |= a .^ "arr" .! x .! one_+ ])+ (mconcat [r1 |= null_, r2 |= one_])+ ]+ IndexByteArrayOp_Word8AsFloat -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_f32 a i+ IndexByteArrayOp_Word8AsDouble -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 7) $ r |= read_boff_f64 a i+ IndexByteArrayOp_Word8AsStablePtr -> \[r1,r2] [a,i] ->+ PrimInline $ mconcat+ [ r1 |= var "h$stablePtrBuf"+ , r2 |= read_boff_i32 a i+ ]+ IndexByteArrayOp_Word8AsInt16 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 1) $ r |= read_boff_i16 a i+ IndexByteArrayOp_Word8AsInt32 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_i32 a i+ IndexByteArrayOp_Word8AsInt64 -> \[h,l] [a,i] ->+ PrimInline $ mconcat+ [ h |= read_boff_i32 a (Add i (Int 4))+ , l |= read_boff_u32 a i+ ]+ IndexByteArrayOp_Word8AsInt -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_i32 a i+ IndexByteArrayOp_Word8AsWord16 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 1) $ r |= read_boff_u16 a i+ IndexByteArrayOp_Word8AsWord32 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_u32 a i+ IndexByteArrayOp_Word8AsWord64 -> \[h,l] [a,i] ->+ PrimInline . boundsChecked bound a (Add i 7) $ mconcat+ [ h |= read_boff_u32 a (Add i (Int 4))+ , l |= read_boff_u32 a i+ ]+ IndexByteArrayOp_Word8AsWord -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_u32 a i++ ReadByteArrayOp_Word8AsChar -> \[r] [a,i] -> PrimInline . boundsChecked bound a i $ r |= read_boff_u8 a i+ ReadByteArrayOp_Word8AsWideChar -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_i32 a i+ ReadByteArrayOp_Word8AsAddr -> \[r1,r2] [a,i] ->+ PrimInline $ jVar \x -> mconcat+ [ x |= i .<<. two_+ , boundsChecked bound (a .^ "arr") x $+ ifS (a .^ "arr" .&&. a .^ "arr" .! x)+ (mconcat [ r1 |= a .^ "arr" .! x .! zero_+ , r2 |= a .^ "arr" .! x .! one_+ ])+ (mconcat [r1 |= null_, r2 |= one_])+ ]+ ReadByteArrayOp_Word8AsFloat -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_f32 a i+ ReadByteArrayOp_Word8AsDouble -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 7) $ r |= read_boff_f64 a i+ ReadByteArrayOp_Word8AsStablePtr -> \[r1,r2] [a,i] ->+ PrimInline $ mconcat+ [ r1 |= var "h$stablePtrBuf"+ , r2 |= read_boff_i32 a i+ ]+ ReadByteArrayOp_Word8AsInt16 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 1) $ r |= read_boff_i16 a i+ ReadByteArrayOp_Word8AsInt32 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_i32 a i+ ReadByteArrayOp_Word8AsInt64 -> \[h,l] [a,i] ->+ PrimInline $ mconcat+ [ h |= read_boff_i32 a (Add i (Int 4))+ , l |= read_boff_u32 a i+ ]+ ReadByteArrayOp_Word8AsInt -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_i32 a i+ ReadByteArrayOp_Word8AsWord16 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 1) $ r |= read_boff_u16 a i+ ReadByteArrayOp_Word8AsWord32 -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_u32 a i+ ReadByteArrayOp_Word8AsWord64 -> \[h,l] [a,i] ->+ PrimInline . boundsChecked bound a (Add i 7) $ mconcat+ [ h |= read_boff_u32 a (Add i (Int 4))+ , l |= read_boff_u32 a i+ ]+ ReadByteArrayOp_Word8AsWord -> \[r] [a,i] -> PrimInline . boundsChecked bound a (Add i 3) $ r |= read_boff_u32 a i++ WriteByteArrayOp_Word8AsChar -> \[] [a,i,e] -> PrimInline . boundsChecked bound a i $ write_boff_i8 a i e+ WriteByteArrayOp_Word8AsWideChar -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_boff_i32 a i e+ WriteByteArrayOp_Word8AsAddr -> \[] [a,i,e1,e2] ->+ PrimInline $ mconcat+ [ ifS (Not (a .^ "arr")) (a .^ "arr" |= ValExpr (JList [])) mempty+ , boundsChecked bound (a .^ "arr") (i .<<. two_) $+ a .^ "arr" .! (i .<<. two_) |= ValExpr (JList [e1, e2])+ ]++ WriteByteArrayOp_Word8AsFloat -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_boff_f32 a i e+ WriteByteArrayOp_Word8AsDouble -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 7) $ write_boff_f64 a i e+ WriteByteArrayOp_Word8AsStablePtr -> \[] [a,i,_e1,e2] -> PrimInline . boundsChecked bound a (Add i 3) $ write_boff_i32 a i e2+ WriteByteArrayOp_Word8AsInt16 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 1) $ write_boff_i16 a i e+ WriteByteArrayOp_Word8AsInt32 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_boff_i32 a i e+ WriteByteArrayOp_Word8AsInt64 -> \[] [a,i,h,l] ->+ -- JS Numbers are little-endian and 32-bit, so write the lower 4 bytes at i+ -- then write the higher 4 bytes to i+4+ PrimInline . boundsChecked bound a i+ $ mconcat [ write_boff_i32 a (Add i (Int 4)) h+ , write_boff_u32 a i l+ ]+ WriteByteArrayOp_Word8AsInt -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_boff_i32 a i e+ WriteByteArrayOp_Word8AsWord16 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 1) $ write_boff_u16 a i e+ WriteByteArrayOp_Word8AsWord32 -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_boff_u32 a i e+ WriteByteArrayOp_Word8AsWord64 -> \[] [a,i,h,l] ->+ PrimInline . boundsChecked bound a (Add i 7)+ $ mconcat [ write_boff_u32 a (Add i (Int 4)) h+ , write_boff_u32 a i l+ ]+ WriteByteArrayOp_Word8AsWord -> \[] [a,i,e] -> PrimInline . boundsChecked bound a (Add i 3) $ write_boff_u32 a i e++ CasByteArrayOp_Int -> \[r] [a,i,old,new] -> PrimInline . boundsChecked bound a (Add i 3) $ casOp read_i32 write_i32 r a i old new+ CasByteArrayOp_Int8 -> \[r] [a,i,old,new] -> PrimInline . boundsChecked bound a i $ casOp read_i8 write_i8 r a i old new+ CasByteArrayOp_Int16 -> \[r] [a,i,old,new] -> PrimInline . boundsChecked bound a (Add i 1) $ casOp read_i16 write_i16 r a i old new+ CasByteArrayOp_Int32 -> \[r] [a,i,old,new] -> PrimInline . boundsChecked bound a (Add i 3) $ casOp read_i32 write_i32 r a i old new++ CasByteArrayOp_Int64 -> \[r_h,r_l] [a,i,old_h,old_l,new_h,new_l] -> PrimInline . boundsChecked bound a (Add (i .<<. one_) one_) $+ jVar \t_h t_l -> mconcat [ t_h |= read_i32 a (Add (i .<<. one_) one_)+ , t_l |= read_u32 a (i .<<. one_)+ , r_h |= t_h+ , r_l |= t_l+ , ifS (t_l .===. old_l) -- small optimization, check low bits first, fail fast+ (ifBlockS (t_h .===. old_h)+ -- Pre-Condition is good, do the write+ [ write_i32 a (Add (i .<<. one_) one_) new_h+ , write_u32 a (i .<<. one_) new_l+ ]+ -- no good, don't write+ mempty)+ mempty+ ]++ CasAddrOp_Addr -> \[r_a,r_o] [a1,o1,a2,o2,a3,o3] -> PrimInline $+ mconcat [ ifS (app "h$comparePointer" [a1,o1,a2,o2])+ (appS "h$memcpy" [a3,o3,a1,o1,8])+ mempty+ , r_a |= a1+ , r_o |= o1+ ]+ CasAddrOp_Word -> \[r] [a,o,old,new] -> PrimInline $ casOp read_u32 write_u32 r a o old new+ CasAddrOp_Word8 -> \[r] [a,o,old,new] -> PrimInline $ casOp read_u8 write_u8 r a o old new+ CasAddrOp_Word16 -> \[r] [a,o,old,new] -> PrimInline $ casOp read_u16 write_u16 r a o old new+ CasAddrOp_Word32 -> \[r] [a,o,old,new] -> PrimInline $ casOp read_u32 write_u32 r a o old new+ CasAddrOp_Word64 -> \[r_h,r_l] [a,o,old_h,old_l,new_h,new_l] -> PrimInline $+ mconcat [ r_h |= read_u32 a (Add o (Int 4))+ , r_l |= read_u32 a o+ , ifS (r_l .===. old_l)+ (ifBlockS (r_h .===. old_h)+ [ write_u32 a (Add o (Int 4)) new_h+ , write_u32 a o new_l+ ]+ mempty)+ mempty+ ]++ FetchAddAddrOp_Word -> \[r] [a,o,v] -> PrimInline $ fetchOpAddr Add r a o v+ FetchSubAddrOp_Word -> \[r] [a,o,v] -> PrimInline $ fetchOpAddr Sub r a o v+ FetchAndAddrOp_Word -> \[r] [a,o,v] -> PrimInline $ fetchOpAddr BAnd r a o v+ FetchNandAddrOp_Word -> \[r] [a,o,v] -> PrimInline $ fetchOpAddr ((BNot .) . BAnd) r a o v+ FetchOrAddrOp_Word -> \[r] [a,o,v] -> PrimInline $ fetchOpAddr BOr r a o v+ FetchXorAddrOp_Word -> \[r] [a,o,v] -> PrimInline $ fetchOpAddr BXor r a o v++ InterlockedExchange_Addr -> \[r_a,r_o] [a1,o1,_a2,o2] -> PrimInline $+ -- this primop can't be implemented+ -- correctly because we don't store+ -- the array reference part of an Addr#,+ -- only the offset part.+ --+ -- So let's assume that all the array+ -- references are the same...+ --+ -- Note: we could generate an assert+ -- that checks that a1 === a2. However+ -- we can't check that the Addr# read+ -- at Addr# a2[o2] also comes from this+ -- a1/a2 array.+ mconcat [ r_a |= a1 -- might be wrong (see above)+ , r_o |= read_boff_u32 a1 o1+ -- TODO (see above)+ -- assert that a1 === a2+ , write_boff_u32 a1 o1 o2+ ]+ InterlockedExchange_Word -> \[r] [a,o,w] -> PrimInline $+ mconcat [ r |= read_boff_u32 a o+ , write_boff_u32 a o w+ ]++ ShrinkSmallMutableArrayOp_Char -> \[] [a,n] -> PrimInline $ appS "h$shrinkMutableCharArray" [a,n]+ GetSizeofSmallMutableArrayOp -> \[r] [a] -> PrimInline $ r |= a .^ "length"++ AtomicReadAddrOp_Word -> \[r] [a,o] -> PrimInline $ r |= read_boff_u32 a o+ AtomicWriteAddrOp_Word -> \[] [a,o,w] -> PrimInline $ write_boff_u32 a o w+++------------------------------ Unhandled primops -------------------++ NewPromptTagOp -> unhandledPrimop op+ PromptOp -> unhandledPrimop op+ Control0Op -> unhandledPrimop op++ NewIOPortOp -> unhandledPrimop op+ ReadIOPortOp -> unhandledPrimop op+ WriteIOPortOp -> unhandledPrimop op++ GetSparkOp -> unhandledPrimop op+ AnyToAddrOp -> unhandledPrimop op+ MkApUpd0_Op -> unhandledPrimop op+ NewBCOOp -> unhandledPrimop op+ UnpackClosureOp -> unhandledPrimop op+ ClosureSizeOp -> unhandledPrimop op+ GetApStackValOp -> unhandledPrimop op+ WhereFromOp -> unhandledPrimop op -- should be easily implementable with o.f.n++ SetThreadAllocationCounter -> unhandledPrimop op++------------------------------- Vector -----------------------------------------+-- For now, vectors are unsupported on the JS backend. Simply put, they do not+-- make much sense to support given support for arrays and lack of SIMD support+-- in JS. We could try to roll something special but we would not be able to+-- give any performance guarentees to the user and so we leave these has+-- unhandled for now.+ VecBroadcastOp _ _ _ -> unhandledPrimop op+ VecPackOp _ _ _ -> unhandledPrimop op+ VecUnpackOp _ _ _ -> unhandledPrimop op+ VecInsertOp _ _ _ -> unhandledPrimop op+ VecAddOp _ _ _ -> unhandledPrimop op+ VecSubOp _ _ _ -> unhandledPrimop op+ VecMulOp _ _ _ -> unhandledPrimop op+ VecDivOp _ _ _ -> unhandledPrimop op+ VecQuotOp _ _ _ -> unhandledPrimop op+ VecRemOp _ _ _ -> unhandledPrimop op+ VecNegOp _ _ _ -> unhandledPrimop op+ VecIndexByteArrayOp _ _ _ -> unhandledPrimop op+ VecReadByteArrayOp _ _ _ -> unhandledPrimop op+ VecWriteByteArrayOp _ _ _ -> unhandledPrimop op+ VecIndexOffAddrOp _ _ _ -> unhandledPrimop op+ VecReadOffAddrOp _ _ _ -> unhandledPrimop op+ VecWriteOffAddrOp _ _ _ -> unhandledPrimop op++ VecIndexScalarByteArrayOp _ _ _ -> unhandledPrimop op+ VecReadScalarByteArrayOp _ _ _ -> unhandledPrimop op+ VecWriteScalarByteArrayOp _ _ _ -> unhandledPrimop op+ VecIndexScalarOffAddrOp _ _ _ -> unhandledPrimop op+ VecReadScalarOffAddrOp _ _ _ -> unhandledPrimop op+ VecWriteScalarOffAddrOp _ _ _ -> unhandledPrimop op++ PrefetchByteArrayOp3 -> noOp+ PrefetchMutableByteArrayOp3 -> noOp+ PrefetchAddrOp3 -> noOp+ PrefetchValueOp3 -> noOp+ PrefetchByteArrayOp2 -> noOp+ PrefetchMutableByteArrayOp2 -> noOp+ PrefetchAddrOp2 -> noOp+ PrefetchValueOp2 -> noOp+ PrefetchByteArrayOp1 -> noOp+ PrefetchMutableByteArrayOp1 -> noOp+ PrefetchAddrOp1 -> noOp+ PrefetchValueOp1 -> noOp+ PrefetchByteArrayOp0 -> noOp+ PrefetchMutableByteArrayOp0 -> noOp+ PrefetchAddrOp0 -> noOp+ PrefetchValueOp0 -> noOp++unhandledPrimop :: PrimOp -> [JExpr] -> [JExpr] -> PrimRes+unhandledPrimop op rs as = PrimInline $ mconcat+ [ appS "h$log" [toJExpr $ mconcat+ [ "warning, unhandled primop: "+ , renderWithContext defaultSDocContext (ppr op)+ , " "+ , show (length rs, length as)+ ]]+ , appS (mkFastString $ "h$primop_" ++ zEncodeString (renderWithContext defaultSDocContext (ppr op))) as+ -- copyRes+ , mconcat $ zipWith (\r reg -> r |= toJExpr reg) rs (enumFrom Ret1)+ ]++-- | A No Op, used for primops the JS platform cannot or do not support. For+-- example, the prefetching primops do not make sense on the JS platform because+-- we do not have enough control over memory to provide any kind of prefetching+-- mechanism. Hence, these are NoOps.+noOp :: Foldable f => f a -> f a -> PrimRes+noOp = const . const $ PrimInline mempty++-- tuple returns+appT :: [JExpr] -> FastString -> [JExpr] -> JStat+appT [] f xs = appS f xs+appT (r:rs) f xs = mconcat+ [ r |= app f xs+ , mconcat (zipWith (\r ret -> r |= toJExpr ret) rs (enumFrom Ret1))+ ]++--------------------------------------------+-- ByteArray indexing+--------------------------------------------++-- For every ByteArray, the RTS creates the following views:+-- i3: Int32 view+-- u8: Word8 view+-- u1: Word16 view+-- f3: Float32 view+-- f6: Float64 view+-- dv: generic DataView+-- It seems a bit weird to mix Int and Word views like this, but perhaps they+-- are the more common.+--+-- See 'h$newByteArray' in 'ghc/rts/js/mem.js' for details.+--+-- Note that *byte* indexing can only be done with the generic DataView. Use+-- read_boff_* and write_boff_* for this.+--+-- Other read_* and write_* helpers directly use the more specific views.+-- Prefer using them over idx_* to make your intent clearer.++idx_i32, idx_u8, idx_u16, idx_f64, idx_f32 :: JExpr -> JExpr -> JExpr+idx_i32 a i = IdxExpr (a .^ "i3") i+idx_u8 a i = IdxExpr (a .^ "u8") i+idx_u16 a i = IdxExpr (a .^ "u1") i+idx_f64 a i = IdxExpr (a .^ "f6") i+idx_f32 a i = IdxExpr (a .^ "f3") i++read_u8 :: JExpr -> JExpr -> JExpr+read_u8 a i = idx_u8 a i++read_u16 :: JExpr -> JExpr -> JExpr+read_u16 a i = idx_u16 a i++read_u32 :: JExpr -> JExpr -> JExpr+read_u32 a i = toU32 (idx_i32 a i)++read_i8 :: JExpr -> JExpr -> JExpr+read_i8 a i = signExtend8 (idx_u8 a i)++read_i16 :: JExpr -> JExpr -> JExpr+read_i16 a i = signExtend16 (idx_u16 a i)++read_i32 :: JExpr -> JExpr -> JExpr+read_i32 a i = idx_i32 a i++read_f32 :: JExpr -> JExpr -> JExpr+read_f32 a i = idx_f32 a i++read_f64 :: JExpr -> JExpr -> JExpr+read_f64 a i = idx_f64 a i++write_u8 :: JExpr -> JExpr -> JExpr -> JStat+write_u8 a i v = idx_u8 a i |= v++write_u16 :: JExpr -> JExpr -> JExpr -> JStat+write_u16 a i v = idx_u16 a i |= v++write_u32 :: JExpr -> JExpr -> JExpr -> JStat+write_u32 a i v = idx_i32 a i |= v++write_i8 :: JExpr -> JExpr -> JExpr -> JStat+write_i8 a i v = idx_u8 a i |= v++write_i16 :: JExpr -> JExpr -> JExpr -> JStat+write_i16 a i v = idx_u16 a i |= v++write_i32 :: JExpr -> JExpr -> JExpr -> JStat+write_i32 a i v = idx_i32 a i |= v++write_f32 :: JExpr -> JExpr -> JExpr -> JStat+write_f32 a i v = idx_f32 a i |= v++write_f64 :: JExpr -> JExpr -> JExpr -> JStat+write_f64 a i v = idx_f64 a i |= v++-- Data View helper functions: byte indexed!+--+-- The argument list consists of the array @a@, the index @i@, and the new value+-- to set (in the case of a setter) @v@.++write_boff_i8, write_boff_u8, write_boff_i16, write_boff_u16, write_boff_i32, write_boff_u32, write_boff_f32, write_boff_f64 :: JExpr -> JExpr -> JExpr -> JStat+write_boff_i8 a i v = write_i8 a i v+write_boff_u8 a i v = write_u8 a i v+write_boff_i16 a i v = ApplStat (a .^ "dv" .^ "setInt16" ) [i, v, true_]+write_boff_u16 a i v = ApplStat (a .^ "dv" .^ "setUint16" ) [i, v, true_]+write_boff_i32 a i v = ApplStat (a .^ "dv" .^ "setInt32" ) [i, v, true_]+write_boff_u32 a i v = ApplStat (a .^ "dv" .^ "setUint32" ) [i, v, true_]+write_boff_f32 a i v = ApplStat (a .^ "dv" .^ "setFloat32") [i, v, true_]+write_boff_f64 a i v = ApplStat (a .^ "dv" .^ "setFloat64") [i, v, true_]++read_boff_i8, read_boff_u8, read_boff_i16, read_boff_u16, read_boff_i32, read_boff_u32, read_boff_f32, read_boff_f64 :: JExpr -> JExpr -> JExpr+read_boff_i8 a i = read_i8 a i+read_boff_u8 a i = read_u8 a i+read_boff_i16 a i = ApplExpr (a .^ "dv" .^ "getInt16" ) [i, true_]+read_boff_u16 a i = ApplExpr (a .^ "dv" .^ "getUint16" ) [i, true_]+read_boff_i32 a i = ApplExpr (a .^ "dv" .^ "getInt32" ) [i, true_]+read_boff_u32 a i = ApplExpr (a .^ "dv" .^ "getUint32" ) [i, true_]+read_boff_f32 a i = ApplExpr (a .^ "dv" .^ "getFloat32") [i, true_]+read_boff_f64 a i = ApplExpr (a .^ "dv" .^ "getFloat64") [i, true_]++fetchOpByteArray :: (JExpr -> JExpr -> JExpr) -> JExpr -> JExpr -> JExpr -> JExpr -> JStat+fetchOpByteArray op tgt src i v = mconcat+ [ tgt |= read_i32 src i+ , write_i32 src i (op tgt v)+ ]++fetchOpAddr :: (JExpr -> JExpr -> JExpr) -> JExpr -> JExpr -> JExpr -> JExpr -> JStat+fetchOpAddr op tgt src i v = mconcat+ [ tgt |= read_boff_u32 src i+ , write_boff_u32 src i (op tgt v)+ ]++casOp+ :: (JExpr -> JExpr -> JExpr) -- read+ -> (JExpr -> JExpr -> JExpr -> JStat) -- write+ -> JExpr -- target register to store result+ -> JExpr -- source arrays+ -> JExpr -- index+ -> JExpr -- old value to compare+ -> JExpr -- new value to write+ -> JStat+casOp read write tgt src i old new = mconcat+ [ tgt |= read src i+ , ifS (tgt .===. old)+ (write src i new)+ mempty+ ]++--------------------------------------------------------------------------------+-- Lifted Arrays+--------------------------------------------------------------------------------+-- | lifted arrays+cloneArray :: JExpr -> JExpr -> Maybe JExpr -> JExpr -> JStat+cloneArray tgt src mb_offset len = mconcat+ [ tgt |= ApplExpr (src .^ "slice") [start, end]+ , tgt .^ closureMeta_ |= zero_+ , tgt .^ "__ghcjsArray" |= true_+ ]+ where+ start = fromMaybe zero_ mb_offset+ end = maybe len (Add len) mb_offset++newArray :: JExpr -> JExpr -> JExpr -> JStat+newArray tgt len elem =+ tgt |= app "h$newArray" [len, elem]++newByteArray :: JExpr -> JExpr -> JStat+newByteArray tgt len =+ tgt |= app "h$newByteArray" [len]++boundsChecked :: Bool -- ^ Should we do bounds checking?+ -> JExpr -- ^ Array+ -> JExpr -- ^ Index+ -> JStat -- ^ Result+ -> JStat+boundsChecked False _ _ r = r+boundsChecked True xs i r =+ ifS ((i .<. xs .^ "length") .&&. (i .>=. zero_))+ r+ (returnS $ app "h$exitProcess" [Int 134])++-- e|0 (32 bit signed integer truncation) required because of JS numbers. e|0+-- converts e to an Int32. Note that e|0 _is still a Double_ because JavaScript.+-- So (x|0) * (y|0) can still return values outside of the Int32 range. You have+-- been warned!+toI32 :: JExpr -> JExpr+toI32 e = BOr e zero_++-- e>>>0 (32 bit unsigned integer truncation)+-- required because of JS numbers. e>>>0 converts e to a Word32+-- so (-2147483648) >>> 0 = 2147483648+-- and ((-2147483648) >>>0) | 0 = -2147483648+toU32 :: JExpr -> JExpr+toU32 e = e .>>>. zero_++quotShortInt :: Int -> JExpr -> JExpr -> JExpr+quotShortInt bits x y = BAnd (signed x `Div` signed y) mask+ where+ signed z = (z .<<. shift) .>>. shift+ shift = toJExpr (32 - bits)+ mask = toJExpr (((2::Integer) ^ bits) - 1)++remShortInt :: Int -> JExpr -> JExpr -> JExpr+remShortInt bits x y = BAnd (signed x `Mod` signed y) mask+ where+ signed z = (z .<<. shift) .>>. shift+ shift = toJExpr (32 - bits)+ mask = toJExpr (((2::Integer) ^ bits) - 1)
+ compiler/GHC/StgToJS/Printer.hs view
@@ -0,0 +1,218 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MagicHash #-}++-----------------------------------------------------------------------------+-- |+-- Module : GHC.StgToJS.Printer+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Sylvain Henry <sylvain.henry@iohk.io>+-- Stability : experimental+--+-- Custom prettyprinter for JS AST uses the JS PPr module for most of+-- the work+--+--+-----------------------------------------------------------------------------+module GHC.StgToJS.Printer+ ( pretty+ , ghcjsRenderJs+ , prettyBlock+ )+where++import GHC.Prelude+import GHC.Int+import GHC.Exts++import GHC.JS.Syntax+import GHC.JS.Ppr++import GHC.Utils.Ppr as PP+import GHC.Data.FastString+import GHC.Types.Unique.Map++import Data.List (sortOn)+import Data.Char (isAlpha,isDigit,ord)+import qualified Data.ByteString.Short as SBS++pretty :: JStat -> Doc+pretty = jsToDocR ghcjsRenderJs++ghcjsRenderJs :: RenderJs+ghcjsRenderJs = defaultRenderJs+ { renderJsV = ghcjsRenderJsV+ , renderJsS = ghcjsRenderJsS+ , renderJsI = ghcjsRenderJsI+ }++hdd :: SBS.ShortByteString+hdd = SBS.pack (map (fromIntegral . ord) "h$$")++ghcjsRenderJsI :: RenderJs -> Ident -> Doc+ghcjsRenderJsI _ (TxtI fs)+ -- Fresh symbols are prefixed with "h$$". They aren't explicitly referred by+ -- name in user code, only in compiled code. Hence we can rename them if we do+ -- it consistently in all the linked code.+ --+ -- These symbols are usually very large because their name includes the+ -- unit-id, the module name, and some unique number. So we rename these+ -- symbols with a much shorter globally unique number.+ --+ -- Here we reuse their FastString unique for this purpose! Note that it only+ -- works if we pretty-print all the JS code linked together at once, which we+ -- currently do. GHCJS used to maintain a CompactorState to support+ -- incremental linking: it contained the mapping between original symbols and+ -- their renaming.+ | hdd `SBS.isPrefixOf` fastStringToShortByteString fs+ , u <- uniqueOfFS fs+ = text "h$$" <> hexDoc (fromIntegral u)+ | otherwise+ = ftext fs++-- | Render as an hexadecimal number in reversed order (because it's faster and we+-- don't care about the actual value).+hexDoc :: Word -> Doc+hexDoc 0 = char '0'+hexDoc v = text $ go v+ where+ sym (I# i) = C# (indexCharOffAddr# chars i)+ chars = "0123456789abcdef"#+ go = \case+ 0 -> []+ n -> sym (fromIntegral (n .&. 0x0F))+ : sym (fromIntegral ((n .&. 0xF0) `shiftR` 4))+ : go (n `shiftR` 8)+++++-- attempt to resugar some of the common constructs+ghcjsRenderJsS :: RenderJs -> JStat -> Doc+ghcjsRenderJsS r (BlockStat xs) = prettyBlock r (flattenBlocks xs)+ghcjsRenderJsS r s = renderJsS defaultRenderJs r s++-- don't quote keys in our object literals, so closure compiler works+ghcjsRenderJsV :: RenderJs -> JVal -> Doc+ghcjsRenderJsV r (JHash m)+ | isNullUniqMap m = text "{}"+ | otherwise = braceNest . PP.fsep . punctuate comma .+ map (\(x,y) -> quoteIfRequired x <> PP.colon <+> jsToDocR r y)+ -- nonDetEltsUniqMap doesn't introduce non-determinism here because+ -- we sort the elements lexically+ . sortOn (LexicalFastString . fst) $ nonDetEltsUniqMap m+ where+ quoteIfRequired :: FastString -> Doc+ quoteIfRequired x+ | isUnquotedKey x' = text x'+ | otherwise = PP.squotes (text x')+ where x' = unpackFS x++ isUnquotedKey :: String -> Bool+ isUnquotedKey x | null x = False+ | all isDigit x = True+ | otherwise = validFirstIdent (head x)+ && all validOtherIdent (tail x)+++ validFirstIdent c = c == '_' || c == '$' || isAlpha c+ validOtherIdent c = isAlpha c || isDigit c+ghcjsRenderJsV r v = renderJsV defaultRenderJs r v++prettyBlock :: RenderJs -> [JStat] -> Doc+prettyBlock r xs = vcat $ map addSemi (prettyBlock' r xs)++-- recognize common patterns in a block and convert them to more idiomatic/concise javascript+prettyBlock' :: RenderJs -> [JStat] -> [Doc]+-- return/...+prettyBlock' r ( x@(ReturnStat _)+ : xs+ )+ | not (null xs)+ = prettyBlock' r [x]+-- declare/assign+prettyBlock' r ( (DeclStat i Nothing)+ : (AssignStat (ValExpr (JVar i')) v)+ : xs+ )+ | i == i'+ = prettyBlock' r (DeclStat i (Just v) : xs)++-- resugar for loops with/without var declaration+prettyBlock' r ( (DeclStat i (Just v0))+ : (WhileStat False p (BlockStat bs))+ : xs+ )+ | not (null flat) && isForUpdStat (last flat)+ = mkFor r True i v0 p (last flat) (init flat) : prettyBlock' r xs+ where+ flat = flattenBlocks bs+prettyBlock' r ( (AssignStat (ValExpr (JVar i)) v0)+ : (WhileStat False p (BlockStat bs))+ : xs+ )+ | not (null flat) && isForUpdStat (last flat)+ = mkFor r False i v0 p (last flat) (init flat) : prettyBlock' r xs+ where+ flat = flattenBlocks bs++-- global function (does not preserve semantics but works for GHCJS)+prettyBlock' r ( (DeclStat i (Just (ValExpr (JFunc is b))))+ : xs+ )+ = (hangBrace (text "function" <+> jsToDocR r i <> parens (fsep . punctuate comma . map (jsToDocR r) $ is))+ (jsToDocR r b)+ ) : prettyBlock' r xs+-- modify/assign operators+prettyBlock' r ( (AssignStat (ValExpr (JVar i)) (InfixExpr AddOp (ValExpr (JVar i')) (ValExpr (JInt 1))))+ : xs+ )+ | i == i' = (text "++" <> jsToDocR r i) : prettyBlock' r xs+prettyBlock' r ( (AssignStat (ValExpr (JVar i)) (InfixExpr SubOp (ValExpr (JVar i')) (ValExpr (JInt 1))))+ : xs+ )+ | i == i' = (text "--" <> jsToDocR r i) : prettyBlock' r xs+prettyBlock' r ( (AssignStat (ValExpr (JVar i)) (InfixExpr AddOp (ValExpr (JVar i')) e))+ : xs+ )+ | i == i' = (jsToDocR r i <+> text "+=" <+> jsToDocR r e) : prettyBlock' r xs+prettyBlock' r ( (AssignStat (ValExpr (JVar i)) (InfixExpr SubOp (ValExpr (JVar i')) e))+ : xs+ )+ | i == i' = (jsToDocR r i <+> text "-=" <+> jsToDocR r e) : prettyBlock' r xs+++prettyBlock' r (x:xs) = jsToDocR r x : prettyBlock' r xs+prettyBlock' _ [] = []++-- build the for block+mkFor :: RenderJs -> Bool -> Ident -> JExpr -> JExpr -> JStat -> [JStat] -> Doc+mkFor r decl i v0 p s1 sb = hangBrace (text "for" <> forCond)+ (jsToDocR r $ BlockStat sb)+ where+ c0 | decl = text "var" <+> jsToDocR r i <+> char '=' <+> jsToDocR r v0+ | otherwise = jsToDocR r i <+> char '=' <+> jsToDocR r v0+ forCond = parens $ hcat $ interSemi+ [ c0+ , jsToDocR r p+ , parens (jsToDocR r s1)+ ]++-- check if a statement is suitable to be converted to something in the for(;;x) position+isForUpdStat :: JStat -> Bool+isForUpdStat UOpStat {} = True+isForUpdStat AssignStat {} = True+isForUpdStat ApplStat {} = True+isForUpdStat _ = False++interSemi :: [Doc] -> [Doc]+interSemi [] = [PP.empty]+interSemi [s] = [s]+interSemi (x:xs) = x <> text ";" : interSemi xs++addSemi :: Doc -> Doc+addSemi x = x <> text ";"
+ compiler/GHC/StgToJS/Profiling.hs view
@@ -0,0 +1,178 @@+{-# LANGUAGE OverloadedStrings #-}++module GHC.StgToJS.Profiling+ ( initCostCentres+ , emitCostCentreDecl+ , emitCostCentreStackDecl+ , enterCostCentreFun+ , enterCostCentreThunk+ , setCC+ , pushRestoreCCS+ , jCurrentCCS+ , jCafCCS+ , jSystemCCS+ , costCentreLbl+ , costCentreStackLbl+ , singletonCCSLbl+ , ccsVarJ+ -- * Predicates+ , profiling+ , ifProfiling+ , ifProfilingM+ -- * helpers+ , profStat+ )+where++import GHC.Prelude++import GHC.JS.Syntax+import GHC.JS.Make++import GHC.StgToJS.Regs+import GHC.StgToJS.Types+import GHC.StgToJS.Symbols+import GHC.StgToJS.Monad++import GHC.Types.CostCentre++import GHC.Data.FastString+import GHC.Unit.Module+import GHC.Utils.Encoding+import GHC.Utils.Outputable+import GHC.Utils.Panic+import qualified Control.Monad.Trans.State.Strict as State++--------------------------------------------------------------------------------+-- Initialization++initCostCentres :: CollectedCCs -> G ()+initCostCentres (local_CCs, singleton_CCSs) = do+ mapM_ emitCostCentreDecl local_CCs+ mapM_ emitCostCentreStackDecl singleton_CCSs++emitCostCentreDecl :: CostCentre -> G ()+emitCostCentreDecl cc = do+ ccsLbl <- costCentreLbl cc+ let is_caf = isCafCC cc+ label = costCentreUserName cc+ modl = moduleNameString $ moduleName $ cc_mod cc+ loc = renderWithContext defaultSDocContext (ppr (costCentreSrcSpan cc))+ js = ccsLbl ||= UOpExpr NewOp (ApplExpr (var "h$CC")+ [ toJExpr label+ , toJExpr modl+ , toJExpr loc+ , toJExpr is_caf+ ])+ emitGlobal js++emitCostCentreStackDecl :: CostCentreStack -> G ()+emitCostCentreStackDecl ccs =+ case maybeSingletonCCS ccs of+ Just cc -> do+ ccsLbl <- singletonCCSLbl cc+ ccLbl <- costCentreLbl cc+ let js = ccsLbl ||= UOpExpr NewOp (ApplExpr (var "h$CCS") [null_, toJExpr ccLbl])+ emitGlobal js+ Nothing -> pprPanic "emitCostCentreStackDecl" (ppr ccs)++--------------------------------------------------------------------------------+-- Entering to cost-centres++enterCostCentreFun :: CostCentreStack -> JStat+enterCostCentreFun ccs+ | isCurrentCCS ccs = ApplStat (var "h$enterFunCCS") [jCurrentCCS, r1 .^ "cc"]+ | otherwise = mempty -- top-level function, nothing to do++enterCostCentreThunk :: JStat+enterCostCentreThunk = ApplStat (var "h$enterThunkCCS") [r1 .^ "cc"]++setCC :: CostCentre -> Bool -> Bool -> G JStat+setCC cc _tick True = do+ ccI@(TxtI _ccLbl) <- costCentreLbl cc+ addDependency $ OtherSymb (cc_mod cc)+ (moduleGlobalSymbol $ cc_mod cc)+ return $ jCurrentCCS |= ApplExpr (var "h$pushCostCentre") [jCurrentCCS, toJExpr ccI]+setCC _cc _tick _push = return mempty++pushRestoreCCS :: JStat+pushRestoreCCS = ApplStat (var "h$pushRestoreCCS") []++--------------------------------------------------------------------------------+-- Some cost-centre stacks to be used in generator++jCurrentCCS :: JExpr+jCurrentCCS = var "h$currentThread" .^ "ccs"++jCafCCS :: JExpr+jCafCCS = var "h$CAF"++jSystemCCS :: JExpr+jSystemCCS = var "h$CCS_SYSTEM"+--------------------------------------------------------------------------------+-- Helpers for generating profiling related things++profiling :: G Bool+profiling = csProf <$> getSettings++ifProfiling :: Monoid m => m -> G m+ifProfiling m = do+ prof <- profiling+ return $ if prof then m else mempty++ifProfilingM :: Monoid m => G m -> G m+ifProfilingM m = do+ prof <- profiling+ if prof then m else return mempty++-- | If profiling is enabled, then use input JStat, else ignore+profStat :: StgToJSConfig -> JStat -> JStat+profStat cfg e = if csProf cfg then e else mempty+--------------------------------------------------------------------------------+-- Generating cost-centre and cost-centre stack variables++costCentreLbl' :: CostCentre -> G String+costCentreLbl' cc = do+ curModl <- State.gets gsModule+ let lbl = renderWithContext defaultSDocContext+ $ withPprStyle PprCode (ppr cc)+ return . ("h$"++) . zEncodeString $+ moduleNameColons (moduleName curModl) ++ "_" ++ if isCafCC cc then "CAF_ccs" else lbl++costCentreLbl :: CostCentre -> G Ident+costCentreLbl cc = TxtI . mkFastString <$> costCentreLbl' cc++costCentreStackLbl' :: CostCentreStack -> G (Maybe String)+costCentreStackLbl' ccs = do+ ifProfilingM f+ where+ f | isCurrentCCS ccs = return $ Just "h$currentThread.ccs"+ | dontCareCCS == ccs = return $ Just "h$CCS_DONT_CARE"+ | otherwise =+ case maybeSingletonCCS ccs of+ Just cc -> Just <$> singletonCCSLbl' cc+ Nothing -> pure Nothing++costCentreStackLbl :: CostCentreStack -> G (Maybe Ident)+costCentreStackLbl ccs = fmap (TxtI . mkFastString) <$> costCentreStackLbl' ccs++singletonCCSLbl' :: CostCentre -> G String+singletonCCSLbl' cc = do+ curModl <- State.gets gsModule+ ccLbl <- costCentreLbl' cc+ let ccsLbl = ccLbl ++ "_ccs"+ return . zEncodeString $ mconcat+ [ moduleNameColons (moduleName curModl)+ , "_"+ , ccsLbl+ ]++singletonCCSLbl :: CostCentre -> G Ident+singletonCCSLbl cc = TxtI . mkFastString <$> singletonCCSLbl' cc++ccsVarJ :: CostCentreStack -> G (Maybe JExpr)+ccsVarJ ccs = do+ prof <- profiling+ if prof+ then fmap (ValExpr . JVar) <$> costCentreStackLbl ccs+ else pure Nothing
+ compiler/GHC/StgToJS/Regs.hs view
@@ -0,0 +1,142 @@+{-# LANGUAGE OverloadedStrings #-}++module GHC.StgToJS.Regs+ ( StgReg (..)+ , Special(..)+ , sp+ , stack+ , r1, r2, r3, r4+ , regsFromR1+ , regsFromR2+ , jsRegsFromR1+ , jsRegsFromR2+ , StgRet (..)+ , jsRegToInt+ , intToJSReg+ , jsReg+ , maxReg+ , minReg+ )+where++import GHC.Prelude++import GHC.JS.Syntax+import GHC.JS.Make++import GHC.Data.FastString++import Data.Array+import Data.Char++-- | General purpose "registers"+--+-- The JS backend arbitrarily supports 128 registers+data StgReg+ = R1 | R2 | R3 | R4 | R5 | R6 | R7 | R8+ | R9 | R10 | R11 | R12 | R13 | R14 | R15 | R16+ | R17 | R18 | R19 | R20 | R21 | R22 | R23 | R24+ | R25 | R26 | R27 | R28 | R29 | R30 | R31 | R32+ | R33 | R34 | R35 | R36 | R37 | R38 | R39 | R40+ | R41 | R42 | R43 | R44 | R45 | R46 | R47 | R48+ | R49 | R50 | R51 | R52 | R53 | R54 | R55 | R56+ | R57 | R58 | R59 | R60 | R61 | R62 | R63 | R64+ | R65 | R66 | R67 | R68 | R69 | R70 | R71 | R72+ | R73 | R74 | R75 | R76 | R77 | R78 | R79 | R80+ | R81 | R82 | R83 | R84 | R85 | R86 | R87 | R88+ | R89 | R90 | R91 | R92 | R93 | R94 | R95 | R96+ | R97 | R98 | R99 | R100 | R101 | R102 | R103 | R104+ | R105 | R106 | R107 | R108 | R109 | R110 | R111 | R112+ | R113 | R114 | R115 | R116 | R117 | R118 | R119 | R120+ | R121 | R122 | R123 | R124 | R125 | R126 | R127 | R128+ deriving (Eq, Ord, Show, Enum, Bounded, Ix)++-- | Stack registers+data Special+ = Stack+ | Sp+ deriving (Show, Eq)++-- | Return registers+--+-- Extra results from foreign calls can be stored here (while first result is+-- directly returned)+data StgRet = Ret1 | Ret2 | Ret3 | Ret4 | Ret5 | Ret6 | Ret7 | Ret8 | Ret9 | Ret10+ deriving (Eq, Ord, Show, Enum, Bounded, Ix)++instance ToJExpr Special where+ toJExpr Stack = var "h$stack"+ toJExpr Sp = var "h$sp"++instance ToJExpr StgReg where+ toJExpr r = registers ! r++instance ToJExpr StgRet where+ toJExpr r = rets ! r++---------------------------------------------------+-- helpers+---------------------------------------------------++sp :: JExpr+sp = toJExpr Sp++stack :: JExpr+stack = toJExpr Stack++r1, r2, r3, r4 :: JExpr+r1 = toJExpr R1+r2 = toJExpr R2+r3 = toJExpr R3+r4 = toJExpr R4+++jsRegToInt :: StgReg -> Int+jsRegToInt = (+1) . fromEnum++intToJSReg :: Int -> StgReg+intToJSReg r = toEnum (r - 1)++jsReg :: Int -> JExpr+jsReg r = toJExpr (intToJSReg r)++maxReg :: Int+maxReg = jsRegToInt maxBound++minReg :: Int+minReg = jsRegToInt minBound++-- | List of registers, starting from R1+regsFromR1 :: [StgReg]+regsFromR1 = enumFrom R1++-- | List of registers, starting from R2+regsFromR2 :: [StgReg]+regsFromR2 = tail regsFromR1++-- | List of registers, starting from R1 as JExpr+jsRegsFromR1 :: [JExpr]+jsRegsFromR1 = fmap toJExpr regsFromR1++-- | List of registers, starting from R2 as JExpr+jsRegsFromR2 :: [JExpr]+jsRegsFromR2 = tail jsRegsFromR1++---------------------------------------------------+-- caches+---------------------------------------------------++-- cache JExpr representing StgReg+registers :: Array StgReg JExpr+registers = listArray (minBound, maxBound) (map regN regsFromR1)+ where+ regN r+ | fromEnum r < 32 = var . mkFastString . ("h$"++) . map toLower . show $ r+ | otherwise = IdxExpr (var "h$regs")+ (toJExpr ((fromEnum r) - 32))++-- cache JExpr representing StgRet+rets :: Array StgRet JExpr+rets = listArray (minBound, maxBound) (map retN (enumFrom Ret1))+ where+ retN = var . mkFastString . ("h$"++) . map toLower . show
+ compiler/GHC/StgToJS/Rts/Rts.hs view
@@ -0,0 +1,661 @@+{-# LANGUAGE OverloadedStrings #-}++{-# OPTIONS_GHC -O0 #-}++-----------------------------------------------------------------------------+-- |+-- Module : GHC.StgToJS.Rts.Rts+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Sylvain Henry <sylvain.henry@iohk.io>+-- Josh Meredith <josh.meredith@iohk.io>+-- Stability : experimental+--+-- Top level driver of the JavaScript Backend RTS. This file is an+-- implementation of the JS RTS for the JS backend written as an EDSL in+-- Haskell. It assumes the existence of pre-generated JS functions, included as+-- js-sources in base. These functions are similarly assumed for non-inline+-- Primops, See 'GHC.StgToJS.Prim'. Most of the elements in this module are+-- constants in Haskell Land which define pieces of the JS RTS.+--+-----------------------------------------------------------------------------++module GHC.StgToJS.Rts.Rts where++import GHC.Prelude++import GHC.JS.Syntax+import GHC.JS.Make+import GHC.JS.Transform++import GHC.StgToJS.Apply+import GHC.StgToJS.Closure+import GHC.StgToJS.Heap+import GHC.StgToJS.Printer+import GHC.StgToJS.Profiling+import GHC.StgToJS.Regs+import GHC.StgToJS.Types+import GHC.StgToJS.Stack++import GHC.Data.FastString+import GHC.Types.Unique.Map++import Data.Array+import Data.Monoid+import Data.Char (toLower, toUpper)+import qualified Data.Bits as Bits++-- | The garbageCollector resets registers and result variables.+garbageCollector :: JStat+garbageCollector =+ mconcat [ TxtI "h$resetRegisters" ||= jLam (mconcat $ map resetRegister [minBound..maxBound])+ , TxtI "h$resetResultVars" ||= jLam (mconcat $ map resetResultVar [minBound..maxBound])+ ]++-- | Reset the register 'r' in JS Land. Note that this "resets" by setting the+-- register to a dummy variable called "null", /not/ by setting to JS's nil+-- value.+resetRegister :: StgReg -> JStat+resetRegister r = toJExpr r |= null_++-- | Reset the return variable 'r' in JS Land. Note that this "resets" by+-- setting the register to a dummy variable called "null", /not/ by setting to+-- JS's nil value.+resetResultVar :: StgRet -> JStat+resetResultVar r = toJExpr r |= null_++-- | Define closures based on size, these functions are syntactic sugar, e.g., a+-- Haskell function which generates some useful JS. Each Closure constructor+-- follows the naming convention h$cN, where N is a natural number. For example,+-- h$c (with the nat omitted) is a JS Land Constructor for a closure in JS land+-- which has a single entry function 'f', and no fields; identical to h$c0. h$c1+-- is a JS Land Constructor for a closure with an entry function 'f', and a+-- /single/ field 'x1', 'Just foo' is an example of this kind of closure. h$c2+-- is a JS Land Constructor for a closure with an entry function and two data+-- fields: 'x1' and 'x2'. And so on. Note that this has JIT performance+-- implications; you should use h$c1, h$c2, h$c3, ... h$c24 instead of making+-- objects manually so layouts and fields can be changed more easily and so the+-- JIT can optimize better.+closureConstructors :: StgToJSConfig -> JStat+closureConstructors s = BlockStat+ [ declClsConstr "h$c" ["f"] $ Closure+ { clEntry = var "f"+ , clField1 = null_+ , clField2 = null_+ , clMeta = 0+ , clCC = ccVal+ }+ , declClsConstr "h$c0" ["f"] $ Closure+ { clEntry = var "f"+ , clField1 = null_+ , clField2 = null_+ , clMeta = 0+ , clCC = ccVal+ }+ , declClsConstr "h$c1" ["f", "x1"] $ Closure+ { clEntry = var "f"+ , clField1 = var "x1"+ , clField2 = null_+ , clMeta = 0+ , clCC = ccVal+ }+ , declClsConstr "h$c2" ["f", "x1", "x2"] $ Closure+ { clEntry = var "f"+ , clField1 = var "x1"+ , clField2 = var "x2"+ , clMeta = 0+ , clCC = ccVal+ }+ , mconcat (map mkClosureCon [3..24])+ , mconcat (map mkDataFill [1..24])+ ]+ where+ prof = csProf s+ (ccArg,ccVal)+ -- the cc argument happens to be named just like the cc field...+ | prof = ([TxtI closureCC_], Just (var closureCC_))+ | otherwise = ([], Nothing)+ addCCArg as = map TxtI as ++ ccArg+ addCCArg' as = as ++ ccArg++ declClsConstr i as cl = TxtI i ||= ValExpr (JFunc (addCCArg as)+ ( jVar $ \x ->+ [ checkC+ , x |= newClosure cl+ , notifyAlloc x+ , traceAlloc x+ , returnS x+ ]+ ))++ traceAlloc x | csTraceRts s = appS "h$traceAlloc" [x]+ | otherwise = mempty++ notifyAlloc x | csDebugAlloc s = appS "h$debugAlloc_notifyAlloc" [x]+ | otherwise = mempty++ -- only JSVal can typically contain undefined or null+ -- although it's possible (and legal) to make other Haskell types+ -- to contain JS refs directly+ -- this can cause false positives here+ checkC :: JStat+ checkC | csAssertRts s =+ jVar $ \msg ->+ jwhenS (var "arguments" .! 0 .!==. jString "h$baseZCGHCziJSziPrimziJSVal_con_e")+ (loop 1 (.<. var "arguments" .^ "length")+ (\i ->+ mconcat [msg |= jString "warning: undefined or null in argument: "+ + i+ + jString " allocating closure: " + (var "arguments" .! 0 .^ "n")+ , appS "h$log" [msg]+ , jwhenS (var "console" .&&. (var "console" .^ "trace")) ((var "console" .^ "trace") `ApplStat` [msg])+ , postIncrS i+ ])++ )+ | otherwise = mempty++ -- h$d is never used for JSVal (since it's only for constructors with+ -- at least three fields, so we always warn here+ checkD | csAssertRts s =+ loop 0 (.<. var "arguments" .^ "length")+ (\i -> jwhenS ((var "arguments" .! i .===. null_)+ .||. (var "arguments" .! i .===. undefined_))+ (jVar $ \msg ->+ mconcat [ msg |= jString "warning: undefined or null in argument: " + i + jString " allocating fields"+ , jwhenS (var "console" .&&. (var "console" .^ "trace"))+ ((var "console" .^ "trace") `ApplStat` [msg])+ ]))++ | otherwise = mempty++ mkClosureCon :: Int -> JStat+ mkClosureCon n = funName ||= toJExpr fun+ where+ funName = TxtI $ mkFastString ("h$c" ++ show n)+ -- args are: f x1 x2 .. xn [cc]+ args = TxtI "f" : addCCArg' (map (TxtI . mkFastString . ('x':) . show) [(1::Int)..n])+ fun = JFunc args funBod+ -- x1 goes into closureField1. All the other args are bundled into an+ -- object in closureField2: { d1 = x2, d2 = x3, ... }+ --+ extra_args = ValExpr . JHash . listToUniqMap $ zip+ (map (mkFastString . ('d':) . show) [(1::Int)..])+ (map (toJExpr . TxtI . mkFastString . ('x':) . show) [2..n])++ funBod = jVar $ \x ->+ [ checkC+ , x |= newClosure Closure+ { clEntry = var "f"+ , clField1 = var "x1"+ , clField2 = extra_args+ , clMeta = 0+ , clCC = ccVal+ }+ , notifyAlloc x+ , traceAlloc x+ , returnS x+ ]++ mkDataFill :: Int -> JStat+ mkDataFill n = funName ||= toJExpr fun+ where+ funName = TxtI $ mkFastString ("h$d" ++ show n)+ ds = map (mkFastString . ('d':) . show) [(1::Int)..n]+ extra_args = ValExpr . JHash . listToUniqMap . zip ds $ map (toJExpr . TxtI) ds+ fun = JFunc (map TxtI ds) (checkD <> returnS extra_args)++-- | JS Payload to perform stack manipulation in the RTS+stackManip :: JStat+stackManip = mconcat (map mkPush [1..32]) <>+ mconcat (map mkPpush [1..255])+ where+ mkPush :: Int -> JStat+ mkPush n = let funName = TxtI $ mkFastString ("h$p" ++ show n)+ as = map (TxtI . mkFastString . ('x':) . show) [1..n]+ fun = JFunc as ((sp |= sp + toJExpr n)+ <> mconcat (zipWith (\i a -> stack .! (sp - toJExpr (n-i)) |= toJExpr a)+ [1..] as))+ in funName ||= toJExpr fun++ -- partial pushes, based on bitmap, increases Sp by highest bit+ mkPpush :: Integer -> JStat+ mkPpush sig | sig Bits..&. (sig+1) == 0 = mempty -- already handled by h$p+ mkPpush sig = let funName = TxtI $ mkFastString ("h$pp" ++ show sig)+ bits = bitsIdx sig+ n = length bits+ h = last bits+ args = map (TxtI . mkFastString . ('x':) . show) [1..n]+ fun = JFunc args $+ mconcat [ sp |= sp + toJExpr (h+1)+ , mconcat (zipWith (\b a -> stack .! (sp - toJExpr (h-b)) |= toJExpr a) bits args)+ ]+ in funName ||= toJExpr fun++bitsIdx :: Integer -> [Int]+bitsIdx n | n < 0 = error "bitsIdx: negative"+ | otherwise = go n 0+ where+ go 0 _ = []+ go m b | Bits.testBit m b = b : go (Bits.clearBit m b) (b+1)+ | otherwise = go (Bits.clearBit m b) (b+1)++bhLneStats :: StgToJSConfig -> JExpr -> JExpr -> JStat+bhLneStats _s p frameSize =+ jVar $ \v ->+ mconcat [ v |= stack .! p+ , ifS v+ ((sp |= sp - frameSize)+ <> ifS (v .===. var "h$blackhole")+ (returnS $ app "h$throw" [var "h$baseZCControlziExceptionziBasezinonTermination", false_])+ (mconcat [r1 |= v+ , sp |= sp - frameSize+ , returnStack+ ]))+ ((stack .! p |= var "h$blackhole") <> returnS null_)+ ]+++-- | JS payload to declare the registers+declRegs :: JStat+declRegs =+ mconcat [ TxtI "h$regs" ||= toJExpr (JList [])+ , mconcat (map declReg (enumFromTo R1 R32))+ , regGettersSetters+ , loadRegs+ ]+ where+ declReg r = (decl . TxtI . mkFastString . ("h$"++) . map toLower . show) r+ <> BlockStat [AssignStat (toJExpr r) (ValExpr (JInt 0))] -- [j| `r` = 0; |]++-- | JS payload to define getters and setters on the registers.+regGettersSetters :: JStat+regGettersSetters =+ mconcat [ TxtI "h$getReg" ||= jLam (\n -> SwitchStat n getRegCases mempty)+ , TxtI "h$setReg" ||= jLam (\n v -> SwitchStat n (setRegCases v) mempty)+ ]+ where+ getRegCases =+ map (\r -> (toJExpr (jsRegToInt r) , returnS (toJExpr r))) regsFromR1+ setRegCases v =+ map (\r -> (toJExpr (jsRegToInt r), (toJExpr r |= toJExpr v) <> returnS undefined_)) regsFromR1++-- | JS payload that defines the functions to load each register+loadRegs :: JStat+loadRegs = mconcat $ map mkLoad [1..32]+ where+ mkLoad :: Int -> JStat+ mkLoad n = let args = map (TxtI . mkFastString . ("x"++) . show) [1..n]+ assign = zipWith (\a r -> toJExpr r |= toJExpr a)+ args (reverse $ take n regsFromR1)+ fname = TxtI $ mkFastString ("h$l" ++ show n)+ fun = JFunc args (mconcat assign)+ in fname ||= toJExpr fun++-- | Assign registers R1 ... Rn in descending order, that is assign Rn first.+-- This function uses the 'assignRegs'' array to construct functions which set+-- the registers.+assignRegs :: StgToJSConfig -> [JExpr] -> JStat+assignRegs _ [] = mempty+assignRegs s xs+ | l <= 32 && not (csInlineLoadRegs s)+ = ApplStat (ValExpr (JVar $ assignRegs'!l)) (reverse xs)+ | otherwise = mconcat . reverse $+ zipWith (\r ex -> toJExpr r |= ex) (take l regsFromR1) xs+ where+ l = length xs++-- | JS payload which defines an array of function symbols that set N registers+-- from M parameters. For example, h$l2 compiles to:+-- @+-- function h$l4(x1, x2, x3, x4) {+-- h$r4 = x1;+-- h$r3 = x2;+-- h$r2 = x3;+-- h$r1 = x4;+-- };+-- @+assignRegs' :: Array Int Ident+assignRegs' = listArray (1,32) (map (TxtI . mkFastString . ("h$l"++) . show) [(1::Int)..32])++-- | JS payload to declare return variables.+declRets :: JStat+declRets = mconcat $ map (decl . TxtI . mkFastString . ("h$"++) . map toLower . show) (enumFrom Ret1)++-- | JS payload defining the types closures.+closureTypes :: JStat+closureTypes = mconcat (map mkClosureType (enumFromTo minBound maxBound)) <> closureTypeName+ where+ mkClosureType :: ClosureType -> JStat+ mkClosureType c = let s = TxtI . mkFastString $ "h$" ++ map toUpper (show c) ++ "_CLOSURE"+ in s ||= toJExpr c+ closureTypeName :: JStat+ closureTypeName =+ TxtI "h$closureTypeName" ||= jLam (\c ->+ mconcat (map (ifCT c) [minBound..maxBound])+ <> returnS (jString "InvalidClosureType"))++ ifCT :: JExpr -> ClosureType -> JStat+ ifCT arg ct = jwhenS (arg .===. toJExpr ct) (returnS (toJExpr (show ct)))++-- | JS payload declaring the RTS functions.+rtsDecls :: JStat+rtsDecls = jsSaturate (Just "h$RTSD") $+ mconcat [ TxtI "h$currentThread" ||= null_ -- thread state object for current thread+ , TxtI "h$stack" ||= null_ -- stack for the current thread+ , TxtI "h$sp" ||= 0 -- stack pointer for the current thread+ , TxtI "h$initStatic" ||= toJExpr (JList []) -- we need delayed initialization for static objects, push functions here to be initialized just before haskell runs+ , TxtI "h$staticThunks" ||= toJExpr (jhFromList []) -- funcName -> heapidx map for srefs+ , TxtI "h$staticThunksArr" ||= toJExpr (JList []) -- indices of updatable thunks in static heap+ , TxtI "h$CAFs" ||= toJExpr (JList [])+ , TxtI "h$CAFsReset" ||= toJExpr (JList [])+ -- stg registers+ , declRegs+ , declRets]++-- | print the embedded RTS to a String+rtsText :: StgToJSConfig -> String+rtsText = show . pretty . rts++-- | print the RTS declarations to a String.+rtsDeclsText :: String+rtsDeclsText = show . pretty $ rtsDecls++-- | Wrapper over the RTS to guarentee saturation, see 'GHC.JS.Transform'+rts :: StgToJSConfig -> JStat+rts = jsSaturate (Just "h$RTS") . rts'++-- | JS Payload which defines the embedded RTS.+rts' :: StgToJSConfig -> JStat+rts' s =+ mconcat [ closureConstructors s+ , garbageCollector+ , stackManip+ , TxtI "h$rts_traceForeign" ||= toJExpr (csTraceForeign s)+ , TxtI "h$rts_profiling" ||= toJExpr (csProf s)+ , TxtI "h$ct_fun" ||= toJExpr Fun+ , TxtI "h$ct_con" ||= toJExpr Con+ , TxtI "h$ct_thunk" ||= toJExpr Thunk+ , TxtI "h$ct_pap" ||= toJExpr Pap+ , TxtI "h$ct_blackhole" ||= toJExpr Blackhole+ , TxtI "h$ct_stackframe" ||= toJExpr StackFrame+ , TxtI "h$vt_ptr" ||= toJExpr PtrV+ , TxtI "h$vt_void" ||= toJExpr VoidV+ , TxtI "h$vt_double" ||= toJExpr IntV+ , TxtI "h$vt_long" ||= toJExpr LongV+ , TxtI "h$vt_addr" ||= toJExpr AddrV+ , TxtI "h$vt_rtsobj" ||= toJExpr RtsObjV+ , TxtI "h$vt_obj" ||= toJExpr ObjV+ , TxtI "h$vt_arr" ||= toJExpr ArrV+ , TxtI "h$bh" ||= jLam (bhStats s True)+ , TxtI "h$bh_lne" ||= jLam (\x frameSize -> bhLneStats s x frameSize)+ , closure (ClosureInfo (TxtI "h$blackhole") (CIRegs 0 []) "blackhole" (CILayoutUnknown 2) CIBlackhole mempty)+ (appS "throw" [jString "oops: entered black hole"])+ , closure (ClosureInfo (TxtI "h$blackholeTrap") (CIRegs 0 []) "blackhole" (CILayoutUnknown 2) CIThunk mempty)+ (appS "throw" [jString "oops: entered multiple times"])+ , closure (ClosureInfo (TxtI "h$done") (CIRegs 0 [PtrV]) "done" (CILayoutUnknown 0) CIStackFrame mempty)+ (appS "h$finishThread" [var "h$currentThread"] <> returnS (var "h$reschedule"))+ , closure (ClosureInfo (TxtI "h$doneMain_e") (CIRegs 0 [PtrV]) "doneMain" (CILayoutUnknown 0) CIStackFrame mempty)+ (returnS (var "h$doneMain"))+ , conClosure (TxtI "h$false_e") "GHC.Types.False" (CILayoutFixed 0 []) 1+ , conClosure (TxtI "h$true_e" ) "GHC.Types.True" (CILayoutFixed 0 []) 2+ -- generic data constructor with 1 non-heapobj field+ , conClosure (TxtI "h$data1_e") "data1" (CILayoutFixed 1 [ObjV]) 1+ -- generic data constructor with 2 non-heapobj fields+ , conClosure (TxtI "h$data2_e") "data2" (CILayoutFixed 2 [ObjV,ObjV]) 1+ , closure (ClosureInfo (TxtI "h$noop_e") (CIRegs 1 [PtrV]) "no-op IO ()" (CILayoutFixed 0 []) (CIFun 1 0) mempty)+ (returnS (stack .! sp))+ <> (TxtI "h$noop" ||= ApplExpr (var "h$c0") (var "h$noop_e" : [jSystemCCS | csProf s]))+ , closure (ClosureInfo (TxtI "h$catch_e") (CIRegs 0 [PtrV]) "exception handler" (CILayoutFixed 2 [PtrV,IntV]) CIStackFrame mempty)+ (adjSpN' 3 <> returnS (stack .! sp))+ , closure (ClosureInfo (TxtI "h$dataToTag_e") (CIRegs 0 [PtrV]) "data to tag" (CILayoutFixed 0 []) CIStackFrame mempty)+ $ mconcat [ r1 |= if_ (r1 .===. true_) 1 (if_ (typeof r1 .===. jTyObject) (r1 .^ "f" .^ "a" - 1) 0)+ , adjSpN' 1+ , returnS (stack .! sp)+ ]+ -- function application to one argument+ , closure (ClosureInfo (TxtI "h$ap1_e") (CIRegs 0 [PtrV]) "apply1" (CILayoutFixed 2 [PtrV, PtrV]) CIThunk mempty)+ (jVar $ \d1 d2 ->+ mconcat [ d1 |= closureField1 r1+ , d2 |= closureField2 r1+ , appS "h$bh" []+ , profStat s enterCostCentreThunk+ , r1 |= d1+ , r2 |= d2+ , returnS (app "h$ap_1_1_fast" [])+ ])+ -- function application to two arguments+ , closure (ClosureInfo (TxtI "h$ap2_e") (CIRegs 0 [PtrV]) "apply2" (CILayoutFixed 3 [PtrV, PtrV, PtrV]) CIThunk mempty)+ (jVar $ \d1 d2 d3 ->+ mconcat [ d1 |= closureField1 r1+ , d2 |= closureField2 r1 .^ "d1"+ , d3 |= closureField2 r1 .^ "d2"+ , appS "h$bh" []+ , profStat s enterCostCentreThunk+ , r1 |= d1+ , r2 |= d2+ , r3 |= d3+ , returnS (app "h$ap_2_2_fast" [])+ ])+ -- function application to three arguments+ , closure (ClosureInfo (TxtI "h$ap3_e") (CIRegs 0 [PtrV]) "apply3" (CILayoutFixed 4 [PtrV, PtrV, PtrV, PtrV]) CIThunk mempty)+ (jVar $ \d1 d2 d3 d4 ->+ mconcat [ d1 |= closureField1 r1+ , d2 |= closureField2 r1 .^ "d1"+ , d3 |= closureField2 r1 .^ "d2"+ , d4 |= closureField2 r1 .^ "d3"+ , appS "h$bh" []+ , r1 |= d1+ , r2 |= d2+ , r3 |= d3+ , r4 |= d4+ , returnS (app "h$ap_3_3_fast" [])+ ])+ -- select first field+ , closure (ClosureInfo (TxtI "h$select1_e") (CIRegs 0 [PtrV]) "select1" (CILayoutFixed 1 [PtrV]) CIThunk mempty)+ (jVar $ \t ->+ mconcat [ t |= closureField1 r1+ , adjSp' 3+ , stack .! (sp - 2) |= r1+ , stack .! (sp - 1) |= var "h$upd_frame"+ , stack .! sp |= var "h$select1_ret"+ , closureEntry r1 |= var "h$blackhole"+ , closureField1 r1 |= var "h$currentThread"+ , closureField2 r1 |= null_+ , r1 |= t+ , returnS (app "h$ap_0_0_fast" [])+ ])+ , closure (ClosureInfo (TxtI "h$select1_ret") (CIRegs 0 [PtrV]) "select1ret" (CILayoutFixed 0 []) CIStackFrame mempty)+ ((r1 |= closureField1 r1)+ <> adjSpN' 1+ <> returnS (app "h$ap_0_0_fast" [])+ )+ -- select second field of a two-field constructor+ , closure (ClosureInfo (TxtI "h$select2_e") (CIRegs 0 [PtrV]) "select2" (CILayoutFixed 1 [PtrV]) CIThunk mempty)+ (jVar $ \t ->+ mconcat [t |= closureField1 r1+ , adjSp' 3+ , stack .! (sp - 2) |= r1+ , stack .! (sp - 1) |= var "h$upd_frame"+ , stack .! sp |= var "h$select2_ret"+ , closureEntry r1 |= var "h$blackhole"+ , closureField1 r1 |= var "h$currentThread"+ , closureField2 r1 |= null_+ , r1 |= t+ , returnS (app "h$ap_0_0_fast" [])+ ]+ )+ , closure (ClosureInfo (TxtI "h$select2_ret") (CIRegs 0 [PtrV]) "select2ret" (CILayoutFixed 0 []) CIStackFrame mempty)+ $ mconcat [ r1 |= closureField2 r1+ , adjSpN' 1+ , returnS (app "h$ap_0_0_fast" [])+ ]+ , closure (ClosureInfo (TxtI "h$keepAlive_e") (CIRegs 0 [PtrV]) "keepAlive" (CILayoutFixed 1 [PtrV]) CIStackFrame mempty)+ (mconcat [ adjSpN' 2+ , returnS (stack .! sp)+ ]+ )+ -- a thunk that just raises a synchronous exception+ , closure (ClosureInfo (TxtI "h$raise_e") (CIRegs 0 [PtrV]) "h$raise_e" (CILayoutFixed 0 []) CIThunk mempty)+ (returnS (app "h$throw" [closureField1 r1, false_]))+ , closure (ClosureInfo (TxtI "h$raiseAsync_e") (CIRegs 0 [PtrV]) "h$raiseAsync_e" (CILayoutFixed 0 []) CIThunk mempty)+ (returnS (app "h$throw" [closureField1 r1, true_]))+ , closure (ClosureInfo (TxtI "h$raiseAsync_frame") (CIRegs 0 []) "h$raiseAsync_frame" (CILayoutFixed 1 []) CIStackFrame mempty)+ (jVar $ \ex ->+ mconcat [ ex |= stack .! (sp - 1)+ , adjSpN' 2+ , returnS (app "h$throw" [ex, true_])+ ])+ {- reduce result if it's a thunk, follow if it's an ind+ add this to the stack if you want the outermost result+ to always be reduced to whnf, and not an ind+ -}+ , closure (ClosureInfo (TxtI "h$reduce") (CIRegs 0 [PtrV]) "h$reduce" (CILayoutFixed 0 []) CIStackFrame mempty)+ (ifS (isThunk r1)+ (returnS (r1 .^ "f"))+ (adjSpN' 1 <> returnS (stack .! sp))+ )+ , rtsApply s+ , closureTypes+ , closure (ClosureInfo (TxtI "h$runio_e") (CIRegs 0 [PtrV]) "runio" (CILayoutFixed 1 [PtrV]) CIThunk mempty)+ $ mconcat [ r1 |= closureField1 r1+ , stack .! PreInc sp |= var "h$ap_1_0"+ , returnS (var "h$ap_1_0")+ ]+ , closure (ClosureInfo (TxtI "h$flushStdout_e") (CIRegs 0 []) "flushStdout" (CILayoutFixed 0 []) CIThunk mempty)+ $ mconcat [ r1 |= var "h$baseZCGHCziIOziHandlezihFlush"+ , r2 |= var "h$baseZCGHCziIOziHandleziFDzistdout"+ , returnS (app "h$ap_1_1_fast" [])+ ]+ , TxtI "h$flushStdout" ||= app "h$static_thunk" [var "h$flushStdout_e"]+ -- the scheduler pushes this frame when suspending a thread that+ -- has not called h$reschedule explicitly+ , closure (ClosureInfo (TxtI "h$restoreThread") (CIRegs 0 []) "restoreThread" CILayoutVariable CIStackFrame mempty)+ (jVar $ \f frameSize nregs ->+ mconcat [f |= stack .! (sp - 2)+ , frameSize |= stack .! (sp - 1)+ , nregs |= frameSize - 3+ , loop 1 (.<=. nregs)+ (\i -> appS "h$setReg" [i, stack .! (sp - 2 - i)] <> postIncrS i)+ , sp |= sp - frameSize+ , returnS f+ ])+ -- return a closure in the stack frame to the next thing on the stack+ , closure (ClosureInfo (TxtI "h$return") (CIRegs 0 []) "return" (CILayoutFixed 1 [PtrV]) CIStackFrame mempty)+ ((r1 |= stack .! (sp - 1))+ <> adjSpN' 2+ <> returnS (stack .! sp))+ -- return a function in the stack frame for the next call+ , closure (ClosureInfo (TxtI "h$returnf") (CIRegs 0 [PtrV]) "returnf" (CILayoutFixed 1 [ObjV]) CIStackFrame mempty)+ (jVar $ \r ->+ mconcat [ r |= stack .! (sp - 1)+ , adjSpN' 2+ , returnS r+ ])+ -- return this function when the scheduler needs to come into action+ -- (yield, delay etc), returning thread needs to push all relevant+ -- registers to stack frame, thread will be resumed by calling the stack top+ , closure (ClosureInfo (TxtI "h$reschedule") (CIRegs 0 []) "reschedule" (CILayoutFixed 0 []) CIThunk mempty)+ (returnS $ var "h$reschedule")+ -- debug thing, insert on stack to dump current result, should be boxed+ , closure (ClosureInfo (TxtI "h$dumpRes") (CIRegs 0 [PtrV]) "dumpRes" (CILayoutFixed 1 [ObjV]) CIThunk mempty)+ (jVar $ \re ->+ mconcat [ appS "h$log" [jString "h$dumpRes result: " + stack .! (sp-1)]+ , appS "h$log" [r1]+ , appS "h$log" [app "h$collectProps" [r1]]+ , jwhenS ((r1 .^ "f") .&&. (r1 .^ "f" .^ "n"))+ (appS "h$log" [jString "name: " + r1 .^ "f" .^ "n"])+ , jwhenS (ApplExpr (r1 .^ "hasOwnProperty") [jString closureField1_])+ (appS "h$log" [jString "d1: " + closureField1 r1])+ , jwhenS (ApplExpr (r1 .^ "hasOwnProperty") [jString closureField2_])+ (appS "h$log" [jString "d2: " + closureField2 r1])+ , jwhenS (r1 .^ "f") $ mconcat+ [ re |= New (app "RegExp" [jString "([^\\n]+)\\n(.|\\n)*"])+ , appS "h$log" [jString "function"+ + ApplExpr (ApplExpr ((jString "" + r1 .^ "f") .^ "substring") [0, 50] .^ "replace") [r1, jString "$1"]]+ ]+ , adjSpN' 2+ , r1 |= null_+ , returnS (stack .! sp)+ ])+ , closure (ClosureInfo (TxtI "h$resume_e") (CIRegs 0 [PtrV]) "resume" (CILayoutFixed 0 []) CIThunk mempty)+ (jVar $ \ss ->+ mconcat [ss |= closureField1 r1+ , updateThunk' s+ , loop 0 (.<. ss .^ "length") (\i -> (stack .! (sp+1+i) |= ss .! i)+ <> postIncrS i)+ , sp |= sp + ss .^ "length"+ , r1 |= null_+ , returnS (stack .! sp)+ ])+ , closure (ClosureInfo (TxtI "h$unmaskFrame") (CIRegs 0 [PtrV]) "unmask" (CILayoutFixed 0 []) CIStackFrame mempty)+ ((var "h$currentThread" .^ "mask" |= 0)+ <> adjSpN' 1+ -- back to scheduler to give us async exception if pending+ <> ifS (var "h$currentThread" .^ "excep" .^ "length" .>. 0)+ (push' s [r1, var "h$return"] <> returnS (var "h$reschedule"))+ (returnS (stack .! sp)))+ , closure (ClosureInfo (TxtI "h$maskFrame") (CIRegs 0 [PtrV]) "mask" (CILayoutFixed 0 []) CIStackFrame mempty)+ ((var "h$currentThread" .^ "mask" |= 2)+ <> adjSpN' 1+ <> returnS (stack .! sp))+ , closure (ClosureInfo (TxtI "h$maskUnintFrame") (CIRegs 0 [PtrV]) "maskUnint" (CILayoutFixed 0 []) CIStackFrame mempty)+ ((var "h$currentThread" .^ "mask" |= 1)+ <> adjSpN' 1+ <> returnS (stack .! sp))+ , closure (ClosureInfo (TxtI "h$unboxFFIResult") (CIRegs 0 [PtrV]) "unboxFFI" (CILayoutFixed 0 []) CIStackFrame mempty)+ (jVar $ \d ->+ mconcat [d |= closureField1 r1+ , loop 0 (.<. d .^ "length") (\i -> appS "h$setReg" [i + 1, d .! i] <> postIncrS i)+ , adjSpN' 1+ , returnS (stack .! sp)+ ])+ , closure (ClosureInfo (TxtI "h$unbox_e") (CIRegs 0 [PtrV]) "unboxed value" (CILayoutFixed 1 [DoubleV]) CIThunk mempty)+ ((r1 |= closureField1 r1) <> returnS (stack .! sp))+ , closure (ClosureInfo (TxtI "h$retryInterrupted") (CIRegs 0 [ObjV]) "retry interrupted operation" (CILayoutFixed 1 [ObjV]) CIStackFrame mempty)+ (jVar $ \a ->+ mconcat [ a |= stack .! (sp - 1)+ , adjSpN' 2+ , returnS (ApplExpr (a .! 0 .^ "apply") [var "this", ApplExpr (a .^ "slice") [1]])+ ])+ , closure (ClosureInfo (TxtI "h$atomically_e") (CIRegs 0 [PtrV]) "atomic operation" (CILayoutFixed 1 [PtrV]) CIStackFrame mempty)+ (ifS (app "h$stmValidateTransaction" [])+ (appS "h$stmCommitTransaction" []+ <> adjSpN' 2+ <> returnS (stack .! sp))+ (returnS (app "h$stmStartTransaction" [stack .! (sp - 1)])))++ , closure (ClosureInfo (TxtI "h$stmCatchRetry_e") (CIRegs 0 [PtrV]) "catch retry" (CILayoutFixed 1 [PtrV]) CIStackFrame mempty)+ (adjSpN' 2+ <> appS "h$stmCommitTransaction" []+ <> returnS (stack .! sp))+ , closure (ClosureInfo (TxtI "h$catchStm_e") (CIRegs 0 [PtrV]) "STM catch" (CILayoutFixed 3 [ObjV,PtrV,ObjV]) CIStackFrame mempty)+ (adjSpN' 4+ <> appS "h$stmCommitTransaction" []+ <> returnS (stack .! sp))+ , closure (ClosureInfo (TxtI "h$stmResumeRetry_e") (CIRegs 0 [PtrV]) "resume retry" (CILayoutFixed 0 []) CIStackFrame mempty)+ (jVar $ \blocked ->+ mconcat [ jwhenS (stack .! (sp - 2) .!==. var "h$atomically_e")+ (appS "throw" [jString "h$stmResumeRetry_e: unexpected value on stack"])+ , blocked |= stack .! (sp - 1)+ , adjSpN' 2+ , appS "h$stmRemoveBlockedThread" [blocked, var "h$currentThread"]+ , returnS (app "h$stmStartTransaction" [stack .! (sp - 1)])+ ])+ , closure (ClosureInfo (TxtI "h$lazy_e") (CIRegs 0 [PtrV]) "generic lazy value" (CILayoutFixed 0 []) CIThunk mempty)+ (jVar $ \x ->+ mconcat [x |= ApplExpr (closureField1 r1) []+ , appS "h$bh" []+ , profStat s enterCostCentreThunk+ , r1 |= x+ , returnS (stack .! sp)+ ])+ -- Top-level statements to generate only in profiling mode+ , profStat s (closure (ClosureInfo (TxtI "h$setCcs_e") (CIRegs 0 [PtrV]) "set cost centre stack" (CILayoutFixed 1 [ObjV]) CIStackFrame mempty)+ (appS "h$restoreCCS" [ stack .! (sp - 1)]+ <> adjSpN' 2+ <> returnS (stack .! sp)))+ ]
+ compiler/GHC/StgToJS/Rts/Types.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE CPP,+ FlexibleInstances,+ OverloadedStrings #-}++-----------------------------------------------------------------------------+-- |+-- Module : GHC.StgToJS.Rts.Apply+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Sylvain Henry <sylvain.henry@iohk.io>+-- Josh Meredith <josh.meredith@iohk.io>+-- Stability : experimental+--+-- Types and utility functions used in the JS RTS.+-----------------------------------------------------------------------------++module GHC.StgToJS.Rts.Types where++import GHC.Prelude++import GHC.JS.Make+import GHC.JS.Syntax+import GHC.StgToJS.Regs+import GHC.StgToJS.Types++--------------------------------------------------------------------------------+-- Syntactic Sugar for some Utilities we want in JS land+--------------------------------------------------------------------------------++-- | Syntactic sugar, i.e., a Haskell function which generates useful JS code.+-- Given a @JExpr@, 'ex', inject a trace statement on 'ex' in the compiled JS+-- program+traceRts :: StgToJSConfig -> JExpr -> JStat+traceRts s ex | (csTraceRts s) = appS "h$log" [ex]+ | otherwise = mempty++-- | Syntactic sugar. Given a @JExpr@, 'ex' which is assumed to be a predicate,+-- and a message 'm', assert that 'not ex' is True, if not throw an exception in+-- JS land with message 'm'.+assertRts :: ToJExpr a => StgToJSConfig -> JExpr -> a -> JStat+assertRts s ex m | csAssertRts s = jwhenS (UOpExpr NotOp ex) (appS "throw" [toJExpr m])+ | otherwise = mempty++-- | name of the closure 'c'+clName :: JExpr -> JExpr+clName c = c .^ "n"++-- | Type name of the closure 'c'+clTypeName :: JExpr -> JExpr+clTypeName c = app "h$closureTypeName" [c .^ "t"]++-- number of arguments (arity & 0xff = arguments, arity >> 8 = number of registers)+stackFrameSize :: JExpr -- ^ assign frame size to this+ -> JExpr -- ^ stack frame header function+ -> JStat -- ^ size of the frame, including header+stackFrameSize tgt f =+ ifS (f .===. var "h$ap_gen") -- h$ap_gen is special+ (tgt |= (stack .! (sp - 1) .>>. 8) + 2)+ (jVar (\tag ->+ mconcat+ [tag |= f .^ "size"+ , ifS (tag .<. 0) -- if tag is less than 0+ (tgt |= stack .! (sp - 1)) -- set target to stack pointer - 1+ (tgt |= mask8 tag + 1) -- else set to mask'd tag + 1+ ]+ ))++--------------------------------------------------------------------------------+-- Register utilities+--------------------------------------------------------------------------------++-- | Perform the computation 'f', on the range of registers bounded by 'start'+-- and 'end'.+withRegs :: StgReg -> StgReg -> (StgReg -> JStat) -> JStat+withRegs start end f = mconcat $ fmap f [start..end]
+ compiler/GHC/StgToJS/Sinker.hs view
@@ -0,0 +1,180 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE LambdaCase #-}++module GHC.StgToJS.Sinker (sinkPgm) where++import GHC.Prelude+import GHC.Types.Unique.Set+import GHC.Types.Unique.FM+import GHC.Types.Var.Set+import GHC.Stg.Syntax+import GHC.Types.Id+import GHC.Types.Name+import GHC.Unit.Module+import GHC.Types.Literal+import GHC.Data.Graph.Directed++import GHC.StgToJS.CoreUtils++import Data.Char+import Data.Either+import Data.List (partition)+import Data.Maybe+++-- | Unfloat some top-level unexported things+--+-- GHC floats constants to the top level. This is fine in native code, but with JS+-- they occupy some global variable name. We can unfloat some unexported things:+--+-- - global constructors, as long as they're referenced only once by another global+-- constructor and are not in a recursive binding group+-- - literals (small literals may also be sunk if they are used more than once)+sinkPgm :: Module+ -> [CgStgTopBinding]+ -> (UniqFM Id CgStgExpr, [CgStgTopBinding])+sinkPgm m pgm = (sunk, map StgTopLifted pgm'' ++ stringLits)+ where+ selectLifted (StgTopLifted b) = Left b+ selectLifted x = Right x+ (pgm', stringLits) = partitionEithers (map selectLifted pgm)+ (sunk, pgm'') = sinkPgm' m pgm'++sinkPgm'+ :: Module+ -- ^ the module, since we treat definitions from the current module+ -- differently+ -> [CgStgBinding]+ -- ^ the bindings+ -> (UniqFM Id CgStgExpr, [CgStgBinding])+ -- ^ a map with sunken replacements for nodes, for where the replacement+ -- does not fit in the 'StgBinding' AST and the new bindings+sinkPgm' m pgm =+ let usedOnce = collectUsedOnce pgm+ sinkables = listToUFM $+ concatMap alwaysSinkable pgm +++ filter ((`elementOfUniqSet` usedOnce) . fst) (concatMap (onceSinkable m) pgm)+ isSunkBind (StgNonRec b _e) | elemUFM b sinkables = True+ isSunkBind _ = False+ in (sinkables, filter (not . isSunkBind) $ topSortDecls m pgm)++-- | always sinkable, values that may be duplicated in the generated code (e.g.+-- small literals)+alwaysSinkable :: CgStgBinding -> [(Id, CgStgExpr)]+alwaysSinkable (StgRec {}) = []+alwaysSinkable (StgNonRec b rhs) = case rhs of+ StgRhsClosure _ _ _ _ e@(StgLit l)+ | isSmallSinkableLit l+ , isLocal b+ -> [(b,e)]+ StgRhsCon _ccs dc cnum _ticks as@[StgLitArg l]+ | isSmallSinkableLit l+ , isLocal b+ , isUnboxableCon dc+ -> [(b,StgConApp dc cnum as [])]+ _ -> []++isSmallSinkableLit :: Literal -> Bool+isSmallSinkableLit (LitChar c) = ord c < 100000+isSmallSinkableLit (LitNumber _ i) = abs i < 100000+isSmallSinkableLit _ = False+++-- | once sinkable: may be sunk, but duplication is not ok+onceSinkable :: Module -> CgStgBinding -> [(Id, CgStgExpr)]+onceSinkable _m (StgNonRec b rhs)+ | Just e <- getSinkable rhs+ , isLocal b = [(b,e)]+ where+ getSinkable = \case+ StgRhsCon _ccs dc cnum _ticks args -> Just (StgConApp dc cnum args [])+ StgRhsClosure _ _ _ _ e@(StgLit{}) -> Just e+ _ -> Nothing+onceSinkable _ _ = []++-- | collect all idents used only once in an argument at the top level+-- and never anywhere else+collectUsedOnce :: [CgStgBinding] -> IdSet+collectUsedOnce binds = intersectUniqSets (usedOnce args) (usedOnce top_args)+ where+ top_args = concatMap collectArgsTop binds+ args = concatMap collectArgs binds+ usedOnce = fst . foldr g (emptyUniqSet, emptyUniqSet)+ g i t@(once, mult)+ | i `elementOfUniqSet` mult = t+ | i `elementOfUniqSet` once+ = (delOneFromUniqSet once i, addOneToUniqSet mult i)+ | otherwise = (addOneToUniqSet once i, mult)++-- | fold over all id in StgArg used at the top level in an StgRhsCon+collectArgsTop :: CgStgBinding -> [Id]+collectArgsTop = \case+ StgNonRec _b r -> collectArgsTopRhs r+ StgRec bs -> concatMap (collectArgsTopRhs . snd) bs++collectArgsTopRhs :: CgStgRhs -> [Id]+collectArgsTopRhs = \case+ StgRhsCon _ccs _dc _mu _ticks args -> concatMap collectArgsA args+ StgRhsClosure {} -> []++-- | fold over all Id in StgArg in the AST+collectArgs :: CgStgBinding -> [Id]+collectArgs = \case+ StgNonRec _b r -> collectArgsR r+ StgRec bs -> concatMap (collectArgsR . snd) bs++collectArgsR :: CgStgRhs -> [Id]+collectArgsR = \case+ StgRhsClosure _x0 _x1 _x2 _x3 e -> collectArgsE e+ StgRhsCon _ccs _con _mu _ticks args -> concatMap collectArgsA args++collectArgsAlt :: CgStgAlt -> [Id]+collectArgsAlt alt = collectArgsE (alt_rhs alt)++collectArgsE :: CgStgExpr -> [Id]+collectArgsE = \case+ StgApp x args+ -> x : concatMap collectArgsA args+ StgConApp _con _mn args _ts+ -> concatMap collectArgsA args+ StgOpApp _x args _t+ -> concatMap collectArgsA args+ StgCase e _b _a alts+ -> collectArgsE e ++ concatMap collectArgsAlt alts+ StgLet _x b e+ -> collectArgs b ++ collectArgsE e+ StgLetNoEscape _x b e+ -> collectArgs b ++ collectArgsE e+ StgTick _i e+ -> collectArgsE e+ StgLit _+ -> []++collectArgsA :: StgArg -> [Id]+collectArgsA = \case+ StgVarArg i -> [i]+ StgLitArg _ -> []++isLocal :: Id -> Bool+isLocal i = isNothing (nameModule_maybe . idName $ i) && not (isExportedId i)++-- | since we have sequential initialization, topsort the non-recursive+-- constructor bindings+topSortDecls :: Module -> [CgStgBinding] -> [CgStgBinding]+topSortDecls _m binds = rest ++ nr'+ where+ (nr, rest) = partition isNonRec binds+ isNonRec StgNonRec{} = True+ isNonRec _ = False+ vs = map getV nr+ keys = mkUniqSet (map node_key vs)+ getV e@(StgNonRec b _) = DigraphNode e b []+ getV _ = error "topSortDecls: getV, unexpected binding"+ collectDeps (StgNonRec b (StgRhsCon _cc _dc _cnum _ticks args)) =+ [ (i, b) | StgVarArg i <- args, i `elementOfUniqSet` keys ]+ collectDeps _ = []+ g = graphFromVerticesAndAdjacency vs (concatMap collectDeps nr)+ nr' | (not . null) [()| CyclicSCC _ <- stronglyConnCompG g]+ = error "topSortDecls: unexpected cycle"+ | otherwise = map node_payload (topologicalSortG g)
+ compiler/GHC/StgToJS/Stack.hs view
@@ -0,0 +1,373 @@+{-# LANGUAGE OverloadedStrings #-}++-----------------------------------------------------------------------------+-- |+-- Module : GHC.JS.Stack+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Sylvain Henry <sylvain.henry@iohk.io>+-- Josh Meredith <josh.meredith@iohk.io>+-- Stability : experimental+--+-- Utilities and wrappers for Stack manipulation in JS Land.+--+-- In general, functions suffixed with a tick do the actual work, functions+-- suffixed with an "I" are identical to the non-I versions but work on 'Ident's+--+-- The stack in JS land is held in the special JS array 'h$stack' and the stack+-- pointer is held in 'h$sp'. The top of the stack thus exists at+-- 'h$stack[h$sp]'. h$stack[h$sp + i] where i > 0, moves deeper into the stack+-- into older entries, whereas h$stack[h$sp - i] moves towards the top of the+-- stack.+--+-- The stack layout algorithm is slightly peculiar. It makes an effort to+-- remember recently popped things so that if these values need to be pushed+-- then they can be quickly. The implementation for this is storing these values+-- above the stack pointer, and the pushing will skip slots that we know we will+-- use and fill in slots marked as unknown. Thus, you may find that our push and+-- pop functions do some non-traditional stack manipulation such as adding slots+-- in pop or removing slots in push.+-----------------------------------------------------------------------------++module GHC.StgToJS.Stack+ ( resetSlots+ , isolateSlots+ , setSlots+ , getSlots+ , addSlots+ , dropSlots+ , addUnknownSlots+ , push+ , push'+ , adjSpN+ , adjSpN'+ , adjSp'+ , adjSp+ , pushNN+ , pushNN'+ , pushN'+ , pushN+ , pushOptimized'+ , pushOptimized+ , pushLneFrame+ , popN+ , popSkip+ , popSkipI+ , loadSkip+ -- * Thunk update+ , updateThunk+ , updateThunk'+ , bhStats+ )+where++import GHC.Prelude++import GHC.JS.Syntax+import GHC.JS.Make++import GHC.StgToJS.Types+import GHC.StgToJS.Monad+import GHC.StgToJS.Ids+import GHC.StgToJS.ExprCtx+import GHC.StgToJS.Heap+import GHC.StgToJS.Regs++import GHC.Types.Id+import GHC.Utils.Misc+import GHC.Data.FastString++import qualified Data.Bits as Bits+import qualified Data.List as L+import qualified Control.Monad.Trans.State.Strict as State+import Data.Array+import Data.Monoid+import Control.Monad++-- | Run the action, 'm', with no stack info+resetSlots :: G a -> G a+resetSlots m = do+ s <- getSlots+ d <- getStackDepth+ setSlots []+ a <- m+ setSlots s+ setStackDepth d+ return a++-- | run the action, 'm', with current stack info, but don't let modifications+-- propagate+isolateSlots :: G a -> G a+isolateSlots m = do+ s <- getSlots+ d <- getStackDepth+ a <- m+ setSlots s+ setStackDepth d+ pure a++-- | Set stack depth+setStackDepth :: Int -> G ()+setStackDepth d = modifyGroup (\s -> s { ggsStackDepth = d})++-- | Get stack depth+getStackDepth :: G Int+getStackDepth = State.gets (ggsStackDepth . gsGroup)++-- | Modify stack depth+modifyStackDepth :: (Int -> Int) -> G ()+modifyStackDepth f = modifyGroup (\s -> s { ggsStackDepth = f (ggsStackDepth s) })++-- | overwrite our stack knowledge+setSlots :: [StackSlot] -> G ()+setSlots xs = modifyGroup (\g -> g { ggsStack = xs})++-- | retrieve our current stack knowledge+getSlots :: G [StackSlot]+getSlots = State.gets (ggsStack . gsGroup)++-- | Modify stack slots+modifySlots :: ([StackSlot] -> [StackSlot]) -> G ()+modifySlots f = modifyGroup (\g -> g { ggsStack = f (ggsStack g)})++-- | add `n` unknown slots to our stack knowledge+addUnknownSlots :: Int -> G ()+addUnknownSlots n = addSlots (replicate n SlotUnknown)++-- | add knowledge about the stack slots+addSlots :: [StackSlot] -> G ()+addSlots xs = do+ s <- getSlots+ setSlots (xs ++ s)++-- | drop 'n' slots from our stack knowledge+dropSlots :: Int -> G ()+dropSlots n = modifySlots (drop n)++push :: [JExpr] -> G JStat+push xs = do+ dropSlots (length xs)+ modifyStackDepth (+ (length xs))+ flip push' xs <$> getSettings++push' :: StgToJSConfig -> [JExpr] -> JStat+push' _ [] = mempty+push' cs xs+ | csInlinePush cs || l > 32 || l < 2 = adjSp' l <> mconcat items+ | otherwise = ApplStat (toJExpr $ pushN ! l) xs+ where+ items = zipWith f [(1::Int)..] xs+ offset i | i == l = sp+ | otherwise = InfixExpr SubOp sp (toJExpr (l - i))+ l = length xs+ f i e = AssignStat ((IdxExpr stack) (toJExpr (offset i))) (toJExpr e)+++-- | Grow the stack pointer by 'n' without modifying the stack depth. The stack+-- is just a JS array so we add to grow (instead of the traditional subtract)+adjSp' :: Int -> JStat+adjSp' 0 = mempty+adjSp' n = sp |= InfixExpr AddOp sp (toJExpr n)++-- | Shrink the stack pointer by 'n'. The stack grows downward so substract+adjSpN' :: Int -> JStat+adjSpN' 0 = mempty+adjSpN' n = sp |= InfixExpr SubOp sp (toJExpr n)++-- | Wrapper which adjusts the stack pointer /and/ modifies the stack depth+-- tracked in 'G'. See also 'adjSp'' which actually does the stack pointer+-- manipulation.+adjSp :: Int -> G JStat+adjSp 0 = return mempty+adjSp n = do+ -- grow depth by n+ modifyStackDepth (+n)+ return (adjSp' n)++-- | Shrink the stack and stack pointer. NB: This function is unsafe when the+-- input 'n', is negative. This function wraps around 'adjSpN' which actually+-- performs the work.+adjSpN :: Int -> G JStat+adjSpN 0 = return mempty+adjSpN n = do+ modifyStackDepth (\x -> x - n)+ return (adjSpN' n)++-- | A constant array that holds global function symbols which do N pushes onto+-- the stack. For example:+-- @+-- function h$p1(x1) {+-- ++h$sp;+-- h$stack[(h$sp - 0)] = x1;+-- };+-- function h$p2(x1, x2) {+-- h$sp += 2;+-- h$stack[(h$sp - 1)] = x1;+-- h$stack[(h$sp - 0)] = x2;+-- };+-- @+--+-- and so on up to 32.+pushN :: Array Int Ident+pushN = listArray (1,32) $ map (TxtI . mkFastString . ("h$p"++) . show) [(1::Int)..32]++-- | Convert all function symbols in 'pushN' to global top-level functions. This+-- is a hack which converts the function symbols to variables. This hack is+-- caught in 'GHC.StgToJS.Printer.prettyBlock'' to turn these into global+-- functions.+pushN' :: Array Int JExpr+pushN' = fmap (ValExpr . JVar) pushN++-- | Partial Push functions. Like 'pushN' except these push functions skip+-- slots. For example,+-- @+-- function h$pp33(x1, x2) {+-- h$sp += 6;+-- h$stack[(h$sp - 5)] = x1;+-- h$stack[(h$sp - 0)] = x2;+-- };+-- @+--+-- The 33rd entry skips slots 1-4 to bind the top of the stack and the 6th+-- slot. See 'pushOptimized' and 'pushOptimized'' for use cases.+pushNN :: Array Integer Ident+pushNN = listArray (1,255) $ map (TxtI . mkFastString . ("h$pp"++) . show) [(1::Int)..255]++-- | Like 'pushN'' but for the partial push functions+pushNN' :: Array Integer JExpr+pushNN' = fmap (ValExpr . JVar) pushNN++pushOptimized' :: [(Id,Int)] -> G JStat+pushOptimized' xs = do+ slots <- getSlots+ pushOptimized =<< (zipWithM f xs (slots++repeat SlotUnknown))+ where+ f (i1,n1) xs2 = do+ xs <- varsForId i1+ let !id_n1 = xs !! (n1-1)++ case xs2 of+ SlotId i2 n2 -> pure (id_n1,i1==i2&&n1==n2)+ _ -> pure (id_n1,False)++-- | optimized push that reuses existing values on stack automatically chooses+-- an optimized partial push (h$ppN) function when possible.+pushOptimized :: [(JExpr,Bool)] -- ^ contents of the slots, True if same value is already there+ -> G JStat+pushOptimized [] = return mempty+pushOptimized xs = do+ dropSlots l+ modifyStackDepth (+ length xs)+ go . csInlinePush <$> getSettings+ where+ go True = inlinePush+ go _+ | all snd xs = adjSp' l+ | all (not.snd) xs && l <= 32 =+ ApplStat (pushN' ! l) (map fst xs)+ | l <= 8 && not (snd $ last xs) =+ ApplStat (pushNN' ! sig) [ e | (e,False) <- xs ]+ | otherwise = inlinePush+ l = length xs+ sig :: Integer+ sig = L.foldl1' (Bits..|.) $ zipWith (\(_e,b) i -> if not b then Bits.bit i else 0) xs [0..]+ inlinePush = adjSp' l <> mconcat (zipWith pushSlot [1..] xs)+ pushSlot i (ex, False) = IdxExpr stack (offset i) |= ex+ pushSlot _ _ = mempty+ offset i | i == l = sp+ | otherwise = InfixExpr SubOp sp (toJExpr (l - i))++-- | push a let-no-escape frame onto the stack+pushLneFrame :: HasDebugCallStack => Int -> ExprCtx -> G JStat+pushLneFrame size ctx =+ let ctx' = ctxLneShrinkStack ctx size+ in pushOptimized' (ctxLneFrameVars ctx')++-- | Pop things, don't update the stack knowledge in 'G'+popSkip :: Int -- ^ number of slots to skip+ -> [JExpr] -- ^ assign stack slot values to these+ -> JStat+popSkip 0 [] = mempty+popSkip n [] = adjSpN' n+popSkip n tgt = loadSkip n tgt <> adjSpN' (length tgt + n)++-- | Load 'length (xs :: [JExpr])' things from the stack at offset 'n :: Int'.+-- This function does no stack pointer manipulation, it merely indexes into the+-- stack and loads payloads into 'xs'.+loadSkip :: Int -> [JExpr] -> JStat+loadSkip = loadSkipFrom sp+ where+ loadSkipFrom :: JExpr -> Int -> [JExpr] -> JStat+ loadSkipFrom fr n xs = mconcat items+ where+ items = reverse $ zipWith f [(0::Int)..] (reverse xs)+ -- helper to generate sp - n offset to index with+ offset 0 = fr+ offset n = InfixExpr SubOp fr (toJExpr n)+ -- helper to load stack .! i into ex, e.g., ex = stack[i]+ f i ex = ex |= IdxExpr stack (toJExpr (offset (i+n)))+++-- | Pop but preserve the first N slots+popSkipI :: Int -> [(Ident,StackSlot)] -> G JStat+popSkipI 0 [] = pure mempty+popSkipI n [] = popN n+popSkipI n xs = do+ -- add N unknown slots+ addUnknownSlots n+ -- now add the slots from xs, after this line the stack should look like+ -- [xs] ++ [Unknown...] ++ old_stack+ addSlots (map snd xs)+ -- move stack pointer into the stack by (length xs + n), basically resetting+ -- the stack pointer+ a <- adjSpN (length xs + n)+ -- now load skipping first N slots+ return (loadSkipI n (map fst xs) <> a)++-- | Just like 'loadSkip' but operate on 'Ident's rather than 'JExpr'+loadSkipI :: Int -> [Ident] -> JStat+loadSkipI = loadSkipIFrom sp+ where loadSkipIFrom :: JExpr -> Int -> [Ident] -> JStat+ loadSkipIFrom fr n xs = mconcat items+ where+ items = reverse $ zipWith f [(0::Int)..] (reverse xs)+ offset 0 = fr+ offset n = InfixExpr SubOp fr (toJExpr n)+ f i ex = ex ||= IdxExpr stack (toJExpr (offset (i+n)))++-- | Blindly pop N slots+popN :: Int -> G JStat+popN n = addUnknownSlots n >> adjSpN n++-- | Generate statements to update the current node with a blackhole+bhStats :: StgToJSConfig -> Bool -> JStat+bhStats s pushUpd = mconcat+ [ if pushUpd then push' s [r1, var "h$upd_frame"] else mempty+ , toJExpr R1 .^ closureEntry_ |= var "h$blackhole"+ , toJExpr R1 .^ closureField1_ |= var "h$currentThread"+ , toJExpr R1 .^ closureField2_ |= null_ -- will be filled with waiters array+ ]++-- | Wrapper around 'updateThunk'', performs the stack manipulation before+-- updating the Thunk.+updateThunk :: G JStat+updateThunk = do+ settings <- getSettings+ -- update frame size+ let adjPushStack :: Int -> G ()+ adjPushStack n = do modifyStackDepth (+n)+ dropSlots n+ adjPushStack 2+ return $ (updateThunk' settings)++-- | Update a thunk by checking 'StgToJSConfig'. If the config inlines black+-- holes then update inline, else make an explicit call to the black hole+-- handler.+updateThunk' :: StgToJSConfig -> JStat+updateThunk' settings =+ if csInlineBlackhole settings+ then bhStats settings True+ else ApplStat (var "h$bh") []
+ compiler/GHC/StgToJS/StaticPtr.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE OverloadedStrings #-}++module GHC.StgToJS.StaticPtr+ ( initStaticPtrs+ )+where++import GHC.Prelude+import GHC.Linker.Types (SptEntry(..))+import GHC.Fingerprint.Type+import GHC.Types.Literal++import GHC.JS.Syntax+import GHC.JS.Make++import GHC.StgToJS.Types+import GHC.StgToJS.Literal+import GHC.StgToJS.Ids++initStaticPtrs :: [SptEntry] -> G JStat+initStaticPtrs ptrs = mconcat <$> mapM initStatic ptrs+ where+ initStatic (SptEntry sp_id (Fingerprint w1 w2)) = do+ i <- varForId sp_id+ fpa <- concat <$> mapM (genLit . mkLitWord64 . fromIntegral) [w1,w2]+ let sptInsert = ApplExpr (var "h$hs_spt_insert") (fpa ++ [i])+ return $ (var "h$initStatic" .^ "push") `ApplStat` [jLam sptInsert]+
+ compiler/GHC/StgToJS/StgUtils.hs view
@@ -0,0 +1,266 @@+{-# LANGUAGE LambdaCase #-}++module GHC.StgToJS.StgUtils+ ( bindingRefs+ , hasExport+ , collectTopIds+ , collectIds+ , removeTick+ , isUpdatableRhs+ , isInlineExpr+ , exprRefs+ -- * Live vars+ , LiveVars+ , liveVars+ , liveStatic+ , stgRhsLive+ , stgExprLive+ , stgTopBindLive+ , stgLetNoEscapeLive+ , stgLneLiveExpr+ , stgLneLive+ , stgLneLive'+ )+where++import GHC.Prelude++import GHC.Stg.Syntax+import GHC.Core.DataCon+import GHC.Core.Type+import GHC.Core.TyCon++import GHC.Types.Unique.FM+import GHC.Types.Unique.Set+import GHC.Types.Unique+import GHC.Types.Id+import GHC.Types.Id.Info+import GHC.Types.ForeignCall+import GHC.Types.TyThing+import GHC.Types.Name+import GHC.Types.Var.Set++import GHC.Builtin.Names+import GHC.Builtin.PrimOps (PrimOp(SeqOp), primOpIsReallyInline)+import GHC.Utils.Misc (seqList)+import GHC.Utils.Panic++import qualified Data.Foldable as F+import qualified Data.Set as S+import qualified Data.List as L+import Data.Set (Set)+import Data.Monoid++s :: a -> Set a+s = S.singleton++l :: (a -> Set Id) -> [a] -> Set Id+l = F.foldMap++-- | collect Ids that this binding refers to+-- (does not include the bindees themselves)+-- first argument is Id -> StgExpr map for unfloated arguments+bindingRefs :: UniqFM Id CgStgExpr -> CgStgBinding -> Set Id+bindingRefs u = \case+ StgNonRec _ rhs -> rhsRefs u rhs+ StgRec bs -> l (rhsRefs u . snd) bs++rhsRefs :: UniqFM Id CgStgExpr -> CgStgRhs -> Set Id+rhsRefs u = \case+ StgRhsClosure _ _ _ _ body -> exprRefs u body+ StgRhsCon _ccs d _mu _ticks args -> l s [ i | AnId i <- dataConImplicitTyThings d] <> l (argRefs u) args++exprRefs :: UniqFM Id CgStgExpr -> CgStgExpr -> Set Id+exprRefs u = \case+ StgApp f args -> s f <> l (argRefs u) args+ StgConApp d _n args _ -> l s [ i | AnId i <- dataConImplicitTyThings d] <> l (argRefs u) args+ StgOpApp _ args _ -> l (argRefs u) args+ StgLit {} -> mempty+ StgCase expr _ _ alts -> exprRefs u expr <> mconcat (fmap (altRefs u) alts)+ StgLet _ bnd expr -> bindingRefs u bnd <> exprRefs u expr+ StgLetNoEscape _ bnd expr -> bindingRefs u bnd <> exprRefs u expr+ StgTick _ expr -> exprRefs u expr++altRefs :: UniqFM Id CgStgExpr -> CgStgAlt -> Set Id+altRefs u alt = exprRefs u (alt_rhs alt)++argRefs :: UniqFM Id CgStgExpr -> StgArg -> Set Id+argRefs u = \case+ StgVarArg id+ | Just e <- lookupUFM u id -> exprRefs u e+ | otherwise -> s id+ _ -> mempty++hasExport :: CgStgBinding -> Bool+hasExport bnd =+ case bnd of+ StgNonRec b e -> isExportedBind b e+ StgRec bs -> any (uncurry isExportedBind) bs+ where+ isExportedBind _i (StgRhsCon _cc con _ _ _) =+ getUnique con == staticPtrDataConKey+ isExportedBind _ _ = False++collectTopIds :: CgStgBinding -> [Id]+collectTopIds (StgNonRec b _) = [b]+collectTopIds (StgRec bs) = let xs = map (zapFragileIdInfo . fst) bs+ in seqList xs `seq` xs++collectIds :: UniqFM Id CgStgExpr -> CgStgBinding -> [Id]+collectIds unfloated b =+ let xs = map zapFragileIdInfo .+ filter acceptId $ S.toList (bindingRefs unfloated b)+ in seqList xs `seq` xs+ where+ acceptId i = all ($ i) [not . isForbidden] -- fixme test this: [isExported[isGlobalId, not.isForbidden]+ -- the GHC.Prim module has no js source file+ isForbidden i+ | Just m <- nameModule_maybe (getName i) = m == gHC_PRIM+ | otherwise = False++removeTick :: CgStgExpr -> CgStgExpr+removeTick (StgTick _ e) = e+removeTick e = e++-----------------------------------------------------+-- Live vars+--+-- TODO: should probably be moved into GHC.Stg.LiveVars++type LiveVars = DVarSet++liveStatic :: LiveVars -> LiveVars+liveStatic = filterDVarSet isGlobalId++liveVars :: LiveVars -> LiveVars+liveVars = filterDVarSet (not . isGlobalId)++stgTopBindLive :: CgStgTopBinding -> [(Id, LiveVars)]+stgTopBindLive = \case+ StgTopLifted b -> stgBindLive b+ StgTopStringLit {} -> []++stgBindLive :: CgStgBinding -> [(Id, LiveVars)]+stgBindLive = \case+ StgNonRec b rhs -> [(b, stgRhsLive rhs)]+ StgRec bs -> map (\(b,rhs) -> (b, stgRhsLive rhs)) bs++stgBindRhsLive :: CgStgBinding -> LiveVars+stgBindRhsLive b =+ let (bs, ls) = unzip (stgBindLive b)+ in delDVarSetList (unionDVarSets ls) bs++stgRhsLive :: CgStgRhs -> LiveVars+stgRhsLive = \case+ StgRhsClosure _ _ _ args e -> delDVarSetList (stgExprLive True e) args+ StgRhsCon _ _ _ _ args -> unionDVarSets (map stgArgLive args)++stgArgLive :: StgArg -> LiveVars+stgArgLive = \case+ StgVarArg occ -> unitDVarSet occ+ StgLitArg {} -> emptyDVarSet++stgExprLive :: Bool -> CgStgExpr -> LiveVars+stgExprLive includeLHS = \case+ StgApp occ args -> unionDVarSets (unitDVarSet occ : map stgArgLive args)+ StgLit {} -> emptyDVarSet+ StgConApp _dc _n args _tys -> unionDVarSets (map stgArgLive args)+ StgOpApp _op args _ty -> unionDVarSets (map stgArgLive args)+ StgCase e b _at alts+ | includeLHS -> el `unionDVarSet` delDVarSet al b+ | otherwise -> delDVarSet al b+ where+ al = unionDVarSets (map stgAltLive alts)+ el = stgExprLive True e+ StgLet _ b e -> delDVarSetList (stgBindRhsLive b `unionDVarSet` stgExprLive True e) (bindees b)+ StgLetNoEscape _ b e -> delDVarSetList (stgBindRhsLive b `unionDVarSet` stgExprLive True e) (bindees b)+ StgTick _ti e -> stgExprLive True e++stgAltLive :: CgStgAlt -> LiveVars+stgAltLive alt =+ delDVarSetList (stgExprLive True (alt_rhs alt)) (alt_bndrs alt)++stgLetNoEscapeLive :: Bool -> StgBinding -> StgExpr -> LiveVars+stgLetNoEscapeLive _someBool _b _e = panic "stgLetNoEscapeLive"++bindees :: CgStgBinding -> [Id]+bindees = \case+ StgNonRec b _e -> [b]+ StgRec bs -> map fst bs++isUpdatableRhs :: CgStgRhs -> Bool+isUpdatableRhs (StgRhsClosure _ _ u _ _) = isUpdatable u+isUpdatableRhs _ = False++stgLneLive' :: CgStgBinding -> [Id]+stgLneLive' b = filter (`notElem` bindees b) (stgLneLive b)++stgLneLive :: CgStgBinding -> [Id]+stgLneLive (StgNonRec _b e) = stgLneLiveExpr e+stgLneLive (StgRec bs) = L.nub $ concatMap (stgLneLiveExpr . snd) bs++stgLneLiveExpr :: CgStgRhs -> [Id]+stgLneLiveExpr rhs = dVarSetElems (liveVars $ stgRhsLive rhs)+-- stgLneLiveExpr (StgRhsClosure _ _ _ _ e) = dVarSetElems (liveVars (stgExprLive e))+-- stgLneLiveExpr StgRhsCon {} = []++-- | returns True if the expression is definitely inline+isInlineExpr :: UniqSet Id -> CgStgExpr -> (UniqSet Id, Bool)+isInlineExpr v = \case+ StgApp i args+ -> (emptyUniqSet, isInlineApp v i args)+ StgLit{}+ -> (emptyUniqSet, True)+ StgConApp{}+ -> (emptyUniqSet, True)+ StgOpApp (StgFCallOp f _) _ _+ -> (emptyUniqSet, isInlineForeignCall f)+ StgOpApp (StgPrimOp SeqOp) [StgVarArg e] t+ -> (emptyUniqSet, e `elementOfUniqSet` v || isStrictType t)+ StgOpApp (StgPrimOp op) _ _+ -> (emptyUniqSet, primOpIsReallyInline op)+ StgOpApp (StgPrimCallOp _c) _ _+ -> (emptyUniqSet, True)+ StgCase e b _ alts+ ->let (_ve, ie) = isInlineExpr v e+ v' = addOneToUniqSet v b+ (vas, ias) = unzip $ map (isInlineExpr v') (fmap alt_rhs alts)+ vr = L.foldl1' intersectUniqSets vas+ in (vr, (ie || b `elementOfUniqSet` v) && and ias)+ StgLet _ b e+ -> isInlineExpr (inspectInlineBinding v b) e+ StgLetNoEscape _ _b e+ -> isInlineExpr v e+ StgTick _ e+ -> isInlineExpr v e++inspectInlineBinding :: UniqSet Id -> CgStgBinding -> UniqSet Id+inspectInlineBinding v = \case+ StgNonRec i r -> inspectInlineRhs v i r+ StgRec bs -> foldl' (\v' (i,r) -> inspectInlineRhs v' i r) v bs++inspectInlineRhs :: UniqSet Id -> Id -> CgStgRhs -> UniqSet Id+inspectInlineRhs v i = \case+ StgRhsCon{} -> addOneToUniqSet v i+ StgRhsClosure _ _ ReEntrant _ _ -> addOneToUniqSet v i+ _ -> v++isInlineForeignCall :: ForeignCall -> Bool+isInlineForeignCall (CCall (CCallSpec _ cconv safety)) =+ not (playInterruptible safety) &&+ not (cconv /= JavaScriptCallConv && playSafe safety)++isInlineApp :: UniqSet Id -> Id -> [StgArg] -> Bool+isInlineApp v i = \case+ _ | isJoinId i -> False+ [] -> isUnboxedTupleType (idType i) ||+ isStrictType (idType i) ||+ i `elementOfUniqSet` v++ [StgVarArg a]+ | DataConWrapId dc <- idDetails i+ , isNewTyCon (dataConTyCon dc)+ , isStrictType (idType a) || a `elementOfUniqSet` v || isStrictId a+ -> True+ _ -> False+
+ compiler/GHC/StgToJS/Symbols.hs view
@@ -0,0 +1,89 @@++-- | JS symbol generation+module GHC.StgToJS.Symbols+ ( moduleGlobalSymbol+ , moduleExportsSymbol+ , mkJsSymbol+ , mkJsSymbolBS+ , mkFreshJsSymbol+ , mkRawSymbol+ , intBS+ ) where++import GHC.Prelude++import GHC.Data.FastString+import GHC.Unit.Module+import Data.ByteString (ByteString)+import qualified Data.ByteString.Char8 as BSC+import qualified Data.ByteString.Builder as BSB+import qualified Data.ByteString.Lazy as BSL++-- | Hexadecimal representation of an int+--+-- Used for uniques. We could use base-62 as GHC usually does but this is likely+-- faster.+intBS :: Int -> ByteString+intBS = BSL.toStrict . BSB.toLazyByteString . BSB.wordHex . fromIntegral++-- | Return z-encoded unit:module+unitModuleStringZ :: Module -> ByteString+unitModuleStringZ mod = mconcat+ [ fastZStringToByteString (zEncodeFS (unitIdFS (moduleUnitId mod)))+ , BSC.pack "ZC" -- z-encoding for ":"+ , fastZStringToByteString (zEncodeFS (moduleNameFS (moduleName mod)))+ ]++-- | the global linkable unit of a module exports this symbol, depend on it to+-- include that unit (used for cost centres)+moduleGlobalSymbol :: Module -> FastString+moduleGlobalSymbol m = mkFastStringByteString $ mconcat+ [ hd+ , unitModuleStringZ m+ , BSC.pack "_<global>"+ ]++moduleExportsSymbol :: Module -> FastString+moduleExportsSymbol m = mkFastStringByteString $ mconcat+ [ hd+ , unitModuleStringZ m+ , BSC.pack "_<exports>"+ ]++-- | Make JS symbol corresponding to the given Haskell symbol in the given+-- module+mkJsSymbolBS :: Bool -> Module -> FastString -> ByteString+mkJsSymbolBS exported mod s = mconcat+ [ if exported then hd else hdd+ , unitModuleStringZ mod+ , BSC.pack "zi" -- z-encoding of "."+ , fastZStringToByteString (zEncodeFS s)+ ]++-- | Make JS symbol corresponding to the given Haskell symbol in the given+-- module+mkJsSymbol :: Bool -> Module -> FastString -> FastString+mkJsSymbol exported mod s = mkFastStringByteString (mkJsSymbolBS exported mod s)++-- | Make JS symbol for given module and unique.+mkFreshJsSymbol :: Module -> Int -> FastString+mkFreshJsSymbol mod i = mkFastStringByteString $ mconcat+ [ hdd+ , unitModuleStringZ mod+ , BSC.pack "_"+ , intBS i+ ]++-- | Make symbol "h$XYZ" or "h$$XYZ"+mkRawSymbol :: Bool -> FastString -> FastString+mkRawSymbol exported fs+ | exported = mkFastStringByteString $ mconcat [ hd, bytesFS fs ]+ | otherwise = mkFastStringByteString $ mconcat [ hdd, bytesFS fs ]++-- | "h$$" constant string+hdd :: ByteString+hdd = BSC.pack "h$$"++-- | "h$" constant string+hd :: ByteString+hd = BSC.take 2 hdd
+ compiler/GHC/StgToJS/Types.hs view
@@ -0,0 +1,430 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-}++-----------------------------------------------------------------------------+-- |+-- Module : GHC.StgToJS.Types+-- Copyright : (c) The University of Glasgow 2001+-- License : BSD-style (see the file LICENSE)+--+-- Maintainer : Jeffrey Young <jeffrey.young@iohk.io>+-- Luite Stegeman <luite.stegeman@iohk.io>+-- Sylvain Henry <sylvain.henry@iohk.io>+-- Josh Meredith <josh.meredith@iohk.io>+-- Stability : experimental+--+--+-- Module that holds the Types required for the StgToJS pass+-----------------------------------------------------------------------------++module GHC.StgToJS.Types where++import GHC.Prelude++import GHC.JS.Syntax+import GHC.JS.Make+import GHC.JS.Ppr ()++import GHC.Stg.Syntax+import GHC.Core.TyCon++import GHC.Types.Unique+import GHC.Types.Unique.FM+import GHC.Types.Var+import GHC.Types.ForeignCall++import Control.Monad.Trans.State.Strict+import GHC.Utils.Outputable (Outputable (..), text, SDocContext, (<+>), ($$))++import GHC.Data.FastString+import GHC.Data.FastMutInt++import GHC.Unit.Module++import qualified Data.Map as M+import Data.Set (Set)+import qualified Data.ByteString as BS+import Data.Monoid+import Data.Typeable (Typeable)+import GHC.Generics (Generic)+import Control.DeepSeq++-- | A State monad over IO holding the generator state.+type G = StateT GenState IO++-- | The JS code generator state+data GenState = GenState+ { gsSettings :: !StgToJSConfig -- ^ codegen settings, read-only+ , gsModule :: !Module -- ^ current module+ , gsId :: {-# UNPACK #-} !FastMutInt -- ^ unique number for the id generator+ , gsIdents :: !IdCache -- ^ hash consing for identifiers from a Unique+ , gsUnfloated :: !(UniqFM Id CgStgExpr) -- ^ unfloated arguments+ , gsGroup :: GenGroupState -- ^ state for the current binding group+ , gsGlobal :: [JStat] -- ^ global (per module) statements (gets included when anything else from the module is used)+ }++-- | The JS code generator state relevant for the current binding group+data GenGroupState = GenGroupState+ { ggsToplevelStats :: [JStat] -- ^ extra toplevel statements for the binding group+ , ggsClosureInfo :: [ClosureInfo] -- ^ closure metadata (info tables) for the binding group+ , ggsStatic :: [StaticInfo] -- ^ static (CAF) data in our binding group+ , ggsStack :: [StackSlot] -- ^ stack info for the current expression+ , ggsStackDepth :: Int -- ^ current stack depth+ , ggsExtraDeps :: Set OtherSymb -- ^ extra dependencies for the linkable unit that contains this group+ , ggsGlobalIdCache :: GlobalIdCache+ , ggsForeignRefs :: [ForeignJSRef]+ }++-- | The Configuration record for the StgToJS pass+data StgToJSConfig = StgToJSConfig+ -- flags+ { csInlinePush :: !Bool+ , csInlineBlackhole :: !Bool+ , csInlineLoadRegs :: !Bool+ , csInlineEnter :: !Bool+ , csInlineAlloc :: !Bool+ , csTraceRts :: !Bool+ , csAssertRts :: !Bool+ , csBoundsCheck :: !Bool+ , csDebugAlloc :: !Bool+ , csTraceForeign :: !Bool+ , csProf :: !Bool -- ^ Profiling enabled+ , csRuntimeAssert :: !Bool -- ^ Enable runtime assertions+ -- settings+ , csContext :: !SDocContext+ }++-- | Information relevenat to code generation for closures.+data ClosureInfo = ClosureInfo+ { ciVar :: Ident -- ^ object being infod+ , ciRegs :: CIRegs -- ^ size of the payload (in number of JS values)+ , ciName :: FastString -- ^ friendly name for printing+ , ciLayout :: CILayout -- ^ heap/stack layout of the object+ , ciType :: CIType -- ^ type of the object, with extra info where required+ , ciStatic :: CIStatic -- ^ static references of this object+ }+ deriving stock (Eq, Show, Generic)++-- | Closure information, 'ClosureInfo', registers+data CIRegs+ = CIRegsUnknown -- ^ A value witnessing a state of unknown registers+ | CIRegs { ciRegsSkip :: Int -- ^ unused registers before actual args start+ , ciRegsTypes :: [VarType] -- ^ args+ }+ deriving stock (Eq, Ord, Show, Generic)++instance NFData CIRegs++-- | Closure Information, 'ClosureInfo', layout+data CILayout+ = CILayoutVariable -- ^ layout stored in object itself, first position from the start+ | CILayoutUnknown -- ^ fixed size, but content unknown (for example stack apply frame)+ { layoutSize :: !Int+ }+ | CILayoutFixed -- ^ whole layout known+ { layoutSize :: !Int -- ^ closure size in array positions, including entry+ , layout :: [VarType] -- ^ The set of sized Types to layout+ }+ deriving stock (Eq, Ord, Show, Generic)++instance NFData CILayout++-- | The type of 'ClosureInfo'+data CIType+ = CIFun { citArity :: !Int -- ^ function arity+ , citRegs :: !Int -- ^ number of registers for the args+ }+ | CIThunk -- ^ The closure is a THUNK+ | CICon { citConstructor :: !Int } -- ^ The closure is a Constructor+ | CIPap -- ^ The closure is a Partial Application+ | CIBlackhole -- ^ The closure is a black hole+ | CIStackFrame -- ^ The closure is a stack frame+ deriving stock (Eq, Ord, Show, Generic)++instance NFData CIType++-- | Static references that must be kept alive+newtype CIStatic = CIStaticRefs { staticRefs :: [FastString] }+ deriving stock (Eq, Generic)+ deriving newtype (Semigroup, Monoid, Show)++-- | static refs: array = references, null = nothing to report+-- note: only works after all top-level objects have been created+instance ToJExpr CIStatic where+ toJExpr (CIStaticRefs []) = null_ -- [je| null |]+ toJExpr (CIStaticRefs rs) = toJExpr (map TxtI rs)++-- | Free variable types+data VarType+ = PtrV -- ^ pointer = reference to heap object (closure object)+ | VoidV -- ^ no fields+ | DoubleV -- ^ A Double: one field+ | IntV -- ^ An Int (32bit because JS): one field+ | LongV -- ^ A Long: two fields one for the upper 32bits, one for the lower (NB: JS is little endian)+ | AddrV -- ^ a pointer not to the heap: two fields, array + index+ | RtsObjV -- ^ some RTS object from GHCJS (for example TVar#, MVar#, MutVar#, Weak#)+ | ObjV -- ^ some JS object, user supplied, be careful around these, can be anything+ | ArrV -- ^ boxed array+ deriving stock (Eq, Ord, Enum, Bounded, Show, Generic)++instance NFData VarType++instance ToJExpr VarType where+ toJExpr = toJExpr . fromEnum++-- | The type of identifiers. These determine the suffix of generated functions+-- in JS Land. For example, the entry function for the 'Just' constructor is a+-- 'IdConEntry' which compiles to:+-- @+-- function h$baseZCGHCziMaybeziJust_con_e() { return h$rs() };+-- @+-- which just returns whatever the stack point is pointing to. Whereas the entry+-- function to 'Just' is an 'IdEntry' and does the work. It compiles to:+-- @+-- function h$baseZCGHCziMaybeziJust_e() {+-- var h$$baseZCGHCziMaybezieta_8KXnScrCjF5 = h$r2;+-- h$r1 = h$c1(h$baseZCGHCziMaybeziJust_con_e, h$$baseZCGHCziMaybezieta_8KXnScrCjF5);+-- return h$rs();+-- };+-- @+-- Which loads some payload from register 2, and applies the Constructor Entry+-- function for the Just to the payload, returns the result in register 1 and+-- returns whatever is on top of the stack+data IdType+ = IdPlain -- ^ A plain identifier for values, no suffix added+ | IdEntry -- ^ An entry function, suffix = "_e" in 'GHC.StgToJS.Ids.makeIdentForId'+ | IdConEntry -- ^ A Constructor entry function, suffix = "_con_e" in 'GHC.StgToJS.Ids.makeIdentForId'+ deriving (Enum, Eq, Ord)++-- | Keys to differentiate Ident's in the ID Cache+data IdKey+ = IdKey !Int !Int !IdType+ deriving (Eq, Ord)++-- | Some other symbol+data OtherSymb+ = OtherSymb !Module !FastString+ deriving Eq++instance Ord OtherSymb where+ compare (OtherSymb m1 t1) (OtherSymb m2 t2)+ = stableModuleCmp m1 m2 <> lexicalCompareFS t1 t2++-- | The identifier cache indexed on 'IdKey' local to a module+newtype IdCache = IdCache (M.Map IdKey Ident)++-- | The global Identifier Cache+newtype GlobalIdCache = GlobalIdCache (UniqFM Ident (IdKey, Id))++-- | A Stack Slot is either known or unknown. We avoid maybe here for more+-- strictness.+data StackSlot+ = SlotId !Id !Int+ | SlotUnknown+ deriving (Eq, Ord)++data StaticInfo = StaticInfo+ { siVar :: !FastString -- ^ global object+ , siVal :: !StaticVal -- ^ static initialization+ , siCC :: !(Maybe Ident) -- ^ optional CCS name+ } deriving stock (Eq, Show, Typeable, Generic)++data StaticVal+ = StaticFun !FastString [StaticArg]+ -- ^ heap object for function+ | StaticThunk !(Maybe (FastString,[StaticArg]))+ -- ^ heap object for CAF (field is Nothing when thunk is initialized in an+ -- alternative way, like string thunks through h$str)+ | StaticUnboxed !StaticUnboxed+ -- ^ unboxed constructor (Bool, Int, Double etc)+ | StaticData !FastString [StaticArg]+ -- ^ regular datacon app+ | StaticList [StaticArg] (Maybe FastString)+ -- ^ list initializer (with optional tail)+ deriving stock (Eq, Show, Generic)++data StaticUnboxed+ = StaticUnboxedBool !Bool+ | StaticUnboxedInt !Integer+ | StaticUnboxedDouble !SaneDouble+ | StaticUnboxedString !BS.ByteString+ | StaticUnboxedStringOffset !BS.ByteString+ deriving stock (Eq, Ord, Show, Generic)++instance NFData StaticUnboxed++-- | Static Arguments. Static Arguments are things that are statically+-- allocated, i.e., they exist at program startup. These are static heap objects+-- or literals or things that have been floated to the top level binding by ghc.+data StaticArg+ = StaticObjArg !FastString -- ^ reference to a heap object+ | StaticLitArg !StaticLit -- ^ literal+ | StaticConArg !FastString [StaticArg] -- ^ unfloated constructor+ deriving stock (Eq, Show, Generic)++instance Outputable StaticArg where+ ppr x = text (show x)++-- | A Static literal value+data StaticLit+ = BoolLit !Bool+ | IntLit !Integer+ | NullLit+ | DoubleLit !SaneDouble -- should we actually use double here?+ | StringLit !FastString+ | BinLit !BS.ByteString+ | LabelLit !Bool !FastString -- ^ is function pointer, label (also used for string / binary init)+ deriving (Eq, Show, Generic)++instance Outputable StaticLit where+ ppr x = text (show x)+++instance ToJExpr StaticLit where+ toJExpr (BoolLit b) = toJExpr b+ toJExpr (IntLit i) = toJExpr i+ toJExpr NullLit = null_+ toJExpr (DoubleLit d) = toJExpr (unSaneDouble d)+ toJExpr (StringLit t) = app (mkFastString "h$str") [toJExpr t]+ toJExpr (BinLit b) = app (mkFastString "h$rstr") [toJExpr (map toInteger (BS.unpack b))]+ toJExpr (LabelLit _isFun lbl) = var lbl++-- | A foreign reference to some JS code+data ForeignJSRef = ForeignJSRef+ { foreignRefSrcSpan :: !FastString+ , foreignRefPattern :: !FastString+ , foreignRefSafety :: !Safety+ , foreignRefCConv :: !CCallConv+ , foreignRefArgs :: ![FastString]+ , foreignRefResult :: !FastString+ } deriving stock (Generic)++-- | data used to generate one ObjUnit in our object file+data LinkableUnit = LinkableUnit+ { luObjUnit :: ObjUnit -- ^ serializable unit info+ , luIdExports :: [Id] -- ^ exported names from haskell identifiers+ , luOtherExports :: [FastString] -- ^ other exports+ , luIdDeps :: [Id] -- ^ identifiers this unit depends on+ , luPseudoIdDeps :: [Unique] -- ^ pseudo-id identifiers this unit depends on (fixme)+ , luOtherDeps :: [OtherSymb] -- ^ symbols not from a haskell id that this unit depends on+ , luRequired :: Bool -- ^ always link this unit+ , luForeignRefs :: [ForeignJSRef]+ }++-- | one toplevel block in the object file+data ObjUnit = ObjUnit+ { oiSymbols :: ![FastString] -- ^ toplevel symbols (stored in index)+ , oiClInfo :: ![ClosureInfo] -- ^ closure information of all closures in block+ , oiStatic :: ![StaticInfo] -- ^ static closure data+ , oiStat :: JStat -- ^ the code+ , oiRaw :: !BS.ByteString -- ^ raw JS code+ , oiFExports :: ![ExpFun]+ , oiFImports :: ![ForeignJSRef]+ }++data ExpFun = ExpFun+ { isIO :: !Bool+ , args :: [JSFFIType]+ , result :: !JSFFIType+ } deriving (Eq, Ord, Show)++-- | Types of FFI values+data JSFFIType+ = Int8Type+ | Int16Type+ | Int32Type+ | Int64Type+ | Word8Type+ | Word16Type+ | Word32Type+ | Word64Type+ | DoubleType+ | ByteArrayType+ | PtrType+ | RefType+ deriving (Show, Ord, Eq, Enum)+++-- | Typed expression+data TypedExpr = TypedExpr+ { typex_typ :: !PrimRep+ , typex_expr :: [JExpr]+ }++instance Outputable TypedExpr where+ ppr x = text "TypedExpr: " <+> ppr (typex_expr x)+ $$ text "PrimReps: " <+> ppr (typex_typ x)++-- | A Primop result is either an inlining of some JS payload, or a primitive+-- call to a JS function defined in Shim files in base.+data PrimRes+ = PrimInline JStat -- ^ primop is inline, result is assigned directly+ | PRPrimCall JStat -- ^ primop is async call, primop returns the next+ -- function to run. result returned to stack top in registers++data ExprResult+ = ExprCont+ | ExprInline (Maybe [JExpr])+ deriving (Eq)++newtype ExprValData = ExprValData [JExpr]+ deriving newtype (Eq)++-- | A Closure is one of six types+data ClosureType+ = Thunk -- ^ The closure is a THUNK+ | Fun -- ^ The closure is a Function+ | Pap -- ^ The closure is a Partial Application+ | Con -- ^ The closure is a Constructor+ | Blackhole -- ^ The closure is a Blackhole+ | StackFrame -- ^ The closure is a stack frame+ deriving (Show, Eq, Ord, Enum, Bounded)++-- | Convert 'ClosureType' to an Int+ctNum :: ClosureType -> Int+ctNum Fun = 1+ctNum Con = 2+ctNum Thunk = 0+ctNum Pap = 3+ctNum Blackhole = 5+ctNum StackFrame = -1++-- | Convert 'ClosureType' to a String+ctJsName :: ClosureType -> String+ctJsName = \case+ Thunk -> "CLOSURE_TYPE_THUNK"+ Fun -> "CLOSURE_TYPE_FUN"+ Pap -> "CLOSURE_TYPE_PAP"+ Con -> "CLOSURE_TYPE_CON"+ Blackhole -> "CLOSURE_TYPE_BLACKHOLE"+ StackFrame -> "CLOSURE_TYPE_STACKFRAME"++instance ToJExpr ClosureType where+ toJExpr e = toJExpr (ctNum e)+++-- | A thread is in one of 4 states+data ThreadStatus+ = Running -- ^ The thread is running+ | Blocked -- ^ The thread is blocked+ | Finished -- ^ The thread is done+ | Died -- ^ The thread has died+ deriving (Show, Eq, Ord, Enum, Bounded)++-- | Convert the status of a thread in JS land to an Int+threadStatusNum :: ThreadStatus -> Int+threadStatusNum = \case+ Running -> 0+ Blocked -> 1+ Finished -> 16+ Died -> 17++-- | convert the status of a thread in JS land to a string+threadStatusJsName :: ThreadStatus -> String+threadStatusJsName = \case+ Running -> "THREAD_RUNNING"+ Blocked -> "THREAD_BLOCKED"+ Finished -> "THREAD_FINISHED"+ Died -> "THREAD_DIED"
+ compiler/GHC/StgToJS/Utils.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE OverloadedStrings #-}++module GHC.StgToJS.Utils+ ( assignToTypedExprs+ , assignCoerce1+ , assignToExprCtx+ )+where++import GHC.Prelude++import GHC.StgToJS.Types+import GHC.StgToJS.ExprCtx++import GHC.JS.Syntax+import GHC.JS.Make++import GHC.Core.TyCon++import GHC.Utils.Misc+import GHC.Utils.Panic+import GHC.Utils.Outputable++assignToTypedExprs :: HasDebugCallStack => [TypedExpr] -> [JExpr] -> JStat+assignToTypedExprs tes es =+ assignAllEqual (concatMap typex_expr tes) es++assignTypedExprs :: [TypedExpr] -> [TypedExpr] -> JStat+assignTypedExprs tes es =+ -- TODO: check primRep (typex_typ) here?+ assignToTypedExprs tes (concatMap typex_expr es)++assignToExprCtx :: HasDebugCallStack => ExprCtx -> [JExpr] -> JStat+assignToExprCtx ctx es = assignToTypedExprs (ctxTarget ctx) es++-- | Assign first expr only (if it exists), performing coercions between some+-- PrimReps (e.g. StablePtr# and Addr#).+assignCoerce1 :: HasDebugCallStack => [TypedExpr] -> [TypedExpr] -> JStat+assignCoerce1 [x] [y] = assignCoerce x y+assignCoerce1 [] [] = mempty+assignCoerce1 x y = pprPanic "assignCoerce1"+ (vcat [ text "lengths do not match"+ , ppr x+ , ppr y+ ])++-- | Assign p2 to p1 with optional coercion+assignCoerce :: TypedExpr -> TypedExpr -> JStat+-- Coercion between StablePtr# and Addr#+assignCoerce (TypedExpr AddrRep [a_val, a_off]) (TypedExpr UnliftedRep [sptr]) = mconcat+ [ a_val |= var "h$stablePtrBuf"+ , a_off |= sptr+ ]+assignCoerce (TypedExpr UnliftedRep [sptr]) (TypedExpr AddrRep [_a_val, a_off]) =+ sptr |= a_off+assignCoerce p1 p2 = assignTypedExprs [p1] [p2]+
compiler/GHC/SysTools.hs view
@@ -14,7 +14,6 @@ module GHC.SysTools ( -- * Initialisation initSysTools,- lazyInitLlvmConfig, -- * Interface to system tools module GHC.SysTools.Tasks,@@ -32,7 +31,6 @@ import GHC.Prelude -import GHC.Settings.Utils import GHC.Utils.Panic import GHC.Driver.Session@@ -44,9 +42,7 @@ import GHC.Settings.IO import Control.Monad.Trans.Except (runExceptT)-import System.FilePath import System.IO-import System.IO.Unsafe (unsafeInterleaveIO) import Foreign.Marshal.Alloc (allocaBytes) import System.Directory (copyFile) @@ -101,47 +97,6 @@ * * ************************************************************************ -}---- Note [LLVM configuration]--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--- The `llvm-targets` and `llvm-passes` files are shipped with GHC and contain--- information needed by the LLVM backend to invoke `llc` and `opt`.--- Specifically:------ * llvm-targets maps autoconf host triples to the corresponding LLVM--- `data-layout` declarations. This information is extracted from clang using--- the script in utils/llvm-targets/gen-data-layout.sh and should be updated--- whenever we target a new version of LLVM.------ * llvm-passes maps GHC optimization levels to sets of LLVM optimization--- flags that GHC should pass to `opt`.------ This information is contained in files rather the GHC source to allow users--- to add new targets to GHC without having to recompile the compiler.------ Since this information is only needed by the LLVM backend we load it lazily--- with unsafeInterleaveIO. Consequently it is important that we lazily pattern--- match on LlvmConfig until we actually need its contents.--lazyInitLlvmConfig :: String- -> IO LlvmConfig-lazyInitLlvmConfig top_dir- = unsafeInterleaveIO $ do -- see Note [LLVM configuration]- targets <- readAndParse "llvm-targets"- passes <- readAndParse "llvm-passes"- return $ LlvmConfig { llvmTargets = fmap mkLlvmTarget <$> targets,- llvmPasses = passes }- where- readAndParse :: Read a => String -> IO a- readAndParse name =- do let llvmConfigFile = top_dir </> name- llvmConfigStr <- readFile llvmConfigFile- case maybeReadFuzzy llvmConfigStr of- Just s -> return s- Nothing -> pgmError ("Can't parse " ++ show llvmConfigFile)-- mkLlvmTarget :: (String, String, String) -> LlvmTarget- mkLlvmTarget (dl, cpu, attrs) = LlvmTarget dl cpu (words attrs) initSysTools :: String -- TopDir path
compiler/GHC/SysTools/Ar.hs view
@@ -1,19 +1,20 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving #-}-{- Note: [The need for Ar.hs]-Building `-staticlib` required the presence of libtool, and was a such-restricted to mach-o only. As libtool on macOS and gnu libtool are very+{- Note [The need for Ar.hs]+ ~~~~~~~~~~~~~~~~~~~~~~~~~+Building `-staticlib` previously required the presence of libtool, and was a+such restricted to mach-o only. As libtool on macOS and gnu libtool are very different, there was no simple portable way to support this. -libtool for static archives does essentially: concatinate the input archives,+libtool for static archives does essentially: concatenate the input archives, add the input objects, and create a symbol index. Using `ar` for this task fails as even `ar` (bsd and gnu, llvm, ...) do not provide the same features across platforms (e.g. index prefixed retrieval of objects with the same name.) -As Archives are rather simple structurally, we can just build the archives+As archives are rather simple structurally, we can just build the archives with Haskell directly and use ranlib on the final result to get the symbol-index. This should allow us to work around with the differences/abailability+index. This should allow us to work around with the differences/availability of libtool across different platforms. -} module GHC.SysTools.Ar@@ -143,7 +144,7 @@ if C.unpack (C.take 1 name) == "/" then case C.takeWhile (/= ' ') name of name@"/" -> name -- symbol table- name@"//" -> name -- extendedn file names table+ name@"//" -> name -- extended file names table name -> getExtName extInfo (read . C.unpack $ C.drop 1 name) else C.takeWhile (/= '/') name case name of@@ -157,7 +158,7 @@ getExtName (Just info) offset = C.takeWhile (/= '/') . C.drop offset $ filedata info -- | put an Archive Entry. This assumes that the entries--- have been preprocessed to account for the extenden file name+-- have been preprocessed to account for the extended file name -- table section "//" e.g. for GNU Archives. Or that the names -- have been move into the payload for BSD Archives. putArchEntry :: ArchiveEntry -> PutM ()
+ compiler/GHC/SysTools/Cpp.hs view
@@ -0,0 +1,233 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE LambdaCase #-}++#include <ghcplatform.h>++module GHC.SysTools.Cpp+ ( doCpp+ , CppOpts (..)+ , getGhcVersionPathName+ , applyCDefs+ , offsetIncludePaths+ )+where++import GHC.Prelude+import GHC.Driver.Session+import GHC.Driver.Backend+import GHC.CmmToLlvm.Config+import GHC.Platform+import GHC.Platform.ArchOS++import GHC.SysTools++import GHC.Unit.Env+import GHC.Unit.Info+import GHC.Unit.State+import GHC.Unit.Types++import GHC.Utils.Logger+import GHC.Utils.TmpFs+import GHC.Utils.Panic++import Data.Version+import Data.List (intercalate)+import Data.Maybe++import Control.Monad++import System.Directory+import System.FilePath++data CppOpts = CppOpts+ { cppUseCc :: !Bool -- ^ Use "cc -E" as preprocessor, otherwise use "cpp"+ , cppLinePragmas :: !Bool -- ^ Enable generation of LINE pragmas+ }++-- | Run CPP+--+-- UnitEnv is needed to compute MIN_VERSION macros+doCpp :: Logger -> TmpFs -> DynFlags -> UnitEnv -> CppOpts -> FilePath -> FilePath -> IO ()+doCpp logger tmpfs dflags unit_env opts input_fn output_fn = do+ let hscpp_opts = picPOpts dflags+ let cmdline_include_paths = offsetIncludePaths dflags (includePaths dflags)+ let unit_state = ue_units unit_env+ pkg_include_dirs <- mayThrowUnitErr+ (collectIncludeDirs <$> preloadUnitsInfo unit_env)+ -- MP: This is not quite right, the headers which are supposed to be installed in+ -- the package might not be the same as the provided include paths, but it's a close+ -- enough approximation for things to work. A proper solution would be to have to declare which paths should+ -- be propagated to dependent packages.+ let home_pkg_deps =+ [homeUnitEnv_dflags . ue_findHomeUnitEnv uid $ unit_env | uid <- ue_transitiveHomeDeps (ue_currentUnit unit_env) unit_env]+ dep_pkg_extra_inputs = [offsetIncludePaths fs (includePaths fs) | fs <- home_pkg_deps]++ let include_paths_global = foldr (\ x xs -> ("-I" ++ x) : xs) []+ (includePathsGlobal cmdline_include_paths ++ pkg_include_dirs+ ++ concatMap includePathsGlobal dep_pkg_extra_inputs)+ let include_paths_quote = foldr (\ x xs -> ("-iquote" ++ x) : xs) []+ (includePathsQuote cmdline_include_paths +++ includePathsQuoteImplicit cmdline_include_paths)+ let include_paths = include_paths_quote ++ include_paths_global++ let verbFlags = getVerbFlags dflags++ let cpp_prog args+ | cppUseCc opts = GHC.SysTools.runCc Nothing logger tmpfs dflags+ (GHC.SysTools.Option "-E" : args)+ | otherwise = GHC.SysTools.runCpp logger dflags args++ let platform = targetPlatform dflags+ targetArch = stringEncodeArch $ platformArch platform+ targetOS = stringEncodeOS $ platformOS platform+ isWindows = platformOS platform == OSMinGW32+ let target_defs =+ [ "-D" ++ HOST_OS ++ "_BUILD_OS",+ "-D" ++ HOST_ARCH ++ "_BUILD_ARCH",+ "-D" ++ targetOS ++ "_HOST_OS",+ "-D" ++ targetArch ++ "_HOST_ARCH" ]+ -- remember, in code we *compile*, the HOST is the same our TARGET,+ -- and BUILD is the same as our HOST.++ let io_manager_defs =+ [ "-D__IO_MANAGER_WINIO__=1" | isWindows ] +++ [ "-D__IO_MANAGER_MIO__=1" ]++ let sse_defs =+ [ "-D__SSE__" | isSseEnabled platform ] +++ [ "-D__SSE2__" | isSse2Enabled platform ] +++ [ "-D__SSE4_2__" | isSse4_2Enabled dflags ]++ let avx_defs =+ [ "-D__AVX__" | isAvxEnabled dflags ] +++ [ "-D__AVX2__" | isAvx2Enabled dflags ] +++ [ "-D__AVX512CD__" | isAvx512cdEnabled dflags ] +++ [ "-D__AVX512ER__" | isAvx512erEnabled dflags ] +++ [ "-D__AVX512F__" | isAvx512fEnabled dflags ] +++ [ "-D__AVX512PF__" | isAvx512pfEnabled dflags ]++ backend_defs <- applyCDefs (backendCDefs $ backend dflags) logger dflags++ let th_defs = [ "-D__GLASGOW_HASKELL_TH__" ]+ -- Default CPP defines in Haskell source+ ghcVersionH <- getGhcVersionPathName dflags unit_env+ let hsSourceCppOpts = [ "-include", ghcVersionH ]++ -- MIN_VERSION macros+ let uids = explicitUnits unit_state+ pkgs = mapMaybe (lookupUnit unit_state . fst) uids+ mb_macro_include <-+ if not (null pkgs) && gopt Opt_VersionMacros dflags+ then do macro_stub <- newTempName logger tmpfs (tmpDir dflags) TFL_CurrentModule "h"+ writeFile macro_stub (generatePackageVersionMacros pkgs)+ -- Include version macros for every *exposed* package.+ -- Without -hide-all-packages and with a package database+ -- size of 1000 packages, it takes cpp an estimated 2+ -- milliseconds to process this file. See #10970+ -- comment 8.+ return [GHC.SysTools.FileOption "-include" macro_stub]+ else return []++ let line_pragmas+ | cppLinePragmas opts = [] -- on by default+ | otherwise = [GHC.SysTools.Option "-P"] -- disable LINE markers++ cpp_prog ( map GHC.SysTools.Option verbFlags+ ++ map GHC.SysTools.Option include_paths+ ++ map GHC.SysTools.Option hsSourceCppOpts+ ++ map GHC.SysTools.Option target_defs+ ++ map GHC.SysTools.Option backend_defs+ ++ map GHC.SysTools.Option th_defs+ ++ map GHC.SysTools.Option hscpp_opts+ ++ map GHC.SysTools.Option sse_defs+ ++ map GHC.SysTools.Option avx_defs+ ++ map GHC.SysTools.Option io_manager_defs+ ++ mb_macro_include+ ++ line_pragmas+ -- Set the language mode to assembler-with-cpp when preprocessing. This+ -- alleviates some of the C99 macro rules relating to whitespace and the hash+ -- operator, which we tend to abuse. Clang in particular is not very happy+ -- about this.+ ++ [ GHC.SysTools.Option "-x"+ , GHC.SysTools.Option "assembler-with-cpp"+ , GHC.SysTools.Option input_fn+ -- We hackily use Option instead of FileOption here, so that the file+ -- name is not back-slashed on Windows. cpp is capable of+ -- dealing with / in filenames, so it works fine. Furthermore+ -- if we put in backslashes, cpp outputs #line directives+ -- with *double* backslashes. And that in turn means that+ -- our error messages get double backslashes in them.+ -- In due course we should arrange that the lexer deals+ -- with these \\ escapes properly.+ , GHC.SysTools.Option "-o"+ , GHC.SysTools.FileOption "" output_fn+ ])++-- ---------------------------------------------------------------------------+-- Macros (cribbed from Cabal)++generatePackageVersionMacros :: [UnitInfo] -> String+generatePackageVersionMacros pkgs = concat+ -- Do not add any C-style comments. See #3389.+ [ generateMacros "" pkgname version+ | pkg <- pkgs+ , let version = unitPackageVersion pkg+ pkgname = map fixchar (unitPackageNameString pkg)+ ]++fixchar :: Char -> Char+fixchar '-' = '_'+fixchar c = c++generateMacros :: String -> String -> Version -> String+generateMacros prefix name version =+ concat+ ["#define ", prefix, "VERSION_",name," ",show (showVersion version),"\n"+ ,"#define MIN_", prefix, "VERSION_",name,"(major1,major2,minor) (\\\n"+ ," (major1) < ",major1," || \\\n"+ ," (major1) == ",major1," && (major2) < ",major2," || \\\n"+ ," (major1) == ",major1," && (major2) == ",major2," && (minor) <= ",minor,")"+ ,"\n\n"+ ]+ where+ take3 = \case+ (a:b:c:_) -> (a,b,c)+ _ -> error "take3"+ (major1,major2,minor) = take3 $ map show (versionBranch version) ++ repeat "0"+++-- | Find out path to @ghcversion.h@ file+getGhcVersionPathName :: DynFlags -> UnitEnv -> IO FilePath+getGhcVersionPathName dflags unit_env = do+ candidates <- case ghcVersionFile dflags of+ Just path -> return [path]+ Nothing -> do+ ps <- mayThrowUnitErr (preloadUnitsInfo' unit_env [rtsUnitId])+ return ((</> "ghcversion.h") <$> collectIncludeDirs ps)++ found <- filterM doesFileExist candidates+ case found of+ [] -> throwGhcExceptionIO (InstallationError+ ("ghcversion.h missing; tried: "+ ++ intercalate ", " candidates))+ (x:_) -> return x++applyCDefs :: DefunctionalizedCDefs -> Logger -> DynFlags -> IO [String]+applyCDefs NoCDefs _ _ = return []+applyCDefs LlvmCDefs logger dflags = do+ llvmVer <- figureLlvmVersion logger dflags+ return $ case fmap llvmVersionList llvmVer of+ Just [m] -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (m,0) ]+ Just (m:n:_) -> [ "-D__GLASGOW_HASKELL_LLVM__=" ++ format (m,n) ]+ _ -> []+ where+ format (major, minor)+ | minor >= 100 = error "backendCDefs: Unsupported minor version"+ | otherwise = show (100 * major + minor :: Int) -- Contract is Int+++-- Note [Filepaths and Multiple Home Units]+offsetIncludePaths :: DynFlags -> IncludeSpecs -> IncludeSpecs+offsetIncludePaths dflags (IncludeSpecs incs quotes impl) =+ let go = map (augmentByWorkingDirectory dflags)+ in IncludeSpecs (go incs) (go quotes) (go impl)
compiler/GHC/SysTools/Elf.hs view
@@ -70,7 +70,7 @@ same thing applies to the field containing the number of segments, but we don't care about it here). - ELF-next: to solve this, theses fields in the ELF header have an escape+ ELF-next: to solve this, these fields in the ELF header have an escape value (different for each case), and the actual section number is stashed into unused fields in the first section header.
compiler/GHC/SysTools/Info.hs view
@@ -113,7 +113,7 @@ | any (\line -> "LLD" `isPrefixOf` line || "LLD" `elem` words line) stdo = return (LlvmLLD $ map Option [ --see Note [ELF needed shared libs]- "-Wl,--no-as-needed"])+ "-Wl,--no-as-needed" | osElfTarget os || os == OSMinGW32 ]) -- Unknown linker. | otherwise = fail "invalid --version output, or linker is unsupported"
compiler/GHC/SysTools/Tasks.hs view
@@ -14,9 +14,10 @@ import GHC.ForeignSrcLang import GHC.IO (catchException) -import GHC.CmmToLlvm.Base (llvmVersionStr, supportedLlvmVersionUpperBound, parseLlvmVersion, supportedLlvmVersionLowerBound)-import GHC.CmmToLlvm.Config (LlvmVersion)+import GHC.CmmToLlvm.Config (LlvmVersion, llvmVersionStr, supportedLlvmVersionUpperBound, parseLlvmVersion, supportedLlvmVersionLowerBound) +import GHC.Settings+ import GHC.SysTools.Process import GHC.SysTools.Info @@ -45,7 +46,7 @@ -} runUnlit :: Logger -> DynFlags -> [Option] -> IO ()-runUnlit logger dflags args = traceToolCommand logger "unlit" $ do+runUnlit logger dflags args = traceSystoolCommand logger "unlit" $ do let prog = pgm_L dflags opts = getOpts dflags opt_L runSomething logger "Literate pre-processor" prog@@ -61,7 +62,7 @@ augmentImports dflags (fp1: fp2: fps) = fp1 : augmentImports dflags (fp2:fps) runCpp :: Logger -> DynFlags -> [Option] -> IO ()-runCpp logger dflags args = traceToolCommand logger "cpp" $ do+runCpp logger dflags args = traceSystoolCommand logger "cpp" $ do let opts = getOpts dflags opt_P modified_imports = augmentImports dflags opts let (p,args0) = pgm_P dflags@@ -73,14 +74,14 @@ (args0 ++ args1 ++ args2 ++ args) Nothing mb_env runPp :: Logger -> DynFlags -> [Option] -> IO ()-runPp logger dflags args = traceToolCommand logger "pp" $ do+runPp logger dflags args = traceSystoolCommand logger "pp" $ do let prog = pgm_F dflags opts = map Option (getOpts dflags opt_F) runSomething logger "Haskell pre-processor" prog (args ++ opts) -- | Run compiler of C-like languages and raw objects (such as gcc or clang). runCc :: Maybe ForeignSrcLang -> Logger -> TmpFs -> DynFlags -> [Option] -> IO ()-runCc mLanguage logger tmpfs dflags args = traceToolCommand logger "cc" $ do+runCc mLanguage logger tmpfs dflags args = traceSystoolCommand logger "cc" $ do let args1 = map Option userOpts args2 = languageOptions ++ args ++ args1 -- We take care to pass -optc flags in args1 last to ensure that the@@ -160,6 +161,10 @@ ,pgm_c dflags, "Asm Compiler") RawObject -> ("c", [] ,pgm_c dflags, "C Compiler") -- claim C for lack of a better idea+ --JS backend shouldn't reach here, so we just pass+ -- strings to satisfy the totality checker+ LangJs -> ("js", []+ ,pgm_c dflags, "JS Backend Compiler") userOpts_c = getOpts dflags opt_c userOpts_cxx = getOpts dflags opt_cxx @@ -168,7 +173,7 @@ -- | Run the linker with some arguments and return the output askLd :: Logger -> DynFlags -> [Option] -> IO String-askLd logger dflags args = traceToolCommand logger "linker" $ do+askLd logger dflags args = traceSystoolCommand logger "linker" $ do let (p,args0) = pgm_l dflags args1 = map Option (getOpts dflags opt_l) args2 = args0 ++ args1 ++ args@@ -177,7 +182,7 @@ readCreateProcessWithExitCode' (proc p real_args){ env = mb_env } runAs :: Logger -> DynFlags -> [Option] -> IO ()-runAs logger dflags args = traceToolCommand logger "as" $ do+runAs logger dflags args = traceSystoolCommand logger "as" $ do let (p,args0) = pgm_a dflags args1 = map Option (getOpts dflags opt_a) args2 = args0 ++ args1 ++ args@@ -186,7 +191,7 @@ -- | Run the LLVM Optimiser runLlvmOpt :: Logger -> DynFlags -> [Option] -> IO ()-runLlvmOpt logger dflags args = traceToolCommand logger "opt" $ do+runLlvmOpt logger dflags args = traceSystoolCommand logger "opt" $ do let (p,args0) = pgm_lo dflags args1 = map Option (getOpts dflags opt_lo) -- We take care to pass -optlo flags (e.g. args0) last to ensure that the@@ -195,7 +200,7 @@ -- | Run the LLVM Compiler runLlvmLlc :: Logger -> DynFlags -> [Option] -> IO ()-runLlvmLlc logger dflags args = traceToolCommand logger "llc" $ do+runLlvmLlc logger dflags args = traceSystoolCommand logger "llc" $ do let (p,args0) = pgm_lc dflags args1 = map Option (getOpts dflags opt_lc) runSomething logger "LLVM Compiler" p (args0 ++ args1 ++ args)@@ -204,7 +209,7 @@ -- backend on OS X as LLVM doesn't support the OS X system -- assembler) runClang :: Logger -> DynFlags -> [Option] -> IO ()-runClang logger dflags args = traceToolCommand logger "clang" $ do+runClang logger dflags args = traceSystoolCommand logger "clang" $ do let (clang,_) = pgm_lcc dflags -- be careful what options we call clang with -- see #5903 and #7617 for bugs caused by this.@@ -222,9 +227,15 @@ throwIO err ) +runEmscripten :: Logger -> DynFlags -> [Option] -> IO ()+runEmscripten logger dflags args = traceSystoolCommand logger "emcc" $ do+ let (p,args0) = pgm_a dflags+ args1 = args0 ++ args+ runSomething logger "Emscripten" p args1+ -- | Figure out which version of LLVM we are running this session figureLlvmVersion :: Logger -> DynFlags -> IO (Maybe LlvmVersion)-figureLlvmVersion logger dflags = traceToolCommand logger "llc" $ do+figureLlvmVersion logger dflags = traceSystoolCommand logger "llc" $ do let (pgm,opts) = pgm_lc dflags args = filter notNull (map showOpt opts) -- we grab the args even though they should be useless just in@@ -267,7 +278,7 @@ runLink :: Logger -> TmpFs -> DynFlags -> [Option] -> IO ()-runLink logger tmpfs dflags args = traceToolCommand logger "linker" $ do+runLink logger tmpfs dflags args = traceSystoolCommand logger "linker" $ do -- See Note [Run-time linker info] -- -- `-optl` args come at the end, so that later `-l` options@@ -332,7 +343,7 @@ -- See Note [Merging object files for GHCi] in GHC.Driver.Pipeline. runMergeObjects :: Logger -> TmpFs -> DynFlags -> [Option] -> IO () runMergeObjects logger tmpfs dflags args =- traceToolCommand logger "merge-objects" $ do+ traceSystoolCommand logger "merge-objects" $ do let (p,args0) = fromMaybe err (pgm_lm dflags) err = throwGhcException $ UsageError $ unwords [ "Attempted to merge object files but the configured linker"@@ -348,38 +359,29 @@ else do runSomething logger "Merge objects" p args2 -runLibtool :: Logger -> DynFlags -> [Option] -> IO ()-runLibtool logger dflags args = traceToolCommand logger "libtool" $ do- linkargs <- neededLinkArgs `fmap` getLinkerInfo logger dflags- let args1 = map Option (getOpts dflags opt_l)- args2 = [Option "-static"] ++ args1 ++ args ++ linkargs- libtool = pgm_libtool dflags- mb_env <- getGccEnv args2- runSomethingFiltered logger id "Libtool" libtool args2 Nothing mb_env- runAr :: Logger -> DynFlags -> Maybe FilePath -> [Option] -> IO ()-runAr logger dflags cwd args = traceToolCommand logger "ar" $ do+runAr logger dflags cwd args = traceSystoolCommand logger "ar" $ do let ar = pgm_ar dflags runSomethingFiltered logger id "Ar" ar args cwd Nothing -askOtool :: Logger -> DynFlags -> Maybe FilePath -> [Option] -> IO String-askOtool logger dflags mb_cwd args = do- let otool = pgm_otool dflags+askOtool :: Logger -> ToolSettings -> Maybe FilePath -> [Option] -> IO String+askOtool logger toolSettings mb_cwd args = do+ let otool = toolSettings_pgm_otool toolSettings runSomethingWith logger "otool" otool args $ \real_args -> readCreateProcessWithExitCode' (proc otool real_args){ cwd = mb_cwd } -runInstallNameTool :: Logger -> DynFlags -> [Option] -> IO ()-runInstallNameTool logger dflags args = do- let tool = pgm_install_name_tool dflags+runInstallNameTool :: Logger -> ToolSettings -> [Option] -> IO ()+runInstallNameTool logger toolSettings args = do+ let tool = toolSettings_pgm_install_name_tool toolSettings runSomethingFiltered logger id "Install Name Tool" tool args Nothing Nothing runRanlib :: Logger -> DynFlags -> [Option] -> IO ()-runRanlib logger dflags args = traceToolCommand logger "ranlib" $ do+runRanlib logger dflags args = traceSystoolCommand logger "ranlib" $ do let ranlib = pgm_ranlib dflags runSomethingFiltered logger id "Ranlib" ranlib args Nothing Nothing runWindres :: Logger -> DynFlags -> [Option] -> IO ()-runWindres logger dflags args = traceToolCommand logger "windres" $ do+runWindres logger dflags args = traceSystoolCommand logger "windres" $ do let cc_args = map Option (sOpt_c (settings dflags)) windres = pgm_windres dflags opts = map Option (getOpts dflags opt_windres)@@ -387,17 +389,5 @@ runSomethingFiltered logger id "Windres" windres (opts ++ args) Nothing mb_env touch :: Logger -> DynFlags -> String -> String -> IO ()-touch logger dflags purpose arg = traceToolCommand logger "touch" $+touch logger dflags purpose arg = traceSystoolCommand logger "touch" $ runSomething logger purpose (pgm_T dflags) [FileOption "" arg]---- * Tracing utility---- | Record in the eventlog when the given tool command starts--- and finishes, prepending the given 'String' with--- \"systool:\", to easily be able to collect and process--- all the systool events.------ For those events to show up in the eventlog, you need--- to run GHC with @-v2@ or @-ddump-timings@.-traceToolCommand :: Logger -> String -> IO a -> IO a-traceToolCommand logger tool = withTiming logger (text "systool:" <> text tool) (const ())
compiler/GHC/Tc/Deriv.hs view
@@ -28,7 +28,7 @@ import GHC.Tc.TyCl.Class( instDeclCtxt3, tcATDefault ) import GHC.Tc.Utils.Env import GHC.Tc.Deriv.Generate-import GHC.Tc.Validity( allDistinctTyVars, checkValidInstHead )+import GHC.Tc.Validity( checkValidInstHead ) import GHC.Core.InstEnv import GHC.Tc.Utils.Instantiate import GHC.Core.FamInstEnv@@ -201,7 +201,6 @@ ; famInsts2 <- concatMapM genFamInsts infer_specs ; let famInsts = famInsts1 ++ famInsts2 - ; dflags <- getDynFlags ; logger <- getLogger -- We must put all the derived type family instances (from both@@ -229,7 +228,7 @@ ; let (_, aux_specs, fvs) = unzip3 (given_inst_binds ++ infer_inst_binds) ; loc <- getSrcSpanM- ; let aux_binds = genAuxBinds dflags loc (unionManyBags aux_specs)+ ; let aux_binds = genAuxBinds loc (unionManyBags aux_specs) ; let infer_inst_infos = map fstOf3 infer_inst_binds ; let inst_infos = given_inst_infos ++ infer_inst_infos@@ -289,8 +288,8 @@ -- Bring the extra deriving stuff into scope -- before renaming the instances themselves ; traceTc "rnd" (vcat (map (\i -> pprInstInfoDetails i $$ text "") inst_infos))- ; (aux_binds, aux_sigs) <- mapAndUnzipBagM return bagBinds- ; let aux_val_binds = ValBinds NoAnnSortKey aux_binds (bagToList aux_sigs)+ ; let (aux_binds, aux_sigs) = unzipBag bagBinds+ aux_val_binds = ValBinds NoAnnSortKey aux_binds (bagToList aux_sigs) -- Importantly, we use rnLocalValBindsLHS, not rnTopBindsLHS, to rename -- auxiliary bindings as if they were defined locally. -- See Note [Auxiliary binders] in GHC.Tc.Deriv.Generate.@@ -636,8 +635,8 @@ -- is the case. | Just inst_ty <- lastMaybe inst_tys -> do- let via_kind = tcTypeKind via_ty- inst_ty_kind = tcTypeKind inst_ty+ let via_kind = typeKind via_ty+ inst_ty_kind = typeKind inst_ty mb_match = tcUnifyTy inst_ty_kind via_kind checkTc (isJust mb_match)@@ -645,9 +644,9 @@ DerivErrDerivingViaWrongKind inst_ty_kind via_ty via_kind) let Just kind_subst = mb_match- ki_subst_range = getTCvSubstRangeFVs kind_subst+ ki_subst_range = getSubstRangeTyCoFVs kind_subst -- See Note [Unification of two kind variables in deriving]- unmapped_tkvs = filter (\v -> v `notElemTCvSubst` kind_subst+ unmapped_tkvs = filter (\v -> v `notElemSubst` kind_subst && not (v `elemVarSet` ki_subst_range)) tvs (subst, _) = substTyVarBndrs kind_subst unmapped_tkvs@@ -748,7 +747,7 @@ -- See Note [tc_args and tycon arity] (tc_args_to_keep, args_to_drop) = splitAt n_args_to_keep tc_args- inst_ty_kind = tcTypeKind (mkTyConApp tc tc_args_to_keep)+ inst_ty_kind = typeKind (mkTyConApp tc tc_args_to_keep) -- Match up the kinds, and apply the resulting kind substitution -- to the types. See Note [Unify kinds in deriving]@@ -757,6 +756,12 @@ enough_args = n_args_to_keep >= 0 -- Check that the result really is well-kinded+ ; traceTc "deriveTyData" $+ vcat [ text "class:" <+> ppr cls <+> dcolon <+> ppr (tyConKind (classTyCon cls))+ , text "cls_tys:" <+> ppr cls_tys+ , text "tycon:" <+> ppr tc <+> dcolon <+> ppr (tyConKind tc)+ , text "cls_arg:" <+> ppr (mkTyConApp tc tc_args_to_keep) <+> dcolon <+> ppr inst_ty_kind+ , text "cls_arg_kind:" <+> ppr cls_arg_kind ] ; checkTc (enough_args && isJust mb_match) (TcRnCannotDeriveInstance cls cls_tys Nothing NoGeneralizedNewtypeDeriving $ DerivErrNotWellKinded tc cls_arg_kind n_args_to_keep)@@ -769,9 +774,9 @@ propagate_subst kind_subst tkvs' cls_tys' tc_args' mb_deriv_strat' = (final_tkvs, final_cls_tys, final_tc_args, final_mb_deriv_strat) where- ki_subst_range = getTCvSubstRangeFVs kind_subst+ ki_subst_range = getSubstRangeTyCoFVs kind_subst -- See Note [Unification of two kind variables in deriving]- unmapped_tkvs = filter (\v -> v `notElemTCvSubst` kind_subst+ unmapped_tkvs = filter (\v -> v `notElemSubst` kind_subst && not (v `elemVarSet` ki_subst_range)) tkvs' (subst, _) = substTyVarBndrs kind_subst unmapped_tkvs@@ -798,9 +803,9 @@ -- Perform an additional unification with the kind of the `via` -- type and the result of the previous kind unification. Just (ViaStrategy via_ty) -> do- let via_kind = tcTypeKind via_ty+ let via_kind = typeKind via_ty inst_ty_kind- = tcTypeKind (mkTyConApp tc tc_args')+ = typeKind (mkTyConApp tc tc_args') via_match = tcUnifyTy inst_ty_kind via_kind checkTc (isJust via_match)@@ -1008,7 +1013,7 @@ We used to accomplish this by doing the following: - unmapped_tkvs = filter (`notElemTCvSubst` kind_subst) all_tkvs+ unmapped_tkvs = filter (`notElemSubst` kind_subst) all_tkvs (subst, _) = substTyVarBndrs kind_subst unmapped_tkvs Where all_tkvs contains all kind variables in the class and instance types (in@@ -1024,9 +1029,9 @@ To prevent this, we need to filter out any variable from all_tkvs which either -1. Appears in the domain of kind_subst. notElemTCvSubst checks this.+1. Appears in the domain of kind_subst. notElemSubst checks this. 2. Appears in the range of kind_subst. To do this, we compute the free- variable set of the range of kind_subst with getTCvSubstRangeFVs, and check+ variable set of the range of kind_subst with getSubstRangeTyCoFVs, and check if a kind variable appears in that set. Note [Eta-reducing type synonyms]@@ -1632,7 +1637,7 @@ newtype T1 a = T1 (StateT S1 IO a ) deriving( Monad ) Remember, too, that type families are currently (conservatively) given a recursive flag, so this also allows newtype deriving to work-for type famillies.+for type families. We used to exclude recursive types, because we had a rather simple minded way of generating the instance decl:@@ -1920,13 +1925,13 @@ -- See Note [DeriveAnyClass and default family instances] DerivSpecAnyClass -> do let mini_env = mkVarEnv (classTyVars clas `zip` inst_tys)- mini_subst = mkTvSubst (mkInScopeSet (mkVarSet tyvars)) mini_env+ mini_subst = mkTvSubst (mkInScopeSetList tyvars) mini_env dflags <- getDynFlags tyfam_insts <- -- canDeriveAnyClass should ensure that this code can't be reached -- unless -XDeriveAnyClass is enabled. assertPpr (xopt LangExt.DeriveAnyClass dflags)- (ppr "genFamInsts: bad derived class" <+> ppr clas) $+ (text "genFamInsts: bad derived class" <+> ppr clas) $ mapM (tcATDefault loc mini_subst emptyNameSet) (classATItems clas) pure $ concat tyfam_insts
compiler/GHC/Tc/Deriv/Functor.hs view
@@ -505,11 +505,11 @@ -> Type -> (a, Bool) -- (result of type a, does type contain var) - go co ty | Just ty' <- tcView ty = go co ty'+ go co ty | Just ty' <- coreView ty = go co ty' go co (TyVarTy v) | v == var = (if co then caseCoVar else caseVar,True) go co (FunTy { ft_arg = x, ft_res = y, ft_af = af })- | InvisArg <- af = go co y- | xc || yc = (caseFun xr yr,True)+ | isInvisibleFunArg af = go co y+ | xc || yc = (caseFun xr yr,True) where (xr,xc) = go (not co) x (yr,yc) = go co y go co (AppTy x y) | xc = (caseWrongArg, True)@@ -532,8 +532,8 @@ -- actually needs. See #12399 (xrs,xcs) = unzip (map (go co) (dropRuntimeRepArgs args)) go co (ForAllTy (Bndr v vis) x)- | isVisibleArgFlag vis = panic "unexpected visible binder"- | v /= var && xc = (caseForAll v xr,True)+ | isVisibleForAllTyFlag vis = panic "unexpected visible binder"+ | v /= var && xc = (caseForAll v xr,True) where (xr,xc) = go co x go _ _ = (caseTrivial,False)
compiler/GHC/Tc/Deriv/Generate.hs view
@@ -45,8 +45,8 @@ import GHC.Prelude import GHC.Tc.Utils.Monad-import GHC.Tc.TyCl.Class ( substATBndrs ) import GHC.Hs+import GHC.Types.FieldLabel import GHC.Types.Name.Reader import GHC.Types.Basic import GHC.Types.Fixity@@ -54,7 +54,6 @@ import GHC.Types.Name import GHC.Types.SourceText -import GHC.Driver.Session import GHC.Tc.Instance.Family import GHC.Core.FamInstEnv import GHC.Builtin.Names@@ -73,8 +72,8 @@ import GHC.Builtin.Types import GHC.Core.Type import GHC.Core.Class+ import GHC.Types.Unique.FM ( lookupUFM, listToUFM )-import GHC.Types.Var.Set import GHC.Types.Var.Env import GHC.Utils.Misc import GHC.Types.Var@@ -86,6 +85,8 @@ import GHC.Data.Pair import GHC.Data.Bag +import Language.Haskell.Syntax.Basic (FieldLabelString(..))+ import Data.List ( find, partition, intersperse ) import GHC.Data.Maybe ( expectJust ) import GHC.Unit.Module@@ -451,7 +452,7 @@ = nlHsCase (nlHsVar a_RDR) $ map (mkOrdOpAlt op) tycon_data_cons -- i.e. case a of { C1 x y -> case b of C1 x y -> ....compare x,y...- -- C2 x -> case b of C2 x -> ....comopare x.... }+ -- C2 x -> case b of C2 x -> ....compare x.... } | null non_nullary_cons -- All nullary, so go straight to comparing tags = mkTagCmp op@@ -853,7 +854,7 @@ enum_index = mkSimpleGeneratedFunBind loc unsafeIndex_RDR- [noLocA (AsPat noAnn (noLocA c_RDR)+ [noLocA (AsPat noAnn (noLocA c_RDR) noHsTok (nlTuplePat [a_Pat, nlWildPat] Boxed)), d_Pat] ( untag_Expr [(a_RDR, ah_RDR)] (@@ -1112,7 +1113,7 @@ field_stmts = zipWithEqual "lbl_stmts" read_field labels as_needed con_arity = dataConSourceArity data_con- labels = map flLabel $ dataConFieldLabels data_con+ labels = map (field_label . flLabel) $ dataConFieldLabels data_con dc_nm = getName data_con is_infix = dataConIsInfix data_con is_record = labels `lengthExceeds` 0@@ -1169,14 +1170,14 @@ where lbl_str = unpackFS lbl mk_read_field read_field_rdr lbl- = nlHsApps read_field_rdr [nlHsLit (mkHsString lbl)]+ = nlHsApps read_field_rdr [nlHsLit (mkHsStringFS lbl)] read_field | isSym lbl_str- = mk_read_field readSymField_RDR lbl_str+ = mk_read_field readSymField_RDR lbl | Just (ss, '#') <- snocView lbl_str -- #14918- = mk_read_field readFieldHash_RDR ss+ = mk_read_field readFieldHash_RDR (mkFastString ss) | otherwise- = mk_read_field readField_RDR lbl_str+ = mk_read_field readField_RDR lbl {- ************************************************************************@@ -1236,7 +1237,7 @@ arg_tys = derivDataConInstArgTys data_con dit -- Correspond 1-1 with bs_needed con_pat = nlConVarPat data_con_RDR bs_needed nullary_con = con_arity == 0- labels = map flLabel $ dataConFieldLabels data_con+ labels = map (field_label . flLabel) $ dataConFieldLabels data_con lab_fields = length labels record_syntax = lab_fields > 0 @@ -1402,7 +1403,6 @@ where data_cons = tyConDataCons rep_tc n_cons = length data_cons- one_constr = n_cons == 1 ------------ gfoldl gfoldl_bind = mkFunBindEC 3 loc gfoldl_RDR id (map gfoldl_eqn data_cons)@@ -1419,11 +1419,11 @@ ------------ gunfold gunfold_bind = mkSimpleGeneratedFunBind loc gunfold_RDR- [k_Pat, z_Pat, if one_constr then nlWildPat else c_Pat]+ [k_Pat, z_Pat, if n_cons == 1 then nlWildPat else c_Pat] gunfold_rhs gunfold_rhs- | one_constr = mk_unfold_rhs (head data_cons) -- No need for case+ | [con] <- data_cons = mk_unfold_rhs con -- No need for case | otherwise = nlHsCase (nlHsVar conIndex_RDR `nlHsApp` c_Expr) (map gunfold_alt data_cons) @@ -1662,8 +1662,8 @@ mk_untyped_bracket = HsUntypedBracket noAnn . ExpBr noExtField mk_typed_bracket = HsTypedBracket noAnn - mk_usplice = HsUntypedSplice EpAnnNotUsed DollarSplice- mk_tsplice = HsTypedSplice EpAnnNotUsed DollarSplice+ mk_tsplice = HsTypedSplice (EpAnnNotUsed, noAnn)+ mk_usplice = HsUntypedSplice EpAnnNotUsed . HsUntypedSpliceExpr noAnn data_cons = getPossibleDataCons tycon tycon_args pats_etc mk_bracket mk_splice lift_name data_con@@ -1678,7 +1678,7 @@ (map lift_var as_needed) lift_var :: RdrName -> LHsExpr (GhcPass 'Parsed)- lift_var x = noLocA (HsSpliceE EpAnnNotUsed (mk_splice x (nlHsPar (mk_lift_expr x))))+ lift_var x = noLocA (mk_splice (nlHsPar (mk_lift_expr x))) mk_lift_expr :: RdrName -> LHsExpr (GhcPass 'Parsed) mk_lift_expr x = nlHsApps (Exact lift_name) [nlHsVar x]@@ -2080,7 +2080,7 @@ ats = classATs cls locn = noAnnSrcSpan loc' cls_tvs = classTyVars cls- in_scope = mkInScopeSet $ mkVarSet inst_tvs+ in_scope = mkInScopeSetList inst_tvs lhs_env = zipTyEnv cls_tvs inst_tys lhs_subst = mkTvSubst in_scope lhs_env rhs_env = zipTyEnv cls_tvs underlying_inst_tys@@ -2097,8 +2097,8 @@ newFamInst SynFamilyInst axiom where fam_tvs = tyConTyVars fam_tc- (_, rep_lhs_tys) = substATBndrs lhs_subst fam_tvs- (_, rep_rhs_tys) = substATBndrs rhs_subst fam_tvs+ rep_lhs_tys = substTyVars lhs_subst fam_tvs+ rep_rhs_tys = substTyVars rhs_subst fam_tvs rep_rhs_ty = mkTyConApp fam_tc rep_rhs_tys rep_tcvs = tyCoVarsOfTypesList rep_lhs_tys (rep_tvs, rep_cvs) = partition isTyVar rep_tcvs@@ -2106,7 +2106,7 @@ rep_cvs' = scopedSort rep_cvs nlHsAppType :: LHsExpr GhcPs -> Type -> LHsExpr GhcPs-nlHsAppType e s = noLocA (HsAppType noSrcSpan e hs_ty)+nlHsAppType e s = noLocA (HsAppType noExtField e noHsTok hs_ty) where hs_ty = mkHsWildCardBndrs $ parenthesizeHsType appPrec $ nlHsCoreTy s @@ -2130,7 +2130,7 @@ (substTy lhs_subst user_meth_ty) where cls_tvs = classTyVars cls- in_scope = mkInScopeSet $ mkVarSet inst_tvs+ in_scope = mkInScopeSetList inst_tvs lhs_subst = mkTvSubst in_scope (zipTyEnv cls_tvs inst_tys) rhs_subst = mkTvSubst in_scope (zipTyEnv cls_tvs (changeLast inst_tys rhs_ty)) (_class_tvs, _class_constraint, user_meth_ty)@@ -2156,9 +2156,9 @@ -- | Generate the full code for an auxiliary binding. -- See @Note [Auxiliary binders] (Wrinkle: Reducing code duplication)@.-genAuxBindSpecOriginal :: DynFlags -> SrcSpan -> AuxBindSpec+genAuxBindSpecOriginal :: SrcSpan -> AuxBindSpec -> (LHsBind GhcPs, LSig GhcPs)-genAuxBindSpecOriginal dflags loc spec+genAuxBindSpecOriginal loc spec = (gen_bind spec, L loca (TypeSig noAnn [L locn (auxBindSpecRdrName spec)] (genAuxBindSpecSig loc spec)))@@ -2183,11 +2183,10 @@ = mkHsVarBind loc dataT_RDR rhs where tc_name = tyConName tycon- tc_name_string = occNameString (getOccName tc_name)- definition_mod_name = moduleNameString (moduleName (expectJust "gen_bind DerivDataDataType" $ nameModule_maybe tc_name))- ctx = initDefaultSDocContext dflags+ tc_name_string = occNameFS (getOccName tc_name)+ definition_mod_name = moduleNameFS (moduleName (expectJust "gen_bind DerivDataDataType" $ nameModule_maybe tc_name)) rhs = nlHsVar mkDataType_RDR- `nlHsApp` nlHsLit (mkHsString (showSDocOneLine ctx (text definition_mod_name <> dot <> text tc_name_string)))+ `nlHsApp` nlHsLit (mkHsStringFS (concatFS [definition_mod_name, fsLit ".", tc_name_string])) `nlHsApp` nlList (map nlHsVar dataC_RDRs) gen_bind (DerivDataConstr dc dataC_RDR dataT_RDR)@@ -2197,12 +2196,12 @@ constr_args = [ nlHsVar dataT_RDR -- DataType- , nlHsLit (mkHsString (occNameString dc_occ)) -- Constructor name+ , nlHsLit (mkHsStringFS (occNameFS dc_occ)) -- Constructor name , nlHsIntLit (toInteger (dataConTag dc)) -- Constructor tag , nlList labels -- Field labels , nlHsVar fixity ] -- Fixity - labels = map (nlHsLit . mkHsString . unpackFS . flLabel)+ labels = map (nlHsLit . mkHsStringFS . field_label . flLabel) (dataConFieldLabels dc) dc_occ = getOccName dc is_infix = isDataSymOcc dc_occ@@ -2243,9 +2242,9 @@ -- | Take a 'Bag' of 'AuxBindSpec's and generate the code for auxiliary -- bindings based on the declarative descriptions in the supplied -- 'AuxBindSpec's. See @Note [Auxiliary binders]@.-genAuxBinds :: DynFlags -> SrcSpan -> Bag AuxBindSpec+genAuxBinds :: SrcSpan -> Bag AuxBindSpec -> Bag (LHsBind GhcPs, LSig GhcPs)-genAuxBinds dflags loc = snd . foldr gen_aux_bind_spec (emptyOccEnv, emptyBag)+genAuxBinds loc = snd . foldr gen_aux_bind_spec (emptyOccEnv, emptyBag) where -- Perform a CSE-like pass over the generated auxiliary bindings to avoid -- code duplication, as described in@@ -2259,7 +2258,7 @@ case lookupOccEnv original_rdr_name_env spec_occ of Nothing -> ( extendOccEnv original_rdr_name_env spec_occ spec_rdr_name- , genAuxBindSpecOriginal dflags loc spec `consBag` spec_bag )+ , genAuxBindSpecOriginal loc spec `consBag` spec_bag ) Just original_rdr_name -> ( original_rdr_name_env , genAuxBindSpecDup loc original_rdr_name spec `consBag` spec_bag )@@ -2363,7 +2362,7 @@ (replicate arity nlWildPat) (error_Expr str) emptyLocalBinds] else matches- str = "Void " ++ occNameString (rdrNameOcc fun_rdr)+ str = fsLit "Void " `appendFS` occNameFS (rdrNameOcc fun_rdr) box :: String -- The class involved@@ -2550,8 +2549,8 @@ -- impossible_Expr is used in case RHSs that should never happen. -- We generate these to keep the desugarer from complaining that they *might* happen!-error_Expr :: String -> LHsExpr GhcPs-error_Expr string = nlHsApp (nlHsVar error_RDR) (nlHsLit (mkHsString string))+error_Expr :: FastString -> LHsExpr GhcPs+error_Expr string = nlHsApp (nlHsVar error_RDR) (nlHsLit (mkHsStringFS string)) -- illegal_Expr is used when signalling error conditions in the RHS of a derived -- method. It is currently only used by Enum.{succ,pred}@@ -2761,7 +2760,7 @@ -- | Apply a substitution to all of the 'Type's contained in a 'DerivInstTys'. -- See @Note [Instantiating field types in stock deriving]@ for why we need to -- substitute into a 'DerivInstTys' in the first place.-substDerivInstTys :: TCvSubst -> DerivInstTys -> DerivInstTys+substDerivInstTys :: Subst -> DerivInstTys -> DerivInstTys substDerivInstTys subst dit@(DerivInstTys { dit_cls_tys = cls_tys, dit_tc_args = tc_args , dit_rep_tc = rep_tc, dit_rep_tc_args = rep_tc_args })
compiler/GHC/Tc/Deriv/Generics.hs view
@@ -21,21 +21,25 @@ ) where -import GHC.Prelude+import GHC.Prelude hiding (head, init, last, tail) import GHC.Hs-import GHC.Core.Type import GHC.Tc.Utils.TcType import GHC.Tc.Deriv.Generate import GHC.Tc.Deriv.Functor import GHC.Tc.Errors.Types+import GHC.Tc.Instance.Family++import GHC.Core.Type import GHC.Core.DataCon import GHC.Core.TyCon import GHC.Core.FamInstEnv ( FamInst, FamFlavor(..), mkSingleCoAxiom )-import GHC.Tc.Instance.Family-import GHC.Unit.Module ( moduleName, moduleNameFS- , moduleUnit, unitFS, getModule )++import GHC.Unit.Module ( moduleName, moduleUnit+ , unitFS, getModule )+ import GHC.Iface.Env ( newGlobalBinder )+ import GHC.Types.Name hiding ( varName ) import GHC.Types.Name.Reader import GHC.Types.SourceText@@ -58,8 +62,13 @@ import GHC.Data.FastString import GHC.Utils.Misc +import Language.Haskell.Syntax.Basic (FieldLabelString(..))+ import Control.Monad (mplus) import Data.List (zip4, partition)+import qualified Data.List as Partial (last)+import Data.List.NonEmpty (nonEmpty)+import qualified Data.List.NonEmpty as NE import Data.Maybe (isJust) {-@@ -289,9 +298,9 @@ , ft_var = caseVar, ft_co_var = caseVar -- (component_0,component_1,...,component_n)- , ft_tup = \_ components -> if any _ccdg1_hasParam (init components)- then bmbad con- else foldr bmplus bmzero components+ , ft_tup = \_ components -> case nonEmpty components of+ Just components' | any _ccdg1_hasParam (NE.init components') -> bmbad con+ _ -> foldr bmplus bmzero components -- (dom -> rng), where the head of ty is not a tuple tycon , ft_fun = \dom rng -> -- cf #8516@@ -338,11 +347,11 @@ gk2gkDC :: GenericKind -> DataCon -> [Type] -> GenericKind_DC gk2gkDC Gen0 _ _ = Gen0_DC gk2gkDC Gen1 dc tc_args = Gen1_DC $ assert (isTyVarTy last_dc_inst_univ)- $ getTyVar "gk2gkDC" last_dc_inst_univ+ $ getTyVar last_dc_inst_univ where dc_inst_univs = dataConInstUnivs dc tc_args last_dc_inst_univ = assert (not (null dc_inst_univs)) $- last dc_inst_univs+ Partial.last dc_inst_univs -- Bindings for the Generic instance@@ -654,7 +663,7 @@ then promotedTrueDataCon else promotedFalseDataCon - selName = mkStrLitTy . flLabel+ selName = mkStrLitTy . field_label . flLabel mbSel Nothing = mkTyConApp promotedNothingDataCon [typeSymbolKind] mbSel (Just s) = mkTyConApp promotedJustDataCon@@ -677,7 +686,7 @@ pDStrness = mkTyConTy $ case ib of HsLazy -> pDLzy- HsStrict -> pDStr+ HsStrict _ -> pDStr HsUnpack{} -> pDUpk return (mkD tycon)@@ -1069,7 +1078,7 @@ The recommended approach thus far was to increase unfolding threshold, but this makes GHC inline more aggressively in general, whereas it should only be more-aggresive with generics-based code.+aggressive with generics-based code. The solution is to use a heuristic that'll annotate Generic class methods with INLINE[1] pragmas (the explicit phase is used to give users phase control as
compiler/GHC/Tc/Deriv/Infer.hs view
@@ -16,16 +16,6 @@ import GHC.Prelude -import GHC.Data.Bag-import GHC.Types.Basic-import GHC.Core.Class-import GHC.Core.DataCon-import GHC.Utils.Error-import GHC.Utils.Outputable-import GHC.Utils.Panic-import GHC.Utils.Panic.Plain-import GHC.Data.Pair-import GHC.Builtin.Names import GHC.Tc.Deriv.Utils import GHC.Tc.Utils.Env import GHC.Tc.Deriv.Generate@@ -35,24 +25,40 @@ import GHC.Tc.Utils.Monad import GHC.Tc.Types.Origin import GHC.Tc.Types.Constraint-import GHC.Core.Predicate import GHC.Tc.Utils.TcType-import GHC.Core.TyCon-import GHC.Core.TyCo.Ppr (pprTyVars)-import GHC.Core.Type import GHC.Tc.Solver import GHC.Tc.Solver.Monad ( runTcS ) import GHC.Tc.Validity (validDerivPred) import GHC.Tc.Utils.Unify (buildImplicationFor)-import GHC.Builtin.Types (typeToTypeKind)++import GHC.Core.Class+import GHC.Core.DataCon+import GHC.Core.TyCon+import GHC.Core.TyCo.Ppr (pprTyVars)+import GHC.Core.Type+import GHC.Core.Predicate import GHC.Core.Unify (tcUnifyTy)++import GHC.Data.Pair+import GHC.Builtin.Names+import GHC.Builtin.Types (typeToTypeKind)++import GHC.Utils.Error+import GHC.Utils.Outputable+import GHC.Utils.Panic+import GHC.Utils.Panic.Plain import GHC.Utils.Misc++import GHC.Types.Basic import GHC.Types.Var-import GHC.Types.Var.Set +import GHC.Data.Bag+ import Control.Monad import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Reader (ask)+import Data.Function (on)+import Data.Functor.Classes (liftEq) import Data.List (sortBy) import Data.Maybe @@ -179,7 +185,7 @@ :: ([TyVar] -> CtOrigin -> TypeOrKind -> Type- -> [(ThetaSpec, Maybe TCvSubst)])+ -> [(ThetaSpec, Maybe Subst)]) -> (ThetaSpec, [TyVar], [TcType], DerivInstTys) con_arg_constraints get_arg_constraints = let -- Constraints from the fields of each data constructor.@@ -217,8 +223,8 @@ -- kinds with (* -> *). -- See Note [Inferring the instance context] subst = foldl' composeTCvSubst- emptyTCvSubst (catMaybes mbSubsts)- unmapped_tvs = filter (\v -> v `notElemTCvSubst` subst+ emptySubst (catMaybes mbSubsts)+ unmapped_tvs = filter (\v -> v `notElemSubst` subst && not (v `isInScope` subst)) tvs (subst', _) = substTyVarBndrs subst unmapped_tvs stupid_theta_origin = mkDirectThetaSpec@@ -234,7 +240,7 @@ is_generic = main_cls `hasKey` genClassKey is_generic1 = main_cls `hasKey` gen1ClassKey -- is_functor_like: see Note [Inferring the instance context]- is_functor_like = tcTypeKind inst_ty `tcEqKind` typeToTypeKind+ is_functor_like = typeKind inst_ty `tcEqKind` typeToTypeKind || is_generic1 get_gen1_constraints ::@@ -242,7 +248,7 @@ -> [TyVar] -- The universally quantified type variables for the -- data constructor -> CtOrigin -> TypeOrKind -> Type- -> [(ThetaSpec, Maybe TCvSubst)]+ -> [(ThetaSpec, Maybe Subst)] get_gen1_constraints functor_cls dc_univs orig t_or_k ty = mk_functor_like_constraints orig t_or_k functor_cls $ get_gen1_constrained_tys last_dc_univ ty@@ -257,7 +263,7 @@ [TyVar] -- The universally quantified type variables for the -- data constructor -> CtOrigin -> TypeOrKind -> Type- -> [(ThetaSpec, Maybe TCvSubst)]+ -> [(ThetaSpec, Maybe Subst)] get_std_constrained_tys dc_univs orig t_or_k ty | is_functor_like = mk_functor_like_constraints orig t_or_k main_cls $@@ -273,7 +279,7 @@ mk_functor_like_constraints :: CtOrigin -> TypeOrKind -> Class -> [Type]- -> [(ThetaSpec, Maybe TCvSubst)]+ -> [(ThetaSpec, Maybe Subst)] -- 'cls' is usually main_cls (Functor or Traversable etc), but if -- main_cls = Generic1, then 'cls' can be Functor; see -- get_gen1_constraints@@ -286,7 +292,7 @@ -- message which points out the kind mismatch. -- See Note [Inferring the instance context] mk_functor_like_constraints orig t_or_k cls- = map $ \ty -> let ki = tcTypeKind ty in+ = map $ \ty -> let ki = typeKind ty in ( [ mk_cls_pred orig t_or_k cls ty , SimplePredSpec { sps_pred = mkPrimEqPred ki typeToTypeKind@@ -307,7 +313,7 @@ -- and we need the Data constraints to typecheck the method extra_constraints | main_cls `hasKey` dataClassKey- , all (isLiftedTypeKind . tcTypeKind) rep_tc_args+ , all (isLiftedTypeKind . typeKind) rep_tc_args = [ mk_cls_pred deriv_origin t_or_k main_cls ty | (t_or_k, ty) <- zip t_or_ks rep_tc_args] | otherwise@@ -694,7 +700,7 @@ current_solns infer_specs ; new_solns <- checkNoErrs $ extendLocalInstEnv inst_specs $- mapM gen_soln infer_specs+ mapM simplifyDeriv infer_specs ; if (current_solns `eqSolution` new_solns) then return [ setDerivSpecTheta soln spec@@ -702,29 +708,11 @@ else iterate_deriv (n+1) new_solns } - eqSolution a b = eqListBy (eqListBy eqType) (canSolution a) (canSolution b)+ eqSolution = (liftEq . liftEq) eqType `on` canSolution -- Canonicalise for comparison -- See Note [Deterministic simplifyInstanceContexts] canSolution = map (sortBy nonDetCmpType)- ------------------------------------------------------------------- gen_soln :: DerivSpec ThetaSpec -> TcM ThetaType- gen_soln (DS { ds_loc = loc, ds_tvs = tyvars- , ds_cls = clas, ds_tys = inst_tys, ds_theta = deriv_rhs- , ds_skol_info = skol_info, ds_user_ctxt = user_ctxt })- = setSrcSpan loc $- addErrCtxt (derivInstCtxt the_pred) $- do { theta <- simplifyDeriv skol_info user_ctxt tyvars deriv_rhs- -- checkValidInstance tyvars theta clas inst_tys- -- Not necessary; see Note [Exotic derived instance contexts] - ; traceTc "GHC.Tc.Deriv" (ppr deriv_rhs $$ ppr theta)- -- Claim: the result instance declaration is guaranteed valid- -- Hence no need to call:- -- checkValidInstance tyvars theta clas inst_tys- ; return theta }- where- the_pred = mkClassPred clas inst_tys- derivInstCtxt :: PredType -> SDoc derivInstCtxt pred = text "When deriving the instance for" <+> parens (ppr pred)@@ -737,29 +725,27 @@ *********************************************************************************** -} + -- | Given @instance (wanted) => C inst_ty@, simplify 'wanted' as much -- as possible. Fail if not possible.-simplifyDeriv :: SkolemInfo -- ^ The 'SkolemInfo' used to skolemise the- -- 'TcTyVar' arguments- -> UserTypeCtxt -- ^ Used to inform error messages as to whether- -- we are in a @deriving@ clause or a standalone- -- @deriving@ declaration- -> [TcTyVar] -- ^ The tyvars bound by @inst_ty@.- -> ThetaSpec -- ^ The constraints to solve and simplify+simplifyDeriv :: DerivSpec ThetaSpec -> TcM ThetaType -- ^ Needed constraints (after simplification), -- i.e. @['PredType']@.-simplifyDeriv skol_info user_ctxt tvs theta- = do { let skol_set = mkVarSet tvs-+simplifyDeriv (DS { ds_loc = loc, ds_tvs = tvs+ , ds_cls = clas, ds_tys = inst_tys, ds_theta = deriv_rhs+ , ds_skol_info = skol_info, ds_user_ctxt = user_ctxt })+ = setSrcSpan loc $+ addErrCtxt (derivInstCtxt (mkClassPred clas inst_tys)) $+ do { -- See [STEP DAC BUILD] -- Generate the implication constraints, one for each method, to solve -- with the skolemized variables. Start "one level down" because -- we are going to wrap the result in an implication with tvs, -- in step [DAC RESIDUAL]- ; (tc_lvl, wanteds) <- captureThetaSpecConstraints user_ctxt theta+ ; (tc_lvl, wanteds) <- captureThetaSpecConstraints user_ctxt deriv_rhs ; traceTc "simplifyDeriv inputs" $- vcat [ pprTyVars tvs $$ ppr theta $$ ppr wanteds, ppr skol_info ]+ vcat [ pprTyVars tvs $$ ppr deriv_rhs $$ ppr wanteds, ppr skol_info ] -- See [STEP DAC SOLVE] -- Simplify the constraints, starting at the same level at which@@ -776,6 +762,7 @@ -- From the simplified constraints extract a subset 'good' that will -- become the context 'min_theta' for the derived instance. ; let residual_simple = approximateWC True solved_wanteds+ head_size = pSizeClassPred clas inst_tys good = mapMaybeBag get_good residual_simple -- Returns @Just p@ (where @p@ is the type of the Ct) if a Ct is@@ -784,10 +771,8 @@ -- See Note [Exotic derived instance contexts] for what -- constitutes an exotic constraint. get_good :: Ct -> Maybe PredType- get_good ct | validDerivPred skol_set p- = Just p- | otherwise- = Nothing+ get_good ct | validDerivPred head_size p = Just p+ | otherwise = Nothing where p = ctPred ct ; traceTc "simplifyDeriv outputs" $@@ -816,6 +801,13 @@ -- See also Note [Exotic derived instance contexts], which are caught -- in this line of code. ; simplifyTopImplic leftover_implic++ ; traceTc "GHC.Tc.Deriv" (ppr deriv_rhs $$ ppr min_theta)++ -- Claim: the result instance declaration is guaranteed valid+ -- Hence no need to call:+ -- checkValidInstance tyvars theta clas inst_tys+ -- See Note [Exotic derived instance contexts] ; return min_theta }
compiler/GHC/Tc/Deriv/Utils.hs view
@@ -28,23 +28,8 @@ import GHC.Data.Bag import GHC.Types.Basic-import GHC.Core.Class-import GHC.Core.DataCon-import GHC.Core.FamInstEnv-import GHC.Driver.Session-import GHC.Utils.Error-import GHC.Types.Fixity.Env (lookupFixity)-import GHC.Hs+ import GHC.Tc.Utils.Instantiate-import GHC.Core.InstEnv-import GHC.Iface.Load (loadInterfaceForName)-import GHC.Unit.Module (getModule)-import GHC.Unit.Module.ModIface (mi_fix)-import GHC.Types.Name-import GHC.Utils.Outputable-import GHC.Utils.Panic-import GHC.Builtin.Names-import GHC.Types.SrcLoc import GHC.Tc.Deriv.Generate import GHC.Tc.Deriv.Functor import GHC.Tc.Deriv.Generics@@ -55,12 +40,33 @@ import GHC.Tc.Utils.TcType import GHC.Tc.Utils.Unify (tcSubTypeSigma) import GHC.Tc.Utils.Zonk-import GHC.Builtin.Names.TH (liftClassKey)++import GHC.Core.Class+import GHC.Core.DataCon+import GHC.Core.FamInstEnv+import GHC.Core.InstEnv import GHC.Core.TyCon import GHC.Core.Type++import GHC.Hs+import GHC.Driver.Session+import GHC.Unit.Module (getModule)+import GHC.Unit.Module.ModIface (mi_fix)++import GHC.Types.Fixity.Env (lookupFixity)+import GHC.Iface.Load (loadInterfaceForName)+import GHC.Types.Name+import GHC.Types.SrcLoc import GHC.Utils.Misc import GHC.Types.Var.Set +import GHC.Builtin.Names+import GHC.Builtin.Names.TH (liftClassKey)++import GHC.Utils.Outputable+import GHC.Utils.Panic+import GHC.Utils.Error+ import Control.Monad.Trans.Reader import Data.Foldable (traverse_) import Data.Maybe@@ -637,7 +643,7 @@ , sps_type_or_kind = t_or_k }) -substPredSpec :: HasCallStack => TCvSubst -> PredSpec -> PredSpec+substPredSpec :: HasCallStack => Subst -> PredSpec -> PredSpec substPredSpec subst ps = case ps of SimplePredSpec { sps_pred = pred
compiler/GHC/Tc/Errors.hs view
@@ -1,11 +1,11 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE ParallelListComp #-} {-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} {-# OPTIONS_GHC -Wno-incomplete-record-updates #-}-{-# LANGUAGE ParallelListComp #-} module GHC.Tc.Errors( reportUnsolved, reportAllUnsolved, warnAllUnsolved,@@ -13,7 +13,6 @@ -- * GHC API helper functions solverReportMsg_ExpectedActuals,- solverReportInfo_ExpectedActuals ) where import GHC.Prelude@@ -61,8 +60,7 @@ import GHC.Core.Predicate import GHC.Core.Type import GHC.Core.Coercion-import GHC.Core.TyCo.Ppr ( pprTyVars- )+import GHC.Core.TyCo.Ppr ( pprTyVars ) import GHC.Core.InstEnv import GHC.Core.TyCon import GHC.Core.DataCon@@ -79,13 +77,13 @@ import GHC.Data.Maybe import qualified GHC.Data.Strict as Strict -import Control.Monad ( unless, when, foldM, forM_ )-import Data.Foldable ( toList )-import Data.Function ( on )-import Data.List ( partition, sort, sortBy )-import Data.List.NonEmpty ( NonEmpty(..), (<|) )-import qualified Data.List.NonEmpty as NE ( map, reverse )-import Data.Ord ( comparing )+import Control.Monad ( unless, when, foldM, forM_ )+import Data.Foldable ( toList )+import Data.Function ( on )+import Data.List ( partition, sort, sortBy )+import Data.List.NonEmpty ( NonEmpty(..) )+import qualified Data.List.NonEmpty as NE+import Data.Ord ( comparing ) import qualified Data.Semigroup as S {-@@ -175,7 +173,7 @@ -- NB: Type-level holes are OK, because there are no bindings. -- See Note [Deferring coercion errors to runtime] -- Used by solveEqualities for kind equalities--- (see Note [Fail fast on kind errors] in "GHC.Tc.Solver")+-- (see Note [Failure in local type signatures] in GHC.Tc.Solver) reportAllUnsolved :: WantedConstraints -> TcM () reportAllUnsolved wanted = do { ev_binds <- newNoTcEvBinds@@ -263,13 +261,18 @@ -- | Make a report from a single 'TcSolverReportMsg'. important :: SolverReportErrCtxt -> TcSolverReportMsg -> SolverReport-important ctxt doc = mempty { sr_important_msgs = [SolverReportWithCtxt ctxt doc] }+important ctxt doc+ = SolverReport { sr_important_msg = SolverReportWithCtxt ctxt doc+ , sr_supplementary = []+ , sr_hints = [] } -mk_relevant_bindings :: RelevantBindings -> SolverReport-mk_relevant_bindings binds = mempty { sr_supplementary = [SupplementaryBindings binds] }+add_relevant_bindings :: RelevantBindings -> SolverReport -> SolverReport+add_relevant_bindings binds report@(SolverReport { sr_supplementary = supp })+ = report { sr_supplementary = SupplementaryBindings binds : supp } -mk_report_hints :: [GhcHint] -> SolverReport-mk_report_hints hints = mempty { sr_hints = hints }+add_report_hints :: [GhcHint] -> SolverReport -> SolverReport+add_report_hints hints report@(SolverReport { sr_hints = prev_hints })+ = report { sr_hints = prev_hints ++ hints } -- | Returns True <=> the SolverReportErrCtxt indicates that something is deferred deferringAnyBindings :: SolverReportErrCtxt -> Bool@@ -388,7 +391,7 @@ warnRedundantConstraints :: SolverReportErrCtxt -> TcLclEnv -> SkolemInfoAnon -> [EvVar] -> TcM () -- See Note [Tracking redundant constraints] in GHC.Tc.Solver-warnRedundantConstraints ctxt env info ev_vars+warnRedundantConstraints ctxt env info redundant_evs | null redundant_evs = return () @@ -420,23 +423,11 @@ [] ; reportDiagnostic msg } - redundant_evs =- filterOut is_type_error $- case info of -- See Note [Redundant constraints in instance decls]- InstSkol -> filterOut (improving . idType) ev_vars- _ -> ev_vars-- -- See #15232- is_type_error = isJust . userTypeError_maybe . idType-- improving pred -- (transSuperClasses p) does not include p- = any isImprovementPred (pred : transSuperClasses pred)- reportBadTelescope :: SolverReportErrCtxt -> TcLclEnv -> SkolemInfoAnon -> [TcTyVar] -> TcM () reportBadTelescope ctxt env (ForAllSkol telescope) skols = do { msg <- mkErrorReport env- (TcRnSolverReport [report] ErrorWithoutFlag noHints)+ (TcRnSolverReport report ErrorWithoutFlag noHints) (Just ctxt) [] ; reportDiagnostic msg }@@ -446,23 +437,6 @@ reportBadTelescope _ _ skol_info skols = pprPanic "reportBadTelescope" (ppr skol_info $$ ppr skols) -{- Note [Redundant constraints in instance decls]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-For instance declarations, we don't report unused givens if-they can give rise to improvement. Example (#10100):- class Add a b ab | a b -> ab, a ab -> b- instance Add Zero b b- instance Add a b ab => Add (Succ a) b (Succ ab)-The context (Add a b ab) for the instance is clearly unused in terms-of evidence, since the dictionary has no fields. But it is still-needed! With the context, a wanted constraint- Add (Succ Zero) beta (Succ Zero)-we will reduce to (Add Zero beta Zero), and thence we get beta := Zero.-But without the context we won't find beta := Zero.--This only matters in instance declarations..--}- -- | Should we completely ignore this constraint in error reporting? -- It *must* be the case that any constraint for which this returns True -- somehow causes an error to be reported elsewhere.@@ -668,7 +642,7 @@ is_user_type_error item _ = isUserTypeError (errorItemPred item) is_homo_equality _ (EqPred _ ty1 ty2)- = tcTypeKind ty1 `tcEqType` tcTypeKind ty2+ = typeKind ty1 `tcEqType` typeKind ty2 is_homo_equality _ _ = False @@ -905,7 +879,7 @@ frr_origins = acc_errors errs diag = TcRnSolverReport- [SolverReportWithCtxt ctxt (FixedRuntimeRepError frr_origins)]+ (SolverReportWithCtxt ctxt (FixedRuntimeRepError frr_origins)) ErrorWithoutFlag noHints -- Accumulate the different kind of errors arising from syntactic equality.@@ -961,10 +935,10 @@ -- with one from the immediately-enclosing implication. -- See Note [Inaccessible code] - ; (eq_err_msgs, _hints) <- mkEqErr_help ctxt item' ty1 ty2+ ; (eq_err_msg, _hints) <- mkEqErr_help ctxt item' ty1 ty2 -- The hints wouldn't help in this situation, so we discard them. ; let supplementary = [ SupplementaryBindings relevant_binds ]- msg = TcRnInaccessibleCode implic (NE.reverse . NE.map (SolverReportWithCtxt ctxt) $ eq_err_msgs)+ msg = TcRnInaccessibleCode implic (SolverReportWithCtxt ctxt eq_err_msg) ; msg <- mkErrorReport (ctLocEnv loc') msg (Just ctxt) supplementary ; reportDiagnostic msg } where@@ -1061,7 +1035,7 @@ maybeReportError :: SolverReportErrCtxt -> [ErrorItem] -- items covered by the Report -> SolverReport -> TcM ()-maybeReportError ctxt items@(item1:_) (SolverReport { sr_important_msgs = important+maybeReportError ctxt items@(item1:_) (SolverReport { sr_important_msg = important , sr_supplementary = supp , sr_hints = hints }) = unless (cec_suppress ctxt -- Some worse error has occurred, so suppress this diagnostic@@ -1094,18 +1068,18 @@ -> do { -- See Note [Deferred errors for coercion holes] let co_var = coHoleCoVar hole ; addTcEvBind ev_binds_var $ mkWantedEvBind co_var err_tm- ; fillCoercionHole hole (mkTcCoVarCo co_var) } }+ ; fillCoercionHole hole (mkCoVarCo co_var) } } addDeferredBinding _ _ _ = return () -- Do not set any evidence for Given mkErrorTerm :: SolverReportErrCtxt -> CtLoc -> Type -- of the error term -> SolverReport -> TcM EvTerm-mkErrorTerm ctxt ct_loc ty (SolverReport { sr_important_msgs = important, sr_supplementary = supp })+mkErrorTerm ctxt ct_loc ty (SolverReport { sr_important_msg = important, sr_supplementary = supp }) = do { msg <- mkErrorReport (ctLocEnv ct_loc) (TcRnSolverReport important ErrorWithoutFlag noHints) (Just ctxt) supp -- This will be reported at runtime, so we always want "error:" in the report, never "warning:" ; dflags <- getDynFlags- ; let err_msg = pprLocMsgEnvelope msg+ ; let err_msg = pprLocMsgEnvelope (initTcMessageOpts dflags) msg err_str = showSDoc dflags $ err_msg $$ text "(deferred type error)" @@ -1171,10 +1145,13 @@ ErrInfo (fromMaybe empty mb_context) (vcat $ map (pprSolverReportSupplementary hfdc) supplementary)+ ; let detailed_msg = mkDetailedMessage err_info msg ; mkTcRnMessage (RealSrcSpan (tcl_loc tcl_env) Strict.Nothing)- (TcRnMessageWithInfo unit_state $ TcRnMessageDetailed err_info msg) }+ (TcRnMessageWithInfo unit_state $ detailed_msg) } ++ -- | Pretty-print supplementary information, to add to an error report. pprSolverReportSupplementary :: HoleFitDispConfig -> SolverReportSupplementary -> SDoc -- This function should be in "GHC.Tc.Errors.Ppr",@@ -1278,10 +1255,10 @@ mkIrredErr :: SolverReportErrCtxt -> [ErrorItem] -> TcM SolverReport mkIrredErr ctxt items- = do { (ctxt, binds_msg, item1) <- relevantBindings True ctxt item1- ; let msg = important ctxt $+ = do { (ctxt, binds, item1) <- relevantBindings True ctxt item1+ ; let msg = important ctxt $ mkPlainMismatchMsg $ CouldNotDeduce (getUserGivens ctxt) (item1 :| others) Nothing- ; return $ msg `mappend` mk_relevant_bindings binds_msg }+ ; return $ add_relevant_bindings binds msg } where (item1:others) = final_items @@ -1341,12 +1318,12 @@ ; let (imp_errs, hints) = unknownNameSuggestions WL_Anything dflags hpt curr_mod rdr_env- (tcl_rdr lcl_env) imp_info (mkRdrUnqual occ)- errs = [SolverReportWithCtxt ctxt (ReportHoleError hole $ OutOfScopeHole imp_errs)]- report = SolverReport errs [] hints+ (tcl_rdr lcl_env) imp_info occ+ err = SolverReportWithCtxt ctxt (ReportHoleError hole $ OutOfScopeHole imp_errs)+ report = SolverReport err [] hints ; maybeAddDeferredBindings ctxt hole report- ; mkErrorReport lcl_env (TcRnSolverReport errs (cec_out_of_scope_holes ctxt) hints) Nothing []+ ; mkErrorReport lcl_env (TcRnSolverReport err (cec_out_of_scope_holes ctxt) hints) Nothing [] -- Pass the value 'Nothing' for the context, as it's generally not helpful -- to include the context here. }@@ -1360,7 +1337,7 @@ , hole_loc = ct_loc }) = do { rel_binds <- relevant_bindings False lcl_env lcl_name_cache (tyCoVarsOfType hole_ty)- -- The 'False' means "don't filter the bindings"; see Trac #8191+ -- The 'False' means "don't filter the bindings"; see #8191 ; show_hole_constraints <- goptM Opt_ShowHoleConstraints ; let relevant_cts@@ -1376,14 +1353,14 @@ ; (grouped_skvs, other_tvs) <- zonkAndGroupSkolTvs hole_ty ; let reason | ExprHole _ <- sort = cec_expr_holes ctxt | otherwise = cec_type_holes ctxt- errs = [SolverReportWithCtxt ctxt $ ReportHoleError hole $ HoleError sort other_tvs grouped_skvs]+ err = SolverReportWithCtxt ctxt $ ReportHoleError hole $ HoleError sort other_tvs grouped_skvs supp = [ SupplementaryBindings rel_binds , SupplementaryCts relevant_cts , SupplementaryHoleFits hole_fits ] - ; maybeAddDeferredBindings ctxt hole (SolverReport errs supp [])+ ; maybeAddDeferredBindings ctxt hole (SolverReport err supp []) - ; mkErrorReport lcl_env (TcRnSolverReport errs reason noHints) (Just ctxt) supp+ ; mkErrorReport lcl_env (TcRnSolverReport err reason noHints) (Just ctxt) supp } where@@ -1472,16 +1449,16 @@ -- Note [Wanteds rewrite Wanteds] in GHC.Tc.Types.Constraint? Very unclear -- what's best. Let's not worry about this. mkIPErr ctxt items- = do { (ctxt, binds_msg, item1) <- relevantBindings True ctxt item1+ = do { (ctxt, binds, item1) <- relevantBindings True ctxt item1 ; let msg = important ctxt $ UnboundImplicitParams (item1 :| others)- ; return $ msg `mappend` mk_relevant_bindings binds_msg }+ ; return $ add_relevant_bindings binds msg } where item1:others = items ---------------- -- | Report a representation-polymorphism error to the user:--- a type is required to havehave a fixed runtime representation,+-- a type is required to have a fixed runtime representation, -- but doesn't. -- -- See Note [Reporting representation-polymorphism errors] in GHC.Tc.Types.Origin.@@ -1584,19 +1561,13 @@ mkEqErr1 :: SolverReportErrCtxt -> ErrorItem -> TcM SolverReport mkEqErr1 ctxt item -- Wanted only -- givens handled in mkGivenErrorReporter- = do { (ctxt, binds_msg, item) <- relevantBindings True ctxt item- ; rdr_env <- getGlobalRdrEnv- ; fam_envs <- tcGetFamInstEnvs- ; let mb_coercible_msg = case errorItemEqRel item of- NomEq -> Nothing- ReprEq -> ReportCoercibleMsg <$> mkCoercibleExplanation rdr_env fam_envs ty1 ty2+ = do { (ctxt, binds, item) <- relevantBindings True ctxt item ; traceTc "mkEqErr1" (ppr item $$ pprCtOrigin (errorItemOrigin item))- ; (last_msg :| prev_msgs, hints) <- mkEqErr_help ctxt item ty1 ty2+ ; (err_msg, hints) <- mkEqErr_help ctxt item ty1 ty2 ; let- report = foldMap (important ctxt) (reverse prev_msgs)- `mappend` (important ctxt $ mkTcReportWithInfo last_msg $ maybeToList mb_coercible_msg)- `mappend` (mk_relevant_bindings binds_msg)- `mappend` (mk_report_hints hints)+ report = add_relevant_bindings binds+ $ add_report_hints hints+ $ important ctxt err_msg ; return report } where (ty1, ty2) = getEqPredTys (errorItemPred item)@@ -1642,38 +1613,55 @@ | otherwise = False --- | Accumulated messages in reverse order.-type AccReportMsgs = NonEmpty TcSolverReportMsg- mkEqErr_help :: SolverReportErrCtxt -> ErrorItem- -> TcType -> TcType -> TcM (AccReportMsgs, [GhcHint])+ -> TcType -> TcType -> TcM (TcSolverReportMsg, [GhcHint]) mkEqErr_help ctxt item ty1 ty2- | Just casted_tv1 <- tcGetCastedTyVar_maybe ty1+ | Just casted_tv1 <- getCastedTyVar_maybe ty1 = mkTyVarEqErr ctxt item casted_tv1 ty2- | Just casted_tv2 <- tcGetCastedTyVar_maybe ty2+ | Just casted_tv2 <- getCastedTyVar_maybe ty2 = mkTyVarEqErr ctxt item casted_tv2 ty1 | otherwise- = return (reportEqErr ctxt item ty1 ty2 :| [], [])+ = do+ err <- reportEqErr ctxt item ty1 ty2+ return (err, noHints) reportEqErr :: SolverReportErrCtxt -> ErrorItem- -> TcType -> TcType -> TcSolverReportMsg+ -> TcType -> TcType+ -> TcM TcSolverReportMsg reportEqErr ctxt item ty1 ty2- = mkTcReportWithInfo mismatch eqInfos+ = do+ mb_coercible_info <-+ if errorItemEqRel item == ReprEq+ then coercible_msg ty1 ty2+ else return Nothing+ return $+ Mismatch+ { mismatchMsg = mismatch+ , mismatchTyVarInfo = Nothing+ , mismatchAmbiguityInfo = eqInfos+ , mismatchCoercibleInfo = mb_coercible_info } where mismatch = misMatchOrCND False ctxt item ty1 ty2 eqInfos = eqInfoMsgs ty1 ty2 +coercible_msg :: TcType -> TcType -> TcM (Maybe CoercibleMsg)+coercible_msg ty1 ty2+ = do+ rdr_env <- getGlobalRdrEnv+ fam_envs <- tcGetFamInstEnvs+ return $ mkCoercibleExplanation rdr_env fam_envs ty1 ty2+ mkTyVarEqErr :: SolverReportErrCtxt -> ErrorItem- -> (TcTyVar, TcCoercionN) -> TcType -> TcM (AccReportMsgs, [GhcHint])+ -> (TcTyVar, TcCoercionN) -> TcType -> TcM (TcSolverReportMsg, [GhcHint]) -- tv1 and ty2 are already tidied mkTyVarEqErr ctxt item casted_tv1 ty2 = do { traceTc "mkTyVarEqErr" (ppr item $$ ppr casted_tv1 $$ ppr ty2) ; mkTyVarEqErr' ctxt item casted_tv1 ty2 } mkTyVarEqErr' :: SolverReportErrCtxt -> ErrorItem- -> (TcTyVar, TcCoercionN) -> TcType -> TcM (AccReportMsgs, [GhcHint])+ -> (TcTyVar, TcCoercionN) -> TcType -> TcM (TcSolverReportMsg, [GhcHint]) mkTyVarEqErr' ctxt item (tv1, co1) ty2 -- Is this a representation-polymorphism error, e.g.@@ -1681,24 +1669,28 @@ | Just frr_info <- mb_concrete_reason = do (_, infos) <- zonkTidyFRRInfos (cec_tidy ctxt) [frr_info]- return (FixedRuntimeRepError infos :| [], [])+ return (FixedRuntimeRepError infos, []) -- Impredicativity is a simple error to understand; try it before -- anything more complicated. | check_eq_result `cterHasProblem` cteImpredicative = do- tyvar_eq_info <- extraTyVarEqInfo tv1 ty2+ tyvar_eq_info <- extraTyVarEqInfo (tv1, Nothing) ty2 let- poly_msg = CannotUnifyWithPolytype item tv1 ty2- poly_msg_with_info+ poly_msg = CannotUnifyWithPolytype item tv1 ty2 mb_tv_info+ mb_tv_info | isSkolemTyVar tv1- = mkTcReportWithInfo poly_msg tyvar_eq_info+ = Just tyvar_eq_info | otherwise- = poly_msg+ = Nothing+ main_msg =+ CannotUnifyVariable+ { mismatchMsg = headline_msg+ , cannotUnifyReason = poly_msg } -- Unlike the other reports, this discards the old 'report_important' -- instead of augmenting it. This is because the details are not likely -- to be helpful since this is just an unimplemented feature.- return (poly_msg_with_info <| headline_msg :| [], [])+ return (main_msg, []) | isSkolemTyVar tv1 -- ty2 won't be a meta-tyvar; we would have -- swapped in Solver.Canonical.canEqTyVarHomo@@ -1706,30 +1698,43 @@ || errorItemEqRel item == ReprEq -- The cases below don't really apply to ReprEq (except occurs check) = do- tv_extra <- extraTyVarEqInfo tv1 ty2- return (mkTcReportWithInfo headline_msg tv_extra :| [], add_sig)+ tv_extra <- extraTyVarEqInfo (tv1, Nothing) ty2+ reason <-+ if errorItemEqRel item == ReprEq+ then RepresentationalEq tv_extra <$> coercible_msg ty1 ty2+ else return $ DifferentTyVars tv_extra+ let main_msg =+ CannotUnifyVariable+ { mismatchMsg = headline_msg+ , cannotUnifyReason = reason }+ return (main_msg, add_sig) | cterHasOccursCheck check_eq_result -- We report an "occurs check" even for a ~ F t a, where F is a type -- function; it's not insoluble (because in principle F could reduce) -- but we have certainly been unable to solve it- = let extras2 = eqInfoMsgs ty1 ty2+ = let ambiguity_infos = eqInfoMsgs ty1 ty2 interesting_tyvars = filter (not . noFreeVarsOfType . tyVarKind) $ filter isTyVar $ fvVarList $ tyCoFVsOfType ty1 `unionFV` tyCoFVsOfType ty2 - extras3 = case interesting_tyvars of- [] -> []- (tv : tvs) -> [OccursCheckInterestingTyVars (tv :| tvs)]+ occurs_err =+ OccursCheck+ { occursCheckInterestingTyVars = interesting_tyvars+ , occursCheckAmbiguityInfos = ambiguity_infos }+ main_msg =+ CannotUnifyVariable+ { mismatchMsg = headline_msg+ , cannotUnifyReason = occurs_err } - in return (mkTcReportWithInfo headline_msg (extras2 ++ extras3) :| [], [])+ in return (main_msg, []) -- This is wrinkle (4) in Note [Equalities with incompatible kinds] in -- GHC.Tc.Solver.Canonical | hasCoercionHoleCo co1 || hasCoercionHoleTy ty2- = return (mkBlockedEqErr item :| [], [])+ = return (mkBlockedEqErr item, []) -- If the immediately-enclosing implication has 'tv' a skolem, and -- we know by now its an InferSkol kind of skolem, then presumably@@ -1739,16 +1744,26 @@ , Implic { ic_skols = skols } <- implic , tv1 `elem` skols = do- tv_extra <- extraTyVarEqInfo tv1 ty2- return (mkTcReportWithInfo mismatch_msg tv_extra :| [], [])+ tv_extra <- extraTyVarEqInfo (tv1, Nothing) ty2+ let msg = Mismatch+ { mismatchMsg = mismatch_msg+ , mismatchTyVarInfo = Just tv_extra+ , mismatchAmbiguityInfo = []+ , mismatchCoercibleInfo = Nothing }+ return (msg, []) -- Check for skolem escape | (implic:_) <- cec_encl ctxt -- Get the innermost context , Implic { ic_skols = skols } <- implic , let esc_skols = filter (`elemVarSet` (tyCoVarsOfType ty2)) skols , not (null esc_skols)- = return (SkolemEscape item implic esc_skols :| [mismatch_msg], [])+ = let main_msg =+ CannotUnifyVariable+ { mismatchMsg = mismatch_msg+ , cannotUnifyReason = SkolemEscape item implic esc_skols } + in return (main_msg, [])+ -- Nastiest case: attempt to unify an untouchable variable -- So tv is a meta tyvar (or started that way before we -- generalised it). So presumably it is an *untouchable*@@ -1758,12 +1773,19 @@ , Implic { ic_tclvl = lvl } <- implic = assertPpr (not (isTouchableMetaTyVar lvl tv1)) (ppr tv1 $$ ppr lvl) $ do -- See Note [Error messages for untouchables]- let tclvl_extra = UntouchableVariable tv1 implic- tv_extra <- extraTyVarEqInfo tv1 ty2- return (mkTcReportWithInfo tclvl_extra tv_extra :| [mismatch_msg], add_sig)+ tv_extra <- extraTyVarEqInfo (tv1, Just implic) ty2+ let tv_extra' = tv_extra { thisTyVarIsUntouchable = Just implic }+ msg = Mismatch+ { mismatchMsg = mismatch_msg+ , mismatchTyVarInfo = Just tv_extra'+ , mismatchAmbiguityInfo = []+ , mismatchCoercibleInfo = Nothing }+ return (msg, add_sig) | otherwise- = return (reportEqErr ctxt item (mkTyVarTy tv1) ty2 :| [], [])+ = do+ err <- reportEqErr ctxt item (mkTyVarTy tv1) ty2+ return (err, []) -- This *can* happen (#6123) -- Consider an ambiguous top-level constraint (a ~ F a) -- Not an occurs check, because F is a type function.@@ -1802,7 +1824,7 @@ insoluble_occurs_check = check_eq_result `cterHasProblem` cteInsolubleOccurs -eqInfoMsgs :: TcType -> TcType -> [TcSolverReportInfo]+eqInfoMsgs :: TcType -> TcType -> [AmbiguityInfo] -- Report (a) ambiguity if either side is a type function application -- e.g. F a0 ~ Int -- (b) warning about injectivity if both sides are the same@@ -1836,7 +1858,7 @@ = Nothing misMatchOrCND :: Bool -> SolverReportErrCtxt -> ErrorItem- -> TcType -> TcType -> TcSolverReportMsg+ -> TcType -> TcType -> MismatchMsg -- If oriented then ty1 is actual, ty2 is expected misMatchOrCND insoluble_occurs_check ctxt item ty1 ty2 | insoluble_occurs_check -- See Note [Insoluble occurs check]@@ -1904,23 +1926,30 @@ in GHC.Tc.TyCl.PatSyn). -} -extraTyVarEqInfo :: TcTyVar -> TcType -> TcM [TcSolverReportInfo]+extraTyVarEqInfo :: (TcTyVar, Maybe Implication) -> TcType -> TcM TyVarInfo -- Add on extra info about skolem constants -- NB: The types themselves are already tidied-extraTyVarEqInfo tv1 ty2- = (:) <$> extraTyVarInfo tv1 <*> ty_extra ty2+extraTyVarEqInfo (tv1, mb_implic) ty2+ = do+ tv1_info <- extraTyVarInfo tv1+ ty2_info <- ty_extra ty2+ return $+ TyVarInfo+ { thisTyVar = tv1_info+ , thisTyVarIsUntouchable = mb_implic+ , otherTy = ty2_info } where- ty_extra ty = case tcGetCastedTyVar_maybe ty of- Just (tv, _) -> (:[]) <$> extraTyVarInfo tv- Nothing -> return []+ ty_extra ty = case getCastedTyVar_maybe ty of+ Just (tv, _) -> Just <$> extraTyVarInfo tv+ Nothing -> return Nothing -extraTyVarInfo :: TcTyVar -> TcM TcSolverReportInfo+extraTyVarInfo :: TcTyVar -> TcM TyVar extraTyVarInfo tv = assertPpr (isTyVar tv) (ppr tv) $ case tcTyVarDetails tv of SkolemTv skol_info lvl overlaps -> do new_skol_info <- zonkSkolemInfo skol_info- return $ TyVarInfo (mkTcTyVar (tyVarName tv) (tyVarKind tv) (SkolemTv new_skol_info lvl overlaps))- _ -> return $ TyVarInfo tv+ return $ mkTcTyVar (tyVarName tv) (tyVarKind tv) (SkolemTv new_skol_info lvl overlaps)+ _ -> return tv suggestAddSig :: SolverReportErrCtxt -> TcType -> TcType -> Maybe GhcHint@@ -1932,7 +1961,7 @@ = Nothing where inferred_bndrs =- case tcGetTyVar_maybe ty1 of+ case getTyVar_maybe ty1 of Just tv | isSkolemTyVar tv -> find (cec_encl ctxt) False tv _ -> [] @@ -1949,30 +1978,29 @@ = find implics (seen_eqs || ic_given_eqs implic /= NoGivenEqs) tv ---------------------mkMismatchMsg :: ErrorItem -> Type -> Type -> TcSolverReportMsg+mkMismatchMsg :: ErrorItem -> Type -> Type -> MismatchMsg mkMismatchMsg item ty1 ty2 = case orig of TypeEqOrigin { uo_actual, uo_expected, uo_thing = mb_thing } ->- mkTcReportWithInfo- (TypeEqMismatch- { teq_mismatch_ppr_explicit_kinds = ppr_explicit_kinds- , teq_mismatch_item = item- , teq_mismatch_ty1 = ty1- , teq_mismatch_ty2 = ty2- , teq_mismatch_actual = uo_actual- , teq_mismatch_expected = uo_expected- , teq_mismatch_what = mb_thing})- extras+ (TypeEqMismatch+ { teq_mismatch_ppr_explicit_kinds = ppr_explicit_kinds+ , teq_mismatch_item = item+ , teq_mismatch_ty1 = ty1+ , teq_mismatch_ty2 = ty2+ , teq_mismatch_actual = uo_actual+ , teq_mismatch_expected = uo_expected+ , teq_mismatch_what = mb_thing+ , teq_mb_same_occ = sameOccExtras ty2 ty1 }) KindEqOrigin cty1 cty2 sub_o mb_sub_t_or_k ->- mkTcReportWithInfo (Mismatch False item ty1 ty2)- (WhenMatching cty1 cty2 sub_o mb_sub_t_or_k : extras)+ (mkBasicMismatchMsg NoEA item ty1 ty2)+ { mismatch_whenMatching = Just $ WhenMatching cty1 cty2 sub_o mb_sub_t_or_k+ , mismatch_mb_same_occ = mb_same_occ } _ ->- mkTcReportWithInfo- (Mismatch False item ty1 ty2)- extras+ (mkBasicMismatchMsg NoEA item ty1 ty2)+ { mismatch_mb_same_occ = mb_same_occ } where orig = errorItemOrigin item- extras = sameOccExtras ty2 ty1+ mb_same_occ = sameOccExtras ty2 ty1 ppr_explicit_kinds = shouldPprWithExplicitKinds ty1 ty2 orig -- | Whether to print explicit kinds (with @-fprint-explicit-kinds@)@@ -2011,7 +2039,7 @@ want to be as draconian with them.) -} -sameOccExtras :: TcType -> TcType -> [TcSolverReportInfo]+sameOccExtras :: TcType -> TcType -> Maybe SameOccInfo -- See Note [Disambiguating (X ~ X) errors] sameOccExtras ty1 ty2 | Just (tc1, _) <- tcSplitTyConApp_maybe ty1@@ -2022,9 +2050,9 @@ same_pkg = moduleUnit (nameModule n1) == moduleUnit (nameModule n2) , n1 /= n2 -- Different Names , same_occ -- but same OccName- = [SameOcc same_pkg n1 n2]+ = Just $ SameOcc same_pkg n1 n2 | otherwise- = []+ = Nothing {- Note [Suggest adding a type signature] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -2145,26 +2173,24 @@ -- and the result of evaluating ...". mk_dict_err :: HasCallStack => SolverReportErrCtxt -> (ErrorItem, ClsInstLookupResult) -> TcM TcSolverReportMsg--- Report an overlap error if this class constraint results--- from an overlap (returning Left clas), otherwise return (Right pred)-mk_dict_err ctxt (item, (matches, unifiers, unsafe_overlapped))- | null matches -- No matches but perhaps several unifiers- = do { (_, rel_binds, item) <- relevantBindings True ctxt item- ; candidate_insts <- get_candidate_instances- ; (imp_errs, field_suggestions) <- record_field_suggestions- ; return (cannot_resolve_msg item candidate_insts rel_binds imp_errs field_suggestions) }+mk_dict_err ctxt (item, (matches, unifiers, unsafe_overlapped)) = case (NE.nonEmpty matches, NE.nonEmpty unsafe_overlapped) of+ (Nothing, _) -> do -- No matches but perhaps several unifiers+ { (_, rel_binds, item) <- relevantBindings True ctxt item+ ; candidate_insts <- get_candidate_instances+ ; (imp_errs, field_suggestions) <- record_field_suggestions+ ; return (cannot_resolve_msg item candidate_insts rel_binds imp_errs field_suggestions) } - | null unsafe_overlapped -- Some matches => overlap errors- = return $ overlap_msg+ -- Some matches => overlap errors+ (Just matchesNE, Nothing) -> return $+ OverlappingInstances item (NE.map fst matchesNE) (getPotentialUnifiers unifiers) - | otherwise- = return $ safe_haskell_msg+ (Just (match :| []), Just unsafe_overlappedNE) -> return $+ UnsafeOverlap item (fst match) (NE.map fst unsafe_overlappedNE)+ (Just matches@(_ :| _), Just overlaps) -> pprPanic "mk_dict_err: multiple matches with overlap" $ vcat [ text "matches:" <+> ppr matches, text "overlaps:" <+> ppr overlaps ] where orig = errorItemOrigin item pred = errorItemPred item (clas, tys) = getClassPredTys pred- ispecs = [ispec | (ispec, _) <- matches]- unsafe_ispecs = [ispec | (ispec, _) <- unsafe_overlapped] get_candidate_instances :: TcM [ClsInst] -- See Note [Report candidate instances]@@ -2213,18 +2239,6 @@ cannot_resolve_msg item candidate_insts binds imp_errs field_suggestions = CannotResolveInstance item (getPotentialUnifiers unifiers) candidate_insts imp_errs field_suggestions binds - -- Overlap errors.- overlap_msg, safe_haskell_msg :: TcSolverReportMsg- -- Normal overlap error- overlap_msg- = assert (not (null matches)) $ OverlappingInstances item ispecs (getPotentialUnifiers unifiers)-- -- Overlap error because of Safe Haskell (first- -- match should be the most specific match)- safe_haskell_msg- = assert (matches `lengthIs` 1 && not (null unsafe_ispecs)) $- UnsafeOverlap item ispecs unsafe_ispecs- {- Note [Report candidate instances] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If we have an unsolved (Num Int), where `Int` is not the Prelude Int,@@ -2237,7 +2251,7 @@ Discussion in #9611. Note [Highlighting ambiguous type variables]-~-------------------------------------------+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When we encounter ambiguous type variables (i.e. type variables that remain metavariables after type inference), we need a few more conditions before we can reason that *ambiguity* prevents constraints@@ -2468,28 +2482,14 @@ solverReportMsg_ExpectedActuals :: TcSolverReportMsg -> [(Type, Type)] solverReportMsg_ExpectedActuals = \case- TcReportWithInfo msg infos ->- solverReportMsg_ExpectedActuals msg- ++ (solverReportInfo_ExpectedActuals =<< toList infos)- Mismatch { mismatch_ty1 = exp, mismatch_ty2 = act } ->- [(exp, act)]- KindMismatch { kmismatch_expected = exp, kmismatch_actual = act } ->- [(exp, act)]- TypeEqMismatch { teq_mismatch_expected = exp, teq_mismatch_actual = act } ->- [(exp,act)]- _ -> []---- | Retrieves all @"expected"/"actual"@ messages from a 'TcSolverReportInfo'.------ Prefer using this over inspecting the 'TcSolverReportInfo' datatype if--- you just need this information, as the datatype itself is subject to change--- across GHC versions.-solverReportInfo_ExpectedActuals :: TcSolverReportInfo -> [(Type, Type)]-solverReportInfo_ExpectedActuals- = \case- ExpectedActual { ea_expected = exp, ea_actual = act } ->- [(exp, act)]- ExpectedActualAfterTySynExpansion- { ea_expanded_expected = exp, ea_expanded_actual = act } ->- [(exp, act)]+ Mismatch { mismatchMsg = mismatch_msg } ->+ case mismatch_msg of+ BasicMismatch { mismatch_ty1 = exp, mismatch_ty2 = act } ->+ [(exp, act)]+ KindMismatch { kmismatch_expected = exp, kmismatch_actual = act } ->+ [(exp, act)]+ TypeEqMismatch { teq_mismatch_expected = exp, teq_mismatch_actual = act } ->+ [(exp,act)]+ CouldNotDeduce {} ->+ [] _ -> []
compiler/GHC/Tc/Errors/Hole.hs view
@@ -2,8 +2,6 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ExistentialQuantification #-} -{-# OPTIONS_GHC -Wno-incomplete-record-updates #-}- module GHC.Tc.Errors.Hole ( findValidHoleFits , tcCheckHoleFit@@ -82,8 +80,7 @@ import GHC.Tc.Errors.Hole.FitTypes import qualified Data.Set as Set import GHC.Types.SrcLoc-import GHC.Utils.Trace (warnPprTrace)-import GHC.Data.FastString (unpackFS)+import GHC.Data.FastString (NonDetFastString(..)) import GHC.Types.Unique.Map @@ -400,7 +397,7 @@ Note [Speeding up valid hole-fits] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-To fix #16875 we noted that a lot of time was being spent on uneccessary work.+To fix #16875 we noted that a lot of time was being spent on unecessary work. When we'd call `tcCheckHoleFit hole hole_ty ty`, we would end up by generating a constraint to show that `hole_ty ~ ty`, including any constraints in `ty`. For@@ -462,7 +459,7 @@ ; if showDocs then do { dflags <- getDynFlags ; mb_local_docs <- extractDocs dflags =<< getGblEnv- ; (mods_without_docs, fits') <- mapAccumM (upd mb_local_docs) Set.empty fits+ ; (mods_without_docs, fits') <- mapAccumLM (upd mb_local_docs) Set.empty fits ; report mods_without_docs ; return fits' } else return fits }@@ -483,16 +480,16 @@ Just m -> Right m Nothing -> Left $ case nameSrcLoc name of- RealSrcLoc r _ -> unpackFS $ srcLocFile r- UnhelpfulLoc s -> unpackFS $ s+ -- Nondeterminism is fine, this is used only to display a warning+ RealSrcLoc r _ -> NonDetFastString $ srcLocFile r+ UnhelpfulLoc s -> NonDetFastString s report mods = do { let warning = text "WARNING: Couldn't find any documentation for the following modules:" $+$ nest 2- (fsep (punctuate comma- (either text ppr <$> Set.toList mods)) $+$+ (pprWithCommas (either ppr ppr) (Set.toList mods) $+$ text "Make sure the modules are compiled with '-haddock'.")- ; warnPprTrace (not $ Set.null mods)"addHoleFitDocs" warning (pure ())+ ; warnPprTrace (not $ Set.null mods) "addHoleFitDocs" warning (pure ()) } -- For pretty printing hole fits, we display the name and type of the fit,@@ -503,7 +500,7 @@ pprHoleFit (HFDC sWrp sWrpVars sTy sProv sMs) (HoleFit {..}) = hang display 2 provenance where tyApp = sep $ zipWithEqual "pprHoleFit" pprArg vars hfWrap- where pprArg b arg = case binderArgFlag b of+ where pprArg b arg = case binderFlag b of Specified -> text "@" <> pprParendType arg -- Do not print type application for inferred -- variables (#16456)@@ -521,11 +518,11 @@ -- e.g. -- return :: forall (m :: * -> *) Monad m => (forall a . a -> m a) -- into [m, a]- unwrapTypeVars :: Type -> [TyCoVarBinder]+ unwrapTypeVars :: Type -> [ForAllTyBinder] unwrapTypeVars t = vars ++ case splitFunTy_maybe unforalled of- Just (_, _, unfunned) -> unwrapTypeVars unfunned+ Just (_, _, _, unfunned) -> unwrapTypeVars unfunned _ -> []- where (vars, unforalled) = splitForAllTyCoVarBinders t+ where (vars, unforalled) = splitForAllForAllTyBinders t holeVs = sep $ map (parens . (text "_" <+> dcolon <+>) . ppr) hfMatches holeDisp = if sMs then holeVs else sep $ replicate (length hfMatches) $ text "_"@@ -677,8 +674,7 @@ -- of only concrete hole fits like `sum`. mkRefTy :: Int -> TcM (TcType, [TcTyVar]) mkRefTy refLvl = (wrapWithVars &&& id) <$> newTyVars- where newTyVars = replicateM refLvl $ setLvl <$>- (newOpenTypeKind >>= newFlexiTyVar)+ where newTyVars = replicateM refLvl $ setLvl <$> newOpenFlexiTyVar setLvl = flip setMetaTyVarTcLevel hole_lvl wrapWithVars vars = mkVisFunTysMany (map mkTyVarTy vars) hole_ty @@ -919,7 +915,7 @@ _ -> True allConcrete = all notAbstract z_wrp_tys ; z_vars <- zonkTcTyVars ref_vars- ; let z_mtvs = mapMaybe tcGetTyVar_maybe z_vars+ ; let z_mtvs = mapMaybe getTyVar_maybe z_vars ; allFilled <- not <$> anyM isFlexiTyVar z_mtvs ; allowAbstract <- goptM Opt_AbstractRefHoleFits ; if allowAbstract || (allFilled && allConcrete )
compiler/GHC/Tc/Gen/Annotation.hs view
@@ -48,7 +48,7 @@ ; return [] } tcAnnotation :: LAnnDecl GhcRn -> TcM Annotation-tcAnnotation (L loc ann@(HsAnnotation _ _ provenance expr)) = do+tcAnnotation (L loc ann@(HsAnnotation _ provenance expr)) = do -- Work out what the full target of this annotation was mod <- getModule let target = annProvenanceToTarget mod provenance
compiler/GHC/Tc/Gen/App.hs view
@@ -2,13 +2,10 @@ {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-}-{-# LANGUAGE LambdaCase #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} -- Wrinkle in Note [Trees That Grow] -{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}- {- % (c) The University of Glasgow 2006@@ -22,21 +19,14 @@ import {-# SOURCE #-} GHC.Tc.Gen.Expr( tcPolyExpr ) -import GHC.Types.Basic ( Arity, ExprOrPat(Expression) )-import GHC.Types.Id ( idArity, idName, hasNoBinding )-import GHC.Types.Name ( isWiredInName ) import GHC.Types.Var import GHC.Builtin.Types ( multiplicityTy )-import GHC.Core.ConLike ( ConLike(..) )-import GHC.Core.DataCon ( dataConRepArity- , isNewDataCon, isUnboxedSumDataCon, isUnboxedTupleDataCon ) import GHC.Tc.Gen.Head import GHC.Hs import GHC.Tc.Errors.Types import GHC.Tc.Utils.Monad import GHC.Tc.Utils.Unify import GHC.Tc.Utils.Instantiate-import GHC.Tc.Utils.Concrete ( hasFixedRuntimeRep_syntactic ) import GHC.Tc.Instance.Family ( tcGetFamInstEnvs, tcLookupDataFamInst_maybe ) import GHC.Tc.Gen.HsType import GHC.Tc.Utils.TcMType@@ -48,6 +38,7 @@ import GHC.Core.TyCo.Subst (substTyWithInScope) import GHC.Core.TyCo.FVs( shallowTyCoVarsOfType ) import GHC.Core.Type+import GHC.Core.Coercion import GHC.Tc.Types.Evidence import GHC.Types.Var.Set import GHC.Builtin.PrimOps( tagToEnumKey )@@ -94,7 +85,7 @@ * When QL is done, we don't need to turn the un-filled-in instantiation variables into unification variables -- they- already /are/ unification varibles! See also+ already /are/ unification variables! See also Note [Instantiation variables are short lived]. * We cleverly avoid the quadratic cost of QL, alluded to in the paper.@@ -224,7 +215,7 @@ It treats application chains (f e1 @ty e2) specially: -* So we can report errors like "in the third arument of a call of f"+* So we can report errors like "in the third argument of a call of f" * So we can do Visible Type Application (VTA), for which we must not eagerly instantiate the function part of the application.@@ -238,7 +229,7 @@ returning the function in the corner and the arguments splitHsApps can deal with infix as well as prefix application,- and returns a Rebuilder to re-assemble the the application after+ and returns a Rebuilder to re-assemble the application after typechecking. The "list of arguments" is [HsExprArg], described in Note [HsExprArg].@@ -297,6 +288,7 @@ the renamer (Note [Handling overloaded and rebindable constructs] in GHC.Rename.Expr), and we want them to be instantiated impredicatively so that (f `op`), say, will work OK even if `f` is higher rank.+ See Note [Left and right sections] in GHC.Rename.Expr. Note [Unify with expected type before typechecking arguments] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -331,28 +323,16 @@ -- See Note [tcApp: typechecking applications] tcApp rn_expr exp_res_ty | (fun@(rn_fun, fun_ctxt), rn_args) <- splitHsApps rn_expr- = do { (tc_fun, fun_sigma) <- tcInferAppHead fun rn_args+ = do { traceTc "tcApp {" $+ vcat [ text "rn_fun:" <+> ppr rn_fun+ , text "rn_args:" <+> ppr rn_args ] + ; (tc_fun, fun_sigma) <- tcInferAppHead fun rn_args+ -- Instantiate ; do_ql <- wantQuickLook rn_fun ; (delta, inst_args, app_res_rho) <- tcInstFun do_ql True fun fun_sigma rn_args - -- Check for representation polymorphism in the case that- -- the head of the application is a primop or data constructor- -- which has argument types that are representation-polymorphic.- -- This amounts to checking that the leftover argument types,- -- up until the arity, are not representation-polymorphic,- -- so that we can perform eta-expansion later without introducing- -- representation-polymorphic binders.- --- -- See Note [Checking for representation-polymorphic built-ins]- ; traceTc "tcApp FRR" $- vcat- [ text "tc_fun =" <+> ppr tc_fun- , text "inst_args =" <+> ppr inst_args- , text "app_res_rho =" <+> ppr app_res_rho ]- ; hasFixedRuntimeRep_remainingValArgs inst_args app_res_rho tc_fun- -- Quick look at result ; app_res_rho <- if do_ql then quickLookResultType delta app_res_rho exp_res_ty@@ -372,249 +352,56 @@ = addFunResCtxt rn_fun rn_args app_res_rho exp_res_ty $ thing_inside + -- Match up app_res_rho: the result type of rn_expr+ -- with exp_res_ty: the expected result type+ ; do_ds <- xoptM LangExt.DeepSubsumption ; res_wrap <- perhaps_add_res_ty_ctxt $- tcSubTypeNC (exprCtOrigin rn_expr) GenSigCtxt (Just $ HsExprRnThing rn_expr)- app_res_rho exp_res_ty- -- Need tcSubType because of the possiblity of deep subsumption.- -- app_res_rho and exp_res_ty are both rho-types, so without- -- deep subsumption unifyExpectedType would be sufficient+ if not do_ds+ then -- No deep subsumption+ -- app_res_rho and exp_res_ty are both rho-types,+ -- so with simple subsumption we can just unify them+ -- No need to zonk; the unifier does that+ do { co <- unifyExpectedType rn_expr app_res_rho exp_res_ty+ ; return (mkWpCastN co) } - ; whenDOptM Opt_D_dump_tc_trace $- do { inst_args <- mapM zonkArg inst_args -- Only when tracing- ; traceTc "tcApp" (vcat [ text "rn_fun" <+> ppr rn_fun- , text "inst_args" <+> brackets (pprWithCommas pprHsExprArgTc inst_args)- , text "do_ql: " <+> ppr do_ql- , text "fun_sigma: " <+> ppr fun_sigma- , text "delta: " <+> ppr delta- , text "app_res_rho:" <+> ppr app_res_rho- , text "exp_res_ty:" <+> ppr exp_res_ty- , text "rn_expr:" <+> ppr rn_expr ]) }+ else -- Deep subsumption+ -- Even though both app_res_rho and exp_res_ty are rho-types,+ -- they may have nested polymorphism, so if deep subsumption+ -- is on we must call tcSubType.+ -- Zonk app_res_rho first, because QL may have instantiated some+ -- delta variables to polytypes, and tcSubType doesn't expect that+ do { app_res_rho <- zonkQuickLook do_ql app_res_rho+ ; tcSubTypeDS rn_expr app_res_rho exp_res_ty } -- Typecheck the value arguments ; tc_args <- tcValArgs do_ql inst_args - -- Reconstruct, with a special cases for tagToEnum#.+ -- Reconstruct, with a special case for tagToEnum#. ; tc_expr <- if isTagToEnum rn_fun then tcTagToEnum tc_fun fun_ctxt tc_args app_res_rho- else do return (rebuildHsApps tc_fun fun_ctxt tc_args)+ else do rebuildHsApps tc_fun fun_ctxt tc_args app_res_rho + ; whenDOptM Opt_D_dump_tc_trace $+ do { inst_args <- mapM zonkArg inst_args -- Only when tracing+ ; traceTc "tcApp }" (vcat [ text "rn_fun:" <+> ppr rn_fun+ , text "rn_args:" <+> ppr rn_args+ , text "inst_args" <+> brackets (pprWithCommas pprHsExprArgTc inst_args)+ , text "do_ql: " <+> ppr do_ql+ , text "fun_sigma: " <+> ppr fun_sigma+ , text "delta: " <+> ppr delta+ , text "app_res_rho:" <+> ppr app_res_rho+ , text "exp_res_ty:" <+> ppr exp_res_ty+ , text "rn_expr:" <+> ppr rn_expr+ , text "tc_fun:" <+> ppr tc_fun+ , text "tc_args:" <+> ppr tc_args+ , text "tc_expr:" <+> ppr tc_expr ]) }+ -- Wrap the result ; return (mkHsWrap res_wrap tc_expr) } -{--Note [Checking for representation-polymorphic built-ins]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We cannot have representation-polymorphic or levity-polymorphic-function arguments. See Note [Representation polymorphism invariants]-in GHC.Core. That is checked by the calls to `hasFixedRuntimeRep` in-`tcEValArg`.--But some /built-in/ functions have representation-polymorphic argument-types. Users can't define such Ids; they are all GHC built-ins or data-constructors. Specifically they are:--1. A few wired-in Ids like unsafeCoerce#, with compulsory unfoldings.-2. Primops, such as raise#.-3. Newtype constructors with `UnliftedNewtypes` that have- a representation-polymorphic argument.-4. Representation-polymorphic data constructors: unboxed tuples- and unboxed sums.--For (1) consider- badId :: forall r (a :: TYPE r). a -> a- badId = unsafeCoerce# @r @r @a @a--The wired-in function- unsafeCoerce# :: forall (r1 :: RuntimeRep) (r2 :: RuntimeRep)- (a :: TYPE r1) (b :: TYPE r2).- a -> b-has a convenient but representation-polymorphic type. It has no-binding; instead it has a compulsory unfolding, after which we-would have- badId = /\r /\(a :: TYPE r). \(x::a). ...body of unsafeCorece#...-And this is no good because of that rep-poly \(x::a). So we want-to reject this.--On the other hand- goodId :: forall (a :: Type). a -> a- goodId = unsafeCoerce# @LiftedRep @LiftedRep @a @a--is absolutely fine, because after we inline the unfolding, the \(x::a)-is representation-monomorphic.--Test cases: T14561, RepPolyWrappedVar2.--For primops (2) the situation is similar; they are eta-expanded in-CorePrep to be saturated, and that eta-expansion must not add a-representation-polymorphic lambda.--Test cases: T14561b, RepPolyWrappedVar, UnliftedNewtypesCoerceFail.--For (3), consider a representation-polymorphic newtype with-UnliftedNewtypes:-- type Id :: forall r. TYPE r -> TYPE r- newtype Id a where { MkId :: a }-- bad :: forall r (a :: TYPE r). a -> Id a- bad = MkId @r @a -- Want to reject-- good :: forall (a :: Type). a -> Id a- good = MkId @LiftedRep @a -- Want to accept--Test cases: T18481, UnliftedNewtypesLevityBinder--So these three cases need special treatment. We add a special case-in tcApp to check whether an application of an Id has any remaining-representation-polymorphic arguments, after instantiation and application-of previous arguments. This is achieved by the hasFixedRuntimeRep_remainingValArgs-function, which computes the types of the remaining value arguments, and checks-that each of these have a fixed runtime representation using hasFixedRuntimeRep.--Wrinkles--* Because of Note [Typechecking data constructors] in GHC.Tc.Gen.Head,- we desugar a representation-polymorphic data constructor application- like this:- (/\(r :: RuntimeRep) (a :: TYPE r) \(x::a). K r a x) @LiftedRep Int 4- That is, a rep-poly lambda applied to arguments that instantiate it in- a rep-mono way. It's a bit like a compulsory unfolding that has been- inlined, but not yet beta-reduced.-- Because we want to accept this, we switch off Lint's representation- polymorphism checks when Lint checks the output of the desugarer;- see the lf_check_fixed_rep flag in GHC.Core.Lint.lintCoreBindings.-- We then rely on the simple optimiser to beta reduce these- representation-polymorphic lambdas (e.g. GHC.Core.SimpleOpt.simple_app).--* Arity. We don't want to check for arguments past the- arity of the function. For example-- raise# :: forall {r :: RuntimeRep} (a :: Type) (b :: TYPE r). a -> b-- has arity 1, so an instantiation such as:-- foo :: forall w r (z :: TYPE r). w -> z -> z- foo = raise# @w @(z -> z)-- is unproblematic. This means we must take care not to perform a- representation-polymorphism check on `z`.-- To achieve this, we consult the arity of the 'Id' which is the head- of the application (or just use 1 for a newtype constructor),- and keep track of how many value-level arguments we have seen,- to ensure we stop checking once we reach the arity.- This is slightly complicated by the need to include both visible- and invisible arguments, as the arity counts both:- see GHC.Tc.Gen.Head.countVisAndInvisValArgs.-- Test cases: T20330{a,b}---}---- | Check for representation-polymorphism in the remaining argument types--- of a variable or data constructor, after it has been instantiated and applied to some arguments.------ See Note [Checking for representation-polymorphic built-ins]-hasFixedRuntimeRep_remainingValArgs :: [HsExprArg 'TcpInst] -> TcRhoType -> HsExpr GhcTc -> TcM ()-hasFixedRuntimeRep_remainingValArgs applied_args app_res_rho = \case-- HsVar _ (L _ fun_id)-- -- (1): unsafeCoerce#- -- 'unsafeCoerce#' is peculiar: it is patched in manually as per- -- Note [Wiring in unsafeCoerce#] in GHC.HsToCore.- -- Unfortunately, even though its arity is set to 1 in GHC.HsToCore.mkUnsafeCoercePrimPair,- -- at this stage, if we query idArity, we get 0. This is because- -- we end up looking at the non-patched version of unsafeCoerce#- -- defined in Unsafe.Coerce, and that one indeed has arity 0.- --- -- We thus manually specify the correct arity of 1 here.- | idName fun_id == unsafeCoercePrimName- -> check_thing fun_id 1 (FRRNoBindingResArg fun_id)-- -- (2): primops and other wired-in representation-polymorphic functions,- -- such as 'rightSection', 'oneShot', etc; see bindings with Compulsory unfoldings- -- in GHC.Types.Id.Make- | isWiredInName (idName fun_id) && hasNoBinding fun_id- -> check_thing fun_id (idArity fun_id) (FRRNoBindingResArg fun_id)- -- NB: idArity consults the IdInfo of the Id. This can be a problem- -- in the presence of hs-boot files, as we might not have finished- -- typechecking; inspecting the IdInfo at this point can cause- -- strange Core Lint errors (see #20447).- -- We avoid this entirely by only checking wired-in names,- -- as those are the only ones this check is applicable to anyway.--- XExpr (ConLikeTc (RealDataCon con) _ _)- -- (3): Representation-polymorphic newtype constructors.- | isNewDataCon con- -- (4): Unboxed tuples and unboxed sums- || isUnboxedTupleDataCon con- || isUnboxedSumDataCon con- -> check_thing con (dataConRepArity con) (FRRDataConArg Expression con)-- _ -> return ()-- where- nb_applied_vis_val_args :: Int- nb_applied_vis_val_args = count isHsValArg applied_args-- nb_applied_val_args :: Int- nb_applied_val_args = countVisAndInvisValArgs applied_args-- arg_tys :: [(Type,AnonArgFlag)]- arg_tys = getRuntimeArgTys app_res_rho- -- We do not need to zonk app_res_rho first, because the number of arrows- -- in the (possibly instantiated) inferred type of the function will- -- be at least the arity of the function.-- check_thing :: Outputable thing- => thing- -> Arity- -> (Int -> FixedRuntimeRepContext)- -> TcM ()- check_thing thing arity mk_frr_orig = do- traceTc "tcApp remainingValArgs check_thing" (debug_msg thing arity)- go (nb_applied_vis_val_args + 1) (nb_applied_val_args + 1) arg_tys- where- go :: Int -- visible value argument index, starting from 1- -- only used to report the argument position in error messages- -> Int -- value argument index, starting from 1- -- used to count up to the arity to ensure we don't check too many argument types- -> [(Type, AnonArgFlag)] -- run-time argument types- -> TcM ()- go _ i_val _- | i_val > arity- = return ()- go _ _ []- -- Should never happen: it would mean that the arity is higher- -- than the number of arguments apparent from the type- = pprPanic "hasFixedRuntimeRep_remainingValArgs" (debug_msg thing arity)- go i_visval !i_val ((arg_ty, af) : tys)- = case af of- InvisArg ->- go i_visval (i_val + 1) tys- VisArg -> do- hasFixedRuntimeRep_syntactic (mk_frr_orig i_visval) arg_ty- go (i_visval + 1) (i_val + 1) tys-- -- A message containing all the relevant info, in case this functions- -- needs to be debugged again at some point.- debug_msg :: Outputable thing => thing -> Arity -> SDoc- debug_msg thing arity =- vcat- [ text "thing =" <+> ppr thing- , text "arity =" <+> ppr arity- , text "applied_args =" <+> ppr applied_args- , text "nb_applied_val_args =" <+> ppr nb_applied_val_args- , text "arg_tys =" <+> ppr arg_tys ]- -------------------- wantQuickLook :: HsExpr GhcRn -> TcM Bool--- GHC switches on impredicativity all the time for ($) wantQuickLook (HsVar _ (L _ f)) | getUnique f `elem` quickLookKeys = return True wantQuickLook _ = xoptM LangExt.ImpredicativeTypes@@ -649,6 +436,7 @@ ----------------+ tcValArgs :: Bool -- Quick-look on? -> [HsExprArg 'TcpInst] -- Actual argument -> TcM [HsExprArg 'TcpTc] -- Resulting argument@@ -656,9 +444,9 @@ = mapM tc_arg args where tc_arg :: HsExprArg 'TcpInst -> TcM (HsExprArg 'TcpTc)- tc_arg (EPrag l p) = return (EPrag l (tcExprPrag p))- tc_arg (EWrap w) = return (EWrap w)- tc_arg (ETypeArg l hs_ty ty) = return (ETypeArg l hs_ty ty)+ tc_arg (EPrag l p) = return (EPrag l (tcExprPrag p))+ tc_arg (EWrap w) = return (EWrap w)+ tc_arg (ETypeArg l at hs_ty ty) = return (ETypeArg l at hs_ty ty) tc_arg eva@(EValArg { eva_arg = arg, eva_arg_ty = Scaled mult arg_ty , eva_ctxt = ctxt })@@ -698,9 +486,13 @@ = addArgCtxt ctxt larg $ do { traceTc "tcEValArgQL {" (vcat [ ppr inner_fun <+> ppr inner_args ]) ; tc_args <- tcValArgs True inner_args- ; co <- unifyType Nothing app_res_rho exp_arg_sigma- ; let arg' = mkHsWrapCo co $ rebuildHsApps inner_fun fun_ctxt tc_args- ; traceTc "tcEValArgQL }" empty++ ; co <- unifyType Nothing app_res_rho exp_arg_sigma+ ; arg' <- mkHsWrapCo co <$> rebuildHsApps inner_fun fun_ctxt tc_args app_res_rho+ ; traceTc "tcEValArgQL }" $+ vcat [ text "inner_fun:" <+> ppr inner_fun+ , text "app_res_rho:" <+> ppr app_res_rho+ , text "exp_arg_sigma:" <+> ppr exp_arg_sigma ] ; return (L arg_loc arg') } {- *********************************************************************@@ -716,7 +508,7 @@ tcInstFun :: Bool -- True <=> Do quick-look -> Bool -- False <=> Instantiate only /inferred/ variables at the end- -- so may return a sigma-typex+ -- so may return a sigma-type -- True <=> Instantiate all type variables at the end: -- return a rho-type -- The /only/ call site that passes in False is the one@@ -737,18 +529,9 @@ , text "do_ql" <+> ppr do_ql ]) ; go emptyVarSet [] [] fun_sigma rn_args } where- fun_loc = appCtxtLoc fun_ctxt fun_orig = exprCtOrigin (case fun_ctxt of VAExpansion e _ -> e VACall e _ _ -> e)- set_fun_ctxt thing_inside- | not (isGoodSrcSpan fun_loc) -- noSrcSpan => no arguments- = thing_inside -- => context is already set- | otherwise- = setSrcSpan fun_loc $- case fun_ctxt of- VAExpansion orig _ -> addExprCtxt orig thing_inside- VACall {} -> thing_inside -- Count value args only when complaining about a function -- applied to too many value args@@ -760,7 +543,7 @@ HsUnboundVar {} -> True _ -> False - inst_all, inst_inferred, inst_none :: ArgFlag -> Bool+ inst_all, inst_inferred, inst_none :: ForAllTyFlag -> Bool inst_all (Invisible {}) = True inst_all Required = False @@ -770,7 +553,7 @@ inst_none _ = False - inst_fun :: [HsExprArg 'TcpRn] -> ArgFlag -> Bool+ inst_fun :: [HsExprArg 'TcpRn] -> ForAllTyFlag -> Bool inst_fun [] | inst_final = inst_all | otherwise = inst_none -- Using `inst_none` for `:type` avoids@@ -789,7 +572,7 @@ -- go: If fun_ty=kappa, look it up in Theta go delta acc so_far fun_ty args- | Just kappa <- tcGetTyVar_maybe fun_ty+ | Just kappa <- getTyVar_maybe fun_ty , kappa `elemVarSet` delta = do { cts <- readMetaTyVar kappa ; case cts of@@ -807,9 +590,9 @@ | (tvs, body1) <- tcSplitSomeForAllTyVars (inst_fun args) fun_ty , (theta, body2) <- tcSplitPhiTy body1 , not (null tvs && null theta)- = do { (inst_tvs, wrap, fun_rho) <- set_fun_ctxt $+ = do { (inst_tvs, wrap, fun_rho) <- addHeadCtxt fun_ctxt $ instantiateSigma fun_orig tvs theta body2- -- set_fun_ctxt: important for the class constraints+ -- addHeadCtxt: important for the class constraints -- that may be emitted from instantiating fun_sigma ; go (delta `extendVarSetList` inst_tvs) (addArgWrap wrap acc) so_far fun_rho args }@@ -828,19 +611,19 @@ = go1 delta (EPrag sp prag : acc) so_far fun_ty args -- Rule ITYARG from Fig 4 of the QL paper- go1 delta acc so_far fun_ty ( ETypeArg { eva_ctxt = ctxt, eva_hs_ty = hs_ty }+ go1 delta acc so_far fun_ty ( ETypeArg { eva_ctxt = ctxt, eva_at = at, eva_hs_ty = hs_ty } : rest_args ) | fun_is_out_of_scope -- See Note [VTA for out-of-scope functions] = go delta acc so_far fun_ty rest_args | otherwise = do { (ty_arg, inst_ty) <- tcVTA fun_ty hs_ty- ; let arg' = ETypeArg { eva_ctxt = ctxt, eva_hs_ty = hs_ty, eva_ty = ty_arg }+ ; let arg' = ETypeArg { eva_ctxt = ctxt, eva_at = at, eva_hs_ty = hs_ty, eva_ty = ty_arg } ; go delta (arg' : acc) so_far inst_ty rest_args } -- Rule IVAR from Fig 4 of the QL paper: go1 delta acc so_far fun_ty args@(EValArg {} : _)- | Just kappa <- tcGetTyVar_maybe fun_ty+ | Just kappa <- getTyVar_maybe fun_ty , kappa `elemVarSet` delta = -- Function type was of form f :: forall a b. t1 -> t2 -> b -- with 'b', one of the quantified type variables, in the corner@@ -867,8 +650,8 @@ ; let delta' = delta `extendVarSetList` (res_nu:arg_nus) arg_tys = mkTyVarTys arg_nus res_ty = mkTyVarTy res_nu- fun_ty' = mkVisFunTys (zipWithEqual "tcInstFun" mkScaled mults arg_tys) res_ty- co_wrap = mkWpCastN (mkTcGReflLeftCo Nominal fun_ty' kind_co)+ fun_ty' = mkScaledFunTys (zipWithEqual "tcInstFun" mkScaled mults arg_tys) res_ty+ co_wrap = mkWpCastN (mkGReflLeftCo Nominal fun_ty' kind_co) acc' = addArgWrap co_wrap acc -- Suppose kappa :: kk -- Then fun_ty :: kk, fun_ty' :: Type, kind_co :: Type ~ kk@@ -898,19 +681,26 @@ addArgCtxt :: AppCtxt -> LHsExpr GhcRn -> TcM a -> TcM a--- Adds a "In the third argument of f, namely blah"--- context, unless we are in generated code, in which case--- use "In the expression: arg"+-- There are two cases:+-- * In the normal case, we add an informative context+-- "In the third argument of f, namely blah"+-- * If we are deep inside generated code (isGeneratedCode)+-- or if all or part of this particular application is an expansion+-- (VAExpansion), just use the less-informative context+-- "In the expression: arg"+-- Unless the arg is also a generated thing, in which case do nothing. ---See Note [Rebindable syntax and HsExpansion] in GHC.Hs.Expr-addArgCtxt (VACall fun arg_no _) (L arg_loc arg) thing_inside- = setSrcSpanA arg_loc $- addErrCtxt (funAppCtxt fun arg arg_no) $- thing_inside+addArgCtxt ctxt (L arg_loc arg) thing_inside+ = do { in_generated_code <- inGeneratedCode+ ; case ctxt of+ VACall fun arg_no _ | not in_generated_code+ -> setSrcSpanA arg_loc $+ addErrCtxt (funAppCtxt fun arg arg_no) $+ thing_inside -addArgCtxt (VAExpansion {}) (L arg_loc arg) thing_inside- = setSrcSpanA arg_loc $- addExprCtxt arg $ -- Auto-suppressed if arg_loc is generated- thing_inside+ _ -> setSrcSpanA arg_loc $+ addExprCtxt arg $ -- Auto-suppressed if arg_loc is generated+ thing_inside } {- ********************************************************************* * *@@ -925,7 +715,7 @@ -- The function type has already had its Inferred binders instantiated tcVTA fun_ty hs_ty | Just (tvb, inner_ty) <- tcSplitForAllTyVarBinder_maybe fun_ty- , binderArgFlag tvb == Specified+ , binderFlag tvb == Specified -- It really can't be Inferred, because we've just -- instantiated those. But, oddly, it might just be Required. -- See Note [Required quantifiers in the type of a term]@@ -940,11 +730,12 @@ insted_ty = substTyWithInScope in_scope [tv] [ty_arg] inner_ty -- NB: tv and ty_arg have the same kind, so this -- substitution is kind-respecting- ; traceTc "VTA" (vcat [ppr tv, debugPprType kind- , debugPprType ty_arg- , debugPprType (tcTypeKind ty_arg)- , debugPprType inner_ty- , debugPprType insted_ty ])+ ; traceTc "VTA" (vcat [ text "fun_ty" <+> ppr fun_ty+ , text "tv" <+> ppr tv <+> dcolon <+> debugPprType kind+ , text "ty_arg" <+> debugPprType ty_arg <+> dcolon+ <+> debugPprType (typeKind ty_arg)+ , text "inner_ty" <+> debugPprType inner_ty+ , text "insted_ty" <+> debugPprType insted_ty ]) ; return (ty_arg, insted_ty) } | otherwise@@ -967,7 +758,7 @@ We want to reject this type application to Int, but in earlier GHCs we had an ASSERT that Required could not occur here. -The ice is thin; c.f. Note [No Required TyCoBinder in terms]+The ice is thin; c.f. Note [No Required PiTyBinder in terms] in GHC.Core.TyCo.Rep. Note [VTA for out-of-scope functions]@@ -1085,7 +876,7 @@ -- This top-level zonk step, which is the reason -- we need a local 'go' loop, is subtle -- See Section 9 of the QL paper- | Just kappa <- tcGetTyVar_maybe arg_ty+ | Just kappa <- getTyVar_maybe arg_ty , kappa `elemVarSet` delta = do { info <- readMetaTyVar kappa ; case info of@@ -1199,8 +990,8 @@ -- Now, and only now, expand synonyms go bvs rho1 rho2- | Just rho1 <- tcView rho1 = go bvs rho1 rho2- | Just rho2 <- tcView rho2 = go bvs rho1 rho2+ | Just rho1 <- coreView rho1 = go bvs rho1 rho2+ | Just rho2 <- coreView rho2 = go bvs rho1 rho2 go bvs (TyConApp tc1 tys1) (TyConApp tc2 tys2) | tc1 == tc2@@ -1210,25 +1001,25 @@ -- Decompose (arg1 -> res1) ~ (arg2 -> res2) -- and (c1 => res1) ~ (c2 => res2)- -- But for the latter we only learn instantiation info from t1~t2+ -- But for the latter we only learn instantiation info from res1~res2 -- We look at the multiplicity too, although the chances of getting -- impredicative instantiation info from there seems...remote. go bvs (FunTy { ft_af = af1, ft_arg = arg1, ft_res = res1, ft_mult = mult1 }) (FunTy { ft_af = af2, ft_arg = arg2, ft_res = res2, ft_mult = mult2 })- | af1 == af2- = do { when (af1 == VisArg) $- do { go bvs arg1 arg2; go bvs mult1 mult2 }+ | af1 == af2 -- Match the arrow TyCon+ = do { when (isVisibleFunArg af1) (go bvs arg1 arg2)+ ; when (isFUNArg af1) (go bvs mult1 mult2) ; go bvs res1 res2 } -- ToDo: c.f. Tc.Utils.unify.uType, -- which does not split FunTy here- -- Also NB tcRepSplitAppTy here, which does not split (c => t)+ -- Also NB tcSplitAppTyNoView here, which does not split (c => t) go bvs (AppTy t1a t1b) ty2- | Just (t2a, t2b) <- tcRepSplitAppTy_maybe ty2+ | Just (t2a, t2b) <- tcSplitAppTyNoView_maybe ty2 = do { go bvs t1a t2a; go bvs t1b t2b } go bvs ty1 (AppTy t2a t2b)- | Just (t1a, t1b) <- tcRepSplitAppTy_maybe ty1+ | Just (t1a, t1b) <- tcSplitAppTyNoView_maybe ty1 = do { go bvs t1a t2a; go bvs t1b t2b } go (bvs1, bvs2) (ForAllTy bv1 ty1) (ForAllTy bv2 ty2)@@ -1269,7 +1060,7 @@ ; writeMetaTyVar kappa (mkCastTy ty2 co) } | otherwise- = return () -- Occurs-check or forall-bound varialbe+ = return () -- Occurs-check or forall-bound variable {- Note [Actual unification in qlUnify]@@ -1294,7 +1085,7 @@ * What if kappa and ty have different kinds? We solve that problem by calling unifyKind, producing a coercion perhaps emitting some deferred equality constraints. That is /different/ from the approach we use in- the main constraint solver for herterogeneous equalities; see Note+ the main constraint solver for heterogeneous equalities; see Note [Equalities with incompatible kinds] in Solver.Canonical Why different? Because:@@ -1424,17 +1215,19 @@ check_enumeration res_ty rep_tc ; let rep_ty = mkTyConApp rep_tc rep_args tc_fun' = mkHsWrap (WpTyApp rep_ty) tc_fun- tc_expr = rebuildHsApps tc_fun' fun_ctxt [val_arg]- df_wrap = mkWpCastR (mkTcSymCo coi)+ df_wrap = mkWpCastR (mkSymCo coi)+ ; tc_expr <- rebuildHsApps tc_fun' fun_ctxt [val_arg] res_ty ; return (mkHsWrap df_wrap tc_expr) }}}}} | otherwise = failWithTc TcRnTagToEnumMissingValArg where- vanilla_result = return (rebuildHsApps tc_fun fun_ctxt tc_args)+ vanilla_result = rebuildHsApps tc_fun fun_ctxt tc_args res_ty check_enumeration ty' tc+ | -- isTypeDataTyCon: see Note [Type data declarations] in GHC.Rename.Module+ isTypeDataTyCon tc = addErrTc (TcRnTagToEnumResTyTypeData ty') | isEnumerationTyCon tc = return () | otherwise = addErrTc (TcRnTagToEnumResTyNotAnEnum ty') @@ -1446,4 +1239,4 @@ ********************************************************************* -} tcExprPrag :: HsPragE GhcRn -> HsPragE GhcTc-tcExprPrag (HsPragSCC x1 src ann) = HsPragSCC x1 src ann+tcExprPrag (HsPragSCC x1 ann) = HsPragSCC x1 ann
compiler/GHC/Tc/Gen/Arrow.hs view
@@ -33,6 +33,7 @@ import GHC.Tc.Types.Origin import GHC.Tc.Types.Evidence import GHC.Core.Multiplicity+import GHC.Core.Coercion import GHC.Types.Id( mkLocalId ) import GHC.Tc.Utils.Instantiate import GHC.Builtin.Types@@ -104,8 +105,7 @@ ; (pat', cmd') <- newArrowScope $ tcCheckPat (ArrowMatchCtxt ProcExpr) pat (unrestricted arg_ty) $ tcCmdTop cmd_env names' cmd (unitTy, res_ty)- ; let res_co = mkTcTransCo co- (mkTcAppCo co1 (mkTcNomReflCo res_ty))+ ; let res_co = co `mkTransCo` mkAppCo co1 (mkNomReflCo res_ty) ; return (pat', cmd', res_co) } {-@@ -339,7 +339,7 @@ -> TcM (HsWrapper, MatchGroup GhcTc (LHsCmd GhcTc)) tcCmdMatchLambda env ctxt- mg@MG { mg_alts = L l matches }+ mg@MG { mg_alts = L l matches, mg_ext = origin } (cmd_stk, res_ty) = do { (co, arg_tys, cmd_stk') <- matchExpectedCmdArgs n_pats cmd_stk @@ -349,7 +349,7 @@ ; let arg_tys' = map unrestricted arg_tys mg' = mg { mg_alts = L l matches'- , mg_ext = MatchGroupTc arg_tys' res_ty }+ , mg_ext = MatchGroupTc arg_tys' res_ty origin } ; return (mkWpCastN co, mg') } where@@ -383,11 +383,11 @@ matchExpectedCmdArgs :: Arity -> TcType -> TcM (TcCoercionN, [TcTypeFRR], TcType) matchExpectedCmdArgs 0 ty- = return (mkTcNomReflCo ty, [], ty)+ = return (mkNomReflCo ty, [], ty) matchExpectedCmdArgs n ty = do { (co1, [ty1, ty2]) <- matchExpectedTyConApp pairTyCon ty ; (co2, tys, res_ty) <- matchExpectedCmdArgs (n-1) ty2- ; return (mkTcTyConAppCo Nominal pairTyCon [co1, co2], ty1:tys, res_ty) }+ ; return (mkTyConAppCo Nominal pairTyCon [co1, co2], ty1:tys, res_ty) } {- ************************************************************************@@ -424,7 +424,8 @@ , recS_rec_ids = rec_names }) res_ty thing_inside = do { let tup_names = rec_names ++ filterOut (`elem` rec_names) later_names ; tup_elt_tys <- newFlexiTyVarTys (length tup_names) liftedTypeKind- ; let tup_ids = zipWith (\n p -> mkLocalId n Many p) tup_names tup_elt_tys -- Many because it's a recursive definition+ ; let tup_ids = zipWith (\n p -> mkLocalId n ManyTy p) tup_names tup_elt_tys+ -- Many because it's a recursive definition ; tcExtendIdEnv tup_ids $ do { (stmts', tup_rets) <- tcStmtsAndThen ctxt (tcArrDoStmt env) stmts res_ty $ \ _res_ty' ->@@ -435,7 +436,7 @@ -- NB: The rec_ids for the recursive things -- already scope over this part. This binding may shadow -- some of them with polymorphic things with the same Name- -- (see Note [RecStmt] in GHC.Hs.Expr)+ -- (see Note [How RecStmt works] in Language.Haskell.Syntax.Expr) ; let rec_ids = takeList rec_names tup_ids ; later_ids <- tcLookupLocalIds later_names
compiler/GHC/Tc/Gen/Bind.hs view
@@ -28,10 +28,11 @@ import {-# SOURCE #-} GHC.Tc.TyCl.PatSyn ( tcPatSynDecl, tcPatSynBuilderBind ) import GHC.Types.Tickish (CoreTickish, GenTickish (..))-import GHC.Types.CostCentre (mkUserCC, CCFlavour(DeclCC))+import GHC.Types.CostCentre (mkUserCC, mkDeclCCFlavour) import GHC.Driver.Session import GHC.Data.FastString import GHC.Hs+ import GHC.Tc.Errors.Types import GHC.Tc.Gen.Sig import GHC.Tc.Utils.Concrete ( hasFixedRuntimeRep_syntactic )@@ -42,45 +43,53 @@ import GHC.Tc.Solver import GHC.Tc.Types.Evidence import GHC.Tc.Types.Constraint-import GHC.Core.Predicate+ import GHC.Tc.Gen.HsType import GHC.Tc.Gen.Pat import GHC.Tc.Utils.TcMType+import GHC.Tc.Instance.Family( tcGetFamInstEnvs )+import GHC.Tc.Utils.TcType+import GHC.Tc.Validity (checkValidType, checkEscapingKind)++import GHC.Core.Predicate import GHC.Core.Reduction ( Reduction(..) ) import GHC.Core.Multiplicity import GHC.Core.FamInstEnv( normaliseType )-import GHC.Tc.Instance.Family( tcGetFamInstEnvs ) import GHC.Core.Class ( Class )-import GHC.Tc.Utils.TcType+import GHC.Core.Coercion( mkSymCo ) import GHC.Core.Type (mkStrLitTy, tidyOpenType, mkCastTy)-import GHC.Builtin.Types ( mkBoxedTupleTy )+import GHC.Core.TyCo.Ppr( pprTyVars )++import GHC.Builtin.Types ( mkConstraintTupleTy ) import GHC.Builtin.Types.Prim+import GHC.Unit.Module+ import GHC.Types.SourceText import GHC.Types.Id import GHC.Types.Var as Var import GHC.Types.Var.Set-import GHC.Types.Var.Env( TidyEnv )-import GHC.Unit.Module+import GHC.Types.Var.Env( TidyEnv, TyVarEnv, mkVarEnv, lookupVarEnv ) import GHC.Types.Name import GHC.Types.Name.Set import GHC.Types.Name.Env import GHC.Types.SrcLoc-import GHC.Data.Bag+ import GHC.Utils.Error-import GHC.Data.Graph.Directed-import GHC.Data.Maybe import GHC.Utils.Misc import GHC.Types.Basic import GHC.Types.CompleteMatch import GHC.Utils.Outputable as Outputable import GHC.Utils.Panic import GHC.Builtin.Names( ipClassName )-import GHC.Tc.Validity (checkValidType, checkEscapingKind) import GHC.Types.Unique.FM import GHC.Types.Unique.DSet import GHC.Types.Unique.Set import qualified GHC.LanguageExtensions as LangExt +import GHC.Data.Bag+import GHC.Data.Graph.Directed+import GHC.Data.Maybe+ import Control.Monad import Data.Foldable (find) @@ -208,7 +217,7 @@ -- combinations are invalid it will be found so at match sites. -- There it is also where we consider if the type of the pattern match is -- compatible with the result type constructor 'mb_tc'.- doOne (L loc c@(CompleteMatchSig _ext _src_txt (L _ ns) mb_tc_nm))+ doOne (L loc c@(CompleteMatchSig (_ext, _src_txt) (L _ ns) mb_tc_nm)) = fmap Just $ setSrcSpanA loc $ addErrCtxt (text "In" <+> ppr c) $ do cls <- mkUniqDSet <$> mapM (addLocMA tcLookupConLike) ns mb_tc <- traverse @Maybe tcLookupLocatedTyCon mb_tc_nm@@ -270,7 +279,7 @@ ; let p = mkStrLitTy $ hsIPNameFS ip ; ip_id <- newDict ipClass [ p, ty ] ; expr' <- tcCheckMonoExpr expr ty- ; let d = mapLoc (toDict ipClass p ty) expr'+ ; let d = fmap (toDict ipClass p ty) expr' ; return (ip_id, (IPBind ip_id l_name d)) } -- Coerces a `t` into a dictionary for `IP "x" t`.@@ -505,7 +514,7 @@ ; traceTc "Generalisation plan" (ppr plan) ; result@(_, poly_ids) <- case plan of NoGen -> tcPolyNoGen rec_tc prag_fn sig_fn bind_list- InferGen mn -> tcPolyInfer rec_tc prag_fn sig_fn mn bind_list+ InferGen -> tcPolyInfer rec_tc prag_fn sig_fn bind_list CheckGen lbind sig -> tcPolyCheck prag_fn sig lbind ; mapM_ (\ poly_id ->@@ -539,7 +548,7 @@ , Just poly_id <- completeSigPolyId_maybe sig = poly_id | otherwise- = mkLocalId name Many forall_a_a+ = mkLocalId name ManyTy forall_a_a forall_a_a :: TcType -- At one point I had (forall r (a :: TYPE r). a), but of course@@ -634,8 +643,8 @@ ; let bind' = FunBind { fun_id = L nm_loc poly_id2 , fun_matches = matches'- , fun_ext = wrap_gen <.> wrap_res- , fun_tick = tick }+ , fun_ext = (wrap_gen <.> wrap_res, tick)+ } export = ABE { abe_wrap = idHsWrapper , abe_poly = poly_id@@ -658,7 +667,7 @@ funBindTicks :: SrcSpan -> TcId -> Module -> [LSig GhcRn] -> TcM [CoreTickish] funBindTicks loc fun_id mod sigs- | (mb_cc_str : _) <- [ cc_name | L _ (SCCFunSig _ _ _ cc_name) <- sigs ]+ | (mb_cc_str : _) <- [ cc_name | L _ (SCCFunSig _ _ cc_name) <- sigs ] -- this can only be a singleton list, as duplicate pragmas are rejected -- by the renamer , let cc_str@@ -666,9 +675,9 @@ = sl_fs $ unLoc cc_str | otherwise = getOccFS (Var.varName fun_id)- cc_name = moduleNameFS (moduleName mod) `appendFS` consFS '.' cc_str+ cc_name = concatFS [moduleNameFS (moduleName mod), fsLit ".", cc_str] = do- flavour <- DeclCC <$> getCCIndexTcM cc_name+ flavour <- mkDeclCCFlavour <$> getCCIndexTcM cc_name let cc = mkUserCC cc_name mod loc flavour return [ProfNote cc True True] | otherwise@@ -698,20 +707,20 @@ :: RecFlag -- Whether it's recursive after breaking -- dependencies based on type signatures -> TcPragEnv -> TcSigFun- -> Bool -- True <=> apply the monomorphism restriction -> [LHsBind GhcRn] -> TcM (LHsBinds GhcTc, [TcId])-tcPolyInfer rec_tc prag_fn tc_sig_fn mono bind_list+tcPolyInfer rec_tc prag_fn tc_sig_fn bind_list = do { (tclvl, wanted, (binds', mono_infos)) <- pushLevelAndCaptureConstraints $ tcMonoBinds rec_tc tc_sig_fn LetLclBndr bind_list + ; apply_mr <- checkMonomorphismRestriction mono_infos bind_list+ ; traceTc "tcPolyInfer" (ppr apply_mr $$ ppr (map mbi_sig mono_infos))+ ; let name_taus = [ (mbi_poly_name info, idType (mbi_mono_id info)) | info <- mono_infos ] sigs = [ sig | MBI { mbi_sig = Just sig } <- mono_infos ]- infer_mode = if mono then ApplyMR else NoRestrictions-- ; mapM_ (checkOverloadedSig mono) sigs+ infer_mode = if apply_mr then ApplyMR else NoRestrictions ; traceTc "simplifyInfer call" (ppr tclvl $$ ppr name_taus $$ ppr wanted) ; ((qtvs, givens, ev_binds, insoluble), residual)@@ -740,6 +749,59 @@ ; return (unitBag abs_bind, poly_ids) } -- poly_ids are guaranteed zonked by mkExport +checkMonomorphismRestriction :: [MonoBindInfo] -> [LHsBind GhcRn] -> TcM Bool+-- True <=> apply the MR+checkMonomorphismRestriction mbis lbinds+ | null partial_sigs -- The normal case+ = do { mr_on <- xoptM LangExt.MonomorphismRestriction+ ; let mr_applies = mr_on && any (restricted . unLoc) lbinds+ ; when mr_applies $ mapM_ checkOverloadedSig sigs+ ; return mr_applies }++ | otherwise -- See Note [Partial type signatures and the monomorphism restriction]+ = return (all is_mono_psig partial_sigs)++ where+ sigs, partial_sigs :: [TcIdSigInst]+ sigs = [sig | MBI { mbi_sig = Just sig } <- mbis]+ partial_sigs = [sig | sig@(TISI { sig_inst_sig = PartialSig {} }) <- sigs]++ complete_sig_bndrs :: NameSet+ complete_sig_bndrs+ = mkNameSet [ idName bndr+ | TISI { sig_inst_sig = CompleteSig { sig_bndr = bndr }} <- sigs ]++ is_mono_psig (TISI { sig_inst_theta = theta, sig_inst_wcx = mb_extra_constraints })+ = null theta && isNothing mb_extra_constraints++ -- The Haskell 98 monomorphism restriction+ restricted (PatBind {}) = True+ restricted (VarBind { var_id = v }) = no_sig v+ restricted (FunBind { fun_id = v, fun_matches = m }) = restricted_match m+ && no_sig (unLoc v)+ restricted b = pprPanic "isRestrictedGroup/unrestricted" (ppr b)++ restricted_match mg = matchGroupArity mg == 0+ -- No args => like a pattern binding+ -- Some args => a function binding++ no_sig nm = not (nm `elemNameSet` complete_sig_bndrs)++checkOverloadedSig :: TcIdSigInst -> TcM ()+-- Example:+-- f :: Eq a => a -> a+-- K f = e+-- The MR applies, but the signature is overloaded, and it's+-- best to complain about this directly+-- c.f #11339+checkOverloadedSig sig+ | not (null (sig_inst_theta sig))+ , let orig_sig = sig_inst_sig sig+ = setSrcSpan (sig_loc orig_sig) $+ failWith $ TcRnOverloadedSig orig_sig+ | otherwise+ = return ()+ -------------- mkExport :: TcPragEnv -> WantedConstraints -- residual constraints, already emitted (for errors only)@@ -851,7 +913,7 @@ -- do this check; otherwise (#14000) we may report an ambiguity -- error for a rather bogus type. - ; return (mkLocalId poly_name Many inferred_poly_ty) }+ ; return (mkLocalId poly_name ManyTy inferred_poly_ty) } chooseInferredQuantifiers :: WantedConstraints -- residual constraints@@ -882,7 +944,8 @@ ; let psig_qtvs = map binderVar psig_qtv_bndrs psig_qtv_set = mkVarSet psig_qtvs psig_qtv_prs = psig_qtv_nms `zip` psig_qtvs-+ psig_bndr_map :: TyVarEnv InvisTVBinder+ psig_bndr_map = mkVarEnv [ (binderVar tvb, tvb) | tvb <- psig_qtv_bndrs ] -- Check whether the quantified variables of the -- partial signature have been unified together@@ -898,32 +961,35 @@ ; annotated_theta <- zonkTcTypes annotated_theta ; (free_tvs, my_theta) <- choose_psig_context psig_qtv_set annotated_theta wcx+ -- NB: free_tvs includes tau_tvs - ; let keep_me = free_tvs `unionVarSet` psig_qtv_set- final_qtvs = [ mkTyVarBinder vis tv- | tv <- qtvs -- Pulling from qtvs maintains original order- , tv `elemVarSet` keep_me- , let vis = case lookupVarBndr tv psig_qtv_bndrs of- Just spec -> spec- Nothing -> InferredSpec ]+ ; let (_,final_qtvs) = foldr (choose_qtv psig_bndr_map) (free_tvs, []) qtvs+ -- Pulling from qtvs maintains original order+ -- NB: qtvs is already in dependency order + ; traceTc "chooseInferredQuantifiers" $+ vcat [ text "qtvs" <+> pprTyVars qtvs+ , text "psig_qtv_bndrs" <+> ppr psig_qtv_bndrs+ , text "free_tvs" <+> ppr free_tvs+ , text "final_tvs" <+> ppr final_qtvs ]+ ; return (final_qtvs, my_theta) } where- report_dup_tyvar_tv_err (n1,n2)- = addErrTc (TcRnPartialTypeSigTyVarMismatch n1 n2 fn_name hs_ty)-- report_mono_sig_tv_err (n,tv)- = addErrTc (TcRnPartialTypeSigBadQuantifier n fn_name m_unif_ty hs_ty)- where- m_unif_ty = listToMaybe- [ rhs- -- recall that residuals are always implications- | residual_implic <- bagToList $ wc_impl residual- , residual_ct <- bagToList $ wc_simple (ic_wanted residual_implic)- , let residual_pred = ctPred residual_ct- , Just (Nominal, lhs, rhs) <- [ getEqPredTys_maybe residual_pred ]- , Just lhs_tv <- [ tcGetTyVar_maybe lhs ]- , lhs_tv == tv ]+ choose_qtv :: TyVarEnv InvisTVBinder -> TcTyVar+ -> (TcTyVarSet, [InvisTVBinder]) -> (TcTyVarSet, [InvisTVBinder])+ -- Pick which of the original qtvs should be retained+ -- Keep it if (a) it is mentioned in the body of the type (free_tvs)+ -- (b) it is a forall'd variable of the partial signature (psig_qtv_bndrs)+ -- (c) it is mentioned in the kind of a retained qtv (#22065)+ choose_qtv psig_bndr_map tv (free_tvs, qtvs)+ | Just psig_bndr <- lookupVarEnv psig_bndr_map tv+ = (free_tvs', psig_bndr : qtvs)+ | tv `elemVarSet` free_tvs+ = (free_tvs', mkTyVarBinder InferredSpec tv : qtvs)+ | otherwise -- Do not pick it+ = (free_tvs, qtvs)+ where+ free_tvs' = free_tvs `unionVarSet` tyCoVarsOfType (tyVarKind tv) choose_psig_context :: VarSet -> TcThetaType -> Maybe TcType -> TcM (VarSet, TcThetaType)@@ -949,12 +1015,12 @@ -- NB: my_theta already includes all the annotated constraints ; diff_theta <- findInferredDiff annotated_theta my_theta - ; case tcGetCastedTyVar_maybe wc_var_ty of+ ; case getCastedTyVar_maybe wc_var_ty of -- We know that wc_co must have type kind(wc_var) ~ Constraint, as it -- comes from the checkExpectedKind in GHC.Tc.Gen.HsType.tcAnonWildCardOcc. -- So, to make the kinds work out, we reverse the cast here.- Just (wc_var, wc_co) -> writeMetaTyVar wc_var (mk_ctuple diff_theta- `mkCastTy` mkTcSymCo wc_co)+ Just (wc_var, wc_co) -> writeMetaTyVar wc_var (mkConstraintTupleTy diff_theta+ `mkCastTy` mkSymCo wc_co) Nothing -> pprPanic "chooseInferredQuantifiers 1" (ppr wc_var_ty) ; traceTc "completeTheta" $@@ -967,10 +1033,22 @@ -- Return (annotated_theta ++ diff_theta) -- See Note [Extra-constraints wildcards] - mk_ctuple preds = mkBoxedTupleTy preds- -- Hack alert! See GHC.Tc.Gen.HsType:- -- Note [Extra-constraint holes in partial type signatures]+ report_dup_tyvar_tv_err (n1,n2)+ = addErrTc (TcRnPartialTypeSigTyVarMismatch n1 n2 fn_name hs_ty) + report_mono_sig_tv_err (n,tv)+ = addErrTc (TcRnPartialTypeSigBadQuantifier n fn_name m_unif_ty hs_ty)+ where+ m_unif_ty = listToMaybe+ [ rhs+ -- recall that residuals are always implications+ | residual_implic <- bagToList $ wc_impl residual+ , residual_ct <- bagToList $ wc_simple (ic_wanted residual_implic)+ , let residual_pred = ctPred residual_ct+ , Just (Nominal, lhs, rhs) <- [ getEqPredTys_maybe residual_pred ]+ , Just lhs_tv <- [ getTyVar_maybe lhs ]+ , lhs_tv == tv ]+ chooseInferredQuantifiers _ _ _ _ (Just (TISI { sig_inst_sig = sig@(CompleteSig {}) })) = pprPanic "chooseInferredQuantifiers" (ppr sig) @@ -995,22 +1073,6 @@ ; let dia = TcRnPolymorphicBinderMissingSig (idName id) tidy_ty ; addDiagnosticTcM (env1, dia) } -checkOverloadedSig :: Bool -> TcIdSigInst -> TcM ()--- Example:--- f :: Eq a => a -> a--- K f = e--- The MR applies, but the signature is overloaded, and it's--- best to complain about this directly--- c.f #11339-checkOverloadedSig monomorphism_restriction_applies sig- | not (null (sig_inst_theta sig))- , monomorphism_restriction_applies- , let orig_sig = sig_inst_sig sig- = setSrcSpan (sig_loc orig_sig) $- failWith $ TcRnOverloadedSig orig_sig- | otherwise- = return ()- {- Note [Partial type signatures and generalisation] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If /any/ of the signatures in the group is a partial type signature@@ -1036,14 +1098,33 @@ doesn't seem much point. Indeed, adding a partial type signature is a way to get per-binding inferred generalisation. -We apply the MR if /all/ of the partial signatures lack a context.-In particular (#11016):+Note [Partial type signatures and the monomorphism restriction]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+We apply the MR if /none/ of the partial signatures has a context. e.g.+ f :: _ -> Int+ f x = rhs+The partial type signature says, in effect, "there is no context", which+amounts to appplying the MR. Indeed, saying+ f :: _+ f = rhs+is a way for forcing the MR to apply.++But we /don't/ want to apply the MR if the partial signatures do have+a context e.g. (#11016): f2 :: (?loc :: Int) => _ f2 = ?loc It's stupid to apply the MR here. This test includes an extra-constraints wildcard; that is, we don't apply the MR if you write f3 :: _ => blah +But watch out. We don't want to apply the MR to+ type Wombat a = forall b. Eq b => ...b...a...+ f4 :: Wombat _+Here f4 doesn't /look/ as if it has top-level overloading, but in fact it+does, hidden under Wombat. We can't "see" that because we only have access+to the HsType at the moment. That's why we do the check in+checkMonomorphismRestriction.+ Note [Quantified variables in partial type signatures] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Consider@@ -1077,7 +1158,7 @@ We get [W] Foo (), [W] Applicative f. When we do pickCapturedPreds in choose_psig_context, we'll discard Foo ()! Usually would not quantify over such (closed) predicates. So my_theta will be (Applicative f). But we really-do want to quantify over (Foo ()) -- it was speicfied by the programmer.+do want to quantify over (Foo ()) -- it was specified by the programmer. Solution: always return annotated_theta (user-specified) plus the extra piece diff_theta. @@ -1095,22 +1176,6 @@ or multi-parameter type classes - an inferred type that includes unboxed tuples -Note [Inferred type with escaping kind]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Check for an inferred type with an escaping kind; e.g. #23051- forall {k} {f :: k -> RuntimeRep} {g :: k} {a :: TYPE (f g)}. a-where the kind of the body of the forall mentions `f` and `g` which-are bound by the forall. No no no.--This check, mkInferredPolyId, is really in the wrong place:-`inferred_poly_ty` doesn't obey the PKTI and it would be better not to-generalise it in the first place; see #20686. But for now it works.--I considered adjusting the generalisation in GHC.Tc.Solver to directly check for-escaping kind variables; instead, promoting or defaulting them. But that-gets into the defaulting swamp and is a non-trivial and unforced-change, so I have left it alone for now.- Note [Impedance matching] ~~~~~~~~~~~~~~~~~~~~~~~~~ Consider@@ -1200,20 +1265,18 @@ , Nothing <- sig_fn name -- ...with no type signature = setSrcSpanA b_loc $ do { ((co_fn, matches'), rhs_ty')- <- tcInferFRR (FRRBinder name) $ \ exp_ty ->- -- tcInferFRR: the type of a let-binder must have- -- a fixed runtime rep. See #23176+ <- tcInfer $ \ exp_ty -> tcExtendBinderStack [TcIdBndr_ExpType name exp_ty NotTopLevel] $ -- We extend the error context even for a non-recursive -- function so that in type error messages we show the -- type of the thing whose rhs we are type checking tcMatchesFun (L nm_loc name) matches exp_ty- ; mono_id <- newLetBndr no_gen name Many rhs_ty'+ ; mono_id <- newLetBndr no_gen name ManyTy rhs_ty' ; return (unitBag $ L b_loc $ FunBind { fun_id = L nm_loc mono_id, fun_matches = matches',- fun_ext = co_fn, fun_tick = [] },+ fun_ext = (co_fn, []) }, [MBI { mbi_poly_name = name , mbi_sig = Nothing , mbi_mono_id = mono_id }]) }@@ -1224,9 +1287,7 @@ | NonRecursive <- is_rec -- ...binder isn't mentioned in RHS , all (isNothing . sig_fn) bndrs = addErrCtxt (patMonoBindsCtxt pat grhss) $- do { (grhss', pat_ty) <- tcInferFRR FRRPatBind $ \ exp_ty ->- -- tcInferFRR: the type of each let-binder must have- -- a fixed runtime rep. See #23176+ do { (grhss', pat_ty) <- tcInfer $ \ exp_ty -> tcGRHSsPat grhss exp_ty ; let exp_pat_ty :: Scaled ExpSigmaTypeFRR@@ -1236,7 +1297,7 @@ ; return ( unitBag $ L b_loc $ PatBind { pat_lhs = pat', pat_rhs = grhss'- , pat_ext = pat_ty, pat_ticks = ([],[]) }+ , pat_ext = (pat_ty, ([],[])) } , mbis ) } where@@ -1294,7 +1355,7 @@ (combine head ids), and use that to tell us the polymorphic types of x and y. -We don't need to check -XImpredicativeTypes beucase without it+We don't need to check -XImpredicativeTypes because without it these types like [forall a. a->a] are illegal anyway, so this special case code only really has an effect if -XImpredicativeTypes is on. Small exception:@@ -1368,7 +1429,7 @@ | otherwise -- No type signature = do { mono_ty <- newOpenFlexiTyVarTy- ; mono_id <- newLetBndr no_gen name Many mono_ty+ ; mono_id <- newLetBndr no_gen name ManyTy mono_ty -- This ^ generates a binder with Many multiplicity because all -- let/where-binders are unrestricted. When we introduce linear let -- binders, we will need to retrieve the multiplicity information.@@ -1385,7 +1446,7 @@ [ (mbi_poly_name mbi, mbi_mono_id mbi) | mbi <- sig_mbis ] - -- See Note [Existentials in pattern bindings]+ -- See Note [Typechecking pattern bindings] ; ((pat', nosig_mbis), pat_ty) <- addErrCtxt (patMonoBindsCtxt pat grhss) $ tcInferFRR FRRPatBind $ \ exp_ty ->@@ -1439,7 +1500,7 @@ | CompleteSig { sig_bndr = poly_id } <- id_sig = addInlinePrags poly_id (lookupPragEnv prags name) newSigLetBndr no_gen name (TISI { sig_inst_tau = tau })- = newLetBndr no_gen name Many tau+ = newLetBndr no_gen name ManyTy tau -- Binders with a signature are currently always of multiplicity -- Many. Because they come either from toplevel, let, or where -- declarations. Which are all unrestricted currently.@@ -1455,8 +1516,8 @@ matches (mkCheckExpType $ idType mono_id) ; return ( FunBind { fun_id = L (noAnnSrcSpan loc) mono_id , fun_matches = matches'- , fun_ext = co_fn- , fun_tick = [] } ) }+ , fun_ext = (co_fn, [])+ } ) } tcRhs (TcPatBind infos pat' grhss pat_ty) = -- When we are doing pattern bindings we *don't* bring any scoped@@ -1469,8 +1530,7 @@ tcGRHSsPat grhss (mkCheckExpType pat_ty) ; return ( PatBind { pat_lhs = pat', pat_rhs = grhss'- , pat_ext = pat_ty- , pat_ticks = ([],[]) } )}+ , pat_ext = (pat_ty, ([],[])) } )} tcExtendTyVarEnvForRhs :: Maybe TcIdSigInst -> TcM a -> TcM a tcExtendTyVarEnvForRhs Nothing thing_inside@@ -1636,79 +1696,55 @@ = NoGen -- No generalisation, no AbsBinds | InferGen -- Implicit generalisation; there is an AbsBinds- Bool -- True <=> apply the MR; generalise only unconstrained type vars - | CheckGen (LHsBind GhcRn) TcIdSigInfo- -- One FunBind with a signature- -- Explicit generalisation+ | CheckGen -- One FunBind with a complete signature:+ (LHsBind GhcRn) -- do explicit generalisation+ TcIdSigInfo -- Always CompleteSig -- A consequence of the no-AbsBinds choice (NoGen) is that there is -- no "polymorphic Id" and "monmomorphic Id"; there is just the one instance Outputable GeneralisationPlan where ppr NoGen = text "NoGen"- ppr (InferGen b) = text "InferGen" <+> ppr b+ ppr InferGen = text "InferGen" ppr (CheckGen _ s) = text "CheckGen" <+> ppr s decideGeneralisationPlan :: DynFlags -> TopLevelFlag -> IsGroupClosed -> TcSigFun -> [LHsBind GhcRn] -> GeneralisationPlan decideGeneralisationPlan dflags top_lvl closed sig_fn lbinds- | has_partial_sigs = InferGen (and partial_sig_mrs) | Just (bind, sig) <- one_funbind_with_sig = CheckGen bind sig- | do_not_generalise = NoGen- | otherwise = InferGen mono_restriction+ | generalise_binds = InferGen+ | otherwise = NoGen where- binds = map unLoc lbinds-- partial_sig_mrs :: [Bool]- -- One for each partial signature (so empty => no partial sigs)- -- The Bool is True if the signature has no constraint context- -- so we should apply the MR- -- See Note [Partial type signatures and generalisation]- partial_sig_mrs- = [ null $ fromMaybeContext mtheta- | TcIdSig (PartialSig { psig_hs_ty = hs_ty })- <- mapMaybe sig_fn (collectHsBindListBinders CollNoDictBinders lbinds)- , let (mtheta, _) = splitLHsQualTy (hsSigWcType hs_ty) ]-- has_partial_sigs = not (null partial_sig_mrs)-- mono_restriction = xopt LangExt.MonomorphismRestriction dflags- && any restricted binds-- do_not_generalise- | isTopLevel top_lvl = False+ generalise_binds+ | isTopLevel top_lvl = True -- See Note [Always generalise top-level bindings] - | IsGroupClosed _ True <- closed = False+ | IsGroupClosed _ True <- closed = True -- The 'True' means that all of the group's -- free vars have ClosedTypeId=True; so we can ignore -- -XMonoLocalBinds, and generalise anyway - | otherwise = xopt LangExt.MonoLocalBinds dflags+ | has_partial_sigs = True+ -- See Note [Partial type signatures and generalisation] + | otherwise = not (xopt LangExt.MonoLocalBinds dflags)+ -- With OutsideIn, all nested bindings are monomorphic- -- except a single function binding with a signature+ -- except a single function binding with a complete signature one_funbind_with_sig | [lbind@(L _ (FunBind { fun_id = v }))] <- lbinds- , Just (TcIdSig sig) <- sig_fn (unLoc v)+ , Just (TcIdSig sig@(CompleteSig {})) <- sig_fn (unLoc v) = Just (lbind, sig) | otherwise = Nothing - -- The Haskell 98 monomorphism restriction- restricted (PatBind {}) = True- restricted (VarBind { var_id = v }) = no_sig v- restricted (FunBind { fun_id = v, fun_matches = m }) = restricted_match m- && no_sig (unLoc v)- restricted b = pprPanic "isRestrictedGroup/unrestricted" (ppr b)-- restricted_match mg = matchGroupArity mg == 0- -- No args => like a pattern binding- -- Some args => a function binding-- no_sig n = not (hasCompleteSig sig_fn n)+ binders = collectHsBindListBinders CollNoDictBinders lbinds+ has_partial_sigs = any has_partial_sig binders+ has_partial_sig nm = case sig_fn nm of+ Just (TcIdSig (PartialSig {})) -> True+ _ -> False isClosedBndrGroup :: TcTypeEnv -> Bag (LHsBind GhcRn) -> IsGroupClosed isClosedBndrGroup type_env binds
compiler/GHC/Tc/Gen/Default.hs view
@@ -12,7 +12,8 @@ import GHC.Hs import GHC.Core.Class-import GHC.Core.Type ( typeKind )+import GHC.Core.Type( typeKind )+ import GHC.Types.Var( tyVarKind ) import GHC.Tc.Errors.Types import GHC.Tc.Utils.Monad@@ -26,9 +27,10 @@ import GHC.Types.Error import GHC.Types.SrcLoc import GHC.Utils.Outputable-import GHC.Utils.Panic import qualified GHC.LanguageExtensions as LangExt +import Data.List.NonEmpty ( NonEmpty (..) )+ tcDefaults :: [LDefaultDecl GhcRn] -> TcM (Maybe [Type]) -- Defaulting types to heave -- into Tc monad for later use@@ -67,9 +69,9 @@ ; return (Just tau_tys) } -tcDefaults decls@(L locn (DefaultDecl _ _) : _)+tcDefaults (decl@(L locn (DefaultDecl _ _)) : decls) = setSrcSpan (locA locn) $- failWithTc (dupDefaultDeclErr decls)+ failWithTc (dupDefaultDeclErr (decl:|decls)) tc_default_ty :: [Class] -> LHsType GhcRn -> TcM Type@@ -103,7 +105,6 @@ defaultDeclCtxt :: SDoc defaultDeclCtxt = text "When checking the types in a default declaration" -dupDefaultDeclErr :: [LDefaultDecl GhcRn] -> TcRnMessage-dupDefaultDeclErr (L _ (DefaultDecl _ _) : dup_things)+dupDefaultDeclErr :: NonEmpty (LDefaultDecl GhcRn) -> TcRnMessage+dupDefaultDeclErr (L _ (DefaultDecl _ _) :| dup_things) = TcRnMultipleDefaultDeclarations dup_things-dupDefaultDeclErr [] = panic "dupDefaultDeclErr []"
compiler/GHC/Tc/Gen/Export.hs view
@@ -125,7 +125,7 @@ , T defined at A.hs:4:23 ] Then, we get @[C{C;}, C{T;}, T{T;}, T{D;}]@, which eventually gets merged-into @[C{C, T;}, T{T, D;}]@ (which satsifies the AvailTC invariant).+into @[C{C, T;}, T{T, D;}]@ (which satisfies the AvailTC invariant). -} data ExportAccum -- The type of the accumulating parameter of@@ -190,7 +190,7 @@ | explicit_mod = exports | has_main = Just (noLocA [noLocA (IEVar noExtField- (noLocA (IEName $ noLocA default_main)))])+ (noLocA (IEName noExtField $ noLocA default_main)))]) -- ToDo: the 'noLoc' here is unhelpful if 'main' -- turns out to be out of scope | otherwise = Nothing@@ -369,8 +369,8 @@ lookup_ie _ = panic "lookup_ie" -- Other cases covered earlier - lookup_ie_with :: LIEWrappedName RdrName -> [LIEWrappedName RdrName]- -> RnM (Located Name, [LIEWrappedName Name], [Name],+ lookup_ie_with :: LIEWrappedName GhcPs -> [LIEWrappedName GhcPs]+ -> RnM (Located Name, [LIEWrappedName GhcRn], [Name], [Located FieldLabel]) lookup_ie_with (L l rdr) sub_rdrs = do name <- lookupGlobalOccRn $ ieWrappedName rdr@@ -381,7 +381,7 @@ , map (ieWrappedName . unLoc) non_flds , flds) - lookup_ie_all :: IE GhcPs -> LIEWrappedName RdrName+ lookup_ie_all :: IE GhcPs -> LIEWrappedName GhcPs -> RnM (Located Name, [Name], [FieldLabel]) lookup_ie_all ie (L l rdr) = do name <- lookupGlobalOccRn $ ieWrappedName rdr@@ -421,8 +421,7 @@ {- Note [Modules without a module header]----------------------------------------------------+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Haskell 2010 report says in section 5.1: >> An abbreviated form of module, consisting only of the module body, is@@ -477,8 +476,8 @@ -lookupChildrenExport :: Name -> [LIEWrappedName RdrName]- -> RnM ([LIEWrappedName Name], [Located FieldLabel])+lookupChildrenExport :: Name -> [LIEWrappedName GhcPs]+ -> RnM ([LIEWrappedName GhcRn], [Located FieldLabel]) lookupChildrenExport spec_parent rdr_items = do xs <- mapAndReportM doOne rdr_items@@ -493,8 +492,8 @@ | ns == tcName = [dataName, tcName] | otherwise = [ns] -- Process an individual child- doOne :: LIEWrappedName RdrName- -> RnM (Either (LIEWrappedName Name) (Located FieldLabel))+ doOne :: LIEWrappedName GhcPs+ -> RnM (Either (LIEWrappedName GhcRn) (Located FieldLabel)) doOne n = do let bareName = (ieWrappedName . unLoc) n@@ -514,7 +513,7 @@ case name of NameNotFound -> do { ub <- reportUnboundName unboundName ; let l = getLoc n- ; return (Left (L l (IEName (L (la2na l) ub))))}+ ; return (Left (L l (IEName noExtField (L (la2na l) ub))))} FoundChild par child -> do { checkPatSynParent spec_parent par child ; return $ case child of FieldGreName fl -> Right (L (getLocA n) fl)@@ -523,7 +522,8 @@ IncorrectParent p c gs -> failWithDcErr p c gs --- Note: [Typing Pattern Synonym Exports]+-- Note [Typing Pattern Synonym Exports]+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- It proved quite a challenge to precisely specify which pattern synonyms -- should be allowed to be bundled with which type constructors. -- In the end it was decided to be quite liberal in what we allow. Below is@@ -567,8 +567,8 @@ -- type constructor. -- ----- Note: [Types of TyCon]---+-- Note [Types of TyCon]+-- ~~~~~~~~~~~~~~~~~~~~~ -- This check appears to be overly complicated, Richard asked why it -- is not simply just `isAlgTyCon`. The answer for this is that -- a classTyCon is also an `AlgTyCon` which we explicitly want to disallow.@@ -758,4 +758,4 @@ case SrcLoc.leftmost_smallest (greSrcSpan gre1) (greSrcSpan gre2) of LT -> (child1, gre1, ie1, child2, gre2, ie2) GT -> (child2, gre2, ie2, child1, gre1, ie1)- EQ -> panic "exportClashErr: clashing exports have idential location"+ EQ -> panic "exportClashErr: clashing exports have identical location"
compiler/GHC/Tc/Gen/Expr.hs view
@@ -23,11 +23,11 @@ tcPolyExpr, tcExpr, tcSyntaxOp, tcSyntaxOpGen, SyntaxOpType(..), synKnownType, tcCheckId,- getFixedTyVars ) where+ ) where import GHC.Prelude -import {-# SOURCE #-} GHC.Tc.Gen.Splice( tcSpliceExpr, tcTypedBracket, tcUntypedBracket )+import {-# SOURCE #-} GHC.Tc.Gen.Splice( tcTypedSplice, tcTypedBracket, tcUntypedBracket ) import GHC.Hs import GHC.Hs.Syn.Type@@ -37,16 +37,19 @@ import GHC.Tc.Utils.Unify import GHC.Types.Basic import GHC.Types.Error+import GHC.Types.FieldLabel+import GHC.Types.Unique.Map ( UniqMap, listToUniqMap, lookupUniqMap ) import GHC.Core.Multiplicity import GHC.Core.UsageEnv import GHC.Tc.Errors.Types-import GHC.Tc.Utils.Concrete ( hasFixedRuntimeRep_syntactic )+import GHC.Tc.Utils.Concrete ( hasFixedRuntimeRep_syntactic, hasFixedRuntimeRep ) import GHC.Tc.Utils.Instantiate import GHC.Tc.Gen.App import GHC.Tc.Gen.Head import GHC.Tc.Gen.Bind ( tcLocalBinds ) import GHC.Tc.Instance.Family ( tcGetFamInstEnvs ) import GHC.Core.FamInstEnv ( FamInstEnvs )+import GHC.Rename.Expr ( mkExpandedExpr ) import GHC.Rename.Env ( addUsedGRE ) import GHC.Tc.Utils.Env import GHC.Tc.Gen.Arrow@@ -66,10 +69,11 @@ import GHC.Types.Name.Reader import GHC.Core.TyCon import GHC.Core.Type+import GHC.Core.Coercion( mkSymCo ) import GHC.Tc.Types.Evidence-import GHC.Types.Var.Set import GHC.Builtin.Types import GHC.Builtin.Names+import GHC.Builtin.Uniques ( mkBuiltinUnique ) import GHC.Driver.Session import GHC.Types.SrcLoc import GHC.Utils.Misc@@ -82,9 +86,14 @@ import GHC.Core.Class(classTyCon) import GHC.Types.Unique.Set ( UniqSet, mkUniqSet, elementOfUniqSet, nonDetEltsUniqSet ) +import Language.Haskell.Syntax.Basic (FieldLabelString(..))+ import Data.Function-import Data.List (partition, sortBy, groupBy, intersect)+import Data.List (partition, sortBy, intersect)+import qualified Data.List.NonEmpty as NE +import GHC.Data.Bag ( unitBag )+ {- ************************************************************************ * *@@ -178,7 +187,7 @@ tcExpr :: HsExpr GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc) --- Use tcApp to typecheck appplications, which are treated specially+-- Use tcApp to typecheck applications, which are treated specially -- by Quick Look. Specifically: -- - HsVar lone variables, to ensure that they can get an -- impredicative instantiation (via Quick Look@@ -280,7 +289,7 @@ ************************************************************************ -} --- Explict lists [e1,e2,e3] have been expanded already in the renamer+-- Explicit lists [e1,e2,e3] have been expanded already in the renamer -- The expansion includes an ExplicitList, but it is always the built-in -- list type, so that's all we need concern ourselves with here. See -- GHC.Rename.Expr. Note [Handling overloaded and rebindable constructs]@@ -323,7 +332,7 @@ -- See Note [Typechecking data constructors] in GHC.Tc.Gen.Head -- See Note [Don't flatten tuples from HsSyn] in GHC.Core.Make- act_res_ty = mkVisFunTys missing_tys (mkTupleTy1 boxity arg_tys)+ act_res_ty = mkScaledFunTys missing_tys (mkTupleTy1 boxity arg_tys) ; traceTc "ExplicitTuple" (ppr act_res_ty $$ ppr res_ty) @@ -502,319 +511,30 @@ where orig = OccurrenceOf con_name -{--Note [Type of a record update]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-The main complication with RecordUpd is that we need to explicitly-handle the *non-updated* fields. Consider:-- data T a b c = MkT1 { fa :: a, fb :: (b,c) }- | MkT2 { fa :: a, fb :: (b,c), fc :: c -> c }- | MkT3 { fd :: a }-- upd :: T a b c -> (b',c) -> T a b' c- upd t x = t { fb = x}--The result type should be (T a b' c)-not (T a b c), because 'b' *is not* mentioned in a non-updated field-not (T a b' c'), because 'c' *is* mentioned in a non-updated field-NB that it's not good enough to look at just one constructor; we must-look at them all; cf #3219--After all, upd should be equivalent to:- upd t x = case t of- MkT1 p q -> MkT1 p x- MkT2 a b -> MkT2 p b- MkT3 d -> error ...--So we need to give a completely fresh type to the result record,-and then constrain it by the fields that are *not* updated ("p" above).-We call these the "fixed" type variables, and compute them in getFixedTyVars.--Note that because MkT3 doesn't contain all the fields being updated,-its RHS is simply an error, so it doesn't impose any type constraints.-Hence the use of 'relevant_cont'.--Note [Implicit type sharing]-~~~~~~~~~~~~~~~~~~~~~~~~~~~-We also take into account any "implicit" non-update fields. For example- data T a b where { MkT { f::a } :: T a a; ... }-So the "real" type of MkT is: forall ab. (a~b) => a -> T a b--Then consider- upd t x = t { f=x }-We infer the type- upd :: T a b -> a -> T a b- upd (t::T a b) (x::a)- = case t of { MkT (co:a~b) (_:a) -> MkT co x }-We can't give it the more general type- upd :: T a b -> c -> T c b--Note [Criteria for update]-~~~~~~~~~~~~~~~~~~~~~~~~~~-We want to allow update for existentials etc, provided the updated-field isn't part of the existential. For example, this should be ok.- data T a where { MkT { f1::a, f2::b->b } :: T a }- f :: T a -> b -> T b- f t b = t { f1=b }--The criterion we use is this:-- The types of the updated fields- mention only the universally-quantified type variables- of the data constructor--NB: this is not (quite) the same as being a "naughty" record selector-(See Note [Naughty record selectors]) in GHC.Tc.TyCl), at least-in the case of GADTs. Consider- data T a where { MkT :: { f :: a } :: T [a] }-Then f is not "naughty" because it has a well-typed record selector.-But we don't allow updates for 'f'. (One could consider trying to-allow this, but it makes my head hurt. Badly. And no one has asked-for it.)--In principle one could go further, and allow- g :: T a -> T a- g t = t { f2 = \x -> x }-because the expression is polymorphic...but that seems a bridge too far.--Note [Data family example]-~~~~~~~~~~~~~~~~~~~~~~~~~~- data instance T (a,b) = MkT { x::a, y::b }- --->- data :TP a b = MkT { a::a, y::b }- coTP a b :: T (a,b) ~ :TP a b--Suppose r :: T (t1,t2), e :: t3-Then r { x=e } :: T (t3,t1)- --->- case r |> co1 of- MkT x y -> MkT e y |> co2- where co1 :: T (t1,t2) ~ :TP t1 t2- co2 :: :TP t3 t2 ~ T (t3,t2)-The wrapping with co2 is done by the constructor wrapper for MkT--Outgoing invariants-~~~~~~~~~~~~~~~~~~~-In the outgoing (HsRecordUpd scrut binds cons in_inst_tys out_inst_tys):-- * cons are the data constructors to be updated-- * in_inst_tys, out_inst_tys have same length, and instantiate the- *representation* tycon of the data cons. In Note [Data- family example], in_inst_tys = [t1,t2], out_inst_tys = [t3,t2]--Note [Mixed Record Field Updates]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider the following pattern synonym.-- data MyRec = MyRec { foo :: Int, qux :: String }-- pattern HisRec{f1, f2} = MyRec{foo = f1, qux=f2}--This allows updates such as the following-- updater :: MyRec -> MyRec- updater a = a {f1 = 1 }--It would also make sense to allow the following update (which we reject).-- updater a = a {f1 = 1, qux = "two" } ==? MyRec 1 "two"--This leads to confusing behaviour when the selectors in fact refer the same-field.-- updater a = a {f1 = 1, foo = 2} ==? ???--For this reason, we reject a mixture of pattern synonym and normal record-selectors in the same update block. Although of course we still allow the-following.-- updater a = (a {f1 = 1}) {foo = 2}-- > updater (MyRec 0 "str")- MyRec 2 "str"---}- -- Record updates via dot syntax are replaced by desugared expressions -- in the renamer. See Note [Overview of record dot syntax] in -- GHC.Hs.Expr. This is why we match on 'rupd_flds = Left rbnds' here -- and panic otherwise. tcExpr expr@(RecordUpd { rupd_expr = record_expr, rupd_flds = Left rbnds }) res_ty = assert (notNull rbnds) $- do { -- STEP -2: typecheck the record_expr, the record to be updated- (record_expr', record_rho) <- tcScalingUsage Many $ tcInferRho record_expr- -- Record update drops some of the content of the record (namely the- -- content of the field being updated). As a consequence, unless the- -- field being updated is unrestricted in the record, or we need an- -- unrestricted record. Currently, we simply always require an- -- unrestricted record.- --- -- Consider the following example:- --- -- data R a = R { self :: a }- -- bad :: a ⊸ ()- -- bad x = let r = R x in case r { self = () } of { R x' -> x' }- --- -- This should definitely *not* typecheck.-- -- STEP -1 See Note [Disambiguating record fields] in GHC.Tc.Gen.Head- -- After this we know that rbinds is unambiguous- ; rbinds <- disambiguateRecordBinds record_expr record_rho rbnds res_ty- ; let upd_flds = map (unLoc . hfbLHS . unLoc) rbinds- upd_fld_occs = map (occNameFS . rdrNameOcc . rdrNameAmbiguousFieldOcc) upd_flds- sel_ids = map selectorAmbiguousFieldOcc upd_flds- -- STEP 0- -- Check that the field names are really field names- -- and they are all field names for proper records or- -- all field names for pattern synonyms.- ; let bad_guys = [ setSrcSpan loc $ addErrTc (notSelector fld_name)- | fld <- rbinds,- -- Excludes class ops- let L loc sel_id = hsRecUpdFieldId (unLoc fld),- not (isRecordSelector sel_id),- let fld_name = idName sel_id ]- ; unless (null bad_guys) (sequence bad_guys >> failM)- -- See Note [Mixed Record Selectors]- ; let (data_sels, pat_syn_sels) =- partition isDataConRecordSelector sel_ids- ; massert (all isPatSynRecordSelector pat_syn_sels)- ; checkTc ( null data_sels || null pat_syn_sels )- ( mixedSelectors data_sels pat_syn_sels )-- -- STEP 1- -- Figure out the tycon and data cons from the first field name- ; let -- It's OK to use the non-tc splitters here (for a selector)- sel_id : _ = sel_ids-- mtycon :: Maybe TyCon- mtycon = case idDetails sel_id of- RecSelId (RecSelData tycon) _ -> Just tycon- _ -> Nothing-- con_likes :: [ConLike]- con_likes = case idDetails sel_id of- RecSelId (RecSelData tc) _- -> map RealDataCon (tyConDataCons tc)- RecSelId (RecSelPatSyn ps) _- -> [PatSynCon ps]- _ -> panic "tcRecordUpd"- -- NB: for a data type family, the tycon is the instance tycon-- relevant_cons = conLikesWithFields con_likes upd_fld_occs- -- A constructor is only relevant to this process if- -- it contains *all* the fields that are being updated- -- Other ones will cause a runtime error if they occur-- -- Step 2- -- Check that at least one constructor has all the named fields- -- i.e. has an empty set of bad fields returned by badFields- ; checkTc (not (null relevant_cons)) (badFieldsUpd rbinds con_likes)-- -- Take apart a representative constructor- ; let con1 = assert (not (null relevant_cons) ) head relevant_cons- (con1_tvs, _, _, _prov_theta, req_theta, scaled_con1_arg_tys, _)- = conLikeFullSig con1- con1_arg_tys = map scaledThing scaled_con1_arg_tys- -- We can safely drop the fields' multiplicities because- -- they are currently always 1: there is no syntax for record- -- fields with other multiplicities yet. This way we don't need- -- to handle it in the rest of the function- con1_flds = map flLabel $ conLikeFieldLabels con1- con1_tv_tys = mkTyVarTys con1_tvs- con1_res_ty = case mtycon of- Just tc -> mkFamilyTyConApp tc con1_tv_tys- Nothing -> conLikeResTy con1 con1_tv_tys-- -- Check that we're not dealing with a unidirectional pattern- -- synonym- ; checkTc (conLikeHasBuilder con1) $- nonBidirectionalErr (conLikeName con1)-- -- STEP 3 Note [Criteria for update]- -- Check that each updated field is polymorphic; that is, its type- -- mentions only the universally-quantified variables of the data con- ; let flds1_w_tys = zipEqual "tcExpr:RecConUpd" con1_flds con1_arg_tys- bad_upd_flds = filter bad_fld flds1_w_tys- con1_tv_set = mkVarSet con1_tvs- bad_fld (fld, ty) = fld `elem` upd_fld_occs &&- not (tyCoVarsOfType ty `subVarSet` con1_tv_set)- ; checkTc (null bad_upd_flds) (TcRnFieldUpdateInvalidType bad_upd_flds)-- -- STEP 4 Note [Type of a record update]- -- Figure out types for the scrutinee and result- -- Both are of form (T a b c), with fresh type variables, but with- -- common variables where the scrutinee and result must have the same type- -- These are variables that appear in *any* arg of *any* of the- -- relevant constructors *except* in the updated fields- --- ; let fixed_tvs = getFixedTyVars upd_fld_occs con1_tvs relevant_cons- is_fixed_tv tv = tv `elemVarSet` fixed_tvs-- mk_inst_ty :: TCvSubst -> (TyVar, TcType) -> TcM (TCvSubst, TcType)- -- Deals with instantiation of kind variables- -- c.f. GHC.Tc.Utils.TcMType.newMetaTyVars- mk_inst_ty subst (tv, result_inst_ty)- | is_fixed_tv tv -- Same as result type- = return (extendTvSubst subst tv result_inst_ty, result_inst_ty)- | otherwise -- Fresh type, of correct kind- = do { (subst', new_tv) <- newMetaTyVarX subst tv- ; return (subst', mkTyVarTy new_tv) }-- ; (result_subst, con1_tvs') <- newMetaTyVars con1_tvs- ; let result_inst_tys = mkTyVarTys con1_tvs'- init_subst = mkEmptyTCvSubst (getTCvInScope result_subst)-- ; (scrut_subst, scrut_inst_tys) <- mapAccumLM mk_inst_ty init_subst- (con1_tvs `zip` result_inst_tys)-- ; let rec_res_ty = TcType.substTy result_subst con1_res_ty- scrut_ty = TcType.substTy scrut_subst con1_res_ty- con1_arg_tys' = map (TcType.substTy result_subst) con1_arg_tys-- ; co_scrut <- unifyType (Just . HsExprRnThing $ unLoc record_expr) record_rho scrut_ty- -- NB: normal unification is OK here (as opposed to subsumption),- -- because for this to work out, both record_rho and scrut_ty have- -- to be normal datatypes -- no contravariant stuff can go on-- -- STEP 5- -- Typecheck the bindings- ; rbinds' <- tcRecordUpd con1 con1_arg_tys' rbinds-- -- STEP 6: Deal with the stupid theta.- -- See Note [The stupid context] in GHC.Core.DataCon.- ; let theta' = substThetaUnchecked scrut_subst (conLikeStupidTheta con1)- ; instStupidTheta RecordUpdOrigin theta'-- -- Step 7: make a cast for the scrutinee, in the- -- case that it's from a data family- ; let fam_co :: HsWrapper -- RepT t1 .. tn ~R scrut_ty- fam_co | Just tycon <- mtycon- , Just co_con <- tyConFamilyCoercion_maybe tycon- = mkWpCastR (mkTcUnbranchedAxInstCo co_con scrut_inst_tys [])- | otherwise- = idHsWrapper-- -- Step 8: Check that the req constraints are satisfied- -- For normal data constructors req_theta is empty but we must do- -- this check for pattern synonyms.- ; let req_theta' = substThetaUnchecked scrut_subst req_theta- ; req_wrap <- instCallConstraints RecordUpdOrigin req_theta'+ do { -- Desugar the record update. See Note [Record Updates].+ ; (ds_expr, ds_res_ty, err_ctxt) <- desugarRecordUpd record_expr rbnds res_ty - -- Phew!- ; let upd_tc = RecordUpdTc { rupd_cons = relevant_cons- , rupd_in_tys = scrut_inst_tys- , rupd_out_tys = result_inst_tys- , rupd_wrap = req_wrap }- expr' = RecordUpd { rupd_expr = mkLHsWrap fam_co $- mkLHsWrapCo co_scrut record_expr'- , rupd_flds = Left rbinds'- , rupd_ext = upd_tc }+ -- Typecheck the desugared expression.+ ; expr' <- addErrCtxt err_ctxt $+ tcExpr (mkExpandedExpr expr ds_expr) (Check ds_res_ty)+ -- NB: it's important to use ds_res_ty and not res_ty here.+ -- Test case: T18802b. - ; tcWrapResult expr expr' rec_res_ty res_ty }-tcExpr (RecordUpd {}) _ = panic "GHC.Tc.Gen.Expr: tcExpr: The impossible happened!"+ ; addErrCtxt err_ctxt $ tcWrapResultMono expr expr' ds_res_ty res_ty+ -- We need to unify the result type of the desugared+ -- expression with the expected result type.+ --+ -- See Note [Unifying result types in tcRecordUpd].+ -- Test case: T10808.+ } +tcExpr (RecordUpd {}) _ = panic "tcExpr: unexpected overloaded-dot RecordUpd" {- ************************************************************************@@ -850,17 +570,18 @@ ************************************************************************ -} --- HsSpliced is an annotation produced by 'GHC.Rename.Splice.rnSpliceExpr'. -- Here we get rid of it and add the finalizers to the global environment.--- -- See Note [Delaying modFinalizers in untyped splices] in GHC.Rename.Splice.-tcExpr (HsSpliceE _ (HsSpliced _ mod_finalizers (HsSplicedExpr expr)))- res_ty- = do addModFinalizersWithLclEnv mod_finalizers- tcExpr expr res_ty-tcExpr (HsSpliceE _ splice) res_ty = tcSpliceExpr splice res_ty-tcExpr e@(HsTypedBracket _ body) res_ty = tcTypedBracket e body res_ty+tcExpr (HsTypedSplice ext splice) res_ty = tcTypedSplice ext splice res_ty+tcExpr e@(HsTypedBracket _ body) res_ty = tcTypedBracket e body res_ty+ tcExpr e@(HsUntypedBracket ps body) res_ty = tcUntypedBracket e body ps res_ty+tcExpr (HsUntypedSplice splice _) res_ty+ = case splice of+ HsUntypedSpliceTop mod_finalizers expr+ -> do { addModFinalizersWithLclEnv mod_finalizers+ ; tcExpr expr res_ty }+ HsUntypedSpliceNested {} -> panic "tcExpr: invalid nested splice" {- ************************************************************************@@ -928,7 +649,7 @@ arithSeqEltType Nothing res_ty = do { res_ty <- expTypeToType res_ty ; (coi, elt_ty) <- matchExpectedListTy res_ty- ; return (mkWpCastN coi, One, elt_ty, Nothing) }+ ; return (mkWpCastN coi, OneTy, elt_ty, Nothing) } arithSeqEltType (Just fl) res_ty = do { ((elt_mult, elt_ty), fl') <- tcSyntaxOp ListOrigin fl [SynList] res_ty $@@ -1127,7 +848,7 @@ ; (list_co, elt_ty) <- matchExpectedListTy rho_ty -- list_co :: [elt_ty] ~N rho_ty ; result <- thing_inside [elt_ty]- ; return (result, mkWpCastN (mkTcSymCo list_co) <.> inst_wrap) }+ ; return (result, mkWpCastN (mkSymCo list_co) <.> inst_wrap) } tc_syn_arg _ (SynFun {}) _ = pprPanic "tcSynArgA hits a SynFun" (ppr orig) tc_syn_arg res_ty (SynType the_ty) thing_inside@@ -1163,34 +884,545 @@ {- ********************************************************************* * *- Record bindings+ Desugaring record update * * ********************************************************************* -} -getFixedTyVars :: [FieldLabelString] -> [TyVar] -> [ConLike] -> TyVarSet--- These tyvars must not change across the updates-getFixedTyVars upd_fld_occs univ_tvs cons- = mkVarSet [tv1 | con <- cons- , let (u_tvs, _, eqspec, prov_theta- , req_theta, arg_tys, _)- = conLikeFullSig con- theta = eqSpecPreds eqspec- ++ prov_theta- ++ req_theta- flds = conLikeFieldLabels con- fixed_tvs = exactTyCoVarsOfTypes (map scaledThing fixed_tys)- -- fixed_tys: See Note [Type of a record update]- `unionVarSet` tyCoVarsOfTypes theta- -- Universally-quantified tyvars that- -- appear in any of the *implicit*- -- arguments to the constructor are fixed- -- See Note [Implicit type sharing]+{-+Note [Type of a record update]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+The main complication with RecordUpd is that we need to explicitly+handle the *non-updated* fields. Consider: - fixed_tys = [ty | (fl, ty) <- zip flds arg_tys- , not (flLabel fl `elem` upd_fld_occs)]- , (tv1,tv) <- univ_tvs `zip` u_tvs- , tv `elemVarSet` fixed_tvs ]+ data T a b c = MkT1 { fa :: a, fb :: (b,c) }+ | MkT2 { fa :: a, fb :: (b,c), fc :: c -> c }+ | MkT3 { fd :: a } + upd :: T a b c -> (b',c) -> T a b' c+ upd t x = t { fb = x}++The result type should be (T a b' c)+not (T a b c), because 'b' *is not* mentioned in a non-updated field+not (T a b' c'), because 'c' *is* mentioned in a non-updated field+NB that it's not good enough to look at just one constructor; we must+look at them all; cf #3219++After all, upd should be equivalent to:+ upd t x = case t of+ MkT1 p q -> MkT1 p x+ MkT2 a b -> MkT2 p b+ MkT3 d -> error ...++So we need to give a completely fresh type to the result record,+and then constrain it by the fields that are *not* updated ("p" above).+We call these the "fixed" type variables, and compute them in getFixedTyVars.++Note that because MkT3 doesn't contain all the fields being updated,+its RHS is simply an error, so it doesn't impose any type constraints.+Hence the use of 'relevant_cont'.++Note [Implicit type sharing]+~~~~~~~~~~~~~~~~~~~~~~~~~~~+We also take into account any "implicit" non-update fields. For example+ data T a b where { MkT { f::a } :: T a a; ... }+So the "real" type of MkT is: forall ab. (a~b) => a -> T a b++Then consider+ upd t x = t { f=x }+We infer the type+ upd :: T a b -> a -> T a b+ upd (t::T a b) (x::a)+ = case t of { MkT (co:a~b) (_:a) -> MkT co x }+We can't give it the more general type+ upd :: T a b -> c -> T c b++Note [Criteria for update]+~~~~~~~~~~~~~~~~~~~~~~~~~~+We want to allow update for existentials etc, provided the updated+field isn't part of the existential. For example, this should be ok.+ data T a where { MkT { f1::a, f2::b->b } :: T a }+ f :: T a -> b -> T b+ f t b = t { f1=b }++The criterion we use is this:++ The types of the updated fields+ mention only the universally-quantified type variables+ of the data constructor++NB: this is not (quite) the same as being a "naughty" record selector+(See Note [Naughty record selectors]) in GHC.Tc.TyCl), at least+in the case of GADTs. Consider+ data T a where { MkT :: { f :: a } :: T [a] }+Then f is not "naughty" because it has a well-typed record selector.+But we don't allow updates for 'f'. (One could consider trying to+allow this, but it makes my head hurt. Badly. And no one has asked+for it.)++In principle one could go further, and allow+ g :: T a -> T a+ g t = t { f2 = \x -> x }+because the expression is polymorphic...but that seems a bridge too far.++Note [Data family example]+~~~~~~~~~~~~~~~~~~~~~~~~~~+ data instance T (a,b) = MkT { x::a, y::b }+ --->+ data :TP a b = MkT { a::a, y::b }+ coTP a b :: T (a,b) ~ :TP a b++Suppose r :: T (t1,t2), e :: t3+Then r { x=e } :: T (t3,t1)+ --->+ case r |> co1 of+ MkT x y -> MkT e y |> co2+ where co1 :: T (t1,t2) ~ :TP t1 t2+ co2 :: :TP t3 t2 ~ T (t3,t2)+The wrapping with co2 is done by the constructor wrapper for MkT++Outgoing invariants+~~~~~~~~~~~~~~~~~~~+In the outgoing (HsRecordUpd scrut binds cons in_inst_tys out_inst_tys):++ * cons are the data constructors to be updated++ * in_inst_tys, out_inst_tys have same length, and instantiate the+ *representation* tycon of the data cons. In Note [Data+ family example], in_inst_tys = [t1,t2], out_inst_tys = [t3,t2]++Note [Mixed Record Field Updates]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider the following pattern synonym.++ data MyRec = MyRec { foo :: Int, qux :: String }++ pattern HisRec{f1, f2} = MyRec{foo = f1, qux=f2}++This allows updates such as the following++ updater :: MyRec -> MyRec+ updater a = a {f1 = 1 }++It would also make sense to allow the following update (which we reject).++ updater a = a {f1 = 1, qux = "two" } ==? MyRec 1 "two"++This leads to confusing behaviour when the selectors in fact refer the same+field.++ updater a = a {f1 = 1, foo = 2} ==? ???++For this reason, we reject a mixture of pattern synonym and normal record+selectors in the same update block. Although of course we still allow the+following.++ updater a = (a {f1 = 1}) {foo = 2}++ > updater (MyRec 0 "str")+ MyRec 2 "str"++Note [Record Updates]+~~~~~~~~~~~~~~~~~~~~~+To typecheck a record update, we desugar it first. Suppose we have+ data T p q = T1 { x :: Int, y :: Bool, z :: Char }+ | T2 { v :: Char }+ | T3 { x :: Int }+ | T4 { p :: Float, y :: Bool, x :: Int }+ | T5+Then the record update `e { x=e1, y=e2 }` desugars as follows++ e { x=e1, y=e2 }+ ===>+ let { x' = e1; y' = e2 } in+ case e of+ T1 _ _ z -> T1 x' y' z+ T4 p _ _ -> T4 p y' x'+T2, T3 and T5 should not occur, so we omit them from the match.+The critical part of desugaring is to identify T and then T1/T4.++Wrinkle [Disambiguating fields]+As outlined above, to typecheck a record update via desugaring, we first need+to identify the parent record `TyCon` (`T` above). This can be tricky when several+record types share the same field (with `-XDuplicateRecordFields`).++Currently, we use the inferred type of the record to help disambiguate the record+fields. For example, in++ ( mempty :: T a b ) { x = 3 }++the type signature on `mempty` allows us to disambiguate the record `TyCon` to `T`,+when there might be other datatypes with field `x :: Int`.+This complexity is scheduled for removal via the implementation of GHC proposal #366+https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0366-no-ambiguous-field-access.rst++However, for the time being, we still need to disambiguate record fields using the+inferred types. This means that, when typechecking a record update via desugaring,+we need to do the following:++ D1. Perform a first typechecking pass on the record expression (`e` in the example above),+ to infer the type of the record being updated.+ D2. Desugar the record update as described above, using an HsExpansion.+ D3. Typecheck the desugared code.++In (D1), we call inferRho to infer the type of the record being updated. This returns the+inferred type of the record, together with a typechecked expression (of type HsExpr GhcTc)+and a collection of residual constraints.+We have no need for the latter two, because we will typecheck again in (D3). So, for+the time being (and until GHC proposal #366 is implemented), we simply drop them.++Wrinkle [Using IdSig]+As noted above, we want to let-bind the updated fields to avoid code duplication:++ let { x' = e1; y' = e2 } in+ case e of+ T1 _ _ z -> T1 x' y' z+ T4 p _ _ -> T4 p y' x'++However, doing so in a naive way would cause difficulties for type inference.+For example:++ data R b = MkR { f :: (forall a. a -> a) -> (Int,b), c :: Int }+ foo r = r { f = \ k -> (k 3, k 'x') }++If we desugar to:++ ds_foo r =+ let f' = \ k -> (k 3, k 'x')+ in case r of+ MkR _ b -> MkR f' b++then we are unable to infer an appropriately polymorphic type for f', because we+never infer higher-rank types. To circumvent this problem, we proceed as follows:++ 1. Obtain general field types by instantiating any of the constructors+ that contain all the necessary fields. (Note that the field type must be+ identical across different constructors of a given data constructor).+ 2. Let-bind an 'IdSig' with this type. This amounts to giving the let-bound+ 'Id's a partial type signature.++In the above example, it's as if we wrote:++ ds_foo r =+ let f' :: (forall a. a -> a) -> (Int, _b)+ f' = \ k -> (k 3, k 'x')+ in case r of+ MkR _ b -> MkR f' b++This allows us to compute the right type for f', and thus accept this record update.++Note [Unifying result types in tcRecordUpd]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+After desugaring and typechecking a record update in the way described in+Note [Record Updates], we must take care to unify the result types.++Example:++ type family F (a :: Type) :: Type where {}+ data D a = MkD { fld :: F a }++ f :: F Int -> D Bool -> D Int+ f i r = r { fld = i }++This record update desugars to:++ let x :: F alpha -- metavariable+ x = i+ in case r of+ MkD _ -> MkD x++Because the type family F is not injective, our only hope for unifying the+metavariable alpha is through the result type of the record update, which tells+us that we should unify alpha := Int.++Test case: T10808.++Wrinkle [GADT result type in tcRecordUpd]++ When dealing with a GADT, we want to be careful about which result type we use.++ Example:++ data G a b where+ MkG :: { bar :: F a } -> G a Int++ g :: F Int -> G Float b -> G Int b+ g i r = r { bar = i }++ We **do not** want to use the result type from the constructor MkG, which would+ leave us with a result type "G alpha Int". Instead, we should use the result type+ from the GADT header, instantiating as above, to get "G alpha beta" which will get+ unified withy "G Int b".++ Test cases: T18809, HardRecordUpdate.++-}++-- | Desugars a record update @record_expr { fld1 = e1, fld2 = e2}@ into a case expression+-- that matches on the constructors of the record @r@, as described in+-- Note [Record Updates].+--+-- Returns a renamed but not-yet-typechecked expression, together with the+-- result type of this desugared record update.+desugarRecordUpd :: LHsExpr GhcRn+ -- ^ @record_expr@: expression to which the record update is applied+ -> [LHsRecUpdField GhcRn]+ -- ^ the record update fields+ -> ExpRhoType+ -- ^ the expected result type of the record update+ -> TcM ( HsExpr GhcRn+ -- desugared record update expression+ , TcType+ -- result type of desugared record update+ , SDoc+ -- error context to push when typechecking+ -- the desugared code+ )+desugarRecordUpd record_expr rbnds res_ty+ = do { -- STEP -2: typecheck the record_expr, the record to be updated+ -- Until GHC proposal #366 is implemented, we still use the type of+ -- the record to disambiguate its fields, so we must infer the record+ -- type here before we can desugar. See Wrinkle [Disambiguating fields]+ -- in Note [Record Updates].+ ; ((_, record_rho), _lie) <- captureConstraints $ -- see (1) below+ tcScalingUsage ManyTy $ -- see (2) below+ tcInferRho record_expr++ -- (1)+ -- Note that we capture, and then discard, the constraints.+ -- This `tcInferRho` is used *only* to identify the data type,+ -- so we can deal with field disambiguation.+ -- Then we are going to generate a desugared record update, including `record_expr`,+ -- and typecheck it from scratch. We don't want to generate the constraints twice!++ -- (2)+ -- Record update drops some of the content of the record (namely the+ -- content of the field being updated). As a consequence, unless the+ -- field being updated is unrestricted in the record, we need an+ -- unrestricted record. Currently, we simply always require an+ -- unrestricted record.+ --+ -- Consider the following example:+ --+ -- data R a = R { self :: a }+ -- bad :: a ⊸ ()+ -- bad x = let r = R x in case r { self = () } of { R x' -> x' }+ --+ -- This should definitely *not* typecheck.++ -- STEP -1 See Note [Disambiguating record fields] in GHC.Tc.Gen.Head+ -- After this we know that rbinds is unambiguous+ ; rbinds <- disambiguateRecordBinds record_expr record_rho rbnds res_ty+ ; let upd_flds = map (unLoc . hfbLHS . unLoc) rbinds+ upd_fld_occs = map (FieldLabelString . occNameFS . rdrNameOcc . rdrNameAmbiguousFieldOcc) upd_flds+ sel_ids = map selectorAmbiguousFieldOcc upd_flds+ upd_fld_names = map idName sel_ids++ -- STEP 0+ -- Check that the field names are really field names+ -- and they are all field names for proper records or+ -- all field names for pattern synonyms.+ ; let bad_guys = [ setSrcSpan loc $ addErrTc (notSelector fld_name)+ | fld <- rbinds,+ -- Excludes class ops+ let L loc sel_id = hsRecUpdFieldId (unLoc fld),+ not (isRecordSelector sel_id),+ let fld_name = idName sel_id ]+ ; unless (null bad_guys) (sequence bad_guys >> failM)+ -- See Note [Mixed Record Field Updates]+ ; let (data_sels, pat_syn_sels) =+ partition isDataConRecordSelector sel_ids+ ; massert (all isPatSynRecordSelector pat_syn_sels)+ ; checkTc ( null data_sels || null pat_syn_sels )+ ( mixedSelectors data_sels pat_syn_sels )++ -- STEP 1+ -- Figure out the tycon and data cons from the first field name+ ; let -- It's OK to use the non-tc splitters here (for a selector)+ sel_id : _ = sel_ids+ con_likes :: [ConLike]+ con_likes = case idDetails sel_id of+ RecSelId (RecSelData tc) _+ -> map RealDataCon (tyConDataCons tc)+ RecSelId (RecSelPatSyn ps) _+ -> [PatSynCon ps]+ _ -> panic "tcRecordUpd"+ -- NB: for a data type family, the tycon is the instance tycon+ relevant_cons = conLikesWithFields con_likes upd_fld_occs+ -- A constructor is only relevant to this process if+ -- it contains *all* the fields that are being updated+ -- Other ones will cause a runtime error if they occur++ -- STEP 2+ -- Check that at least one constructor has all the named fields+ -- i.e. has an empty set of bad fields returned by badFields+ ; case relevant_cons of+ { [] -> failWithTc (badFieldsUpd rbinds con_likes)+ ; relevant_con : _ ->++ -- STEP 3+ -- Create new variables for the fields we are updating,+ -- so that we can share them across constructors.+ --+ -- Example:+ --+ -- e { x=e1, y=e2 }+ --+ -- We want to let-bind variables to `e1` and `e2`:+ --+ -- let x' :: Int+ -- x' = e1+ -- y' :: Bool+ -- y' = e2+ -- in ...++ do { -- Instantiate the type variables of any relevant constuctor+ -- with metavariables to obtain a type for each 'Id'.+ -- This will allow us to have 'Id's with polymorphic types+ -- by using 'IdSig'. See Wrinkle [Using IdSig] in Note [Record Updates].+ ; let (univ_tvs, ex_tvs, eq_spec, _, _, arg_tys, con_res_ty) = conLikeFullSig relevant_con+ ; (subst, tc_tvs) <- newMetaTyVars (univ_tvs ++ ex_tvs)+ ; let (actual_univ_tys, _actual_ex_tys) = splitAtList univ_tvs $ map mkTyVarTy tc_tvs++ -- See Wrinkle [GADT result type in tcRecordUpd]+ -- for an explanation of the following.+ ds_res_ty = case relevant_con of+ RealDataCon con+ | not (null eq_spec) -- We only need to do this if we have actual GADT equalities.+ -> mkFamilyTyConApp (dataConTyCon con) actual_univ_tys+ _ -> substTy subst con_res_ty++ -- Gather pairs of let-bound Ids and their right-hand sides,+ -- e.g. (x', e1), (y', e2), ...+ ; let mk_upd_id :: Name -> LHsFieldBind GhcTc fld (LHsExpr GhcRn) -> TcM (Name, (TcId, LHsExpr GhcRn))+ mk_upd_id fld_nm (L _ rbind)+ = do { let Scaled m arg_ty = lookupNameEnv_NF arg_ty_env fld_nm+ nm_occ = rdrNameOcc . nameRdrName $ fld_nm+ actual_arg_ty = substTy subst arg_ty+ rhs = hfbRHS rbind+ ; (_co, actual_arg_ty) <- hasFixedRuntimeRep (FRRRecordUpdate fld_nm (unLoc rhs)) actual_arg_ty+ -- We get a better error message by doing a (redundant) representation-polymorphism check here,+ -- rather than delaying until the typechecker typechecks the let-bindings,+ -- because the let-bound Ids have internal names.+ -- (As we will typecheck the let-bindings later, we can drop this coercion here.)+ -- See RepPolyRecordUpdate test.+ ; nm <- newNameAt nm_occ generatedSrcSpan+ ; let id = mkLocalId nm m actual_arg_ty+ -- NB: create fresh names to avoid any accidental shadowing+ -- occurring in the RHS expressions when creating the let bindings:+ --+ -- let x1 = e1; x2 = e2; ...+ ; return (fld_nm, (id, rhs))+ }+ arg_ty_env = mkNameEnv+ $ zipWith (\ lbl arg_ty -> (flSelector lbl, arg_ty))+ (conLikeFieldLabels relevant_con)+ arg_tys++ ; upd_ids <- zipWithM mk_upd_id upd_fld_names rbinds+ ; let updEnv :: UniqMap Name (Id, LHsExpr GhcRn)+ updEnv = listToUniqMap $ upd_ids++ make_pat :: ConLike -> LMatch GhcRn (LHsExpr GhcRn)+ -- As explained in Note [Record Updates], to desugar+ --+ -- e { x=e1, y=e2 }+ --+ -- we generate a case statement, with an equation for+ -- each constructor of the record. For example, for+ -- the constructor+ --+ -- T1 :: { x :: Int, y :: Bool, z :: Char } -> T p q+ --+ -- we let-bind x' = e1, y' = e2 and generate the equation:+ --+ -- T1 _ _ z -> T1 x' y' z+ make_pat conLike = mkSimpleMatch CaseAlt [pat] rhs+ where+ (lhs_con_pats, rhs_con_args)+ = zipWithAndUnzip mk_con_arg [1..] con_fields+ pat = genSimpleConPat con lhs_con_pats+ rhs = wrapGenSpan $ genHsApps con rhs_con_args+ con = conLikeName conLike+ con_fields = conLikeFieldLabels conLike++ mk_con_arg :: Int+ -> FieldLabel+ -> ( LPat GhcRn+ -- LHS constructor pattern argument+ , LHsExpr GhcRn )+ -- RHS constructor argument+ mk_con_arg i fld_lbl =+ -- The following generates the pattern matches of the desugared `case` expression.+ -- For fields being updated (for example `x`, `y` in T1 and T4 in Note [Record Updates]),+ -- wildcards are used to avoid creating unused variables.+ case lookupUniqMap updEnv $ flSelector fld_lbl of+ -- Field is being updated: LHS = wildcard pattern, RHS = appropriate let-bound Id.+ Just (upd_id, _) -> (genWildPat, genLHsVar (idName upd_id))+ -- Field is not being updated: LHS = variable pattern, RHS = that same variable.+ _ -> let fld_nm = mkInternalName (mkBuiltinUnique i)+ (mkVarOccFS (field_label $ flLabel fld_lbl))+ generatedSrcSpan+ in (genVarPat fld_nm, genLHsVar fld_nm)++ -- STEP 4+ -- Desugar to HsCase, as per note [Record Updates]+ ; let ds_expr :: HsExpr GhcRn+ ds_expr = HsLet noExtField noHsTok let_binds noHsTok (L gen case_expr)++ case_expr :: HsExpr GhcRn+ case_expr = HsCase noExtField record_expr (mkMatchGroup Generated (wrapGenSpan matches))+ matches :: [LMatch GhcRn (LHsExpr GhcRn)]+ matches = map make_pat relevant_cons++ let_binds :: HsLocalBindsLR GhcRn GhcRn+ let_binds = HsValBinds noAnn $ XValBindsLR+ $ NValBinds upd_ids_lhs (map mk_idSig upd_ids)+ upd_ids_lhs :: [(RecFlag, LHsBindsLR GhcRn GhcRn)]+ upd_ids_lhs = [ (NonRecursive, unitBag $ genSimpleFunBind (idName id) [] rhs)+ | (_, (id, rhs)) <- upd_ids ]+ mk_idSig :: (Name, (Id, LHsExpr GhcRn)) -> LSig GhcRn+ mk_idSig (_, (id, _)) = L gen $ XSig $ IdSig id+ -- We let-bind variables using 'IdSig' in order to accept+ -- record updates involving higher-rank types.+ -- See Wrinkle [Using IdSig] in Note [Record Updates].+ gen = noAnnSrcSpan generatedSrcSpan++ ; traceTc "desugarRecordUpd" $+ vcat [ text "relevant_con:" <+> ppr relevant_con+ , text "res_ty:" <+> ppr res_ty+ , text "ds_res_ty:" <+> ppr ds_res_ty+ ]++ ; let cons = pprQuotedList relevant_cons+ err_lines =+ (text "In a record update at field" <> plural upd_fld_names <+> pprQuotedList upd_fld_names :)+ $ case relevant_con of+ RealDataCon con ->+ [ text "with type constructor" <+> quotes (ppr (dataConTyCon con))+ , text "data constructor" <+> plural relevant_cons <+> cons ]+ PatSynCon {} ->+ [ text "with pattern synonym" <+> plural relevant_cons <+> cons ]+ ++ if null ex_tvs+ then []+ else [ text "existential variable" <> plural ex_tvs <+> pprQuotedList ex_tvs ]+ err_ctxt = make_lines_msg err_lines++ ; return (ds_expr, ds_res_ty, err_ctxt) } } }++-- | Pretty-print a collection of lines, adding commas at the end of each line,+-- and adding "and" to the start of the last line.+make_lines_msg :: [SDoc] -> SDoc+make_lines_msg [] = empty+make_lines_msg [last] = ppr last <> dot+make_lines_msg [l1,l2] = l1 $$ text "and" <+> l2 <> dot+make_lines_msg (l:ls) = l <> comma $$ make_lines_msg ls++{- *********************************************************************+* *+ Record bindings+* *+********************************************************************* -}+ -- Disambiguate the fields in a record update. -- See Note [Disambiguating record fields] in GHC.Tc.Gen.Head disambiguateRecordBinds :: LHsExpr GhcRn -> TcRhoType@@ -1350,35 +1582,7 @@ , hfbRHS = rhs' , hfbPun = hfbPun fld}))) } -tcRecordUpd- :: ConLike- -> [TcType] -- Expected type for each field- -> [LHsFieldBind GhcTc (LAmbiguousFieldOcc GhcTc) (LHsExpr GhcRn)]- -> TcM [LHsRecUpdField GhcTc] -tcRecordUpd con_like arg_tys rbinds = fmap catMaybes $ mapM do_bind rbinds- where- fields = map flSelector $ conLikeFieldLabels con_like- flds_w_tys = zipEqual "tcRecordUpd" fields arg_tys-- do_bind :: LHsFieldBind GhcTc (LAmbiguousFieldOcc GhcTc) (LHsExpr GhcRn)- -> TcM (Maybe (LHsRecUpdField GhcTc))- do_bind (L l fld@(HsFieldBind { hfbLHS = L loc af- , hfbRHS = rhs }))- = do { let lbl = rdrNameAmbiguousFieldOcc af- sel_id = selectorAmbiguousFieldOcc af- f = L loc (FieldOcc (idName sel_id) (L (l2l loc) lbl))- ; mb <- tcRecordField con_like flds_w_tys f rhs- ; case mb of- Nothing -> return Nothing- Just (f', rhs') ->- return (Just- (L l (fld { hfbLHS- = L loc (Unambiguous- (foExt (unLoc f'))- (L (l2l loc) lbl))- , hfbRHS = rhs' }))) }- tcRecordField :: ConLike -> Assoc Name Type -> LFieldOcc GhcRn -> LHsExpr GhcRn -> TcM (Maybe (LFieldOcc GhcTc, LHsExpr GhcTc))@@ -1386,11 +1590,11 @@ | Just field_ty <- assocMaybe flds_w_tys sel_name = addErrCtxt (fieldCtxt field_lbl) $ do { rhs' <- tcCheckPolyExprNC rhs field_ty- ; hasFixedRuntimeRep_syntactic (FRRRecordUpdate (unLoc lbl) (unLoc rhs'))+ ; hasFixedRuntimeRep_syntactic (FRRRecordCon (unLoc lbl) (unLoc rhs')) field_ty ; let field_id = mkUserLocal (nameOccName sel_name) (nameUnique sel_name)- Many field_ty (locA loc)+ ManyTy field_ty (locA loc) -- Yuk: the field_id has the *unique* of the selector Id -- (so we can find it easily) -- but is a LocalId with the appropriate type of the RHS@@ -1400,7 +1604,7 @@ = do { addErrTc (badFieldConErr (getName con_like) field_lbl) ; return Nothing } where- field_lbl = occNameFS $ rdrNameOcc (unLoc lbl)+ field_lbl = FieldLabelString $ occNameFS $ rdrNameOcc (unLoc lbl) checkMissingFields :: ConLike -> HsRecordBinds GhcRn -> [Scaled TcType] -> TcM ()@@ -1497,7 +1701,7 @@ in -- Fields that don't change the membership status of the set -- are redundant and can be dropped.- map (fst . head) $ groupBy ((==) `on` snd) growingSets+ map (fst . NE.head) $ NE.groupWith snd growingSets aMember = assert (not (null members) ) fst (head members) (members, nonMembers) = partition (or . snd) membership@@ -1506,7 +1710,7 @@ membership :: [(FieldLabelString, [Bool])] membership = sortMembership $ map (\fld -> (fld, map (fld `elementOfUniqSet`) fieldLabelSets)) $- map (occNameFS . rdrNameOcc . rdrNameAmbiguousFieldOcc . unLoc . hfbLHS . unLoc) rbinds+ map (FieldLabelString . occNameFS . rdrNameOcc . rdrNameAmbiguousFieldOcc . unLoc . hfbLHS . unLoc) rbinds fieldLabelSets :: [UniqSet FieldLabelString] fieldLabelSets = map (mkUniqSet . map flLabel . conLikeFieldLabels) data_cons
compiler/GHC/Tc/Gen/Foreign.hs view
@@ -44,29 +44,35 @@ import GHC.Tc.Gen.HsType import GHC.Tc.Gen.Expr import GHC.Tc.Utils.Env-+import GHC.Tc.Utils.TcType import GHC.Tc.Instance.Family+ import GHC.Core.FamInstEnv import GHC.Core.Coercion import GHC.Core.Reduction import GHC.Core.Type import GHC.Core.Multiplicity+import GHC.Core.DataCon+import GHC.Core.TyCon+import GHC.Core.TyCon.RecWalk+ import GHC.Types.ForeignCall-import GHC.Utils.Error import GHC.Types.Id import GHC.Types.Name import GHC.Types.Name.Reader-import GHC.Core.DataCon-import GHC.Core.TyCon-import GHC.Core.TyCon.RecWalk-import GHC.Tc.Utils.TcType+import GHC.Types.SrcLoc+ import GHC.Builtin.Names+import GHC.Builtin.Types.Prim( isArrowTyCon )+ import GHC.Driver.Session import GHC.Driver.Backend++import GHC.Utils.Error import GHC.Utils.Outputable as Outputable import GHC.Utils.Panic import GHC.Platform-import GHC.Types.SrcLoc+ import GHC.Data.Bag import GHC.Driver.Hooks import qualified GHC.LanguageExtensions as LangExt@@ -96,7 +102,7 @@ newtype Age = MkAge Int we want to see that Age -> IO () is the same as Int -> IO (). But, we don't-need to recur on any type parameters, because no paramaterized types (with+need to recur on any type parameters, because no parameterized types (with interesting parameters) are marshalable! The full list of marshalable types is in the body of boxedMarshalableTyCon in GHC.Tc.Utils.TcType. The only members of that list not at kind * are Ptr, FunPtr, and StablePtr, all of which get marshaled@@ -122,13 +128,13 @@ where go :: Role -> RecTcChecker -> Type -> WriterT (Bag GlobalRdrElt) TcM Reduction go role rec_nts ty- | Just ty' <- tcView ty -- Expand synonyms+ | Just ty' <- coreView ty -- Expand synonyms = go role rec_nts ty' | Just (tc, tys) <- splitTyConApp_maybe ty = go_tc_app role rec_nts tc tys - | (bndrs, inner_ty) <- splitForAllTyCoVarBinders ty+ | (bndrs, inner_ty) <- splitForAllForAllTyBinders ty , not (null bndrs) = do redn <- go role rec_nts inner_ty return $ mkHomoForAllRedn bndrs redn@@ -139,14 +145,18 @@ go_tc_app :: Role -> RecTcChecker -> TyCon -> [Type] -> WriterT (Bag GlobalRdrElt) TcM Reduction go_tc_app role rec_nts tc tys+ | isArrowTyCon tc -- Recurse through arrows, or at least the top+ = children_only -- level arrows. Remember, the default case is+ -- "don't recurse" (see last eqn for go_tc_app)++ | tc_key `elem` [ioTyConKey, funPtrTyConKey] -- We don't want to look through the IO newtype, even if it is -- in scope, so we have a special case for it:- | tc_key `elem` [ioTyConKey, funPtrTyConKey, funTyConKey] = children_only | isNewTyCon tc -- Expand newtypes , Just rec_nts' <- checkRecTc rec_nts tc- -- See Note [Expanding newtypes] in GHC.Core.TyCon+ -- See Note [Expanding newtypes and products] in GHC.Core.TyCon.RecWalk -- We can't just use isRecursiveTyCon; sometimes recursion is ok: -- newtype T = T (Ptr T) -- Here, we don't reject the type for being recursive.@@ -173,7 +183,7 @@ children_only = do { args <- unzipRedns <$> zipWithM ( \ ty r -> go r rec_nts ty )- tys (tyConRolesX role tc)+ tys (tyConRoleListX role tc) ; return $ mkTyConAppRedn role tc args } nt_co = mkUnbranchedAxInstCo role (newTyConCo tc) tys [] nt_rhs = newTyConInstRhs tc tys@@ -198,7 +208,7 @@ want to report it as "defined but not used" or "imported but not used". eg newtype D = MkD Int foreign import foo :: D -> IO ()-Here 'MkD' us used. See #7408.+Here 'MkD' is used. See #7408. GHC also expands type functions during this process, so it's not enough just to look at the free variables of the declaration.@@ -244,10 +254,17 @@ do { sig_ty <- tcHsSigType (ForSigCtxt nm) hs_ty ; (Reduction norm_co norm_sig_ty, gres) <- normaliseFfiType sig_ty ; let- -- Drop the foralls before inspecting the- -- structure of the foreign type.- (arg_tys, res_ty) = tcSplitFunTys (dropForAlls norm_sig_ty)- id = mkLocalId nm Many sig_ty+ -- Drop the foralls before inspecting the+ -- structure of the foreign type.+ -- Use splitFunTys, which splits (=>) as well as (->)+ -- so that for foreign import foo :: Eq a => a -> blah+ -- we get "unacceptable argument Eq a" rather than+ -- "unacceptable result Eq a => a -> blah"+ -- Not a big deal. We could make a better error message specially+ -- for overloaded functions, but doesn't seem worth it+ (arg_tys, res_ty) = splitFunTys (dropForAlls norm_sig_ty)++ id = mkLocalId nm ManyTy sig_ty -- Use a LocalId to obey the invariant that locally-defined -- things are LocalIds. However, it does not need zonking, -- (so GHC.Tc.Utils.Zonk.zonkForeignExports ignores it).@@ -264,24 +281,24 @@ -- ------------ Checking types for foreign import ---------------------- -tcCheckFIType :: [Scaled Type] -> Type -> ForeignImport -> TcM ForeignImport+tcCheckFIType :: [Scaled Type] -> Type -> ForeignImport GhcRn -> TcM (ForeignImport GhcTc) -tcCheckFIType arg_tys res_ty idecl@(CImport (L lc cconv) safety mh l@(CLabel _) src)+tcCheckFIType arg_tys res_ty idecl@(CImport src (L lc cconv) safety mh l@(CLabel _)) -- Foreign import label- = do checkCg (Right idecl) checkCOrAsmOrLlvmOrInterp+ = do checkCg (Right idecl) backendValidityOfCImport -- NB check res_ty not sig_ty! -- In case sig_ty is (forall a. ForeignPtr a)- check (isFFILabelTy (mkVisFunTys arg_tys res_ty))+ check (isFFILabelTy (mkScaledFunTys arg_tys res_ty)) (TcRnIllegalForeignType Nothing) cconv' <- checkCConv (Right idecl) cconv- return (CImport (L lc cconv') safety mh l src)+ return (CImport src (L lc cconv') safety mh l) -tcCheckFIType arg_tys res_ty idecl@(CImport (L lc cconv) safety mh CWrapper src) = do+tcCheckFIType arg_tys res_ty idecl@(CImport src (L lc cconv) safety mh CWrapper) = do -- Foreign wrapper (former f.e.d.) -- The type must be of the form ft -> IO (FunPtr ft), where ft is a valid -- foreign type. For legacy reasons ft -> IO (Ptr ft) is accepted, too. -- The use of the latter form is DEPRECATED, though.- checkCg (Right idecl) checkCOrAsmOrLlvmOrInterp+ checkCg (Right idecl) backendValidityOfCImport cconv' <- checkCConv (Right idecl) cconv case arg_tys of [Scaled arg1_mult arg1_ty] -> do@@ -292,39 +309,44 @@ where (arg1_tys, res1_ty) = tcSplitFunTys arg1_ty _ -> addErrTc (TcRnIllegalForeignType Nothing OneArgExpected)- return (CImport (L lc cconv') safety mh CWrapper src)+ return (CImport src (L lc cconv') safety mh CWrapper) -tcCheckFIType arg_tys res_ty idecl@(CImport (L lc cconv) (L ls safety) mh- (CFunction target) src)+tcCheckFIType arg_tys res_ty idecl@(CImport src (L lc cconv) (L ls safety) mh+ (CFunction target)) | isDynamicTarget target = do -- Foreign import dynamic- checkCg (Right idecl) checkCOrAsmOrLlvmOrInterp+ checkCg (Right idecl) backendValidityOfCImport cconv' <- checkCConv (Right idecl) cconv case arg_tys of -- The first arg must be Ptr or FunPtr [] -> addErrTc (TcRnIllegalForeignType Nothing AtLeastOneArgExpected) (Scaled arg1_mult arg1_ty:arg_tys) -> do dflags <- getDynFlags- let curried_res_ty = mkVisFunTys arg_tys res_ty+ let curried_res_ty = mkScaledFunTys arg_tys res_ty checkNoLinearFFI arg1_mult check (isFFIDynTy curried_res_ty arg1_ty) (TcRnIllegalForeignType (Just Arg)) checkForeignArgs (isFFIArgumentTy dflags safety) arg_tys checkForeignRes nonIOok checkSafe (isFFIImportResultTy dflags) res_ty- return $ CImport (L lc cconv') (L ls safety) mh (CFunction target) src+ return $ CImport src (L lc cconv') (L ls safety) mh (CFunction target) | cconv == PrimCallConv = do dflags <- getDynFlags checkTc (xopt LangExt.GHCForeignImportPrim dflags) (TcRnForeignImportPrimExtNotSet idecl)- checkCg (Right idecl) checkCOrAsmOrLlvmOrInterp+ checkCg (Right idecl) backendValidityOfCImport checkCTarget idecl target checkTc (playSafe safety) (TcRnForeignImportPrimSafeAnn idecl) checkForeignArgs (isFFIPrimArgumentTy dflags) arg_tys -- prim import result is more liberal, allows (#,,#) checkForeignRes nonIOok checkSafe (isFFIPrimResultTy dflags) res_ty- return idecl+ return (CImport src (L lc cconv) (L ls safety) mh (CFunction target))+ | cconv == JavaScriptCallConv = do+ cconv' <- checkCConv (Right idecl) cconv+ checkCg (Right idecl) backendValidityOfCImport+ -- leave the rest to the JS backend (at least for now)+ return (CImport src (L lc cconv') (L ls safety) mh (CFunction target)) | otherwise = do -- Normal foreign import- checkCg (Right idecl) checkCOrAsmOrLlvmOrInterp+ checkCg (Right idecl) backendValidityOfCImport cconv' <- checkCConv (Right idecl) cconv checkCTarget idecl target dflags <- getDynFlags@@ -336,18 +358,18 @@ | not (null arg_tys) -> addErrTc (TcRnForeignFunctionImportAsValue idecl) _ -> return ()- return $ CImport (L lc cconv') (L ls safety) mh (CFunction target) src+ return $ CImport src (L lc cconv') (L ls safety) mh (CFunction target) -- This makes a convenient place to check -- that the C identifier is valid for C-checkCTarget :: ForeignImport -> CCallTarget -> TcM ()+checkCTarget :: ForeignImport GhcRn -> CCallTarget -> TcM () checkCTarget idecl (StaticTarget _ str _ _) = do- checkCg (Right idecl) checkCOrAsmOrLlvmOrInterp+ checkCg (Right idecl) backendValidityOfCImport checkTc (isCLabelString str) (TcRnInvalidCIdentifier str) checkCTarget _ DynamicTarget = panic "checkCTarget DynamicTarget" -checkMissingAmpersand :: ForeignImport -> [Type] -> Type -> TcM ()+checkMissingAmpersand :: ForeignImport GhcRn -> [Type] -> Type -> TcM () checkMissingAmpersand idecl arg_tys res_ty | null arg_tys && isFunPtrTy res_ty = addDiagnosticTc $ TcRnFunPtrImportWithoutAmpersand idecl@@ -413,14 +435,14 @@ -- ------------ Checking argument types for foreign export ---------------------- -tcCheckFEType :: Type -> ForeignExport -> TcM ForeignExport-tcCheckFEType sig_ty edecl@(CExport (L l (CExportStatic esrc str cconv)) src) = do- checkCg (Left edecl) checkCOrAsmOrLlvm+tcCheckFEType :: Type -> ForeignExport GhcRn -> TcM (ForeignExport GhcTc)+tcCheckFEType sig_ty edecl@(CExport src (L l (CExportStatic esrc str cconv))) = do+ checkCg (Left edecl) backendValidityOfCExport checkTc (isCLabelString str) (TcRnInvalidCIdentifier str) cconv' <- checkCConv (Left edecl) cconv checkForeignArgs isFFIExternalTy arg_tys checkForeignRes nonIOok noCheckSafe isFFIExportResultTy res_ty- return (CExport (L l (CExportStatic esrc str cconv')) src)+ return (CExport src (L l (CExportStatic esrc str cconv'))) where -- Drop the foralls before inspecting -- the structure of the foreign type.@@ -442,8 +464,8 @@ check (pred ty) (TcRnIllegalForeignType (Just Arg)) checkNoLinearFFI :: Mult -> TcM () -- No linear types in FFI (#18472)-checkNoLinearFFI Many = return ()-checkNoLinearFFI _ = addErrTc $ TcRnIllegalForeignType (Just Arg)+checkNoLinearFFI ManyTy = return ()+checkNoLinearFFI _ = addErrTc $ TcRnIllegalForeignType (Just Arg) LinearTypesNotAllowed ------------ Checking result types for foreign calls ----------------------@@ -464,7 +486,7 @@ -- We disallow nested foralls in foreign types -- (at least, for the time being). See #16702.- | tcIsForAllTy ty+ | isForAllTy ty = addErrTc $ TcRnIllegalForeignType (Just Result) UnexpectedNestedForall -- Case for non-IO result type with FFI Import@@ -497,36 +519,20 @@ checkSafe = True noCheckSafe = False --- | Checking a supported backend is in use-checkCOrAsmOrLlvm :: Backend -> Validity' ExpectedBackends-checkCOrAsmOrLlvm ViaC = IsValid-checkCOrAsmOrLlvm NCG = IsValid-checkCOrAsmOrLlvm LLVM = IsValid-checkCOrAsmOrLlvm _ = NotValid COrAsmOrLlvm---- | Checking a supported backend is in use-checkCOrAsmOrLlvmOrInterp :: Backend -> Validity' ExpectedBackends-checkCOrAsmOrLlvmOrInterp ViaC = IsValid-checkCOrAsmOrLlvmOrInterp NCG = IsValid-checkCOrAsmOrLlvmOrInterp LLVM = IsValid-checkCOrAsmOrLlvmOrInterp Interpreter = IsValid-checkCOrAsmOrLlvmOrInterp _ = NotValid COrAsmOrLlvmOrInterp--checkCg :: Either ForeignExport ForeignImport -> (Backend -> Validity' ExpectedBackends) -> TcM ()+checkCg :: Either (ForeignExport GhcRn) (ForeignImport GhcRn)+ -> (Backend -> Validity' ExpectedBackends) -> TcM () checkCg decl check = do dflags <- getDynFlags let bcknd = backend dflags- case bcknd of- NoBackend -> return ()- _ ->- case check bcknd of- IsValid -> return ()- NotValid expectedBcknd ->- addErrTc $ TcRnIllegalForeignDeclBackend decl bcknd expectedBcknd+ case check bcknd of+ IsValid -> return ()+ NotValid expectedBcknds ->+ addErrTc $ TcRnIllegalForeignDeclBackend decl bcknd expectedBcknds -- Calling conventions -checkCConv :: Either ForeignExport ForeignImport -> CCallConv -> TcM CCallConv+checkCConv :: Either (ForeignExport GhcRn) (ForeignImport GhcRn)+ -> CCallConv -> TcM CCallConv checkCConv _ CCallConv = return CCallConv checkCConv _ CApiConv = return CApiConv checkCConv decl StdCallConv = do
compiler/GHC/Tc/Gen/Head.hs view
@@ -9,8 +9,6 @@ {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE DisambiguateRecordFields #-} -{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}- {- % (c) The University of Glasgow 2006@@ -31,23 +29,24 @@ , tyConOf, tyConOfET, lookupParents, fieldNotInType , notSelector, nonBidirectionalErr - , addExprCtxt, addFunResCtxt ) where+ , addHeadCtxt, addExprCtxt, addFunResCtxt ) where import {-# SOURCE #-} GHC.Tc.Gen.Expr( tcExpr, tcCheckMonoExprNC, tcCheckPolyExprNC ) +import GHC.Prelude+import GHC.Hs+ import GHC.Tc.Gen.HsType+import GHC.Rename.Unbound ( unknownNameSuggestions, WhatLooking(..) )+ import GHC.Tc.Gen.Bind( chooseInferredQuantifiers ) import GHC.Tc.Gen.Sig( tcUserTypeSig, tcInstSig, lhsSigWcTypeContextSpan ) import GHC.Tc.TyCl.PatSyn( patSynBuilderOcc ) import GHC.Tc.Utils.Monad import GHC.Tc.Utils.Unify-import GHC.Types.Basic-import GHC.Types.Error+import GHC.Tc.Utils.Concrete ( hasFixedRuntimeRep_syntactic ) import GHC.Tc.Utils.Instantiate import GHC.Tc.Instance.Family ( tcLookupDataFamInst )-import GHC.Core.FamInstEnv ( FamInstEnvs )-import GHC.Core.UsageEnv ( unitUE )-import GHC.Rename.Unbound ( unknownNameSuggestions, WhatLooking(..) ) import GHC.Unit.Module ( getModule ) import GHC.Tc.Errors.Types import GHC.Tc.Solver ( InferMode(..), simplifyInfer )@@ -55,37 +54,43 @@ import GHC.Tc.Utils.TcMType import GHC.Tc.Types.Origin import GHC.Tc.Utils.TcType as TcType-import GHC.Hs+import GHC.Tc.Types.Evidence import GHC.Hs.Syn.Type-import GHC.Types.Id-import GHC.Types.Id.Info++import GHC.Core.FamInstEnv ( FamInstEnvs )+import GHC.Core.UsageEnv ( unitUE ) import GHC.Core.PatSyn( PatSyn ) import GHC.Core.ConLike( ConLike(..) ) import GHC.Core.DataCon-import GHC.Types.Name-import GHC.Types.Name.Reader import GHC.Core.TyCon import GHC.Core.TyCo.Rep import GHC.Core.Type-import GHC.Tc.Types.Evidence++import GHC.Types.Var( isInvisibleFunArg )+import GHC.Types.Id+import GHC.Types.Id.Info+import GHC.Types.Name+import GHC.Types.Name.Reader+import GHC.Types.SrcLoc+import GHC.Types.Basic+import GHC.Types.Error+ import GHC.Builtin.Types( multiplicityTy ) import GHC.Builtin.Names import GHC.Builtin.Names.TH( liftStringName, liftName )+ import GHC.Driver.Env import GHC.Driver.Session-import GHC.Types.SrcLoc import GHC.Utils.Misc-import GHC.Data.Maybe import GHC.Utils.Outputable as Outputable import GHC.Utils.Panic import GHC.Utils.Panic.Plain++import GHC.Data.Maybe import Control.Monad -import Data.Function -import GHC.Prelude - {- ********************************************************************* * * HsExprArg: auxiliary data type@@ -171,6 +176,7 @@ , eva_arg_ty :: !(XEVAType p) } | ETypeArg { eva_ctxt :: AppCtxt+ , eva_at :: !(LHsToken "@" GhcRn) , eva_hs_ty :: LHsWcType GhcRn -- The type arg , eva_ty :: !(XETAType p) } -- Kind-checked type arg @@ -199,12 +205,40 @@ = VAExpansion (HsExpr GhcRn) -- Inside an expansion of this expression SrcSpan -- The SrcSpan of the expression- -- noSrcSpan if outermost+ -- noSrcSpan if outermost; see Note [AppCtxt] | VACall (HsExpr GhcRn) Int -- In the third argument of function f SrcSpan -- The SrcSpan of the application (f e1 e2 e3)+ -- noSrcSpan if outermost; see Note [AppCtxt] +{- Note [AppCtxt]+~~~~~~~~~~~~~~~~~+In a call (f e1 ... en), we pair up each argument with an AppCtxt. For+example, the AppCtxt for e3 allows us to say+ "In the third argument of `f`"+See splitHsApps.++To do this we must take a quick look into the expression to find the+function at the head (`f` in this case) and how many arguments it+has. That is what the funcion top_ctxt does.++If the function part is an expansion, we don't want to look further.+For example, with rebindable syntax the expression+ (if e1 then e2 else e3) e4 e5+might expand to+ (ifThenElse e1 e2 e3) e4 e5+For e4 we an AppCtxt that says "In the first argument of (if ...)",+not "In the fourth argument of ifThenElse". So top_ctxt stops+at expansions.++The SrcSpan in an AppCtxt describes the whole call. We initialise+it to noSrcSpan, because splitHsApps deals in HsExpr not LHsExpr, so+we don't have a span for the whole call; and we use that noSrcSpan in+GHC.Tc.Gen.App.tcInstFun (set_fun_ctxt) to avoid pushing "In the expression `f`"+a second time.+-}+ appCtxtLoc :: AppCtxt -> SrcSpan appCtxtLoc (VAExpansion _ l) = l appCtxtLoc (VACall _ _ l) = l@@ -234,9 +268,11 @@ mkEValArg ctxt e = EValArg { eva_arg = ValArg e, eva_ctxt = ctxt , eva_arg_ty = noExtField } -mkETypeArg :: AppCtxt -> LHsWcType GhcRn -> HsExprArg 'TcpRn-mkETypeArg ctxt hs_ty = ETypeArg { eva_ctxt = ctxt, eva_hs_ty = hs_ty- , eva_ty = noExtField }+mkETypeArg :: AppCtxt -> LHsToken "@" GhcRn -> LHsWcType GhcRn -> HsExprArg 'TcpRn+mkETypeArg ctxt at hs_ty =+ ETypeArg { eva_ctxt = ctxt+ , eva_at = at, eva_hs_ty = hs_ty+ , eva_ty = noExtField } addArgWrap :: HsWrapper -> [HsExprArg 'TcpInst] -> [HsExprArg 'TcpInst] addArgWrap wrap args@@ -249,9 +285,13 @@ -- See Note [splitHsApps] splitHsApps e = go e (top_ctxt 0 e) [] where+ top_ctxt :: Int -> HsExpr GhcRn -> AppCtxt+ -- Always returns VACall fun n_val_args noSrcSpan+ -- to initialise the argument splitting in 'go'+ -- See Note [AppCtxt] top_ctxt n (HsPar _ _ fun _) = top_lctxt n fun top_ctxt n (HsPragE _ _ fun) = top_lctxt n fun- top_ctxt n (HsAppType _ fun _) = top_lctxt (n+1) fun+ top_ctxt n (HsAppType _ fun _ _) = top_lctxt (n+1) fun top_ctxt n (HsApp _ fun _) = top_lctxt (n+1) fun top_ctxt n (XExpr (HsExpanded orig _)) = VACall orig n noSrcSpan top_ctxt n other_fun = VACall other_fun n noSrcSpan@@ -260,14 +300,16 @@ go :: HsExpr GhcRn -> AppCtxt -> [HsExprArg 'TcpRn] -> ((HsExpr GhcRn, AppCtxt), [HsExprArg 'TcpRn])- go (HsPar _ _ (L l fun) _) ctxt args = go fun (set l ctxt) (EWrap (EPar ctxt) : args)- go (HsPragE _ p (L l fun)) ctxt args = go fun (set l ctxt) (EPrag ctxt p : args)- go (HsAppType _ (L l fun) ty) ctxt args = go fun (dec l ctxt) (mkETypeArg ctxt ty : args)- go (HsApp _ (L l fun) arg) ctxt args = go fun (dec l ctxt) (mkEValArg ctxt arg : args)+ -- Modify the AppCtxt as we walk inwards, so it describes the next argument+ go (HsPar _ _ (L l fun) _) ctxt args = go fun (set l ctxt) (EWrap (EPar ctxt) : args)+ go (HsPragE _ p (L l fun)) ctxt args = go fun (set l ctxt) (EPrag ctxt p : args)+ go (HsAppType _ (L l fun) at ty) ctxt args = go fun (dec l ctxt) (mkETypeArg ctxt at ty : args)+ go (HsApp _ (L l fun) arg) ctxt args = go fun (dec l ctxt) (mkEValArg ctxt arg : args) -- See Note [Looking through HsExpanded] go (XExpr (HsExpanded orig fun)) ctxt args- = go fun (VAExpansion orig (appCtxtLoc ctxt)) (EWrap (EExpand orig) : args)+ = go fun (VAExpansion orig (appCtxtLoc ctxt))+ (EWrap (EExpand orig) : args) -- See Note [Desugar OpApp in the typechecker] go e@(OpApp _ arg1 (L l op) arg2) _ args@@ -287,25 +329,316 @@ dec l (VACall f n _) = VACall f (n-1) (locA l) dec _ ctxt@(VAExpansion {}) = ctxt -rebuildHsApps :: HsExpr GhcTc -> AppCtxt -> [HsExprArg 'TcpTc]-> HsExpr GhcTc-rebuildHsApps fun _ [] = fun-rebuildHsApps fun ctxt (arg : args)+-- | Rebuild an application: takes a type-checked application head+-- expression together with arguments in the form of typechecked 'HsExprArg's+-- and returns a typechecked application of the head to the arguments.+--+-- This performs a representation-polymorphism check to ensure that the+-- remaining value arguments in an application have a fixed RuntimeRep.+--+-- See Note [Checking for representation-polymorphic built-ins].+rebuildHsApps :: HsExpr GhcTc+ -- ^ the function being applied+ -> AppCtxt+ -> [HsExprArg 'TcpTc]+ -- ^ the arguments to the function+ -> TcRhoType+ -- ^ result type of the application+ -> TcM (HsExpr GhcTc)+rebuildHsApps fun ctxt args app_res_rho+ = do { tcRemainingValArgs args app_res_rho fun+ ; return $ rebuild_hs_apps fun ctxt args }++-- | The worker function for 'rebuildHsApps': simply rebuilds+-- an application chain in which arguments are specified as+-- typechecked 'HsExprArg's.+rebuild_hs_apps :: HsExpr GhcTc+ -- ^ the function being applied+ -> AppCtxt+ -> [HsExprArg 'TcpTc]+ -- ^ the arguments to the function+ -> HsExpr GhcTc+rebuild_hs_apps fun _ [] = fun+rebuild_hs_apps fun ctxt (arg : args) = case arg of EValArg { eva_arg = ValArg arg, eva_ctxt = ctxt' }- -> rebuildHsApps (HsApp noAnn lfun arg) ctxt' args- ETypeArg { eva_hs_ty = hs_ty, eva_ty = ty, eva_ctxt = ctxt' }- -> rebuildHsApps (HsAppType ty lfun hs_ty) ctxt' args+ -> rebuild_hs_apps (HsApp noAnn lfun arg) ctxt' args+ ETypeArg { eva_hs_ty = hs_ty, eva_at = at, eva_ty = ty, eva_ctxt = ctxt' }+ -> rebuild_hs_apps (HsAppType ty lfun at hs_ty) ctxt' args EPrag ctxt' p- -> rebuildHsApps (HsPragE noExtField p lfun) ctxt' args+ -> rebuild_hs_apps (HsPragE noExtField p lfun) ctxt' args EWrap (EPar ctxt')- -> rebuildHsApps (gHsPar lfun) ctxt' args+ -> rebuild_hs_apps (gHsPar lfun) ctxt' args EWrap (EExpand orig)- -> rebuildHsApps (XExpr (ExpansionExpr (HsExpanded orig fun))) ctxt args+ -> rebuild_hs_apps (XExpr (ExpansionExpr (HsExpanded orig fun))) ctxt args EWrap (EHsWrap wrap)- -> rebuildHsApps (mkHsWrap wrap fun) ctxt args+ -> rebuild_hs_apps (mkHsWrap wrap fun) ctxt args where lfun = L (noAnnSrcSpan $ appCtxtLoc ctxt) fun +{- Note [Checking for representation-polymorphic built-ins]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+We cannot have representation-polymorphic or levity-polymorphic+function arguments. See Note [Representation polymorphism invariants]+in GHC.Core. That is checked by the calls to `hasFixedRuntimeRep` in+`tcEValArg`.++But some /built-in/ functions have representation-polymorphic argument+types. Users can't define such Ids; they are all GHC built-ins or data+constructors. Specifically they are:++1. A few wired-in Ids such as coerce and unsafeCoerce#,+2. Primops, such as raise#.+3. Newtype constructors with `UnliftedNewtypes` which have+ a representation-polymorphic argument.+4. Representation-polymorphic data constructors: unboxed tuples+ and unboxed sums.++For (1) consider+ badId :: forall r (a :: TYPE r). a -> a+ badId = unsafeCoerce# @r @r @a @a++The wired-in function+ unsafeCoerce# :: forall (r1 :: RuntimeRep) (r2 :: RuntimeRep)+ (a :: TYPE r1) (b :: TYPE r2).+ a -> b+has a convenient but representation-polymorphic type. It has no+binding; instead it has a compulsory unfolding, after which we+would have+ badId = /\r /\(a :: TYPE r). \(x::a). ...body of unsafeCorece#...+And this is no good because of that rep-poly \(x::a). So we want+to reject this.++On the other hand+ goodId :: forall (a :: Type). a -> a+ goodId = unsafeCoerce# @LiftedRep @LiftedRep @a @a++is absolutely fine, because after we inline the unfolding, the \(x::a)+is representation-monomorphic.++Test cases: T14561, RepPolyWrappedVar2.++For primops (2) the situation is similar; they are eta-expanded in+CorePrep to be saturated, and that eta-expansion must not add a+representation-polymorphic lambda.++Test cases: T14561b, RepPolyWrappedVar, UnliftedNewtypesCoerceFail.++For (3), consider a representation-polymorphic newtype with+UnliftedNewtypes:++ type Id :: forall r. TYPE r -> TYPE r+ newtype Id a where { MkId :: a }++ bad :: forall r (a :: TYPE r). a -> Id a+ bad = MkId @r @a -- Want to reject++ good :: forall (a :: Type). a -> Id a+ good = MkId @LiftedRep @a -- Want to accept++Test cases: T18481, UnliftedNewtypesLevityBinder++So these cases need special treatment. We add a special case+in tcApp to check whether an application of an Id has any remaining+representation-polymorphic arguments, after instantiation and application+of previous arguments. This is achieved by the tcRemainingValArgs+function, which computes the types of the remaining value arguments, and checks+that each of these have a fixed runtime representation.++Note that this function also ensures that data constructors always+appear saturated, by performing eta-expansion if necessary.+See Note [Typechecking data constructors].++Wrinkle [Arity]++ We don't want to check for arguments past the arity of the function.++ For example++ raise# :: forall {r :: RuntimeRep} (a :: Type) (b :: TYPE r). a -> b++ has arity 1, so an instantiation such as:++ foo :: forall w r (z :: TYPE r). w -> z -> z+ foo = raise# @w @(z -> z)++ is unproblematic. This means we must take care not to perform a+ representation-polymorphism check on `z`.++ To achieve this, we consult the arity of the 'Id' which is the head+ of the application (or just use 1 for a newtype constructor),+ and keep track of how many value-level arguments we have seen,+ to ensure we stop checking once we reach the arity.+ This is slightly complicated by the need to include both visible+ and invisible arguments, as the arity counts both:+ see GHC.Tc.Gen.Head.countVisAndInvisValArgs.++ Test cases: T20330{a,b}++Wrinkle [Syntactic check]++ We only perform a syntactic check in tcRemainingValArgs. That is,+ we will reject partial applications such as:++ type RR :: RuntimeREp+ type family RR where { RR = IntRep }+ type T :: TYPE RR+ type family T where { T = Int# }++ (# , #) @LiftedRep @RR e1++ Why do we reject? Wee would need to elaborate this partial application+ of (# , #) as follows:++ let x1 = e1+ in+ ( \ @(ty2 :: TYPE RR) (x2 :: ty2 |> TYPE RR[0])+ -> ( ( (# , #) @LiftedRep @RR @Char @ty2 x1 ) |> co1 )+ x2+ ) |> co2++ That is, we need to cast the partial application++ (# , #) @LiftedRep @RR @Char @ty2 x1++ so that the next argument we provide to it has a fixed RuntimeRep,+ and then eta-expand it. This is quite tricky, and other parts+ of the compiler aren't set up to handle this mix of applications+ and casts (e.g. checkCanEtaExpand in GHC.Core.Lint).++ So we refrain from doing so, and instead limit ourselves to a simple syntactic+ check. See the wiki page https://gitlab.haskell.org/ghc/ghc/-/wikis/Remaining-ValArgs+ for a more in-depth discussion.+-}++-- | Typecheck the remaining value arguments in a partial application,+-- ensuring they have a fixed RuntimeRep in the sense of Note [Fixed RuntimeRep]+-- in GHC.Tc.Utils.Concrete.+--+-- Example:+--+-- > repPolyId :: forall r (a :: TYPE r). a -> a+-- > repPolyId = coerce+--+-- This is an invalid instantiation of 'coerce', as we can't eta expand it+-- to+--+-- > \@r \@(a :: TYPE r) (x :: a) -> coerce @r @a @a x+--+-- because the binder `x` does not have a fixed runtime representation.+tcRemainingValArgs :: HasDebugCallStack+ => [HsExprArg 'TcpTc]+ -> TcRhoType+ -> HsExpr GhcTc+ -> TcM ()+tcRemainingValArgs applied_args app_res_rho fun = case fun of++ HsVar _ (L _ fun_id)++ -- (1): unsafeCoerce#+ -- 'unsafeCoerce#' is peculiar: it is patched in manually as per+ -- Note [Wiring in unsafeCoerce#] in GHC.HsToCore.+ -- Unfortunately, even though its arity is set to 1 in GHC.HsToCore.mkUnsafeCoercePrimPair,+ -- at this stage, if we query idArity, we get 0. This is because+ -- we end up looking at the non-patched version of unsafeCoerce#+ -- defined in Unsafe.Coerce, and that one indeed has arity 0.+ --+ -- We thus manually specify the correct arity of 1 here.+ | idName fun_id == unsafeCoercePrimName+ -> tc_remaining_args 1 (RepPolyWiredIn fun_id)++ -- (2): primops and other wired-in representation-polymorphic functions,+ -- such as 'rightSection', 'oneShot', etc; see bindings with Compulsory unfoldings+ -- in GHC.Types.Id.Make+ | isWiredInName (idName fun_id) && hasNoBinding fun_id+ -> tc_remaining_args (idArity fun_id) (RepPolyWiredIn fun_id)+ -- NB: idArity consults the IdInfo of the Id. This can be a problem+ -- in the presence of hs-boot files, as we might not have finished+ -- typechecking; inspecting the IdInfo at this point can cause+ -- strange Core Lint errors (see #20447).+ -- We avoid this entirely by only checking wired-in names,+ -- as those are the only ones this check is applicable to anyway.++ XExpr (ConLikeTc (RealDataCon con) _ _)+ -- (3): Representation-polymorphic newtype constructors.+ | isNewDataCon con+ -- (4): Unboxed tuples and unboxed sums+ || isUnboxedTupleDataCon con+ || isUnboxedSumDataCon con+ -> tc_remaining_args (dc_val_arity con) (RepPolyDataCon con)++ _ -> return ()++ where++ dc_val_arity :: DataCon -> Arity+ dc_val_arity con = count (not . isEqPrimPred) (dataConTheta con)+ + length (dataConStupidTheta con)+ + dataConSourceArity con+ -- Count how many value-level arguments this data constructor expects:+ -- - dictionary arguments from the context (including the stupid context),+ -- - source value arguments.+ -- Tests: EtaExpandDataCon, EtaExpandStupid{1,2}.++ nb_applied_vis_val_args :: Int+ nb_applied_vis_val_args = count isHsValArg applied_args++ nb_applied_val_args :: Int+ nb_applied_val_args = countVisAndInvisValArgs applied_args++ tc_remaining_args :: Arity -> RepPolyFun -> TcM ()+ tc_remaining_args arity rep_poly_fun =+ tc_rem_args+ (nb_applied_vis_val_args + 1)+ (nb_applied_val_args + 1)+ rem_arg_tys++ where++ rem_arg_tys :: [(Scaled Type, FunTyFlag)]+ rem_arg_tys = getRuntimeArgTys app_res_rho+ -- We do not need to zonk app_res_rho first, because the number of arrows+ -- in the (possibly instantiated) inferred type of the function will+ -- be at least the arity of the function.++ -- The following function is essentially "mapM hasFixedRuntimeRep rem_arg_tys",+ -- but we need to keep track of indices for error messages, hence the manual recursion.+ tc_rem_args :: Int+ -- visible value argument index, starting from 1+ -- (only used to report the argument position in error messages)+ -> Int+ -- value argument index, starting from 1+ -- used to count up to the arity to ensure that+ -- we don't check too many argument types+ -> [(Scaled Type, FunTyFlag)]+ -- run-time argument types+ -> TcM ()+ tc_rem_args _ i_val _+ | i_val > arity+ = return ()+ tc_rem_args _ _ []+ -- Should never happen: it would mean that the arity is higher+ -- than the number of arguments apparent from the type.+ = pprPanic "tcRemainingValArgs" debug_msg+ tc_rem_args i_visval !i_val ((Scaled _ arg_ty, af) : tys)+ = do { let (i_visval', arg_pos)+ | isInvisibleFunArg af = ( i_visval , ArgPosInvis )+ | otherwise = ( i_visval + 1, ArgPosVis i_visval )+ frr_ctxt = FRRNoBindingResArg rep_poly_fun arg_pos+ ; hasFixedRuntimeRep_syntactic frr_ctxt arg_ty+ -- Why is this a syntactic check? See Wrinkle [Syntactic check] in+ -- Note [Checking for representation-polymorphic built-ins].+ ; tc_rem_args i_visval' (i_val + 1) tys }++ debug_msg :: SDoc+ debug_msg =+ vcat+ [ text "app_head =" <+> ppr fun+ , text "arity =" <+> ppr arity+ , text "applied_args =" <+> ppr applied_args+ , text "nb_applied_val_args =" <+> ppr nb_applied_val_args ]++ isHsValArg :: HsExprArg id -> Bool isHsValArg (EValArg {}) = True isHsValArg _ = False@@ -431,12 +764,11 @@ -- -- See Note [tcApp: typechecking applications] in GHC.Tc.Gen.App tcInferAppHead (fun,ctxt) args- = setSrcSpan (appCtxtLoc ctxt) $+ = addHeadCtxt ctxt $ do { mb_tc_fun <- tcInferAppHead_maybe fun args ; case mb_tc_fun of Just (fun', fun_sigma) -> return (fun', fun_sigma)- Nothing -> add_head_ctxt fun args $- tcInfer (tcExpr fun) }+ Nothing -> tcInfer (tcExpr fun) } tcInferAppHead_maybe :: HsExpr GhcRn -> [HsExprArg 'TcpRn]@@ -447,20 +779,23 @@ = case fun of HsVar _ (L _ nm) -> Just <$> tcInferId nm HsRecSel _ f -> Just <$> tcInferRecSelId f- ExprWithTySig _ e hs_ty -> add_head_ctxt fun args $- Just <$> tcExprWithSig e hs_ty+ ExprWithTySig _ e hs_ty -> Just <$> tcExprWithSig e hs_ty HsOverLit _ lit -> Just <$> tcInferOverLit lit- HsSpliceE _ (HsSpliced _ _ (HsSplicedExpr e))+ HsUntypedSplice (HsUntypedSpliceTop _ e) _ -> tcInferAppHead_maybe e args _ -> return Nothing -add_head_ctxt :: HsExpr GhcRn -> [HsExprArg 'TcpRn] -> TcM a -> TcM a--- Don't push an expression context if the arguments are empty,--- because it has already been pushed by tcExpr-add_head_ctxt fun args thing_inside- | null args = thing_inside- | otherwise = addExprCtxt fun thing_inside-+addHeadCtxt :: AppCtxt -> TcM a -> TcM a+addHeadCtxt fun_ctxt thing_inside+ | not (isGoodSrcSpan fun_loc) -- noSrcSpan => no arguments+ = thing_inside -- => context is already set+ | otherwise+ = setSrcSpan fun_loc $+ case fun_ctxt of+ VAExpansion orig _ -> addExprCtxt orig thing_inside+ VACall {} -> thing_inside+ where+ fun_loc = appCtxtLoc fun_ctxt {- ********************************************************************* * *@@ -497,8 +832,7 @@ -- nor does it need the 'lifting' treatment -- hence no checkTh stuff here - _ -> failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $- ppr thing <+> text "used where a value identifier was expected" }+ _ -> failWithTc $ TcRnExpectedValueId thing } ------------------------ @@ -553,16 +887,7 @@ UnknownSubordinate (text "field of type" <+> quotes (ppr p)) notSelector :: Name -> TcRnMessage-notSelector field- = TcRnUnknownMessage $ mkPlainError noHints $- hsep [quotes (ppr field), text "is not a record selector"]--naughtyRecordSel :: OccName -> TcRnMessage-naughtyRecordSel lbl- = TcRnUnknownMessage $ mkPlainError noHints $- text "Cannot use record selector" <+> quotes (ppr lbl) <+>- text "as a function due to escaped type variables" $$- text "Probable fix: use pattern-matching syntax instead"+notSelector = TcRnNotARecordSelector {- *********************************************************************@@ -583,7 +908,7 @@ loc = getLocA (dropWildCards hs_ty) ctxt = ExprSigCtxt (lhsSigWcTypeContextSpan hs_ty) -tcExprSig :: UserTypeCtxt -> LHsExpr GhcRn -> TcIdSigInfo -> TcM (LHsExpr GhcTc, TcSigmaType)+tcExprSig :: UserTypeCtxt -> LHsExpr GhcRn -> TcIdSigInfo -> TcM (LHsExpr GhcTc, TcType) tcExprSig ctxt expr (CompleteSig { sig_bndr = poly_id, sig_loc = loc }) = setSrcSpan loc $ -- Sets the location for the implication constraint do { let poly_ty = idType poly_id@@ -627,7 +952,7 @@ ; traceTc "tcExpSig" (ppr qtvs $$ ppr givens $$ ppr inferred_sigma $$ ppr my_sigma) ; let poly_wrap = wrap <.> mkWpTyLams qtvs- <.> mkWpLams givens+ <.> mkWpEvLams givens <.> mkWpLet ev_binds ; return (mkLHsWrap poly_wrap expr', my_sigma) } @@ -635,7 +960,7 @@ {- Note [Partial expression signatures] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Partial type signatures on expressions are easy to get wrong. But-here is a guiding principile+here is a guiding principle e :: ty should behave like let x :: ty@@ -755,8 +1080,7 @@ (tcTyThingTyCon_maybe -> Just tc) -> fail_tycon tc -- TyCon or TcTyCon ATyVar name _ -> fail_tyvar name - _ -> failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $- ppr thing <+> text "used where a value identifier was expected" }+ _ -> failWithTc $ TcRnExpectedValueId thing } where fail_tycon tc = do gre <- getGlobalRdrEnv@@ -779,9 +1103,8 @@ hint_msg = vcat $ map ppr hints import_err_msg = vcat $ map ppr import_errs info = ErrInfo { errInfoContext = pprov, errInfoSupplementary = import_err_msg $$ hint_msg }- msg = TcRnMessageWithInfo unit_state- $ TcRnMessageDetailed info (TcRnIncorrectNameSpace nm False)- failWithTc msg+ failWithTc $ TcRnMessageWithInfo unit_state (+ mkDetailedMessage info (TcRnIncorrectNameSpace nm False)) get_suggestions ns = do let occ = mkOccNameFS ns (occNameFS (occName id_name))@@ -799,11 +1122,11 @@ check_local_id :: Id -> TcM () check_local_id id = do { checkThLocalId id- ; tcEmitBindingUsage $ unitUE (idName id) One }+ ; tcEmitBindingUsage $ unitUE (idName id) OneTy } check_naughty :: OccName -> TcId -> TcM () check_naughty lbl id- | isNaughtyRecordSelector id = failWithTc (naughtyRecordSel lbl)+ | isNaughtyRecordSelector id = failWithTc (TcRnRecSelectorEscapedTyVar lbl) | otherwise = return () tcInferDataCon :: DataCon -> TcM (HsExpr GhcTc, TcSigmaType)@@ -820,19 +1143,22 @@ ; let full_theta = stupid_theta ++ theta all_arg_tys = map unrestricted full_theta ++ scaled_arg_tys- -- stupid-theta must come first+ -- We are building the type of the data con wrapper, so the+ -- type must precisely match the construction in+ -- GHC.Core.DataCon.dataConWrapperType. -- See Note [Instantiating stupid theta]+ -- in GHC.Core.DataCon. ; return ( XExpr (ConLikeTc (RealDataCon con) tvs all_arg_tys) , mkInvisForAllTys tvbs $ mkPhiTy full_theta $- mkVisFunTys scaled_arg_tys res ) }+ mkScaledFunTys scaled_arg_tys res ) } where linear_to_poly :: Scaled Type -> TcM (Scaled Type) -- linear_to_poly implements point (3,4) -- of Note [Typechecking data constructors]- linear_to_poly (Scaled One ty) = do { mul_var <- newFlexiTyVarTy multiplicityTy- ; return (Scaled mul_var ty) }- linear_to_poly scaled_ty = return scaled_ty+ linear_to_poly (Scaled OneTy ty) = do { mul_var <- newFlexiTyVarTy multiplicityTy+ ; return (Scaled mul_var ty) }+ linear_to_poly scaled_ty = return scaled_ty tcInferPatSyn :: Name -> PatSyn -> TcM (HsExpr GhcTc, TcSigmaType) tcInferPatSyn id_name ps@@ -840,27 +1166,34 @@ Just (expr,ty) -> return (expr,ty) Nothing -> failWithTc (nonBidirectionalErr id_name) -nonBidirectionalErr :: Outputable name => name -> TcRnMessage-nonBidirectionalErr name = TcRnUnknownMessage $ mkPlainError noHints $- text "non-bidirectional pattern synonym"- <+> quotes (ppr name) <+> text "used in an expression"+nonBidirectionalErr :: Name -> TcRnMessage+nonBidirectionalErr = TcRnPatSynNotBidirectional -{- Note [Typechecking data constructors]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+{- Note [Adding the implicit parameter to 'assert']+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+The typechecker transforms (assert e1 e2) to (assertError e1 e2).+This isn't really the Right Thing because there's no way to "undo"+if you want to see the original source code in the typechecker+output. We'll have fix this in due course, when we care more about+being able to reconstruct the exact original program.++Note [Typechecking data constructors]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As per Note [Polymorphisation of linear fields] in GHC.Core.Multiplicity, linear fields of data constructors get a polymorphic multiplicity when the data constructor is used as a term: Just :: forall {p} a. a %p -> Maybe a -So at an occurrence of a data constructor we do the following,-mostly in tcInferDataCon:+So at an occurrence of a data constructor we do the following: -1. Get its type, say- K :: forall (r :: RuntimeRep) (a :: TYPE r). a %1 -> T r a- Note the %1: it is linear+1. Typechecking, in tcInferDataCon. -2. We are going to return a ConLikeTc, thus:+ a. Get the original type of the constructor, say+ K :: forall (r :: RuntimeRep) (a :: TYPE r). a %1 -> T r a+ Note the %1: it is linear++ b. We are going to return a ConLikeTc, thus: XExpr (ConLikeTc K [r,a] [Scaled p a]) :: forall (r :: RuntimeRep) (a :: TYPE r). a %p -> T r a where 'p' is a fresh multiplicity unification variable.@@ -870,93 +1203,71 @@ the fresh multiplicity variable in the ConLikeTc; along with the type of the ConLikeTc. This is done by linear_to_poly. -3. If the argument is not linear (perhaps explicitly declared as+ If the argument is not linear (perhaps explicitly declared as non-linear by the user), don't bother with this. -4. The (ConLikeTc K [r,a] [Scaled p a]) is later desugared by- GHC.HsToCore.Expr.dsConLike to:+2. Desugaring, in dsConLike.++ a. The (ConLikeTc K [r,a] [Scaled p a]) is desugared to (/\r (a :: TYPE r). \(x %p :: a). K @r @a x) which has the desired type given in the previous bullet.+ The 'p' is the multiplicity unification variable, which will by now have been unified to something, or defaulted in `GHC.Tc.Utils.Zonk.commitFlexi`. So it won't just be an (unbound) variable. -Wrinkles--* Note that the [TcType] is strictly redundant anyway; those are the- type variables from the dataConUserTyVarBinders of the data constructor.- Similarly in the [Scaled TcType] field of ConLikeTc, the types come directly- from the data constructor. The only bit that /isn't/ redundant is the- fresh multiplicity variables!-- So an alternative would be to define ConLikeTc like this:- | ConLikeTc [TcType] -- Just the multiplicity variables- But then the desugarer would need to repeat some of the work done here.- So for now at least ConLikeTc records this strictly-redundant info.--* The lambda expression we produce in (4) can have representation-polymorphic- arguments, as indeed in (/\r (a :: TYPE r). \(x %p :: a). K @r @a x),- we have a lambda-bound variable x :: (a :: TYPE r).- This goes against the representation polymorphism invariants given in- Note [Representation polymorphism invariants] in GHC.Core. The trick is that- this this lambda will always be instantiated in a way that upholds the invariants.- This is achieved as follows:-- A. Any arguments to such lambda abstractions are guaranteed to have- a fixed runtime representation. This is enforced in 'tcApp' by- 'matchActualFunTySigma'.-- B. If there are fewer arguments than there are bound term variables,- hasFixedRuntimeRep_remainingValArgs will ensure that we are still- instantiating at a representation-monomorphic type, e.g.-- ( /\r (a :: TYPE r). \ (x %p :: a). K @r @a x) @IntRep @Int#- :: Int# -> T IntRep Int#-- We then rely on the simple optimiser to beta reduce the lambda.--* See Note [Instantiating stupid theta] for an extra wrinkle+ So a saturated application (K e), where e::Int will desugar to+ (/\r (a :: TYPE r). ..etc..)+ @LiftedRep @Int e+ and all those lambdas will beta-reduce away in the simple optimiser+ (see Wrinkle [Representation-polymorphic lambdas] below). + But for an /unsaturated/ application, such as `map (K @LiftedRep @Int) xs`,+ beta reduction will leave (\x %Many :: Int. K x), which is the type `map`+ expects whereas if we had just plain K, with its linear type, we'd+ get a type mismatch. That's why we do this funky desugaring. -Note [Adding the implicit parameter to 'assert']-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-The typechecker transforms (assert e1 e2) to (assertError e1 e2).-This isn't really the Right Thing because there's no way to "undo"-if you want to see the original source code in the typechecker-output. We'll have fix this in due course, when we care more about-being able to reconstruct the exact original program.+Wrinkles + [ConLikeTc arguments] -Note [Instantiating stupid theta]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider a data type with a "stupid theta" (see-Note [The stupid context] in GHC.Core.DataCon):+ Note that the [TcType] argument to ConLikeTc is strictly redundant; those are+ the type variables from the dataConUserTyVarBinders of the data constructor.+ Similarly in the [Scaled TcType] field of ConLikeTc, the types come directly+ from the data constructor. The only bit that /isn't/ redundant is the+ fresh multiplicity variables! - data Ord a => T a = MkT (Maybe a)+ So an alternative would be to define ConLikeTc like this:+ | ConLikeTc [TcType] -- Just the multiplicity variables+ But then the desugarer would need to repeat some of the work done here.+ So for now at least ConLikeTc records this strictly-redundant info. -We want to generate an Ord constraint for every use of MkT; but-we also want to allow visible type application, such as- MkT @Int+ [Representation-polymorphic lambdas] -So we generate (ConLikeTc MkT [a] [Ord a, Maybe a]), with type- forall a. Ord a => Maybe a -> T a+ The lambda expression we produce in (4) can have representation-polymorphic+ arguments, as indeed in (/\r (a :: TYPE r). \(x %p :: a). K @r @a x),+ we have a lambda-bound variable x :: (a :: TYPE r).+ This goes against the representation polymorphism invariants given in+ Note [Representation polymorphism invariants] in GHC.Core. The trick is that+ this this lambda will always be instantiated in a way that upholds the invariants.+ This is achieved as follows: -Now visible type application will work fine. But we desugar the-ConLikeTc to- /\a \(d:Ord a) (x:Maybe a). MkT x-Notice that 'd' is dropped in this desugaring. We don't need it;-it was only there to generate a Wanted constraint. (That is why-it is stupid.) To achieve this:+ A. Any arguments to such lambda abstractions are guaranteed to have+ a fixed runtime representation. This is enforced in 'tcApp' by+ 'matchActualFunTySigma'. -* We put the stupid-thata at the front of the list of argument- types in ConLikeTc+ B. If there are fewer arguments than there are bound term variables,+ hasFixedRuntimeRep_remainingValArgs will ensure that we are still+ instantiating at a representation-monomorphic type, e.g. -* GHC.HsToCore.Expr.dsConLike generates /lambdas/ for all the- arguments, but drops the stupid-theta arguments when building the- /application/.+ ( /\r (a :: TYPE r). \ (x %p :: a). K @r @a x) @IntRep @Int#+ :: Int# -> T IntRep Int# -Nice.+ C. In the output of the desugarer in (4) above, we have a representation+ polymorphic lambda, which Lint would normally reject. So for that one+ pass, we switch off Lint's representation-polymorphism checks; see+ the `lf_check_fixed_rep` flag in `LintFlags`. -} {-@@ -1007,7 +1318,7 @@ -- bindings of the same splice proxy, but that doesn't -- matter, although it's a mite untidy. do { let id_ty = idType id- ; checkTc (isTauTy id_ty) (polySpliceErr id)+ ; checkTc (isTauTy id_ty) (TcRnSplicePolymorphicLocalVar id) -- If x is polymorphic, its occurrence sites might -- have different instantiations, so we can't use plain -- 'x' as the splice proxy name. I don't know how to@@ -1026,7 +1337,7 @@ [getRuntimeRep id_ty, id_ty] -- Warning for implicit lift (#17804)- ; addDetailedDiagnostic (TcRnImplicitLift id)+ ; addDetailedDiagnostic (TcRnImplicitLift $ idName id) -- Update the pending splices ; ps <- readMutVar ps_var@@ -1040,11 +1351,6 @@ id_name = idName id checkCrossStageLifting _ _ _ = return ()--polySpliceErr :: Id -> TcRnMessage-polySpliceErr id- = TcRnUnknownMessage $ mkPlainError noHints $- text "Can't splice the polymorphic local variable" <+> quotes (ppr id) {- Note [Lifting strings]
compiler/GHC/Tc/Gen/HsType.hs view
@@ -1,6 +1,6 @@- {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MonadComprehensions #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-}@@ -76,14 +76,13 @@ funAppCtxt, addTyConFlavCtxt ) where -import GHC.Prelude+import GHC.Prelude hiding ( head, init, last, tail ) import GHC.Hs import GHC.Rename.Utils import GHC.Tc.Errors.Types import GHC.Tc.Utils.Monad import GHC.Tc.Types.Origin-import GHC.Core.Predicate import GHC.Tc.Types.Constraint import GHC.Tc.Utils.Env import GHC.Tc.Utils.TcMType@@ -92,12 +91,16 @@ import GHC.IfaceToCore import GHC.Tc.Solver import GHC.Tc.Utils.Zonk-import GHC.Core.TyCo.Rep-import GHC.Core.TyCo.Ppr import GHC.Tc.Utils.TcType import GHC.Tc.Utils.Instantiate ( tcInstInvisibleTyBinders, tcInstInvisibleTyBindersN,- tcInstInvisibleTyBinder, tcSkolemiseInvisibleBndrs )+ tcInstInvisibleTyBinder, tcSkolemiseInvisibleBndrs,+ tcInstTypeBndrs )+ import GHC.Core.Type+import GHC.Core.Predicate+import GHC.Core.TyCo.Rep+import GHC.Core.TyCo.Ppr+ import GHC.Builtin.Types.Prim import GHC.Types.Error import GHC.Types.Name.Env@@ -125,12 +128,15 @@ import qualified GHC.LanguageExtensions as LangExt import GHC.Data.FastString+import GHC.Data.List.Infinite ( Infinite (..) )+import qualified GHC.Data.List.Infinite as Inf import GHC.Data.List.SetOps import GHC.Data.Maybe import GHC.Data.Bag( unitBag ) import Data.Function ( on )-import Data.List.NonEmpty as NE( NonEmpty(..), nubBy )+import Data.List.NonEmpty ( NonEmpty(..), nonEmpty )+import qualified Data.List.NonEmpty as NE import Data.List ( find, mapAccumL ) import Control.Monad import Data.Tuple( swap )@@ -336,7 +342,7 @@ the pattern signature (which is not kind-generalized). When we are checking the *body* of foo, though, we need to unify the type of x with the argument type of bar. At this point, the ambient TcLevel is 1, and spotting a-matavariable with level 2 would violate the (WantedInv) invariant of+metavariable with level 2 would violate the (WantedInv) invariant of Note [TcLevel invariants]. So, instead of kind-generalizing, we promote the metavariable to level 1. This is all done in kindGeneralizeNone. @@ -631,11 +637,10 @@ tcHsDeriv hs_ty = do { ty <- tcTopLHsType DerivClauseCtxt hs_ty ; let (tvs, pred) = splitForAllTyCoVars ty- (kind_args, _) = splitFunTys (tcTypeKind pred)+ (kind_args, _) = splitFunTys (typeKind pred) ; case getClassPredTys_maybe pred of Just (cls, tys) -> return (tvs, cls, tys, map scaledThing kind_args)- Nothing -> failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $- (text "Illegal deriving item" <+> quotes (ppr hs_ty)) }+ Nothing -> failWithTc $ TcRnIllegalDerivingItem hs_ty } -- | Typecheck a deriving strategy. For most deriving strategies, this is a -- no-op, but for the @via@ strategy, this requires typechecking the @via@ type.@@ -659,7 +664,9 @@ tc_deriv_strategy (NewtypeStrategy _) = boring_case (NewtypeStrategy noExtField) tc_deriv_strategy (ViaStrategy hs_sig) = do { ty <- tcTopLHsType DerivClauseCtxt hs_sig- ; rec { (via_tvs, via_pred) <- tcSkolemiseInvisibleBndrs (DerivSkol via_pred) ty}+ -- rec {..}: see Note [Keeping SkolemInfo inside a SkolemTv]+ -- in GHC.Tc.Utils.TcType+ ; rec { (via_tvs, via_pred) <- tcSkolemiseInvisibleBndrs (DerivSkol via_pred) ty } ; pure (ViaStrategy via_pred, via_tvs) } boring_case :: ds -> TcM (ds, [a])@@ -1043,9 +1050,11 @@ -- splices or not. -- -- See Note [Delaying modFinalizers in untyped splices].-tc_infer_hs_type mode (HsSpliceTy _ (HsSpliced _ _ (HsSplicedTy ty)))- = tc_infer_hs_type mode ty+tc_infer_hs_type mode (HsSpliceTy (HsUntypedSpliceTop _ ty) _)+ = tc_infer_lhs_type mode ty +tc_infer_hs_type _ (HsSpliceTy (HsUntypedSpliceNested n) s) = pprPanic "tc_infer_hs_type: invalid nested splice" (pprUntypedSplice True (Just n) s)+ tc_infer_hs_type mode (HsDocTy _ ty _) = tc_infer_lhs_type mode ty -- See Note [Typechecking HsCoreTys]@@ -1056,10 +1065,10 @@ subst_prs = [ (getUnique nm, tv) | ATyVar nm tv <- nonDetNameEnvElts (tcl_env env) ] subst = mkTvSubst- (mkInScopeSet $ mkVarSet $ map snd subst_prs)- (listToUFM_Directly $ map (liftSnd mkTyVarTy) subst_prs)+ (mkInScopeSetList $ map snd subst_prs)+ (listToUFM_Directly $ map (fmap mkTyVarTy) subst_prs) ty' = substTy subst ty- return (ty', tcTypeKind ty')+ return (ty', typeKind ty') tc_infer_hs_type _ (HsExplicitListTy _ _ tys) | null tys -- this is so that we can use visible kind application with '[]@@ -1132,41 +1141,30 @@ -- While top-level bangs at this point are eliminated (eg !(Maybe Int)), -- other kinds of bangs are not (eg ((!Maybe) Int)). These kinds of -- bangs are invalid, so fail. (#7210, #14761)- = do { let bangError err = failWith $ TcRnUnknownMessage $ mkPlainError noHints $- text "Unexpected" <+> text err <+> text "annotation:" <+> ppr ty $$- text err <+> text "annotation cannot appear nested inside a type"- ; case bang of- HsSrcBang _ SrcUnpack _ -> bangError "UNPACK"- HsSrcBang _ SrcNoUnpack _ -> bangError "NOUNPACK"- HsSrcBang _ NoSrcUnpack SrcLazy -> bangError "laziness"- HsSrcBang _ _ _ -> bangError "strictness" }+ = failWith $ TcRnUnexpectedAnnotation ty bang tc_hs_type _ ty@(HsRecTy {}) _ -- Record types (which only show up temporarily in constructor -- signatures) should have been removed by now- = failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $- (text "Record syntax is illegal here:" <+> ppr ty)+ = failWithTc $ TcRnIllegalRecordSyntax ty -- HsSpliced is an annotation produced by 'GHC.Rename.Splice.rnSpliceType'. -- Here we get rid of it and add the finalizers to the global environment -- while capturing the local environment. -- -- See Note [Delaying modFinalizers in untyped splices].-tc_hs_type mode (HsSpliceTy _ (HsSpliced _ mod_finalizers (HsSplicedTy ty)))+tc_hs_type mode (HsSpliceTy (HsUntypedSpliceTop mod_finalizers ty) _) exp_kind = do addModFinalizersWithLclEnv mod_finalizers- tc_hs_type mode ty exp_kind+ tc_lhs_type mode ty exp_kind --- This should never happen; type splices are expanded by the renamer-tc_hs_type _ ty@(HsSpliceTy {}) _exp_kind- = failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $- (text "Unexpected type splice:" <+> ppr ty)+tc_hs_type _ (HsSpliceTy (HsUntypedSpliceNested n) s) _ = pprPanic "tc_hs_type: invalid nested splice" (pprUntypedSplice True (Just n) s) ---------- Functions and applications tc_hs_type mode (HsFunTy _ mult ty1 ty2) exp_kind = tc_fun_type mode mult ty1 ty2 exp_kind tc_hs_type mode (HsOpTy _ _ ty1 (L _ op) ty2) exp_kind- | op `hasKey` funTyConKey+ | op `hasKey` unrestrictedFunTyConKey = tc_fun_type mode (HsUnrestrictedArrow noHsUniTok) ty1 ty2 exp_kind --------- Foralls@@ -1187,10 +1185,10 @@ = tc_lhs_type mode rn_ty exp_kind -- See Note [Body kind of a HsQualTy]- | tcIsConstraintKind exp_kind+ | isConstraintLikeKind exp_kind = do { ctxt' <- tc_hs_context mode ctxt ; ty' <- tc_lhs_type mode rn_ty constraintKind- ; return (mkPhiTy ctxt' ty') }+ ; return (tcMkDFunPhiTy ctxt' ty') } | otherwise = do { ctxt' <- tc_hs_context mode ctxt@@ -1198,7 +1196,7 @@ ; ek <- newOpenTypeKind -- The body kind (result of the function) can -- be TYPE r, for any r, hence newOpenTypeKind ; ty' <- tc_lhs_type mode rn_ty ek- ; checkExpectedKind (unLoc rn_ty) (mkPhiTy ctxt' ty')+ ; checkExpectedKind (unLoc rn_ty) (tcMkPhiTy ctxt' ty') liftedTypeKind exp_kind } --------- Lists, arrays, and tuples@@ -1284,8 +1282,8 @@ constraintKind exp_kind } tc_hs_type _ rn_ty@(HsStarTy _ _) exp_kind- -- Desugaring 'HsStarTy' to 'Data.Kind.Type' here means that we don't have to- -- handle it in 'coreView' and 'tcView'.+ -- Desugaring 'HsStarTy' to 'Data.Kind.Type' here means that we don't+ -- have to handle it in 'coreView' = checkExpectedKind rn_ty liftedTypeKind liftedTypeKind exp_kind --------- Literals@@ -1319,22 +1317,22 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A HsForAllTy contains an HsForAllTelescope to denote the visibility of the forall binder. Furthermore, each invisible type variable binder also has a-Specificity. Together, these determine the variable binders (ArgFlag) for each+Specificity. Together, these determine the variable binders (ForAllTyFlag) for each variable in the generated ForAllTy type. This table summarises this relation: -----------------------------------------------------------------------------| User-written type HsForAllTelescope Specificity ArgFlag+| User-written type HsForAllTelescope Specificity ForAllTyFlag |--------------------------------------------------------------------------- | f :: forall a. type HsForAllInvis SpecifiedSpec Specified | f :: forall {a}. type HsForAllInvis InferredSpec Inferred | f :: forall a -> type HsForAllVis SpecifiedSpec Required | f :: forall {a} -> type HsForAllVis InferredSpec /-| This last form is non-sensical and is thus rejected.+| This last form is nonsensical and is thus rejected. ---------------------------------------------------------------------------- -For more information regarding the interpretation of the resulting ArgFlag, see-Note [VarBndrs, TyCoVarBinders, TyConBinders, and visibility] in "GHC.Core.TyCo.Rep".+For more information regarding the interpretation of the resulting ForAllTyFlag, see+Note [VarBndrs, ForAllTyBinders, TyConBinders, and visibility] in "GHC.Core.TyCo.Rep". -} ------------------------------------------@@ -1345,18 +1343,21 @@ -> TcM TcType tc_fun_type mode mult ty1 ty2 exp_kind = case mode_tyki mode of TypeLevel ->- do { arg_k <- newOpenTypeKind+ do { traceTc "tc_fun_type" (ppr ty1 $$ ppr ty2)+ ; arg_k <- newOpenTypeKind ; res_k <- newOpenTypeKind- ; ty1' <- tc_lhs_type mode ty1 arg_k- ; ty2' <- tc_lhs_type mode ty2 res_k+ ; ty1' <- tc_lhs_type mode ty1 arg_k+ ; ty2' <- tc_lhs_type mode ty2 res_k ; mult' <- tc_mult mode mult- ; checkExpectedKind (HsFunTy noAnn mult ty1 ty2) (mkVisFunTy mult' ty1' ty2')+ ; checkExpectedKind (HsFunTy noAnn mult ty1 ty2)+ (tcMkVisFunTy mult' ty1' ty2') liftedTypeKind exp_kind } KindLevel -> -- no representation polymorphism in kinds. yet.- do { ty1' <- tc_lhs_type mode ty1 liftedTypeKind- ; ty2' <- tc_lhs_type mode ty2 liftedTypeKind+ do { ty1' <- tc_lhs_type mode ty1 liftedTypeKind+ ; ty2' <- tc_lhs_type mode ty2 liftedTypeKind ; mult' <- tc_mult mode mult- ; checkExpectedKind (HsFunTy noAnn mult ty1 ty2) (mkVisFunTy mult' ty1' ty2')+ ; checkExpectedKind (HsFunTy noAnn mult ty1 ty2)+ (tcMkVisFunTy mult' ty1' ty2') liftedTypeKind exp_kind } {- Note [Skolem escape and forall-types]@@ -1401,10 +1402,10 @@ tupKindSort_maybe :: TcKind -> Maybe TupleSort tupKindSort_maybe k | Just (k', _) <- splitCastTy_maybe k = tupKindSort_maybe k'- | Just k' <- tcView k = tupKindSort_maybe k'- | tcIsConstraintKind k = Just ConstraintTuple- | tcIsLiftedTypeKind k = Just BoxedTuple- | otherwise = Nothing+ | Just k' <- coreView k = tupKindSort_maybe k'+ | isConstraintKind k = Just ConstraintTuple+ | tcIsLiftedTypeKind k = Just BoxedTuple+ | otherwise = Nothing tc_tuple :: HsType GhcRn -> TcTyMode -> TupleSort -> [LHsType GhcRn] -> TcKind -> TcM TcType tc_tuple rn_ty mode tup_sort tys exp_kind@@ -1508,7 +1509,7 @@ is_app :: HsType GhcRn -> Bool is_app (HsAppKindTy {}) = True is_app (HsAppTy {}) = True- is_app (HsOpTy _ _ _ (L _ op) _) = not (op `hasKey` funTyConKey)+ is_app (HsOpTy _ _ _ (L _ op) _) = not (op `hasKey` unrestrictedFunTyConKey) -- I'm not sure why this funTyConKey test is necessary -- Can it even happen? Perhaps for t1 `(->)` t2 -- but then maybe it's ok to treat that like a normal@@ -1574,30 +1575,30 @@ go_init n fun all_args = go n fun empty_subst fun_ki all_args where- fun_ki = tcTypeKind fun- -- We do (tcTypeKind fun) here, even though the caller+ fun_ki = typeKind fun+ -- We do (typeKind fun) here, even though the caller -- knows the function kind, to absolutely guarantee -- INVARIANT for 'go' -- Note that in a typical application (F t1 t2 t3),- -- the 'fun' is just a TyCon, so tcTypeKind is fast+ -- the 'fun' is just a TyCon, so typeKind is fast - empty_subst = mkEmptyTCvSubst $ mkInScopeSet $+ empty_subst = mkEmptySubst $ mkInScopeSet $ tyCoVarsOfType fun_ki go :: Int -- The # of the next argument -> TcType -- Function applied to some args- -> TCvSubst -- Applies to function kind+ -> Subst -- Applies to function kind -> TcKind -- Function kind -> [LHsTypeArg GhcRn] -- Un-type-checked args -> TcM (TcType, TcKind) -- Result type and its kind -- INVARIANT: in any call (go n fun subst fun_ki args)- -- tcTypeKind fun = subst(fun_ki)+ -- typeKind fun = subst(fun_ki) -- So the 'subst' and 'fun_ki' arguments are simply- -- there to avoid repeatedly calling tcTypeKind.+ -- there to avoid repeatedly calling typeKind. -- -- Reason for INVARIANT: to support the Purely Kinded Type Invariant -- it's important that if fun_ki has a forall, then so does- -- (tcTypeKind fun), because the next thing we are going to do+ -- (typeKind fun), because the next thing we are going to do -- is apply 'fun' to an argument type. -- Dispatch on all_args first, for performance reasons@@ -1614,16 +1615,16 @@ case ki_binder of -- FunTy with PredTy on LHS, or ForAllTy with Inferred- Named (Bndr _ Inferred) -> instantiate ki_binder inner_ki- Anon InvisArg _ -> instantiate ki_binder inner_ki+ Named (Bndr _ Inferred) -> instantiate ki_binder inner_ki+ Anon _ af | isInvisibleFunArg af -> instantiate ki_binder inner_ki Named (Bndr _ Specified) -> -- Visible kind application do { traceTc "tcInferTyApps (vis kind app)" (vcat [ ppr ki_binder, ppr hs_ki_arg- , ppr (tyBinderType ki_binder)+ , ppr (piTyBinderType ki_binder) , ppr subst ]) - ; let exp_kind = substTy subst $ tyBinderType ki_binder+ ; let exp_kind = substTy subst $ piTyBinderType ki_binder ; arg_mode <- mkHoleMode KindLevel HM_VTA -- HM_VKA: see Note [Wildcards in visible kind application] ; ki_arg <- addErrCtxt (funAppCtxt orig_hs_ty hs_ki_arg n) $@@ -1645,8 +1646,8 @@ ---------------- HsValArg: a normal argument (fun ty) (HsValArg arg : args, Just (ki_binder, inner_ki)) -- next binder is invisible; need to instantiate it- | isInvisibleBinder ki_binder -- FunTy with InvisArg on LHS;- -- or ForAllTy with Inferred or Specified+ | isInvisiblePiTyBinder ki_binder -- FunTy with constraint on LHS;+ -- or ForAllTy with Inferred or Specified -> instantiate ki_binder inner_ki -- "normal" case@@ -1654,9 +1655,9 @@ -> do { traceTc "tcInferTyApps (vis normal app)" (vcat [ ppr ki_binder , ppr arg- , ppr (tyBinderType ki_binder)+ , ppr (piTyBinderType ki_binder) , ppr subst ])- ; let exp_kind = substTy subst $ tyBinderType ki_binder+ ; let exp_kind = substTy subst $ piTyBinderType ki_binder ; arg' <- addErrCtxt (funAppCtxt orig_hs_ty arg n) $ tc_lhs_type mode arg exp_kind ; traceTc "tcInferTyApps (vis normal app) 2" (ppr exp_kind)@@ -1669,7 +1670,7 @@ do { let arrows_needed = n_initial_val_args all_args ; co <- matchExpectedFunKind (HsTypeRnThing $ unLoc hs_ty) arrows_needed substed_fun_ki - ; fun' <- zonkTcType (fun `mkTcCastTy` co)+ ; fun' <- zonkTcType (fun `mkCastTy` co) -- This zonk is essential, to expose the fruits -- of matchExpectedFunKind to the 'go' loop @@ -1677,7 +1678,7 @@ vcat [ ppr fun <+> dcolon <+> ppr fun_ki , ppr arrows_needed , ppr co- , ppr fun' <+> dcolon <+> ppr (tcTypeKind fun')]+ , ppr fun' <+> dcolon <+> ppr (typeKind fun')] ; go_init n fun' all_args } -- Use go_init to establish go's INVARIANT where@@ -1696,7 +1697,7 @@ | otherwise = fallthrough - zapped_subst = zapTCvSubst subst+ zapped_subst = zapSubst subst substed_fun_ki = substTy subst fun_ki hs_ty = appTypeToArg orig_hs_ty (take (n-1) orig_hs_args) @@ -1707,20 +1708,17 @@ n_initial_val_args _ = 0 ty_app_err arg ty- = failWith $ TcRnUnknownMessage $ mkPlainError noHints $- text "Cannot apply function of kind" <+> quotes (ppr ty)- $$ text "to visible kind argument" <+> quotes (ppr arg)-+ = failWith $ TcRnInvalidVisibleKindArgument arg ty -mkAppTyM :: TCvSubst- -> TcType -> TyCoBinder -- fun, plus its top-level binder+mkAppTyM :: Subst+ -> TcType -> PiTyBinder -- fun, plus its top-level binder -> TcType -- arg- -> TcM (TCvSubst, TcType) -- Extended subst, plus (fun arg)+ -> TcM (Subst, TcType) -- Extended subst, plus (fun arg) -- Precondition: the application (fun arg) is well-kinded after zonking -- That is, the application makes sense -- -- Precondition: for (mkAppTyM subst fun bndr arg)--- tcTypeKind fun = Pi bndr. body+-- typeKind fun = Pi bndr. body -- That is, fun always has a ForAllTy or FunTy at the top -- and 'bndr' is fun's pi-binder --@@ -1728,7 +1726,7 @@ -- invariant, then so does the result type (fun arg) -- -- We do not require that--- tcTypeKind arg = tyVarKind (binderVar bndr)+-- typeKind arg = tyVarKind (binderVar bndr) -- This must be true after zonking (precondition 1), but it's not -- required for the (PKTI). mkAppTyM subst fun ki_binder arg@@ -1763,7 +1761,7 @@ (ppr fun <+> dcolon <+> ppr fun_kind $$ ppr arg) $ mkAppTy fun arg where- fun_kind = tcTypeKind fun+ fun_kind = typeKind fun isTrickyTvBinder :: TcTyVar -> Bool -- NB: isTrickyTvBinder is just an optimisation@@ -1774,14 +1772,14 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ During type inference, we maintain this invariant - (PKTI) It is legal to call 'tcTypeKind' on any Type ty,+ (PKTI) It is legal to call 'typeKind' on any Type ty, on any sub-term of ty, /without/ zonking ty Moreover, any such returned kind will itself satisfy (PKTI) -By "legal to call tcTypeKind" we mean "tcTypeKind will not crash".-The way in which tcTypeKind can crash is in applications+By "legal to call typeKind" we mean "typeKind will not crash".+The way in which typeKind can crash is in applications (a t1 t2 .. tn) if 'a' is a type variable whose kind doesn't have enough arrows or foralls. (The crash is in piResultTys.)@@ -1794,7 +1792,7 @@ a :: kappa then consider the type (a Int)-If we call tcTypeKind on that, we'll crash, because the (un-zonked)+If we call typeKind on that, we'll crash, because the (un-zonked) kind of 'a' is just kappa, not an arrow kind. So we must zonk first. So the type inference engine is very careful when building applications.@@ -1853,7 +1851,7 @@ saturateFamApp :: TcType -> TcKind -> TcM (TcType, TcKind) -- Precondition for (saturateFamApp ty kind):--- tcTypeKind ty = kind+-- typeKind ty = kind -- -- If 'ty' is an unsaturated family application with trailing -- invisible arguments, instantiate them.@@ -1861,10 +1859,10 @@ saturateFamApp ty kind | Just (tc, args) <- tcSplitTyConApp_maybe ty- , mustBeSaturated tc+ , tyConMustBeSaturated tc , let n_to_inst = tyConArity tc - length args = do { (extra_args, ki') <- tcInstInvisibleTyBindersN n_to_inst kind- ; return (ty `mkTcAppTys` extra_args, ki') }+ ; return (ty `mkAppTys` extra_args, ki') } | otherwise = return (ty, kind) @@ -1932,7 +1930,7 @@ , text "act_kind':" <+> ppr act_kind' , text "exp_kind:" <+> ppr exp_kind ] - ; let res_ty = ty `mkTcAppTys` new_args+ ; let res_ty = ty `mkAppTys` new_args ; if act_kind' `tcEqType` exp_kind then return res_ty -- This is very common@@ -1940,7 +1938,7 @@ ; traceTc "checkExpectedKind" (vcat [ ppr act_kind , ppr exp_kind , ppr co_k ])- ; return (res_ty `mkTcCastTy` co_k) } }+ ; return (res_ty `mkCastTy` co_k) } } where -- We need to make sure that both kinds have the same number of implicit -- foralls out front. If the actual kind has more, instantiate accordingly.@@ -2236,7 +2234,7 @@ -- in an unexpected place. The renamer rejects these wildcards in 'checkAnonWildcard', -- but it is possible for a wildcard to be introduced by a Template Haskell splice, -- as per #15433. To account for this, we throw a generic catch-all error message.- = failWith $ TcRnIllegalWildcardInType Nothing reason Nothing+ = failWith $ TcRnIllegalWildcardInType Nothing reason where reason = case is_extra of@@ -2460,7 +2458,7 @@ kcInferDeclHeader name flav (HsQTvs { hsq_ext = kv_ns , hsq_explicit = hs_tvs }) kc_res_ki- -- No standalane kind signature and no CUSK.+ -- No standalone kind signature and no CUSK. -- See Note [Required, Specified, and Inferred for types] in GHC.Tc.TyCl = addTyConFlavCtxt name flav $ do { (scoped_kvs, (tc_tvs, res_kind))@@ -2477,7 +2475,7 @@ -- recursive group. -- See Note [Inferring kinds for type declarations] in GHC.Tc.TyCl - tc_binders = mkAnonTyConBinders VisArg tc_tvs+ tc_binders = mkAnonTyConBinders tc_tvs -- Also, note that tc_binders has the tyvars from only the -- user-written tyvarbinders. See S1 in Note [How TcTyCons work] -- in GHC.Tc.TyCl@@ -2593,7 +2591,7 @@ -- Why? So that the TyConBinders of the TyCon will lexically scope over the -- associated types and methods of a class. ; let swizzle_env = mkVarEnv (map swap implicit_prs)- (subst, swizzled_tcbs) = mapAccumL (swizzleTcb swizzle_env) emptyTCvSubst all_tcbs+ (subst, swizzled_tcbs) = mapAccumL (swizzleTcb swizzle_env) emptySubst all_tcbs swizzled_kind = substTy subst tycon_res_kind all_tv_prs = mkTyVarNamePairs (binderVars swizzled_tcbs) @@ -2629,11 +2627,11 @@ -- Argument is excess TyConBinders and tail kind -> TcM ( [TcTyConBinder] -- Skolemised binders, with TcTyVars , a )--- See Note [Matching a kind sigature with a declaration]+-- See Note [Matching a kind signature with a declaration] -- Invariant: Length of returned TyConBinders + length of excess TyConBinders -- = length of incoming TyConBinders matchUpSigWithDecl sig_tcbs sig_res_kind hs_bndrs thing_inside- = go emptyTCvSubst sig_tcbs hs_bndrs+ = go emptySubst sig_tcbs hs_bndrs where go subst tcbs [] = do { let (subst', tcbs') = substTyConBindersX subst tcbs@@ -2641,7 +2639,7 @@ ; return ([], res) } go _ [] hs_bndrs- = failWithTc (tooManyBindersErr sig_res_kind hs_bndrs)+ = failWithTc (TcRnTooManyBinders sig_res_kind hs_bndrs) go subst (tcb : tcbs') hs_bndrs | Bndr tv vis <- tcb@@ -2675,16 +2673,16 @@ ; discardResult $ -- See Note [discardResult in kcCheckDeclHeader_sig] unifyKind (Just (NameThing hs_nm)) sig_kind expected_kind } -substTyConBinderX :: TCvSubst -> TyConBinder -> (TCvSubst, TyConBinder)+substTyConBinderX :: Subst -> TyConBinder -> (Subst, TyConBinder) substTyConBinderX subst (Bndr tv vis) = (subst', Bndr tv' vis) where (subst', tv') = substTyVarBndr subst tv -substTyConBindersX :: TCvSubst -> [TyConBinder] -> (TCvSubst, [TyConBinder])+substTyConBindersX :: Subst -> [TyConBinder] -> (Subst, [TyConBinder]) substTyConBindersX = mapAccumL substTyConBinderX -swizzleTcb :: VarEnv Name -> TCvSubst -> TyConBinder -> (TCvSubst, TyConBinder)+swizzleTcb :: VarEnv Name -> Subst -> TyConBinder -> (Subst, TyConBinder) swizzleTcb swizzle_env subst (Bndr tv vis) = (subst', Bndr tv2 vis) where@@ -2700,13 +2698,6 @@ -- See Note [Source locations for implicitly bound type variables] -- in GHC.Tc.Rename.HsType -tooManyBindersErr :: Kind -> [LHsTyVarBndr () GhcRn] -> TcRnMessage-tooManyBindersErr ki bndrs = TcRnUnknownMessage $ mkPlainError noHints $- hang (text "Not a function kind:")- 4 (ppr ki) $$- hang (text "but extra binders found:")- 4 (fsep (map ppr bndrs))- {- See Note [kcCheckDeclHeader_sig] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Given a kind signature 'sig_kind' and a declaration header,@@ -3022,15 +3013,7 @@ report_dup :: (Name,Name) -> TcM () report_dup (n1,n2) = setSrcSpan (getSrcSpan n2) $- addErrTc $ TcRnUnknownMessage $ mkPlainError noHints $- hang (text "Different names for the same type variable:") 2 info- where- info | nameOccName n1 /= nameOccName n2- = quotes (ppr n1) <+> text "and" <+> quotes (ppr n2)- | otherwise -- Same OccNames! See C2 in- -- Note [Swizzling the tyvars before generaliseTcTyCon]- = vcat [ quotes (ppr n1) <+> text "bound at" <+> ppr (getSrcLoc n1)- , quotes (ppr n2) <+> text "bound at" <+> ppr (getSrcLoc n2) ]+ addErrTc $ TcRnDifferentNamesForTyVar n1 n2 {- *********************************************************************@@ -3056,7 +3039,7 @@ , sm_tvtv = SMDSkolemTv skol_info } ; (req_tv_bndrs, thing) <- tcExplicitTKBndrsX skol_mode bndrs thing_inside -- req_tv_bndrs :: [VarBndr TyVar ()],- -- but we want [VarBndr TyVar ArgFlag]+ -- but we want [VarBndr TyVar ForAllTyFlag] ; return (tyVarReqToBinders req_tv_bndrs, thing) } HsForAllInvis { hsf_invis_bndrs = bndrs }@@ -3065,7 +3048,7 @@ , sm_tvtv = SMDSkolemTv skol_info } ; (inv_tv_bndrs, thing) <- tcExplicitTKBndrsX skol_mode bndrs thing_inside -- inv_tv_bndrs :: [VarBndr TyVar Specificity],- -- but we want [VarBndr TyVar ArgFlag]+ -- but we want [VarBndr TyVar ForAllTyFlag] ; return (tyVarSpecToBinders inv_tv_bndrs, thing) } --------------------------------------@@ -3197,19 +3180,19 @@ -- Push level, capture constraints, and emit an implication constraint. -- The implication constraint has a ForAllSkol ic_info, -- so that it is subject to a telescope test.-tcExplicitTKBndrsX skol_mode bndrs thing_inside- | null bndrs- = do { res <- thing_inside+tcExplicitTKBndrsX skol_mode bndrs thing_inside = case nonEmpty bndrs of+ Nothing -> do+ { res <- thing_inside ; return ([], res) } - | otherwise- = do { (tclvl, wanted, (skol_tvs, res))+ Just bndrs1 -> do+ { (tclvl, wanted, (skol_tvs, res)) <- pushLevelAndCaptureConstraints $ bindExplicitTKBndrsX skol_mode bndrs $ thing_inside -- Set up SkolemInfo for telescope test- ; let bndr_1 = head bndrs; bndr_n = last bndrs+ ; let bndr_1 = NE.head bndrs1; bndr_n = NE.last bndrs1 ; skol_info <- mkSkolemInfo (ForAllSkol (HsTyVarBndrsRn (unLoc <$> bndrs))) -- Notice that we use ForAllSkol here, ignoring the enclosing -- skol_info unlike tcImplicitTKBndrs, because the bad-telescope@@ -3254,14 +3237,14 @@ -> TcM ([TcTyVar], a) -- These do not clone: see Note [Cloning for type variable binders] bindExplicitTKBndrs_Q_Skol skol_info ctxt_kind hs_bndrs thing_inside- = liftFstM binderVars $+ = mapFst binderVars $ bindExplicitTKBndrsX (smVanilla { sm_clone = False, sm_parent = True , sm_kind = ctxt_kind, sm_tvtv = SMDSkolemTv skol_info }) hs_bndrs thing_inside -- sm_clone=False: see Note [Cloning for type variable binders] bindExplicitTKBndrs_Q_Tv ctxt_kind hs_bndrs thing_inside- = liftFstM binderVars $+ = mapFst binderVars $ bindExplicitTKBndrsX (smVanilla { sm_clone = False, sm_parent = True , sm_tvtv = SMDTyVarTv, sm_kind = ctxt_kind }) hs_bndrs thing_inside@@ -3516,6 +3499,8 @@ bindTyClTyVarsAndZonk :: Name -> ([TyConBinder] -> Kind -> TcM a) -> TcM a -- Like bindTyClTyVars, but in addition -- zonk the skolem TcTyVars of a PolyTcTyCon to TyVars+-- We always do this same zonking after a call to bindTyClTyVars, but+-- here we do it right away because there are no more unifications to come bindTyClTyVarsAndZonk tycon_name thing_inside = bindTyClTyVars tycon_name $ \ tc_bndrs tc_kind -> do { ze <- mkEmptyZonkEnv NoFlexi@@ -3533,7 +3518,7 @@ zonkAndScopedSort :: [TcTyVar] -> TcM [TcTyVar] zonkAndScopedSort spec_tkvs = do { spec_tkvs <- zonkTcTyVarsToTcTyVars spec_tkvs- -- Zonk the kinds, to we can do the dependency analayis+ -- Zonk the kinds, to we can do the dependency analysis -- Do a stable topological sort, following -- Note [Ordering of implicit variables] in GHC.Rename.HsType@@ -3631,7 +3616,7 @@ * Find the free variables with level > i, in this case gamma[i] * Skolemise those free variables and quantify over them, giving f :: forall g. beta[i-1] -> g- * Emit the residiual constraint wrapped in an implication for g,+ * Emit the residual constraint wrapped in an implication for g, thus forall g. WC All of this happens for types too. Consider@@ -3690,7 +3675,7 @@ = return ([], res_kind) where tyvars = binderVars tcbs- in_scope = mkInScopeSet (mkVarSet tyvars)+ in_scope = mkInScopeSetList tyvars avoid_occs = map getOccName tyvars needsEtaExpansion :: TyConFlavour -> Bool@@ -3718,14 +3703,12 @@ ; uniqs <- newUniqueSupply ; rdr_env <- getLocalRdrEnv ; lvl <- getTcLevel- ; let new_occs = [ occ- | str <- allNameStrings- , let occ = mkOccName tvName str- , isNothing (lookupLocalRdrOcc rdr_env occ)- -- Note [Avoid name clashes for associated data types]- , not (occ `elem` avoid_occs) ]+ ; let new_occs = Inf.filter (\ occ ->+ isNothing (lookupLocalRdrOcc rdr_env occ) &&+ -- Note [Avoid name clashes for associated data types]+ not (occ `elem` avoid_occs)) $ mkOccName tvName <$> allNameStrings new_uniqs = uniqsFromSupply uniqs- subst = mkEmptyTCvSubst in_scope+ subst = mkEmptySubst in_scope details = SkolemTv skol_info (pushTcLevel lvl) False -- As always, allocate skolems one level in @@ -3733,16 +3716,16 @@ = case splitPiTy_maybe kind of Nothing -> (reverse acc, substTy subst kind) - Just (Anon af arg, kind')+ Just (Anon arg af, kind') -> go occs' uniqs' subst' (tcb : acc) kind' where tcb = Bndr tv (AnonTCB af) arg' = substTy subst (scaledThing arg) name = mkInternalName uniq occ loc tv = mkTcTyVar name arg' details- subst' = extendTCvInScope subst tv- (uniq:uniqs') = uniqs- (occ:occs') = occs+ subst' = extendSubstInScope subst tv+ uniq:uniqs' = uniqs+ Inf occ occs' = occs Just (Named (Bndr tv vis), kind') -> go occs uniqs subst' (tcb : acc) kind'@@ -3753,34 +3736,9 @@ ; return (go new_occs new_uniqs subst [] kind) } --- | A description of whether something is a------ * @data@ or @newtype@ ('DataDeclSort')------ * @data instance@ or @newtype instance@ ('DataInstanceSort')------ * @data family@ ('DataFamilySort')------ At present, this data type is only consumed by 'checkDataKindSig'.-data DataSort- = DataDeclSort NewOrData- | DataInstanceSort NewOrData- | DataFamilySort---- | Local helper type used in 'checkDataKindSig'.------ Superficially similar to 'ContextKind', but it lacks 'AnyKind'--- and 'AnyBoxedKind', and instead of @'TheKind' liftedTypeKind@--- provides 'LiftedKind', which is much simpler to match on and--- handle in 'isAllowedDataResKind'.-data AllowedDataResKind- = AnyTYPEKind- | AnyBoxedKind- | LiftedKind- isAllowedDataResKind :: AllowedDataResKind -> Kind -> Bool-isAllowedDataResKind AnyTYPEKind kind = tcIsRuntimeTypeKind kind-isAllowedDataResKind AnyBoxedKind kind = tcIsBoxedTypeKind kind+isAllowedDataResKind AnyTYPEKind kind = isTypeLikeKind kind+isAllowedDataResKind AnyBoxedKind kind = tcIsBoxedTypeKind kind isAllowedDataResKind LiftedKind kind = tcIsLiftedTypeKind kind -- | Checks that the return kind in a data declaration's kind signature is@@ -3814,15 +3772,6 @@ -- Look for the result kind after -- peeling off any foralls and arrows - pp_dec :: SDoc- pp_dec = text $- case data_sort of- DataDeclSort DataType -> "Data type"- DataDeclSort NewType -> "Newtype"- DataInstanceSort DataType -> "Data instance"- DataInstanceSort NewType -> "Newtype instance"- DataFamilySort -> "Data family"- is_newtype :: Bool is_newtype = case data_sort of@@ -3872,59 +3821,46 @@ -- In the particular case of a data family, permit a return kind of the -- form `:: k` (where `k` is a bare kind variable). is_kind_var :: Bool- is_kind_var | is_data_family = isJust (tcGetCastedTyVar_maybe res_kind)+ is_kind_var | is_data_family = isJust (getCastedTyVar_maybe res_kind) | otherwise = False - pp_allowed_kind dflags =- case allowed_kind dflags of- AnyTYPEKind -> ppr tYPETyCon- AnyBoxedKind -> ppr boxedRepDataConTyCon- LiftedKind -> ppr liftedTypeKind- err_msg :: DynFlags -> TcRnMessage- err_msg dflags = TcRnUnknownMessage $ mkPlainError noHints $- sep [ sep [ pp_dec <+>- text "has non-" <>- pp_allowed_kind dflags- , (if is_data_family then text "and non-variable" else empty) <+>- text "return kind" <+> quotes (ppr kind) ]- , ext_hint dflags ]+ err_msg dflags =+ TcRnInvalidReturnKind data_sort (allowed_kind dflags) kind (ext_hint dflags) ext_hint dflags- | tcIsRuntimeTypeKind kind+ | isTypeLikeKind kind , is_newtype , not (xopt LangExt.UnliftedNewtypes dflags)- = text "Perhaps you intended to use UnliftedNewtypes"+ = Just SuggestUnliftedNewtypes | tcIsBoxedTypeKind kind , is_datatype , not (xopt LangExt.UnliftedDatatypes dflags)- = text "Perhaps you intended to use UnliftedDatatypes"+ = Just SuggestUnliftedDatatypes | otherwise- = empty+ = Nothing -- | Checks that the result kind of a class is exactly `Constraint`, rejecting -- type synonyms and type families that reduce to `Constraint`. See #16826. checkClassKindSig :: Kind -> TcM ()-checkClassKindSig kind = checkTc (tcIsConstraintKind kind) err_msg+checkClassKindSig kind = checkTc (isConstraintKind kind) err_msg where err_msg :: TcRnMessage- err_msg = TcRnUnknownMessage $ mkPlainError noHints $- text "Kind signature on a class must end with" <+> ppr constraintKind $$- text "unobscured by type families"+ err_msg = TcRnClassKindNotConstraint kind tcbVisibilities :: TyCon -> [Type] -> [TyConBndrVis] -- Result is in 1-1 correspondence with orig_args tcbVisibilities tc orig_args = go (tyConKind tc) init_subst orig_args where- init_subst = mkEmptyTCvSubst (mkInScopeSet (tyCoVarsOfTypes orig_args))+ init_subst = mkEmptySubst (mkInScopeSet (tyCoVarsOfTypes orig_args)) go _ _ [] = [] go fun_kind subst all_args@(arg : args) | Just (tcb, inner_kind) <- splitPiTy_maybe fun_kind = case tcb of- Anon af _ -> AnonTCB af : go inner_kind subst args+ Anon _ af -> AnonTCB af : go inner_kind subst args Named (Bndr tv vis) -> NamedTCB vis : go inner_kind subst' args where subst' = extendTCvSubst subst tv arg@@ -4030,7 +3966,9 @@ (theta, wcx) <- tcPartialContext mode hs_ctxt ; ek <- newOpenTypeKind- ; tau <- addTypeCtxt hs_tau $+ ; tau <- -- Don't do (addTypeCtxt hs_tau) here else we get+ -- In the type <blah>+ -- In the type signature: foo :: <blah> tc_lhs_type mode hs_tau ek ; return (wcs, wcx, theta, tau) }@@ -4042,7 +3980,7 @@ -- No kind-generalization here: ; kindGeneralizeNone (mkInvisForAllTys outer_tv_bndrs $- mkPhiTy theta $+ tcMkPhiTy theta $ tau) -- Spit out the wildcards (including the extra-constraints one)@@ -4050,12 +3988,13 @@ -- See Note [Extra-constraint holes in partial type signatures] ; mapM_ emitNamedTypeHole wcs - -- Zonk, so that any nested foralls can "see" their occurrences- -- See Note [Checking partial type signatures], and in particular- -- Note [Levels for wildcards]- ; outer_tv_bndrs <- mapM zonkInvisTVBinder outer_tv_bndrs- ; theta <- mapM zonkTcType theta- ; tau <- zonkTcType tau+ -- The "tau" from tcHsPartialSigType might very well have some foralls+ -- at the top, hidden behind a type synonym. Instantiate them! E.g.+ -- type T x = forall b. x -> b -> b+ -- f :: forall a. T (a,_)+ -- We must instantiate the `forall b` just as we do the `forall a`!+ -- Missing this led to #21667.+ ; (tv_prs', theta', tau) <- tcInstTypeBndrs tau -- We return a proper (Name,InvisTVBinder) environment, to be sure that -- we bring the right name into scope in the function body.@@ -4065,6 +4004,13 @@ tv_prs :: [(Name,InvisTVBinder)] tv_prs = outer_bndr_names `zip` outer_tv_bndrs + -- Zonk, so that any nested foralls can "see" their occurrences+ -- See Note [Checking partial type signatures], and in particular+ -- Note [Levels for wildcards]+ ; tv_prs <- mapSndM zonkInvisTVBinder (tv_prs ++ tv_prs')+ ; theta <- mapM zonkTcType (theta ++ theta')+ ; tau <- zonkTcType tau+ -- NB: checkValidType on the final inferred type will be -- done later by checkInferredPolyId. We can't do it -- here because we don't have a complete type to check@@ -4117,7 +4063,7 @@ source-code LHsSigWcType * Then, for f and g /separately/, we call tcInstSig, which in turn- call tchsPartialSig (defined near this Note). It kind-checks the+ call tcHsPartialSig (defined near this Note). It kind-checks the LHsSigWcType, creating fresh unification variables for each "_" wildcard. It's important that the wildcards for f and g are distinct because they might get instantiated completely differently. E.g.@@ -4130,7 +4076,7 @@ * Just as for ordinary signatures, we must solve local equalities and zonk the type after kind-checking it, to ensure that all the nested- forall binders can "see" their occurrenceds+ forall binders can "see" their occurrences Just as for ordinary signatures, this zonk also gets any Refl casts out of the way of instantiation. Example: #18008 had@@ -4202,9 +4148,6 @@ * Because it is ill-kinded (unifying something of kind Constraint with something of kind Type), it should trip an assert in writeMetaTyVarRef.- However, writeMetaTyVarRef uses eqType, not tcEqType, to avoid falling- over in this scenario (and another scenario, as detailed in- Note [coreView vs tcView] in GHC.Core.Type). Result works fine, but it may eventually bite us. @@ -4378,22 +4321,7 @@ promotionErr :: Name -> PromotionErr -> TcM a promotionErr name err- = failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $- (hang (pprPECategory err <+> quotes (ppr name) <+> text "cannot be used here")- 2 (parens reason))- where- reason = case err of- ConstrainedDataConPE pred- -> text "it has an unpromotable context"- <+> quotes (ppr pred)- FamDataConPE -> text "it comes from a data family instance"- NoDataKindsDC -> text "perhaps you intended to use DataKinds"- PatSynPE -> text "pattern synonyms cannot be promoted"- RecDataConPE -> same_rec_group_msg- ClassPE -> same_rec_group_msg- TyConPE -> same_rec_group_msg-- same_rec_group_msg = text "it is defined and used in the same recursive group"+ = failWithTc $ TcRnUnpromotableThing name err {- ************************************************************************
compiler/GHC/Tc/Gen/Match.hs view
@@ -71,7 +71,6 @@ import GHC.Utils.Misc import GHC.Driver.Session ( getDynFlags ) -import GHC.Types.Error import GHC.Types.Fixity (LexicalFixity(..)) import GHC.Types.Name import GHC.Types.Id@@ -79,6 +78,7 @@ import Control.Monad import Control.Arrow ( second )+import qualified Data.List.NonEmpty as NE {- ************************************************************************@@ -111,7 +111,7 @@ ; matchExpectedFunTys herald ctxt arity exp_ty $ \ pat_tys rhs_ty -> -- NB: exp_type may be polymorphic, but -- matchExpectedFunTys can cope with that- tcScalingUsage Many $+ tcScalingUsage ManyTy $ -- toplevel bindings and let bindings are, at the -- moment, always unrestricted. The value being bound -- must, accordingly, be unrestricted. Hence them@@ -170,7 +170,7 @@ -> TcM (GRHSs GhcTc (LHsExpr GhcTc)) -- Used for pattern bindings tcGRHSsPat grhss res_ty- = tcScalingUsage Many $+ = tcScalingUsage ManyTy $ -- Like in tcMatchesFun, this scaling happens because all -- let bindings are unrestricted. A difference, here, is -- that when this is not the case, any more, we will have to@@ -215,7 +215,7 @@ -> TcM (MatchGroup GhcTc (LocatedA (body GhcTc))) tcMatches ctxt pat_tys rhs_ty (MG { mg_alts = L l matches- , mg_origin = origin })+ , mg_ext = origin }) | null matches -- Deal with case e of {} -- Since there are no branches, no one else will fill in rhs_ty -- when in inference mode, so we must do it ourselves,@@ -224,8 +224,8 @@ ; pat_tys <- mapM scaledExpTypeToType pat_tys ; rhs_ty <- expTypeToType rhs_ty ; return (MG { mg_alts = L l []- , mg_ext = MatchGroupTc pat_tys rhs_ty- , mg_origin = origin }) }+ , mg_ext = MatchGroupTc pat_tys rhs_ty origin+ }) } | otherwise = do { umatches <- mapM (tcCollectingUsage . tcMatch ctxt pat_tys rhs_ty) matches@@ -234,8 +234,8 @@ ; pat_tys <- mapM readScaledExpType pat_tys ; rhs_ty <- readExpType rhs_ty ; return (MG { mg_alts = L l matches'- , mg_ext = MatchGroupTc pat_tys rhs_ty- , mg_origin = origin }) }+ , mg_ext = MatchGroupTc pat_tys rhs_ty origin+ }) } ------------- tcMatch :: (AnnoBody body) => TcMatchCtxt body@@ -413,7 +413,7 @@ tcGuardStmt :: TcExprStmtChecker tcGuardStmt _ (BodyStmt _ guard _ _) res_ty thing_inside- = do { guard' <- tcScalingUsage Many $ tcCheckMonoExpr guard boolTy+ = do { guard' <- tcScalingUsage ManyTy $ tcCheckMonoExpr guard boolTy -- Scale the guard to Many (see #19120 and #19193) ; thing <- thing_inside res_ty ; return (BodyStmt boolTy guard' noSyntaxExpr noSyntaxExpr, thing) }@@ -426,7 +426,7 @@ -- The multiplicity of x in u must be the same as the multiplicity at -- which the rhs has been consumed. When solving #18738, we want these -- two multiplicity to still be the same.- (rhs', rhs_ty) <- tcScalingUsage Many $ tcInferRhoNC rhs+ (rhs', rhs_ty) <- tcScalingUsage ManyTy $ tcInferRhoNC rhs -- Stmt has a context already ; hasFixedRuntimeRep_syntactic FRRBindStmtGuard rhs_ty ; (pat', thing) <- tcCheckPat_O (StmtCtxt ctxt) (lexprCtOrigin rhs)@@ -534,11 +534,11 @@ -- typically something like [(Int,Bool,Int)] -- We don't know what tuple_ty is yet, so we use a variable ; let mk_n_bndr :: Name -> TcId -> TcId- mk_n_bndr n_bndr_name bndr_id = mkLocalId n_bndr_name Many (n_app (idType bndr_id))+ mk_n_bndr n_bndr_name bndr_id = mkLocalId n_bndr_name ManyTy (n_app (idType bndr_id)) -- Ensure that every old binder of type `b` is linked up with its -- new binder which should have type `n b`- -- See Note [GroupStmt binder map] in GHC.Hs.Expr+ -- See Note [TransStmt binder map] in GHC.Hs.Expr n_bndr_ids = zipWith mk_n_bndr n_bndr_names bndr_ids bindersMap' = bndr_ids `zip` n_bndr_ids @@ -727,11 +727,11 @@ --------------- Building the bindersMap ---------------- ; let mk_n_bndr :: Name -> TcId -> TcId- mk_n_bndr n_bndr_name bndr_id = mkLocalId n_bndr_name Many (n_app (idType bndr_id))+ mk_n_bndr n_bndr_name bndr_id = mkLocalId n_bndr_name ManyTy (n_app (idType bndr_id)) -- Ensure that every old binder of type `b` is linked up with its -- new binder which should have type `n b`- -- See Note [GroupStmt binder map] in GHC.Hs.Expr+ -- See Note [TransStmt binder map] in GHC.Hs.Expr n_bndr_ids = zipWithEqual "tcMcStmt" mk_n_bndr n_bndr_names bndr_ids bindersMap' = bndr_ids `zip` n_bndr_ids @@ -908,7 +908,7 @@ res_ty thing_inside = do { let tup_names = rec_names ++ filterOut (`elem` rec_names) later_names ; tup_elt_tys <- newFlexiTyVarTys (length tup_names) liftedTypeKind- ; let tup_ids = zipWith (\n t -> mkLocalId n Many t) tup_names tup_elt_tys+ ; let tup_ids = zipWith (\n t -> mkLocalId n ManyTy t) tup_names tup_elt_tys -- Many because it's a recursive definition tup_ty = mkBigCoreTupTy tup_elt_tys @@ -997,7 +997,7 @@ the expected/inferred stuff is back to front (see #3613). Note [typechecking ApplicativeStmt]-+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ join ((\pat1 ... patn -> body) <$> e1 <*> ... <*> en) fresh type variables:@@ -1138,18 +1138,14 @@ checkArgCounts _ (MG { mg_alts = L _ [] }) = return () checkArgCounts matchContext (MG { mg_alts = L _ (match1:matches) })- | not (null bad_matches)- = failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $- (vcat [ err_msg <+>- text "have different numbers of arguments"- , nest 2 (ppr (getLocA match1))- , nest 2 (ppr (getLocA (head bad_matches)))])+ | Just bad_matches <- mb_bad_matches+ = failWithTc $ TcRnMatchesHaveDiffNumArgs matchContext+ $ MatchArgMatches match1 bad_matches | otherwise = return () where n_args1 = args_in_match match1- bad_matches = [m | m <- matches, args_in_match m /= n_args1]- err_msg = pprMatchContextNouns matchContext+ mb_bad_matches = NE.nonEmpty [m | m <- matches, args_in_match m /= n_args1] args_in_match :: (LocatedA (Match GhcRn body1) -> Int) args_in_match (L _ (Match { m_pats = pats })) = length pats
compiler/GHC/Tc/Gen/Pat.hs view
@@ -1,4 +1,3 @@- {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE RankNTypes #-}@@ -21,7 +20,6 @@ , tcCheckPat, tcCheckPat_O, tcInferPat , tcPats , addDataConStupidTheta- , polyPatSig ) where @@ -37,7 +35,7 @@ import GHC.Tc.Gen.Sig( TcPragEnv, lookupPragEnv, addInlinePrags ) import GHC.Tc.Utils.Monad import GHC.Tc.Utils.Instantiate-import GHC.Types.Error+import GHC.Types.FieldLabel import GHC.Types.Id import GHC.Types.Var import GHC.Types.Name@@ -56,6 +54,7 @@ import GHC.Tc.Types.Origin import GHC.Core.TyCon import GHC.Core.Type+import GHC.Core.Coercion import GHC.Core.DataCon import GHC.Core.PatSyn import GHC.Core.ConLike@@ -71,8 +70,14 @@ import qualified GHC.LanguageExtensions as LangExt import Control.Arrow ( second ) import Control.Monad+import GHC.Data.FastString+import qualified Data.List.NonEmpty as NE+ import GHC.Data.List.SetOps ( getNth )+import Language.Haskell.Syntax.Basic (FieldLabelString(..)) +import Data.List( partition )+ {- ************************************************************************ * *@@ -231,7 +236,7 @@ -- level, we'd be in checking mode (see tcConArg) -- hence this assertion do { bndr_ty <- inferResultToType infer_res- ; return (mkTcNomReflCo bndr_ty, bndr_ty) }+ ; return (mkNomReflCo bndr_ty, bndr_ty) } ; let bndr_mult = scaledMult exp_pat_ty ; bndr_id <- newLetBndr no_gen bndr_name bndr_mult bndr_ty ; traceTc "tcPatBndr(nosig)" (vcat [ ppr bind_lvl@@ -313,6 +318,11 @@ , r -- Result of thing inside ) +tcMultiple_ :: Checker inp () -> PatEnv -> [inp] -> TcM r -> TcM r+tcMultiple_ tc_pat penv args thing_inside+ = do { (_, res) <- tcMultiple tc_pat penv args thing_inside+ ; return res }+ tcMultiple :: Checker inp out -> Checker [inp] [out] tcMultiple tc_pat penv args thing_inside = do { err_ctxt <- getErrCtxt@@ -352,7 +362,7 @@ -------------------- -- See Note [Wrapper returned from tcSubMult] in GHC.Tc.Utils.Unify. checkManyPattern :: Scaled a -> TcM HsWrapper-checkManyPattern pat_ty = tcSubMult NonLinearPatternOrigin Many (scaledMult pat_ty)+checkManyPattern pat_ty = tcSubMult NonLinearPatternOrigin ManyTy (scaledMult pat_ty) tc_pat :: Scaled ExpSigmaTypeFRR -- ^ Fully refined result type@@ -391,7 +401,7 @@ -- Check that the expected pattern type is itself lifted ; pat_ty <- readExpType (scaledThing pat_ty)- ; _ <- unifyType Nothing (tcTypeKind pat_ty) liftedTypeKind+ ; _ <- unifyType Nothing (typeKind pat_ty) liftedTypeKind ; return (mkHsWrapPat mult_wrap (LazyPat x pat') pat_ty, res) } @@ -402,7 +412,7 @@ ; pat_ty <- expTypeToType (scaledThing pat_ty) ; return (mkHsWrapPat mult_wrap (WildPat pat_ty) pat_ty, res) } - AsPat x (L nm_loc name) pat -> do+ AsPat x (L nm_loc name) at pat -> do { mult_wrap <- checkManyPattern pat_ty -- See Note [Wrapper returned from tcSubMult] in GHC.Tc.Utils.Unify. ; (wrap, bndr_id) <- setSrcSpanA nm_loc (tcPatBndr penv name pat_ty)@@ -417,7 +427,7 @@ -- -- If you fix it, don't forget the bindInstsOfPatIds! ; pat_ty <- readExpType (scaledThing pat_ty)- ; return (mkHsWrapPat (wrap <.> mult_wrap) (AsPat x (L nm_loc bndr_id) pat') pat_ty, res) }+ ; return (mkHsWrapPat (wrap <.> mult_wrap) (AsPat x (L nm_loc bndr_id) at pat') pat_ty, res) } ViewPat _ expr pat -> do { mult_wrap <- checkManyPattern pat_ty@@ -685,16 +695,14 @@ ge' minus'' ; return (mkHsWrapPat mult_wrap pat' pat_ty, res) } --- HsSpliced is an annotation produced by 'GHC.Rename.Splice.rnSplicePat'. -- Here we get rid of it and add the finalizers to the global environment.--- -- See Note [Delaying modFinalizers in untyped splices] in GHC.Rename.Splice.- SplicePat _ splice -> case splice of- (HsSpliced _ mod_finalizers (HsSplicedPat pat)) -> do+ SplicePat (HsUntypedSpliceTop mod_finalizers pat) _ -> do { addModFinalizersWithLclEnv mod_finalizers ; tc_pat pat_ty penv pat thing_inside }- _ -> panic "invalid splice in splice pat" + SplicePat (HsUntypedSpliceNested _) _ -> panic "tc_pat: nested splice in splice pat"+ XPat (HsPatExpanded lpat rpat) -> do { (rpat', res) <- tc_pat pat_ty penv rpat thing_inside ; return (XPat $ ExpansionPat lpat rpat', res) }@@ -745,26 +753,29 @@ -- and not already in scope. These are the ones -- that should be brought into scope - ; if null sig_tvs then do {+ ; case NE.nonEmpty sig_tvs of+ Nothing -> do { -- Just do the subsumption check and return wrap <- addErrCtxtM (mk_msg sig_ty) $ tcSubTypePat PatSigOrigin PatSigCtxt res_ty sig_ty ; return (sig_ty, [], sig_wcs, wrap)- } else do+ }+ Just sig_tvs_ne -> do -- Type signature binds at least one scoped type variable -- A pattern binding cannot bind scoped type variables -- It is more convenient to make the test here -- than in the renamer- { when in_pat_bind (addErr (patBindSigErr sig_tvs))+ when in_pat_bind+ (addErr (TcRnCannotBindScopedTyVarInPatSig sig_tvs_ne)) - -- Now do a subsumption check of the pattern signature against res_ty- ; wrap <- addErrCtxtM (mk_msg sig_ty) $- tcSubTypePat PatSigOrigin PatSigCtxt res_ty sig_ty+ -- Now do a subsumption check of the pattern signature against res_ty+ wrap <- addErrCtxtM (mk_msg sig_ty) $+ tcSubTypePat PatSigOrigin PatSigCtxt res_ty sig_ty - -- Phew!- ; return (sig_ty, sig_tvs, sig_wcs, wrap)- } }+ -- Phew!+ return (sig_ty, sig_tvs, sig_wcs, wrap)+ } where mk_msg sig_ty tidy_env = do { (tidy_env, sig_ty) <- zonkTidyTcType tidy_env sig_ty@@ -776,14 +787,7 @@ 2 (ppr res_ty)) ] ; return (tidy_env, msg) } -patBindSigErr :: [(Name,TcTyVar)] -> TcRnMessage-patBindSigErr sig_tvs- = TcRnUnknownMessage $ mkPlainError noHints $- hang (text "You cannot bind scoped type variable" <> plural sig_tvs- <+> pprQuotedList (map fst sig_tvs))- 2 (text "in a pattern binding signature") - {- ********************************************************************* * * Most of the work for constructors is here@@ -865,10 +869,10 @@ tcConPat penv con_lname@(L _ con_name) pat_ty arg_pats thing_inside = do { con_like <- tcLookupConLike con_name ; case con_like of- RealDataCon data_con -> tcDataConPat penv con_lname data_con- pat_ty arg_pats thing_inside- PatSynCon pat_syn -> tcPatSynPat penv con_lname pat_syn- pat_ty arg_pats thing_inside+ RealDataCon data_con -> tcDataConPat con_lname data_con pat_ty+ penv arg_pats thing_inside+ PatSynCon pat_syn -> tcPatSynPat con_lname pat_syn pat_ty+ penv arg_pats thing_inside } -- Warn when pattern matching on a GADT or a pattern synonym@@ -884,12 +888,11 @@ -- In #20485 this was made into a warning. } -tcDataConPat :: PatEnv -> LocatedN Name -> DataCon+tcDataConPat :: LocatedN Name -> DataCon -> Scaled ExpSigmaTypeFRR -- Type of the pattern- -> HsConPatDetails GhcRn -> TcM a- -> TcM (Pat GhcTc, a)-tcDataConPat penv (L con_span con_name) data_con pat_ty_scaled- arg_pats thing_inside+ -> Checker (HsConPatDetails GhcRn) (Pat GhcTc)+tcDataConPat (L con_span con_name) data_con pat_ty_scaled+ penv arg_pats thing_inside = do { let tycon = dataConTyCon data_con -- For data families this is the representation tycon (univ_tvs, ex_tvs, eq_spec, theta, arg_tys, _)@@ -925,21 +928,15 @@ -- Why "super"? See Note [Binding when looking up instances] -- in GHC.Core.InstEnv. - ; let arg_tys' = substScaledTys tenv arg_tys- pat_mult = scaledMult pat_ty_scaled+ ; let arg_tys' = substScaledTys tenv arg_tys+ pat_mult = scaledMult pat_ty_scaled arg_tys_scaled = map (scaleScaled pat_mult) arg_tys'+ con_like = RealDataCon data_con -- This check is necessary to uphold the invariant that 'tcConArgs' -- is given argument types with a fixed runtime representation. -- See test case T20363.- ; zipWithM_- ( \ i arg_sty ->- hasFixedRuntimeRep_syntactic- (FRRDataConArg Pattern data_con i)- (scaledThing arg_sty)- )- [1..]- arg_tys'+ ; checkFixedRuntimeRep data_con arg_tys' ; traceTc "tcConPat" (vcat [ text "con_name:" <+> ppr con_name , text "univ_tvs:" <+> pprTyVars univ_tvs@@ -951,11 +948,15 @@ , text "pat_ty:" <+> ppr pat_ty , text "arg_tys':" <+> ppr arg_tys' , text "arg_pats" <+> ppr arg_pats ])++ ; (univ_ty_args, ex_ty_args) <- splitConTyArgs con_like arg_pats+ ; if null ex_tvs && null eq_spec && null theta then do { -- The common case; no class bindings etc -- (see Note [Arrows and patterns])- (arg_pats', res) <- tcConArgs (RealDataCon data_con) arg_tys_scaled- tenv penv arg_pats thing_inside+ (arg_pats', res) <- tcConTyArgs tenv penv univ_ty_args $+ tcConValArgs con_like arg_tys_scaled+ penv arg_pats thing_inside ; let res_pat = ConPat { pat_con = header , pat_args = arg_pats' , pat_con_ext = ConPatTc@@ -978,8 +979,11 @@ ; given <- newEvVars theta' ; (ev_binds, (arg_pats', res))- <- checkConstraints (getSkolemInfo skol_info) ex_tvs' given $- tcConArgs (RealDataCon data_con) arg_tys_scaled tenv penv arg_pats thing_inside+ <- -- See Note [Type applications in patterns] (W4)+ tcConTyArgs tenv penv univ_ty_args $+ checkConstraints (getSkolemInfo skol_info) ex_tvs' given $+ tcConTyArgs tenv penv ex_ty_args $+ tcConValArgs con_like arg_tys_scaled penv arg_pats thing_inside ; let res_pat = ConPat { pat_con = header@@ -995,11 +999,10 @@ ; return (mkHsWrapPat wrap res_pat pat_ty, res) } } -tcPatSynPat :: PatEnv -> LocatedN Name -> PatSyn+tcPatSynPat :: LocatedN Name -> PatSyn -> Scaled ExpSigmaType -- ^ Type of the pattern- -> HsConPatDetails GhcRn -> TcM a- -> TcM (Pat GhcTc, a)-tcPatSynPat penv (L con_span con_name) pat_syn pat_ty arg_pats thing_inside+ -> Checker (HsConPatDetails GhcRn) (Pat GhcTc)+tcPatSynPat (L con_span con_name) pat_syn pat_ty penv arg_pats thing_inside = do { let (univ_tvs, req_theta, ex_tvs, prov_theta, arg_tys, ty) = patSynSig pat_syn ; (subst, univ_tvs') <- newMetaTyVars univ_tvs@@ -1022,23 +1025,27 @@ arg_tys_scaled = map (scaleScaled pat_mult) arg_tys' prov_theta' = substTheta tenv prov_theta req_theta' = substTheta tenv req_theta+ con_like = PatSynCon pat_syn ; when (any isEqPred prov_theta) warnMonoLocalBinds ; mult_wrap <- checkManyPattern pat_ty -- See Note [Wrapper returned from tcSubMult] in GHC.Tc.Utils.Unify. - ; wrap <- tc_sub_type penv (scaledThing pat_ty) ty'- ; traceTc "tcPatSynPat" (ppr pat_syn $$- ppr pat_ty $$- ppr ty' $$- ppr ex_tvs' $$- ppr prov_theta' $$- ppr req_theta' $$- ppr arg_tys')+ ; (univ_ty_args, ex_ty_args) <- splitConTyArgs con_like arg_pats - ; prov_dicts' <- newEvVars prov_theta'+ ; wrap <- tc_sub_type penv (scaledThing pat_ty) ty' + ; traceTc "tcPatSynPat" $+ vcat [ text "Pat syn:" <+> ppr pat_syn+ , text "Expected type:" <+> ppr pat_ty+ , text "Pat res ty:" <+> ppr ty'+ , text "ex_tvs':" <+> pprTyVars ex_tvs'+ , text "prov_theta':" <+> ppr prov_theta'+ , text "req_theta':" <+> ppr req_theta'+ , text "arg_tys':" <+> ppr arg_tys'+ , text "univ_ty_args:" <+> ppr univ_ty_args+ , text "ex_ty_args:" <+> ppr ex_ty_args ] ; req_wrap <- instCall (OccurrenceOf con_name) (mkTyVarTys univ_tvs') req_theta' -- Origin (OccurrenceOf con_name):@@ -1059,11 +1066,16 @@ , text "bad_arg_tys:" <+> ppr bad_arg_tys ] ; traceTc "checkConstraints {" Outputable.empty+ ; prov_dicts' <- newEvVars prov_theta' ; (ev_binds, (arg_pats', res))- <- checkConstraints (getSkolemInfo skol_info) ex_tvs' prov_dicts' $- tcConArgs (PatSynCon pat_syn) arg_tys_scaled tenv penv arg_pats thing_inside-+ <- -- See Note [Type applications in patterns] (W4)+ tcConTyArgs tenv penv univ_ty_args $+ checkConstraints (getSkolemInfo skol_info) ex_tvs' prov_dicts' $+ tcConTyArgs tenv penv ex_ty_args $+ tcConValArgs con_like arg_tys_scaled penv arg_pats $+ thing_inside ; traceTc "checkConstraints }" (ppr ev_binds)+ ; let res_pat = ConPat { pat_con = L con_span $ PatSynCon pat_syn , pat_args = arg_pats' , pat_con_ext = ConPatTc@@ -1077,6 +1089,14 @@ ; pat_ty <- readExpType (scaledThing pat_ty) ; return (mkHsWrapPat (wrap <.> mult_wrap) res_pat pat_ty, res) } +checkFixedRuntimeRep :: DataCon -> [Scaled TcSigmaTypeFRR] -> TcM ()+checkFixedRuntimeRep data_con arg_tys+ = zipWithM_ check_one [1..] arg_tys+ where+ check_one i arg_ty = hasFixedRuntimeRep_syntactic+ (FRRDataConPatArg data_con i)+ (scaledThing arg_ty)+ {- Note [Call-stack tracing of pattern synonyms] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Consider@@ -1111,7 +1131,7 @@ ; (wrap, pat_rho) <- topInstantiate orig pat_ty ; (co, res) <- inner_match pat_rho ; traceTc "matchExpectedPatTy" (ppr pat_ty $$ ppr wrap)- ; return (mkWpCastN (mkTcSymCo co) <.> wrap, res) }+ ; return (mkWpCastN (mkSymCo co) <.> wrap, res) } ---------------------------- matchExpectedConTy :: PatEnv@@ -1148,10 +1168,10 @@ -- for actual vs. expected in error messages. ; let tys' = mkTyVarTys tvs'- co2 = mkTcUnbranchedAxInstCo co_tc tys' []+ co2 = mkUnbranchedAxInstCo Representational co_tc tys' [] -- co2 : T (ty1,ty2) ~R T7 ty1 ty2 - full_co = mkTcSubCo (mkTcSymCo co1) `mkTcTransCo` co2+ full_co = mkSubCo (mkSymCo co1) `mkTransCo` co2 -- full_co :: pat_rho ~R T7 ty1 ty2 ; return ( mkWpCastR full_co <.> wrap, tys') }@@ -1160,7 +1180,7 @@ = do { pat_ty <- expTypeToType (scaledThing exp_pat_ty) ; (wrap, pat_rho) <- topInstantiate orig pat_ty ; (coi, tys) <- matchExpectedTyConApp data_tc pat_rho- ; return (mkWpCastN (mkTcSymCo coi) <.> wrap, tys) }+ ; return (mkWpCastN (mkSymCo coi) <.> wrap, tys) } {- Note [Matching constructor patterns]@@ -1191,84 +1211,128 @@ error messages; it's a purely internal thing -} -{--Note [Typechecking type applications in patterns]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-How should we typecheck type applications in patterns, such as+{- Note [Type applications in patterns]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Type applications in patterns are enabled by -XTypeAbstractions.+For example: f :: Either (Maybe a) [b] -> blah f (Left @x @[y] (v::Maybe x)) = blah -It's quite straightforward, and very similar to the treatment of-pattern signatures.+How should we typecheck them? The basic plan is pretty simple, and is+all done in tcConTyArgs. For each type argument: -* Step 1: bind the newly-in-scope type variables x and y to fresh- unification variables, say x0 and y0.+* Step 1:+ * bind the newly-in-scope type variables (here `x` or `y`) to+ unification variables, say `x0` or `y0` -* Step 2: typecheck those type arguments, @x and @[y], to get the- types x0 and [y0].+ * typecheck the type argument, `@x` or `@[y]` to get the+ types `x0` or `[y0]`. -* Step 3: Unify those types with the type arguments we expect,- in this case (Maybe a) and [b]. These unifications will- (perhaps after the constraint solver has done its work)+ This step is done by `tcHsPatSigType`, similar to the way we+ deal with pattern signatures.++* Step 2: Unify those types with the type arguments we expect from+ the context, in this case (Maybe a) and [b]. These unifications+ will (perhaps after the constraint solver has done its work) unify x0 := Maybe a y0 := b Thus we learn that x stands for (Maybe a) and y for b. -Wrinkles:+* Step 3: Extend the lexical context to bind `x` to `x0` and+ `y` to `y0`, and typecheck the body of the pattern match. -* Surprisingly, we can discard the coercions arising from- these unifications. The *only* thing the unification does is- to side-effect those unification variables, so that we know- what type x and y stand for; and cause an error if the equality- is not soluble. It's a bit like a constraint arising- from a functional dependency, where we don't use the evidence.+However there are several quite tricky wrinkles. -* Exactly the same works for existential arguments- data T where- MkT :: a -> a -> T- f :: T -> blah- f (MkT @x v w) = ...- Here we create a fresh unification variable x0 for x, and- unify it with the fresh existential variable bound by the pattern.+(W1) Surprisingly, we can discard the coercions arising from+ these unifications. The *only* thing the unification does is+ to side-effect those unification variables, so that we know+ what type x and y stand for; and cause an error if the equality+ is not soluble. It's a bit like a constraint arising+ from a functional dependency, where we don't use the evidence. -* Note that both here and in pattern signatures the unification may- not even end up unifying the variable. For example- type S a b = a- f :: Maybe a -> Bool- f (Just @(S a b) x) = True :: b- In Step 3 we will unify (S a0 b0 ~ a), which succeeds, but has no- effect on the unification variable b0, to which 'b' is bound.- Later, in the RHS, we find that b0 must be Bool, and unify it there.- All is fine.+(W2) Note that both here and in pattern signatures the unification may+ not even end up unifying the variable. For example+ type S a b = a+ f :: Maybe a -> Bool+ f (Just @(S a b) x) = True :: b+ In Step 2 we will unify (S a0 b0 ~ a), which succeeds, but has no+ effect on the unification variable b0, to which 'b' is bound.+ Later, in the RHS, we find that b0 must be Bool, and unify it there.+ All is fine.++(W3) The order of the arguments to the /data constructor/ may differ from+ the order of the arguments to the /type constructor/. Example+ data T a b where { MkT :: forall c d. (c,d) -> T d c }+ f :: T Int Bool -> blah+ f (MkT @x @y p) = ...+ The /first/ type argument to `MkT`, namely `@x` corresponds to the+ /second/ argument to `T` in the type `T Int Bool`. So `x` is bound+ to `Bool` -- not to `Int`!. That is why splitConTyArgs uses+ conLikeUserTyVarBinders to match up with the user-supplied type arguments+ in the pattern, not dataConUnivTyVars/dataConExTyVars.++(W4) A similar story works for existentials, but it is subtly different+ (#19847). Consider+ data T a where { MkT :: forall a b. a -> b -> T a }+ f :: T Int -> blah+ f (MkT @x @y v w) = blah+ Here we create a fresh unification variables x0,y0 for x,y and+ unify x0~Int, y0~b, where b is the fresh existential variable bound by+ the pattern. But+ * (x0~Int) must be /outside/ the implication constraint+ * (y0~b) must be /inside/ it+ (and hence x0 and y0 themselves must have different levels).+ Thus:+ x0[1]~Int, (forall[2] b. (y0[2]~b, ...constraints-from-blah...))++ We need (x0~Int) /outside/ so that it can influence the type of the+ pattern in an inferred setting, e.g.+ g :: T _ -> blah+ g (MkT @Int @y v w) = blah+ Here we want to infer `g` to have type `T Int -> blah`. If the+ (x0~Int) was inside the implication, and the the constructor bound+ equality constraints, `x0` would be untouchable. This was the root+ cause of #19847.++ We need (y0~b) to be /inside/ the implication, so that `b` is in+ scope. In fact, we may actually /need/ equalities bound by the+ implication to prove the equality constraint we generate.+ Example data T a where+ MkT :: forall p q. T (p,q)+ f :: T (Int,Bool) -> blah+ f (MkT @Int @Bool) = ...+ We get the implication+ forall[2] p q. (p,q)~(Int,Bool) => (p ~ Int, q ~ Bool, ...)+ where the Given comes from the GADT match, while (p~Int, q~Bool)+ comes from matching the type arguments.++ Wow. That's all quite subtle! See the long discussion on #19847. We+ must treat universal and existential arguments separately, even though+ they are all mixed up (W3). The function splitConTyArgs separates the+ universals from existentials; and we build the implication between+ typechecking the two sets:+ tcConTyArgs ... univ_ty_args $+ checkConstraints ... $+ tcConTyArgs ... ex_ty_args $+ ..typecheck body..+ You can see this code shape in tcDataConPat and tcPatSynPat.++ Where pattern synonyms are involved, this two-level split may not be+ enough. See #22328 for the story. -} -tcConArgs :: ConLike- -> [Scaled TcSigmaTypeFRR]- -> TCvSubst -- Instantiating substitution for constructor type- -> Checker (HsConPatDetails GhcRn) (HsConPatDetails GhcTc)-tcConArgs con_like arg_tys tenv penv con_args thing_inside = case con_args of+tcConValArgs :: ConLike+ -> [Scaled TcSigmaTypeFRR]+ -> Checker (HsConPatDetails GhcRn) (HsConPatDetails GhcTc)+tcConValArgs con_like arg_tys penv con_args thing_inside = case con_args of PrefixCon type_args arg_pats -> do+ -- NB: type_args already dealt with+ -- See Note [Type applications in patterns] { checkTc (con_arity == no_of_args) -- Check correct arity (arityErr (text "constructor") con_like con_arity no_of_args) - -- forgetting to filter out inferred binders led to #20443- ; let con_spec_binders = filter ((== SpecifiedSpec) . binderArgFlag) $- conLikeUserTyVarBinders con_like- ; checkTc (type_args `leLength` con_spec_binders)- (conTyArgArityErr con_like (length con_spec_binders) (length type_args))- ; let pats_w_tys = zipEqual "tcConArgs" arg_pats arg_tys- ; (type_args', (arg_pats', res))- <- tcMultiple tcConTyArg penv type_args $- tcMultiple tcConArg penv pats_w_tys thing_inside-- -- This unification is straight from Figure 7 of- -- "Type Variables in Patterns", Haskell'18- ; _ <- zipWithM (unifyType Nothing) type_args' (substTyVars tenv $- binderVars con_spec_binders)- -- OK to drop coercions here. These unifications are all about- -- guiding inference based on a user-written type annotation- -- See Note [Typechecking type applications in patterns]+ ; (arg_pats', res) <- tcMultiple tcConArg penv pats_w_tys thing_inside ; return (PrefixCon type_args arg_pats', res) } where@@ -1302,7 +1366,7 @@ pun), res) } - find_field_ty :: Name -> FieldLabelString -> TcM (Scaled TcType)+ find_field_ty :: Name -> FastString -> TcM (Scaled TcType) find_field_ty sel lbl = case [ty | (fl, ty) <- field_tys, flSelector fl == sel ] of @@ -1312,7 +1376,7 @@ -- f (R { foo = (a,b) }) = a+b -- If foo isn't one of R's fields, we don't want to crash when -- typechecking the "a+b".- [] -> failWith (badFieldConErr (getName con_like) lbl)+ [] -> failWith (badFieldConErr (getName con_like) (FieldLabelString lbl)) -- The normal case, when the field comes from the right constructor (pat_ty : extras) -> do@@ -1325,23 +1389,73 @@ -- dataConFieldLabels will be empty (and each field in the pattern -- will generate an error below). -tcConTyArg :: Checker (HsPatSigType GhcRn) TcType-tcConTyArg penv rn_ty thing_inside++splitConTyArgs :: ConLike -> HsConPatDetails GhcRn+ -> TcM ( [(HsConPatTyArg GhcRn, TyVar)] -- Universals+ , [(HsConPatTyArg GhcRn, TyVar)] ) -- Existentials+-- See Note [Type applications in patterns] (W4)+-- This function is monadic only because of the error check+-- for too many type arguments+splitConTyArgs con_like (PrefixCon type_args _)+ = do { checkTc (type_args `leLength` con_spec_bndrs)+ (TcRnTooManyTyArgsInConPattern con_like+ (length con_spec_bndrs) (length type_args))+ ; if null ex_tvs -- Short cut common case+ then return (bndr_ty_arg_prs, [])+ else return (partition is_universal bndr_ty_arg_prs) }+ where+ ex_tvs = conLikeExTyCoVars con_like+ con_spec_bndrs = [ tv | Bndr tv SpecifiedSpec <- conLikeUserTyVarBinders con_like ]+ -- conLikeUserTyVarBinders: see (W3) in+ -- Note [Type applications in patterns]+ -- SpecifiedSpec: forgetting to filter out inferred binders led to #20443++ bndr_ty_arg_prs = type_args `zip` con_spec_bndrs+ -- The zip truncates to length(type_args)++ is_universal (_, tv) = not (tv `elem` ex_tvs)+ -- See Note [DataCon user type variable binders] in GHC.Core.DataCon+ -- especially INVARIANT(dataConTyVars).++splitConTyArgs _ (RecCon {}) = return ([], []) -- No type args in RecCon+splitConTyArgs _ (InfixCon {}) = return ([], []) -- No type args in InfixCon++tcConTyArgs :: Subst -> PatEnv -> [(HsConPatTyArg GhcRn, TyVar)]+ -> TcM a -> TcM a+tcConTyArgs tenv penv prs thing_inside+ = tcMultiple_ (tcConTyArg tenv) penv prs thing_inside++tcConTyArg :: Subst -> Checker (HsConPatTyArg GhcRn, TyVar) ()+tcConTyArg tenv penv (HsConPatTyArg _ rn_ty, con_tv) thing_inside = do { (sig_wcs, sig_ibs, arg_ty) <- tcHsPatSigType TypeAppCtxt HM_TyAppPat rn_ty AnyKind -- AnyKind is a bit suspect: it really should be the kind gotten -- from instantiating the constructor type. But this would be -- hard to get right, because earlier type patterns might influence -- the kinds of later patterns. In any case, it all gets checked- -- by the calls to unifyType in tcConArgs, which will also unify- -- kinds.- ; when (not (null sig_ibs) && inPatBind penv) $- addErr (TcRnUnknownMessage $ mkPlainError noHints $- text "Binding type variables is not allowed in pattern bindings")+ -- by the calls to unifyType below which unifies kinds++ ; case NE.nonEmpty sig_ibs of+ Just sig_ibs_ne | inPatBind penv ->+ addErr (TcRnCannotBindTyVarsInPatBind sig_ibs_ne)+ _ -> pure ()++ -- This unification is straight from Figure 7 of+ -- "Type Variables in Patterns", Haskell'18+ -- OK to drop coercions here. These unifications are all about+ -- guiding inference based on a user-written type annotation+ -- See Note [Type applications in patterns] (W1)+ ; _ <- unifyType Nothing arg_ty (substTyVar tenv con_tv)+ ; result <- tcExtendNameTyVarEnv sig_wcs $ tcExtendNameTyVarEnv sig_ibs $ thing_inside- ; return (arg_ty, result) }+ -- NB: Because we call tConTyArgs twice, once for universals and+ -- once for existentials; so this brings things into scope+ -- "out of left-right order". But it doesn't matter; the renamer+ -- has dealt with all that. + ; return ((), result) }+ tcConArg :: Checker (LPat GhcRn, Scaled TcSigmaType) (LPat GhcTc) tcConArg penv (arg_pat, Scaled arg_mult arg_ty) = tc_lpat (Scaled arg_mult (mkCheckExpType arg_ty)) penv arg_pat@@ -1364,15 +1478,6 @@ -- because the constructor might have existentials inst_theta = substTheta tenv stupid_theta -conTyArgArityErr :: ConLike- -> Int -- expected # of arguments- -> Int -- actual # of arguments- -> TcRnMessage-conTyArgArityErr con_like expected_number actual_number- = TcRnUnknownMessage $ mkPlainError noHints $- text "Too many type arguments in constructor pattern for" <+> quotes (ppr con_like) $$- text "Expected no more than" <+> ppr expected_number <> semi <+> text "got" <+> ppr actual_number- {- Note [Arrows and patterns] ~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -1402,12 +1507,8 @@ plan in general to bypass the constraint simplification step entirely when it's not needed. -************************************************************************-* *- Note [Pattern coercions]-* *-************************************************************************-+Note [Pattern coercions]+~~~~~~~~~~~~~~~~~~~~~~~~ In principle, these program would be reasonable: f :: (forall a. a->a) -> Int@@ -1518,8 +1619,3 @@ where has_existentials :: Bool has_existentials = any (`elemVarSet` tyCoVarsOfTypes arg_tys) ex_tvs--polyPatSig :: TcType -> SDoc-polyPatSig sig_ty- = hang (text "Illegal polymorphic type signature in pattern:")- 2 (ppr sig_ty)
compiler/GHC/Tc/Gen/Rule.hs view
@@ -17,7 +17,6 @@ import GHC.Tc.Solver import GHC.Tc.Solver.Monad ( runTcS ) import GHC.Tc.Types.Constraint-import GHC.Core.Predicate import GHC.Tc.Types.Origin import GHC.Tc.Utils.TcMType import GHC.Tc.Utils.TcType@@ -25,9 +24,12 @@ import GHC.Tc.Gen.Expr import GHC.Tc.Utils.Env import GHC.Tc.Utils.Unify( buildImplicationFor )-import GHC.Tc.Types.Evidence( mkTcCoVarCo )+ import GHC.Core.Type+import GHC.Core.Coercion( mkCoVarCo ) import GHC.Core.TyCon( isTypeFamilyTyCon )+import GHC.Core.Predicate+ import GHC.Types.Id import GHC.Types.Var( EvVar, tyVarName ) import GHC.Types.Var.Set@@ -103,23 +105,22 @@ tcRules decls = mapM (wrapLocMA tcRuleDecls) decls tcRuleDecls :: RuleDecls GhcRn -> TcM (RuleDecls GhcTc)-tcRuleDecls (HsRules { rds_src = src+tcRuleDecls (HsRules { rds_ext = src , rds_rules = decls }) = do { tc_decls <- mapM (wrapLocMA tcRule) decls- ; return $ HsRules { rds_ext = noExtField- , rds_src = src+ ; return $ HsRules { rds_ext = src , rds_rules = tc_decls } } tcRule :: RuleDecl GhcRn -> TcM (RuleDecl GhcTc) tcRule (HsRule { rd_ext = ext- , rd_name = rname@(L _ (_,name))+ , rd_name = rname@(L _ name) , rd_act = act , rd_tyvs = ty_bndrs , rd_tmvs = tm_bndrs , rd_lhs = lhs , rd_rhs = rhs }) = addErrCtxt (ruleCtxt name) $- do { traceTc "---- Rule ------" (pprFullRuleName rname)+ do { traceTc "---- Rule ------" (pprFullRuleName (snd ext) rname) ; skol_info <- mkSkolemInfo (RuleSkol name) -- Note [Typechecking rules] ; (tc_lvl, stuff) <- pushTcLevelM $@@ -128,7 +129,7 @@ ; let (id_bndrs, lhs', lhs_wanted , rhs', rhs_wanted, rule_ty) = stuff - ; traceTc "tcRule 1" (vcat [ pprFullRuleName rname+ ; traceTc "tcRule 1" (vcat [ pprFullRuleName (snd ext) rname , ppr lhs_wanted , ppr rhs_wanted ]) @@ -157,7 +158,7 @@ quant_cands = forall_tkvs { dv_kvs = weed_out (dv_kvs forall_tkvs) , dv_tvs = weed_out (dv_tvs forall_tkvs) } ; qtkvs <- quantifyTyVars skol_info DefaultNonStandardTyVars quant_cands- ; traceTc "tcRule" (vcat [ pprFullRuleName rname+ ; traceTc "tcRule" (vcat [ pprFullRuleName (snd ext) rname , text "forall_tkvs:" <+> ppr forall_tkvs , text "quant_cands:" <+> ppr quant_cands , text "don't_default:" <+> ppr don't_default@@ -166,7 +167,8 @@ , text "rule_ty:" <+> ppr rule_ty , text "ty_bndrs:" <+> ppr ty_bndrs , text "qtkvs ++ tpl_ids:" <+> ppr (qtkvs ++ tpl_ids)- , vcat [ ppr id <+> dcolon <+> ppr (idType id) | id <- tpl_ids ]+ , text "tpl_id info:" <+>+ vcat [ ppr id <+> dcolon <+> ppr (idType id) | id <- tpl_ids ] ]) -- SimplfyRule Plan, step 5@@ -230,7 +232,7 @@ tcRuleTmBndrs rule_name (L _ (RuleBndr _ (L _ name)) : rule_bndrs) = do { ty <- newOpenFlexiTyVarTy ; (tyvars, tmvars) <- tcRuleTmBndrs rule_name rule_bndrs- ; return (tyvars, mkLocalId name Many ty : tmvars) }+ ; return (tyvars, mkLocalId name ManyTy ty : tmvars) } tcRuleTmBndrs rule_name (L _ (RuleBndrSig _ (L _ name) rn_ty) : rule_bndrs) -- e.g x :: a->a -- The tyvar 'a' is brought into scope first, just as if you'd written@@ -239,7 +241,7 @@ -- error for each out-of-scope type variable used = do { let ctxt = RuleSigCtxt rule_name name ; (_ , tvs, id_ty) <- tcHsPatSigType ctxt HM_Sig rn_ty OpenKind- ; let id = mkLocalId name Many id_ty+ ; let id = mkLocalId name ManyTy id_ty -- See Note [Typechecking pattern signature binders] in GHC.Tc.Gen.HsType -- The type variables scope over subsequent bindings; yuk@@ -445,7 +447,7 @@ EvVarDest ev_id -> return ev_id HoleDest hole -> -- See Note [Quantifying over coercion holes] do { ev_id <- newEvVar pred- ; fillCoercionHole hole (mkTcCoVarCo ev_id)+ ; fillCoercionHole hole (mkCoVarCo ev_id) ; return ev_id } mk_quant_ev ct = pprPanic "mk_quant_ev" (ppr ct)
compiler/GHC/Tc/Gen/Sig.hs view
@@ -26,6 +26,7 @@ ) where import GHC.Prelude+import GHC.Data.FastString import GHC.Driver.Session import GHC.Driver.Backend@@ -51,9 +52,10 @@ import GHC.Core( hasSomeUnfolding ) import GHC.Core.Type ( mkTyVarBinders ) import GHC.Core.Multiplicity+import GHC.Core.TyCo.Rep( mkNakedFunTy ) import GHC.Types.Error-import GHC.Types.Var ( TyVar, Specificity(..), tyVarKind, binderVars )+import GHC.Types.Var ( TyVar, Specificity(..), tyVarKind, binderVars, invisArgTypeLike ) import GHC.Types.Id ( Id, idName, idType, setInlinePragma , mkLocalId, realIdUnfolding ) import GHC.Types.Basic@@ -68,9 +70,10 @@ import GHC.Utils.Outputable import GHC.Utils.Panic -import GHC.Data.Maybe( orElse )+import GHC.Data.Maybe( orElse, whenIsJust ) import Data.Maybe( mapMaybe )+import qualified Data.List.NonEmpty as NE import Control.Monad( unless ) @@ -111,7 +114,7 @@ but for /partial/ signatures it starts from the HsSyn, so it has to kind-check it etc: tcHsPartialSigType. It's convenient to do this at the same time as instantiation, because we can- make the wildcards into unification variables right away, raather+ make the wildcards into unification variables right away, rather than somehow quantifying over them. And the "TcLevel" of those unification variables is correct because we are in tcMonoBinds. @@ -188,7 +191,7 @@ ; return (poly_ids, lookupNameEnv env) } tcTySig :: LSig GhcRn -> TcM [TcSigInfo]-tcTySig (L _ (IdSig _ id))+tcTySig (L _ (XSig (IdSig id))) = do { let ctxt = FunSigCtxt (idName id) NoRRC -- NoRRC: do not report redundant constraints -- The user has no control over the signature!@@ -228,7 +231,7 @@ = do { sigma_ty <- tcHsSigWcType ctxt_no_rrc hs_sig_ty ; traceTc "tcuser" (ppr sigma_ty) ; return $- CompleteSig { sig_bndr = mkLocalId name Many sigma_ty+ CompleteSig { sig_bndr = mkLocalId name ManyTy sigma_ty -- We use `Many' as the multiplicity here, -- as if this identifier corresponds to -- anything, it is a top-level@@ -245,7 +248,7 @@ where name = case mb_name of Just n -> n- Nothing -> mkUnboundName (mkVarOcc "<expression>")+ Nothing -> mkUnboundName (mkVarOccFS (fsLit "<expression>")) ctxt_rrc = ctxt_fn (lhsSigWcTypeContextSpan hs_sig_ty) ctxt_no_rrc = ctxt_fn NoRRC@@ -315,8 +318,8 @@ && go ty HsQualTy { hst_ctxt = ctxt , hst_body = ty } -> gos (unLoc ctxt) && go ty- HsSpliceTy _ (HsSpliced _ _ (HsSplicedTy ty)) -> go $ L noSrcSpanA ty- HsSpliceTy{} -> True+ HsSpliceTy (HsUntypedSpliceTop _ ty) _ -> go ty+ HsSpliceTy (HsUntypedSpliceNested _) _ -> True HsTyLit{} -> True HsTyVar{} -> True HsStarTy{} -> True@@ -454,7 +457,7 @@ -- Neither argument types nor the return type may be representation polymorphic. -- This is because, when creating a matcher:- -- - the argument types become the the binder types (see test RepPolyPatySynArg),+ -- - the argument types become the binder types (see test RepPolyPatySynArg), -- - the return type becomes the scrutinee type (see test RepPolyPatSynRes). ; let (arg_tys, res_ty) = tcSplitFunTys body_ty ; mapM_@@ -483,11 +486,19 @@ build_patsyn_type implicit_bndrs univ_bndrs req ex_bndrs prov body = mkInvisForAllTys implicit_bndrs $ mkInvisForAllTys univ_bndrs $- mkPhiTy req $+ mk_naked_phi_ty req $ mkInvisForAllTys ex_bndrs $- mkPhiTy prov $+ mk_naked_phi_ty prov $ body + -- Use mk_naked_phi_ty because we call build_patsyn_type /before zonking/+ -- just before kindGeneraliseAll, and the invariants that mkPhiTy checks+ -- don't hold of the un-zonked types. #22521 was a case in point.+ -- (We also called build_patsyn_type on the fully zonked type, so mkPhiTy+ -- would work; but it doesn't seem worth duplicating the code.)+ mk_naked_phi_ty :: [TcPredType] -> TcType -> TcType+ mk_naked_phi_ty theta body = foldr (mkNakedFunTy invisArgTypeLike) body theta+ ppr_tvs :: [TyVar] -> SDoc ppr_tvs tvs = braces (vcat [ ppr tv <+> dcolon <+> ppr (tyVarKind tv) | tv <- tvs])@@ -504,7 +515,7 @@ -- Instantiate a type signature; only used with plan InferGen tcInstSig sig@(CompleteSig { sig_bndr = poly_id, sig_loc = loc }) = setSrcSpan loc $ -- Set the binding site of the tyvars- do { (tv_prs, theta, tau) <- tcInstTypeBndrs poly_id+ do { (tv_prs, theta, tau) <- tcInstTypeBndrs (idType poly_id) -- See Note [Pattern bindings and complete signatures] ; return (TISI { sig_inst_sig = sig@@ -521,6 +532,7 @@ do { traceTc "Staring partial sig {" (ppr hs_sig) ; (wcs, wcx, tv_prs, theta, tau) <- tcHsPartialSigType ctxt hs_ty -- See Note [Checking partial type signatures] in GHC.Tc.Gen.HsType+ ; let inst_sig = TISI { sig_inst_sig = hs_sig , sig_inst_skols = tv_prs , sig_inst_wcs = wcs@@ -579,7 +591,7 @@ get_sig :: LSig GhcRn -> Maybe (Name, LSig GhcRn) get_sig sig@(L _ (SpecSig _ (L _ nm) _ _)) = Just (nm, add_arity nm sig) get_sig sig@(L _ (InlineSig _ (L _ nm) _)) = Just (nm, add_arity nm sig)- get_sig sig@(L _ (SCCFunSig _ _ (L _ nm) _)) = Just (nm, sig)+ get_sig sig@(L _ (SCCFunSig _ (L _ nm) _)) = Just (nm, sig) get_sig _ = Nothing add_arity n sig -- Adjust inl_sat field to match visible arity of function@@ -631,16 +643,10 @@ warn_multiple_inlines inl2 inls | otherwise = setSrcSpanA loc $- let dia = TcRnUnknownMessage $- mkPlainDiagnostic WarningWithoutFlag noHints $- (hang (text "Multiple INLINE pragmas for" <+> ppr poly_id)- 2 (vcat (text "Ignoring all but the first"- : map pp_inl (inl1:inl2:inls))))+ let dia = TcRnMultipleInlinePragmas poly_id inl1 (inl2 NE.:| inls) in addDiagnosticTc dia - pp_inl (L loc prag) = ppr prag <+> parens (ppr loc) - {- Note [Pattern synonym inline arity] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Consider@@ -776,7 +782,7 @@ -- Reason: required by tcSubExp tcSpecPrags poly_id prag_sigs = do { traceTc "tcSpecPrags" (ppr poly_id <+> ppr spec_sigs)- ; unless (null bad_sigs) warn_discarded_sigs+ ; whenIsJust (NE.nonEmpty bad_sigs) warn_discarded_sigs ; pss <- mapAndRecoverM (wrapLocMA (tcSpecPrag poly_id)) spec_sigs ; return $ concatMap (\(L l ps) -> map (L (locA l)) ps) pss } where@@ -784,11 +790,8 @@ bad_sigs = filter is_bad_sig prag_sigs is_bad_sig s = not (isSpecLSig s || isInlineLSig s || isSCCFunSig s) - warn_discarded_sigs- = let dia = TcRnUnknownMessage $- mkPlainDiagnostic WarningWithoutFlag noHints $- (hang (text "Discarding unexpected pragmas for" <+> ppr poly_id)- 2 (vcat (map (ppr . getLoc) bad_sigs)))+ warn_discarded_sigs bad_sigs_ne+ = let dia = TcRnUnexpectedPragmas poly_id bad_sigs_ne in addDiagnosticTc dia --------------@@ -803,9 +806,7 @@ -- what the user wrote (#8537) = addErrCtxt (spec_ctxt prag) $ do { warnIf (not (isOverloadedTy poly_ty || isInlinePragma inl)) $- TcRnUnknownMessage $ mkPlainDiagnostic WarningWithoutFlag noHints- (text "SPECIALISE pragma for non-overloaded function"- <+> quotes (ppr fun_name))+ TcRnNonOverloadedSpecialisePragma fun_name -- Note [SPECIALISE pragmas] ; spec_prags <- mapM tc_one hs_tys ; traceTc "tcSpecPrag" (ppr poly_id $$ nest 2 (vcat (map ppr spec_prags)))@@ -858,12 +859,8 @@ -- when we aren't specialising, or when we aren't generating -- code. The latter happens when Haddocking the base library; -- we don't want complaints about lack of INLINABLE pragmas- not_specialising dflags- | not (gopt Opt_Specialise dflags) = True- | otherwise = case backend dflags of- NoBackend -> True- Interpreter -> True- _other -> False+ not_specialising dflags =+ not (gopt Opt_Specialise dflags) || not (backendRespectsSpecialise (backend dflags)) tcImpSpec :: (Name, Sig GhcRn) -> TcM [TcSpecPrag] tcImpSpec (name, prag)@@ -871,20 +868,9 @@ ; if hasSomeUnfolding (realIdUnfolding id) -- See Note [SPECIALISE pragmas for imported Ids] then tcSpecPrag id prag- else do { let dia = TcRnUnknownMessage $- mkPlainDiagnostic WarningWithoutFlag noHints (impSpecErr name)+ else do { let dia = TcRnSpecialiseNotVisible name ; addDiagnosticTc dia ; return [] } }--impSpecErr :: Name -> SDoc-impSpecErr name- = hang (text "You cannot SPECIALISE" <+> quotes (ppr name))- 2 (vcat [ text "because its definition is not visible in this module"- , text "Hint: make sure" <+> ppr mod <+> text "is compiled with -O"- , text " and that" <+> quotes (ppr name)- <+> text "has an INLINABLE pragma" ])- where- mod = nameModule name {- Note [SPECIALISE pragmas for imported Ids] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compiler/GHC/Tc/Gen/Splice.hs view
@@ -20,2634 +20,2839 @@ -- | Template Haskell splices module GHC.Tc.Gen.Splice(- tcSpliceExpr, tcTypedBracket, tcUntypedBracket,--- runQuasiQuoteExpr, runQuasiQuotePat,--- runQuasiQuoteDecl, runQuasiQuoteType,- runAnnotation,-- runMetaE, runMetaP, runMetaT, runMetaD, runQuasi,- tcTopSpliceExpr, lookupThName_maybe,- defaultRunMeta, runMeta', runRemoteModFinalizers,- finishTH, runTopSplice- ) where--import GHC.Prelude--import GHC.Driver.Errors-import GHC.Driver.Plugins-import GHC.Driver.Main-import GHC.Driver.Session-import GHC.Driver.Env-import GHC.Driver.Hooks-import GHC.Driver.Config.Diagnostic-import GHC.Driver.Config.Finder--import GHC.Hs--import GHC.Tc.Errors.Types-import GHC.Tc.Utils.Monad-import GHC.Tc.Utils.TcType-import GHC.Tc.Gen.Expr-import GHC.Tc.Utils.Unify-import GHC.Tc.Utils.Env-import GHC.Tc.Types.Origin-import GHC.Tc.Types.Evidence-import GHC.Tc.Utils.Zonk-import GHC.Tc.Solver-import GHC.Tc.Utils.TcMType-import GHC.Tc.Gen.HsType-import GHC.Tc.Instance.Family-import GHC.Tc.Utils.Instantiate--import GHC.Core.Multiplicity-import GHC.Core.Coercion( etaExpandCoAxBranch )-import GHC.Core.Type as Type-import GHC.Core.TyCo.Rep as TyCoRep-import GHC.Core.FamInstEnv-import GHC.Core.InstEnv as InstEnv--import GHC.Builtin.Names.TH-import GHC.Builtin.Names-import GHC.Builtin.Types--import GHC.ThToHs-import GHC.HsToCore.Docs-import GHC.HsToCore.Expr-import GHC.HsToCore.Monad-import GHC.IfaceToCore-import GHC.Iface.Load--import GHCi.Message-import GHCi.RemoteTypes-import GHC.Runtime.Interpreter--import GHC.Rename.Splice( traceSplice, SpliceInfo(..))-import GHC.Rename.Expr-import GHC.Rename.Env-import GHC.Rename.Fixity ( lookupFixityRn_help )-import GHC.Rename.HsType--import GHC.Core.Class-import GHC.Core.TyCon-import GHC.Core.Coercion.Axiom-import GHC.Core.PatSyn-import GHC.Core.ConLike-import GHC.Core.DataCon as DataCon--import GHC.Types.FieldLabel-import GHC.Types.SrcLoc-import GHC.Types.Name.Env-import GHC.Types.Name.Set-import GHC.Types.Name.Reader-import GHC.Types.Name.Occurrence as OccName-import GHC.Types.Var-import GHC.Types.Id-import GHC.Types.Id.Info-import GHC.Types.Unique-import GHC.Types.Var.Set-import GHC.Types.Meta-import GHC.Types.Basic hiding( SuccessFlag(..) )-import GHC.Types.Error-import GHC.Types.Fixity as Hs-import GHC.Types.Annotations-import GHC.Types.Name-import GHC.Types.Unique.Map-import GHC.Serialized--import GHC.Unit.Finder-import GHC.Unit.Module-import GHC.Unit.Module.ModIface-import GHC.Unit.Module.Deps--import GHC.Utils.Misc-import GHC.Utils.Panic as Panic-import GHC.Utils.Panic.Plain-import GHC.Utils.Lexeme-import GHC.Utils.Outputable-import GHC.Utils.Logger-import GHC.Utils.Exception (throwIO, ErrorCall(..))--import GHC.Utils.TmpFs ( newTempName, TempFileLifetime(..) )--import GHC.Data.FastString-import GHC.Data.Maybe( MaybeErr(..) )-import qualified GHC.Data.EnumSet as EnumSet--import qualified Language.Haskell.TH as TH--- THSyntax gives access to internal functions and data types-import qualified Language.Haskell.TH.Syntax as TH--#if defined(HAVE_INTERNAL_INTERPRETER)--- Because GHC.Desugar might not be in the base library of the bootstrapping compiler-import GHC.Desugar ( AnnotationWrapper(..) )-import Unsafe.Coerce ( unsafeCoerce )-#endif--import Control.Monad-import Data.Binary-import Data.Binary.Get-import Data.List ( find )-import Data.Maybe-import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as LB-import Data.Dynamic ( fromDynamic, toDyn )-import qualified Data.IntMap as IntMap-import qualified Data.Map as Map-import Data.Typeable ( typeOf, Typeable, TypeRep, typeRep )-import Data.Data (Data)-import Data.Proxy ( Proxy (..) )-import GHC.Parser.HaddockLex (lexHsDoc)-import GHC.Parser (parseIdentifier)-import GHC.Rename.Doc (rnHsDoc)--{--************************************************************************-* *-\subsection{Main interface + stubs for the non-GHCI case-* *-************************************************************************--}--tcTypedBracket :: HsExpr GhcRn -> LHsExpr GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)-tcUntypedBracket :: HsExpr GhcRn -> HsQuote GhcRn -> [PendingRnSplice] -> ExpRhoType- -> TcM (HsExpr GhcTc)-tcSpliceExpr :: HsSplice GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)- -- None of these functions add constraints to the LIE---- runQuasiQuoteExpr :: HsQuasiQuote RdrName -> RnM (LHsExpr RdrName)--- runQuasiQuotePat :: HsQuasiQuote RdrName -> RnM (LPat RdrName)--- runQuasiQuoteType :: HsQuasiQuote RdrName -> RnM (LHsType RdrName)--- runQuasiQuoteDecl :: HsQuasiQuote RdrName -> RnM [LHsDecl RdrName]--runAnnotation :: CoreAnnTarget -> LHsExpr GhcRn -> TcM Annotation-{--************************************************************************-* *-\subsection{Quoting an expression}-* *-************************************************************************--}---- See Note [How brackets and nested splices are handled]-tcTypedBracket rn_expr expr res_ty- = addErrCtxt (quotationCtxtDoc expr) $- do { cur_stage <- getStage- ; ps_ref <- newMutVar []- ; lie_var <- getConstraintVar -- Any constraints arising from nested splices- -- should get thrown into the constraint set- -- from outside the bracket-- -- Make a new type variable for the type of the overall quote- ; m_var <- mkTyVarTy <$> mkMetaTyVar- -- Make sure the type variable satisfies Quote- ; ev_var <- emitQuoteWanted m_var- -- Bundle them together so they can be used in GHC.HsToCore.Quote for desugaring- -- brackets.- ; let wrapper = QuoteWrapper ev_var m_var- -- Typecheck expr to make sure it is valid.- -- The typechecked expression won't be used, but we return it with its type.- -- (See Note [The life cycle of a TH quotation] in GHC.Hs.Expr)- -- We'll typecheck it again when we splice it in somewhere- ; (tc_expr, expr_ty) <- setStage (Brack cur_stage (TcPending ps_ref lie_var wrapper)) $- tcScalingUsage Many $- -- Scale by Many, TH lifting is currently nonlinear (#18465)- tcInferRhoNC expr- -- NC for no context; tcBracket does that- ; let rep = getRuntimeRep expr_ty- ; meta_ty <- tcTExpTy m_var expr_ty- ; ps' <- readMutVar ps_ref- ; codeco <- tcLookupId unsafeCodeCoerceName- ; bracket_ty <- mkAppTy m_var <$> tcMetaTy expTyConName- ; tcWrapResultO (Shouldn'tHappenOrigin "TH typed bracket expression")- rn_expr- (unLoc (mkHsApp (mkLHsWrap (applyQuoteWrapper wrapper)- (nlHsTyApp codeco [rep, expr_ty]))- (noLocA (HsTypedBracket (HsBracketTc (ExpBr noExtField expr) bracket_ty (Just wrapper) ps') tc_expr))))- meta_ty res_ty }---- See Note [Typechecking Overloaded Quotes]-tcUntypedBracket rn_expr brack ps res_ty- = do { traceTc "tc_bracket untyped" (ppr brack $$ ppr ps)--- -- Create the type m Exp for expression bracket, m Type for a type- -- bracket and so on. The brack_info is a Maybe because the- -- VarBracket ('a) isn't overloaded, but also shouldn't contain any- -- splices.- ; (brack_info, expected_type) <- brackTy brack-- -- Match the expected type with the type of all the internal- -- splices. They might have further constrained types and if they do- -- we want to reflect that in the overall type of the bracket.- ; ps' <- case quoteWrapperTyVarTy <$> brack_info of- Just m_var -> mapM (tcPendingSplice m_var) ps- Nothing -> assert (null ps) $ return []-- ; traceTc "tc_bracket done untyped" (ppr expected_type)-- -- Unify the overall type of the bracket with the expected result- -- type- ; tcWrapResultO BracketOrigin rn_expr- (HsUntypedBracket (HsBracketTc brack expected_type brack_info ps') (XQuote noExtField))- -- (XQuote noExtField): see Note [The life cycle of a TH quotation] in GHC.Hs.Expr- expected_type res_ty-- }---- | A type variable with kind * -> * named "m"-mkMetaTyVar :: TcM TyVar-mkMetaTyVar =- newNamedFlexiTyVar (fsLit "m") (mkVisFunTyMany liftedTypeKind liftedTypeKind)----- | For a type 'm', emit the constraint 'Quote m'.-emitQuoteWanted :: Type -> TcM EvVar-emitQuoteWanted m_var = do- quote_con <- tcLookupTyCon quoteClassName- emitWantedEvVar BracketOrigin $- mkTyConApp quote_con [m_var]-------------------- | Compute the expected type of a quotation, and also the QuoteWrapper in--- the case where it is an overloaded quotation. All quotation forms are--- overloaded aprt from Variable quotations ('foo)-brackTy :: HsQuote GhcRn -> TcM (Maybe QuoteWrapper, Type)-brackTy b =- let mkTy n = do- -- New polymorphic type variable for the bracket- m_var <- mkTyVarTy <$> mkMetaTyVar- -- Emit a Quote constraint for the bracket- ev_var <- emitQuoteWanted m_var- -- Construct the final expected type of the quote, for example- -- m Exp or m Type- final_ty <- mkAppTy m_var <$> tcMetaTy n- -- Return the evidence variable and metavariable to be used during- -- desugaring.- let wrapper = QuoteWrapper ev_var m_var- return (Just wrapper, final_ty)- in- case b of- (VarBr {}) -> (Nothing,) <$> tcMetaTy nameTyConName- -- Result type is Var (not Quote-monadic)- (ExpBr {}) -> mkTy expTyConName -- Result type is m Exp- (TypBr {}) -> mkTy typeTyConName -- Result type is m Type- (DecBrG {}) -> mkTy decsTyConName -- Result type is m [Dec]- (PatBr {}) -> mkTy patTyConName -- Result type is m Pat- (DecBrL {}) -> panic "tcBrackTy: Unexpected DecBrL"-------------------- | Typechecking a pending splice from a untyped bracket-tcPendingSplice :: TcType -- Metavariable for the expected overall type of the- -- quotation.- -> PendingRnSplice- -> TcM PendingTcSplice-tcPendingSplice m_var (PendingRnSplice flavour splice_name expr)- -- See Note [Typechecking Overloaded Quotes]- = do { meta_ty <- tcMetaTy meta_ty_name- -- Expected type of splice, e.g. m Exp- ; let expected_type = mkAppTy m_var meta_ty- ; expr' <- tcScalingUsage Many $ tcCheckPolyExpr expr expected_type- -- Scale by Many, TH lifting is currently nonlinear (#18465)- ; return (PendingTcSplice splice_name expr') }- where- meta_ty_name = case flavour of- UntypedExpSplice -> expTyConName- UntypedPatSplice -> patTyConName- UntypedTypeSplice -> typeTyConName- UntypedDeclSplice -> decsTyConName-------------------- Takes a m and tau and returns the type m (TExp tau)-tcTExpTy :: TcType -> TcType -> TcM TcType-tcTExpTy m_ty exp_ty- = do { unless (isTauTy exp_ty) $ addErr (err_msg exp_ty)- ; codeCon <- tcLookupTyCon codeTyConName- ; let rep = getRuntimeRep exp_ty- ; return (mkTyConApp codeCon [rep, m_ty, exp_ty]) }- where- err_msg ty- = TcRnUnknownMessage $ mkPlainError noHints $- vcat [ text "Illegal polytype:" <+> ppr ty- , text "The type of a Typed Template Haskell expression must" <+>- text "not have any quantification." ]--quotationCtxtDoc :: LHsExpr GhcRn -> SDoc-quotationCtxtDoc br_body- = hang (text "In the Template Haskell quotation")- 2 (thTyBrackets . ppr $ br_body)--- -- The whole of the rest of the file is the else-branch (ie stage2 only)--{--Note [How top-level splices are handled]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Top-level splices (those not inside a [| .. |] quotation bracket) are handled-very straightforwardly:-- 1. tcTopSpliceExpr: typecheck the body e of the splice $(e)-- 2. runMetaT: desugar, compile, run it, and convert result back to- GHC.Hs syntax RdrName (of the appropriate flavour, eg HsType RdrName,- HsExpr RdrName etc)-- 3. treat the result as if that's what you saw in the first place- e.g for HsType, rename and kind-check- for HsExpr, rename and type-check-- (The last step is different for decls, because they can *only* be- top-level: we return the result of step 2.)--Note [How brackets and nested splices are handled]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Nested splices (those inside a [| .. |] quotation bracket),-are treated quite differently.--Remember, there are two forms of bracket- typed [|| e ||]- and untyped [| e |]--The life cycle of a typed bracket:- * Starts as HsTypedBracket-- * When renaming:- * Set the ThStage to (Brack s RnPendingTyped)- * Rename the body- * Result is a HsTypedBracket-- * When typechecking:- * Set the ThStage to (Brack s (TcPending ps_var lie_var))- * Typecheck the body, and keep the elaborated result (despite never using it!)- * Nested splices (which must be typed) are typechecked, and- the results accumulated in ps_var; their constraints- accumulate in lie_var- * Result is a HsTypedBracket (HsBracketTc rn_brack ty quote_wrapper pending_splices) tc_brack- where rn_brack is the untyped renamed exp quote constructed from the typed renamed expression :: HsQuote GhcRn--The life cycle of a un-typed bracket:- * Starts as HsUntypedBracket-- * When renaming:- * Set the ThStage to (Brack s (RnPendingUntyped ps_var))- * Rename the body- * Nested splices (which must be untyped) are renamed, and the- results accumulated in ps_var- * Result is a HsUntypedBracket pending_splices rn_body-- * When typechecking:- * Typecheck the pending_splices individually- * Ignore the body of the bracket; just check that the context- expects a bracket of that type (e.g. a [p| pat |] bracket should- be in a context needing a (Q Pat)- * Result is a HsUntypedBracket (HsBracketTc rn_brack ty quote_wrapper pending_splices) (XQuote noExtField)- where rn_brack is the incoming renamed bracket :: HsQuote GhcRn- and (XQuote noExtField) stands for the removal of the `HsQuote GhcTc` field (since `HsQuote GhcTc` isn't possible)--See the related Note [The life cycle of a TH quotation]--In both cases, desugaring happens like this:- * Hs*Bracket is desugared by GHC.HsToCore.Quote.dsBracket using the renamed- expression held in `HsBracketTc` (`type instance X*Bracket GhcTc = HsBracketTc`). It-- a) Extends the ds_meta environment with the PendingSplices- attached to the bracket-- b) Converts the quoted (HsExpr Name) to a CoreExpr that, when- run, will produce a suitable TH expression/type/decl. This- is why we leave the *renamed* expression attached to the bracket:- the quoted expression should not be decorated with all the goop- added by the type checker-- * Each splice carries a unique Name, called a "splice point", thus- ${n}(e). The name is initialised to an (Unqual "splice") when the- splice is created; the renamer gives it a unique.-- * When GHC.HsToCore.Quote (used to desugar the body of the bracket) comes across- a splice, it looks up the splice's Name, n, in the ds_meta envt,- to find an (HsExpr Id) that should be substituted for the splice;- it just desugars it to get a CoreExpr (GHC.HsToCore.Quote.repSplice).--Example:- Source: f = [| Just $(g 3) |]- The [| |] part is a HsUntypedBracket GhcPs-- Typechecked: f = [| Just ${s7}(g 3) |]{s7 = g Int 3}- The [| |] part is a HsUntypedBracket GhcTc, containing *renamed*- (not typechecked) expression (see Note [The life cycle of a TH quotation])- The "s7" is the "splice point"; the (g Int 3) part- is a typechecked expression-- Desugared: f = do { s7 <- g Int 3- ; return (ConE "Data.Maybe.Just" s7) }---Note [Template Haskell state diagram]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Here are the ThStages, s, their corresponding level numbers-(the result of (thLevel s)), and their state transitions.-The top level of the program is stage Comp:-- Start here- |- V- ----------- $ ------------ $- | Comp | ---------> | Splice | -----|- | 1 | | 0 | <----|- ----------- ------------- ^ | ^ |- $ | | [||] $ | | [||]- | v | v- -------------- ----------------- | Brack Comp | | Brack Splice |- | 2 | | 1 |- -------------- ------------------* Normal top-level declarations start in state Comp- (which has level 1).- Annotations start in state Splice, since they are- treated very like a splice (only without a '$')--* Code compiled in state Splice (and only such code)- will be *run at compile time*, with the result replacing- the splice--* The original paper used level -1 instead of 0, etc.--* The original paper did not allow a splice within a- splice, but there is no reason not to. This is the- $ transition in the top right.--Note [Template Haskell levels]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-* Imported things are impLevel (= 0)--* However things at level 0 are not *necessarily* imported.- eg $( \b -> ... ) here b is bound at level 0--* In GHCi, variables bound by a previous command are treated- as impLevel, because we have bytecode for them.--* Variables are bound at the "current level"--* The current level starts off at outerLevel (= 1)--* The level is decremented by splicing $(..)- incremented by brackets [| |]- incremented by name-quoting 'f--* When a variable is used, checkWellStaged compares- bind: binding level, and- use: current level at usage site-- Generally- bind > use Always error (bound later than used)- [| \x -> $(f x) |]-- bind = use Always OK (bound same stage as used)- [| \x -> $(f [| x |]) |]-- bind < use Inside brackets, it depends- Inside splice, OK- Inside neither, OK-- For (bind < use) inside brackets, there are three cases:- - Imported things OK f = [| map |]- - Top-level things OK g = [| f |]- - Non-top-level Only if there is a liftable instance- h = \(x:Int) -> [| x |]-- To track top-level-ness we use the ThBindEnv in TcLclEnv-- For example:- f = ...- g1 = $(map ...) is OK- g2 = $(f ...) is not OK; because we haven't compiled f yet--Note [Typechecking Overloaded Quotes]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--The main function for typechecking untyped quotations is `tcUntypedBracket`.--Consider an expression quote, `[| e |]`, its type is `forall m . Quote m => m Exp`.-When we typecheck it we therefore create a template of a metavariable `m` applied to `Exp` and-emit a constraint `Quote m`. All this is done in the `brackTy` function.-`brackTy` also selects the correct contents type for the quotation (Exp, Type, Decs etc).--The meta variable and the constraint evidence variable are-returned together in a `QuoteWrapper` and then passed along to two further places-during compilation:--1. Typechecking nested splices (immediately in tcPendingSplice)-2. Desugaring quotations (see GHC.HsToCore.Quote)--`tcPendingSplice` takes the `m` type variable as an argument and checks-each nested splice against this variable `m`. During this-process the variable `m` can either be fixed to a specific value or further constrained by the-nested splices.--Once we have checked all the nested splices, the quote type is checked against-the expected return type.--The process is very simple and like typechecking a list where the quotation is-like the container and the splices are the elements of the list which must have-a specific type.--After the typechecking process is completed, the evidence variable for `Quote m`-and the type `m` is stored in a `QuoteWrapper` which is passed through the pipeline-and used when desugaring quotations.--Typechecking typed quotations is a similar idea but the `QuoteWrapper` is stored-in the `PendingStuff` as the nested splices are gathered up in a different way-to untyped splices. Untyped splices are found in the renamer but typed splices are-not typechecked and extracted until during typechecking.---}---- | We only want to produce warnings for TH-splices if the user requests so.--- See Note [Warnings for TH splices].-getThSpliceOrigin :: TcM Origin-getThSpliceOrigin = do- warn <- goptM Opt_EnableThSpliceWarnings- if warn then return FromSource else return Generated--{- Note [Warnings for TH splices]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We only produce warnings for TH splices when the user requests so-(-fenable-th-splice-warnings). There are multiple reasons:-- * It's not clear that the user that compiles a splice is the author of the code- that produces the warning. Think of the situation where they just splice in- code from a third-party library that produces incomplete pattern matches.- In this scenario, the user isn't even able to fix that warning.- * Gathering information for producing the warnings (pattern-match check- warnings in particular) is costly. There's no point in doing so if the user- is not interested in those warnings.--That's why we store Origin flags in the Haskell AST. The functions from ThToHs-take such a flag and depending on whether TH splice warnings were enabled or-not, we pass FromSource (if the user requests warnings) or Generated-(otherwise). This is implemented in getThSpliceOrigin.--For correct pattern-match warnings it's crucial that we annotate the Origin-consistently (#17270). In the future we could offer the Origin as part of the-TH AST. That would enable us to give quotes from the current module get-FromSource origin, and/or third library authors to tag certain parts of-generated code as FromSource to enable warnings.-That effort is tracked in #14838.--}--{--************************************************************************-* *-\subsection{Splicing an expression}-* *-************************************************************************--}--tcSpliceExpr splice@(HsTypedSplice _ _ name expr) res_ty- = addErrCtxt (spliceCtxtDoc splice) $- setSrcSpan (getLocA expr) $ do- { stage <- getStage- ; case stage of- Splice {} -> tcTopSplice expr res_ty- Brack pop_stage pend -> tcNestedSplice pop_stage pend name expr res_ty- RunSplice _ ->- -- See Note [RunSplice ThLevel] in "GHC.Tc.Types".- pprPanic ("tcSpliceExpr: attempted to typecheck a splice when " ++- "running another splice") (ppr splice)- Comp -> tcTopSplice expr res_ty- }-tcSpliceExpr splice _- = pprPanic "tcSpliceExpr" (ppr splice)--{- Note [Collecting modFinalizers in typed splices]- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-'qAddModFinalizer' of the @Quasi TcM@ instance adds finalizers in the local-environment (see Note [Delaying modFinalizers in untyped splices] in-GHC.Rename.Splice). Thus after executing the splice, we move the finalizers to the-finalizer list in the global environment and set them to use the current local-environment (with 'addModFinalizersWithLclEnv').---}--tcNestedSplice :: ThStage -> PendingStuff -> Name- -> LHsExpr GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)- -- See Note [How brackets and nested splices are handled]- -- A splice inside brackets-tcNestedSplice pop_stage (TcPending ps_var lie_var q@(QuoteWrapper _ m_var)) splice_name expr res_ty- = do { res_ty <- expTypeToType res_ty- ; let rep = getRuntimeRep res_ty- ; meta_exp_ty <- tcTExpTy m_var res_ty- ; expr' <- setStage pop_stage $- setConstraintVar lie_var $- tcCheckMonoExpr expr meta_exp_ty- ; untype_code <- tcLookupId unTypeCodeName- ; let expr'' = mkHsApp- (mkLHsWrap (applyQuoteWrapper q)- (nlHsTyApp untype_code [rep, res_ty])) expr'- ; ps <- readMutVar ps_var- ; writeMutVar ps_var (PendingTcSplice splice_name expr'' : ps)-- -- The returned expression is ignored; it's in the pending splices- -- But we still return a plausible expression- -- (a) in case we print it in debug messages, and- -- (b) because we test whether it is tagToEnum in Tc.Gen.Expr.tcApp- ; return (HsSpliceE noAnn $- HsSpliced noExtField (ThModFinalizers []) $- HsSplicedExpr (unLoc expr'')) }---tcNestedSplice _ _ splice_name _ _- = pprPanic "tcNestedSplice: rename stage found" (ppr splice_name)--tcTopSplice :: LHsExpr GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)-tcTopSplice expr res_ty- = do { -- Typecheck the expression,- -- making sure it has type Q (T res_ty)- res_ty <- expTypeToType res_ty- ; q_type <- tcMetaTy qTyConName- -- Top level splices must still be of type Q (TExp a)- ; meta_exp_ty <- tcTExpTy q_type res_ty- ; q_expr <- tcTopSpliceExpr Typed $- tcCheckMonoExpr expr meta_exp_ty- ; lcl_env <- getLclEnv- ; let delayed_splice- = DelayedSplice lcl_env expr res_ty q_expr- ; return (HsSpliceE noAnn (XSplice (HsSplicedT delayed_splice)))-- }----- This is called in the zonker--- See Note [Running typed splices in the zonker]-runTopSplice :: DelayedSplice -> TcM (HsExpr GhcTc)-runTopSplice (DelayedSplice lcl_env orig_expr res_ty q_expr)- = restoreLclEnv lcl_env $- do { zonked_ty <- zonkTcType res_ty- ; zonked_q_expr <- zonkTopLExpr q_expr- -- See Note [Collecting modFinalizers in typed splices].- ; modfinalizers_ref <- newTcRef []- -- Run the expression- ; expr2 <- setStage (RunSplice modfinalizers_ref) $- runMetaE zonked_q_expr- ; mod_finalizers <- readTcRef modfinalizers_ref- ; addModFinalizersWithLclEnv $ ThModFinalizers mod_finalizers- -- We use orig_expr here and not q_expr when tracing as a call to- -- unsafeCodeCoerce is added to the original expression by the- -- typechecker when typed quotes are type checked.- ; traceSplice (SpliceInfo { spliceDescription = "expression"- , spliceIsDecl = False- , spliceSource = Just orig_expr- , spliceGenerated = ppr expr2 })- -- Rename and typecheck the spliced-in expression,- -- making sure it has type res_ty- -- These steps should never fail; this is a *typed* splice- ; (res, wcs) <-- captureConstraints $- addErrCtxt (spliceResultDoc zonked_q_expr) $ do- { (exp3, _fvs) <- rnLExpr expr2- ; tcCheckMonoExpr exp3 zonked_ty }- ; ev <- simplifyTop wcs- ; return $ unLoc (mkHsDictLet (EvBinds ev) res)- }---{--************************************************************************-* *-\subsection{Error messages}-* *-************************************************************************--}--spliceCtxtDoc :: HsSplice GhcRn -> SDoc-spliceCtxtDoc splice- = hang (text "In the Template Haskell splice")- 2 (pprSplice splice)--spliceResultDoc :: LHsExpr GhcTc -> SDoc-spliceResultDoc expr- = sep [ text "In the result of the splice:"- , nest 2 (char '$' <> ppr expr)- , text "To see what the splice expanded to, use -ddump-splices"]----------------------tcTopSpliceExpr :: SpliceType -> TcM (LHsExpr GhcTc) -> TcM (LHsExpr GhcTc)--- Note [How top-level splices are handled]--- Type check an expression that is the body of a top-level splice--- (the caller will compile and run it)--- Note that set the level to Splice, regardless of the original level,--- before typechecking the expression. For example:--- f x = $( ...$(g 3) ... )--- The recursive call to tcCheckPolyExpr will simply expand the--- inner escape before dealing with the outer one--tcTopSpliceExpr isTypedSplice tc_action- = checkNoErrs $ -- checkNoErrs: must not try to run the thing- -- if the type checker fails!- unsetGOptM Opt_DeferTypeErrors $- -- Don't defer type errors. Not only are we- -- going to run this code, but we do an unsafe- -- coerce, so we get a seg-fault if, say we- -- splice a type into a place where an expression- -- is expected (#7276)- setStage (Splice isTypedSplice) $- do { -- Typecheck the expression- (mb_expr', wanted) <- tryCaptureConstraints tc_action- -- If tc_action fails (perhaps because of insoluble constraints)- -- we want to capture and report those constraints, else we may- -- just get a silent failure (#20179). Hence the 'try' part.-- ; const_binds <- simplifyTop wanted-- ; case mb_expr' of- Nothing -> failM -- In this case simplifyTop should have- -- reported some errors- Just expr' -> return $ mkHsDictLet (EvBinds const_binds) expr' }--{--************************************************************************-* *- Annotations-* *-************************************************************************--}--runAnnotation target expr = do- -- Find the classes we want instances for in order to call toAnnotationWrapper- loc <- getSrcSpanM- data_class <- tcLookupClass dataClassName- to_annotation_wrapper_id <- tcLookupId toAnnotationWrapperName-- -- Check the instances we require live in another module (we want to execute it..)- -- and check identifiers live in other modules using TH stage checks. tcSimplifyStagedExpr- -- also resolves the LIE constraints to detect e.g. instance ambiguity- zonked_wrapped_expr' <- zonkTopLExpr =<< tcTopSpliceExpr Untyped (- do { (expr', expr_ty) <- tcInferRhoNC expr- -- We manually wrap the typechecked expression in a call to toAnnotationWrapper- -- By instantiating the call >here< it gets registered in the- -- LIE consulted by tcTopSpliceExpr- -- and hence ensures the appropriate dictionary is bound by const_binds- ; wrapper <- instCall AnnOrigin [expr_ty] [mkClassPred data_class [expr_ty]]- ; let loc' = noAnnSrcSpan loc- ; let specialised_to_annotation_wrapper_expr- = L loc' (mkHsWrap wrapper- (HsVar noExtField (L (noAnnSrcSpan loc) to_annotation_wrapper_id)))- ; return (L loc' (HsApp noComments- specialised_to_annotation_wrapper_expr expr'))- })-- -- Run the appropriately wrapped expression to get the value of- -- the annotation and its dictionaries. The return value is of- -- type AnnotationWrapper by construction, so this conversion is- -- safe- serialized <- runMetaAW zonked_wrapped_expr'- return Annotation {- ann_target = target,- ann_value = serialized- }--convertAnnotationWrapper :: ForeignHValue -> TcM (Either SDoc Serialized)-convertAnnotationWrapper fhv = do- interp <- tcGetInterp- case interpInstance interp of- ExternalInterp {} -> Right <$> runTH THAnnWrapper fhv-#if defined(HAVE_INTERNAL_INTERPRETER)- InternalInterp -> do- annotation_wrapper <- liftIO $ wormhole interp fhv- return $ Right $- case unsafeCoerce annotation_wrapper of- AnnotationWrapper value | let serialized = toSerialized serializeWithData value ->- -- Got the value and dictionaries: build the serialized value and- -- call it a day. We ensure that we seq the entire serialized value- -- in order that any errors in the user-written code for the- -- annotation are exposed at this point. This is also why we are- -- doing all this stuff inside the context of runMeta: it has the- -- facilities to deal with user error in a meta-level expression- seqSerialized serialized `seq` serialized---- | Force the contents of the Serialized value so weknow it doesn't contain any bottoms-seqSerialized :: Serialized -> ()-seqSerialized (Serialized the_type bytes) = the_type `seq` bytes `seqList` ()--#endif--{--************************************************************************-* *-\subsection{Running an expression}-* *-************************************************************************--}--runQuasi :: TH.Q a -> TcM a-runQuasi act = TH.runQ act--runRemoteModFinalizers :: ThModFinalizers -> TcM ()-runRemoteModFinalizers (ThModFinalizers finRefs) = do- let withForeignRefs [] f = f []- withForeignRefs (x : xs) f = withForeignRef x $ \r ->- withForeignRefs xs $ \rs -> f (r : rs)- interp <- tcGetInterp- case interpInstance interp of-#if defined(HAVE_INTERNAL_INTERPRETER)- InternalInterp -> do- qs <- liftIO (withForeignRefs finRefs $ mapM localRef)- runQuasi $ sequence_ qs-#endif-- ExternalInterp conf iserv -> withIServ_ conf iserv $ \i -> do- tcg <- getGblEnv- th_state <- readTcRef (tcg_th_remote_state tcg)- case th_state of- Nothing -> return () -- TH was not started, nothing to do- Just fhv -> do- liftIO $ withForeignRef fhv $ \st ->- withForeignRefs finRefs $ \qrefs ->- writeIServ i (putMessage (RunModFinalizers st qrefs))- () <- runRemoteTH i []- readQResult i--runQResult- :: (a -> String)- -> (Origin -> SrcSpan -> a -> b)- -> (ForeignHValue -> TcM a)- -> SrcSpan- -> ForeignHValue {- TH.Q a -}- -> TcM b-runQResult show_th f runQ expr_span hval- = do { th_result <- runQ hval- ; th_origin <- getThSpliceOrigin- ; traceTc "Got TH result:" (text (show_th th_result))- ; return (f th_origin expr_span th_result) }---------------------runMeta :: (MetaHook TcM -> LHsExpr GhcTc -> TcM hs_syn)- -> LHsExpr GhcTc- -> TcM hs_syn-runMeta unwrap e = do- hooks <- getHooks- case runMetaHook hooks of- Nothing -> unwrap defaultRunMeta e- Just h -> unwrap h e--defaultRunMeta :: MetaHook TcM-defaultRunMeta (MetaE r)- = fmap r . runMeta' True ppr (runQResult TH.pprint convertToHsExpr runTHExp)-defaultRunMeta (MetaP r)- = fmap r . runMeta' True ppr (runQResult TH.pprint convertToPat runTHPat)-defaultRunMeta (MetaT r)- = fmap r . runMeta' True ppr (runQResult TH.pprint convertToHsType runTHType)-defaultRunMeta (MetaD r)- = fmap r . runMeta' True ppr (runQResult TH.pprint convertToHsDecls runTHDec)-defaultRunMeta (MetaAW r)- = fmap r . runMeta' False (const empty) (const convertAnnotationWrapper)- -- We turn off showing the code in meta-level exceptions because doing so exposes- -- the toAnnotationWrapper function that we slap around the user's code-------------------runMetaAW :: LHsExpr GhcTc -- Of type AnnotationWrapper- -> TcM Serialized-runMetaAW = runMeta metaRequestAW--runMetaE :: LHsExpr GhcTc -- Of type (Q Exp)- -> TcM (LHsExpr GhcPs)-runMetaE = runMeta metaRequestE--runMetaP :: LHsExpr GhcTc -- Of type (Q Pat)- -> TcM (LPat GhcPs)-runMetaP = runMeta metaRequestP--runMetaT :: LHsExpr GhcTc -- Of type (Q Type)- -> TcM (LHsType GhcPs)-runMetaT = runMeta metaRequestT--runMetaD :: LHsExpr GhcTc -- Of type Q [Dec]- -> TcM [LHsDecl GhcPs]-runMetaD = runMeta metaRequestD--{- Note [Errors in desugaring a splice]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-What should we do if there are errors when desugaring a splice? We should-abort. There are several cases to consider:--(a) The desugarer hits an unrecoverable error and fails in the monad.-(b) The desugarer hits a recoverable error, reports it, and continues.-(c) The desugarer reports a fatal warning (with -Werror), reports it, and continues.-(d) The desugarer reports a non-fatal warning, and continues.--Each case is tested in th/T19709[abcd].--General principle: we wish to report all messages from dealing with a splice-eagerly, as these messages arise during an earlier stage than type-checking-generally. It's also likely that a compile-time warning from spliced code-will be easier to understand then an error that arises from processing the-code the splice produces. (Rationale: the warning will be about the code the-user actually wrote, not what is generated.)--Case (a): We have no choice but to abort here, but we must make sure that-the messages are printed or logged before aborting. Logging them is annoying,-because we're in the type-checker, and the messages are DsMessages, from the-desugarer. So we report and then fail in the monad. This case is detected-by the fact that initDsTc returns Nothing.--Case (b): We detect this case by looking for errors in the messages returned-from initDsTc and aborting if we spot any (after printing, of course). Note-that initDsTc will return a Just ds_expr in this case, but we don't wish to-use the (likely very bogus) expression.--Case (c): This is functionally the same as (b), except that the expression-isn't bogus. We still don't wish to use it, as the user's request for -Werror-tells us not to.--Case (d): We report the warnings and then carry on with the expression.-This might result in warnings printed out of source order, but this is-appropriate, as the warnings from the splice arise from an earlier stage-of compilation.--Previously, we failed to abort in cases (b) and (c), leading to #19709.--}------------------runMeta' :: Bool -- Whether code should be printed in the exception message- -> (hs_syn -> SDoc) -- how to print the code- -> (SrcSpan -> ForeignHValue -> TcM (Either SDoc hs_syn)) -- How to run x- -> LHsExpr GhcTc -- Of type x; typically x = Q TH.Exp, or- -- something like that- -> TcM hs_syn -- Of type t-runMeta' show_code ppr_hs run_and_convert expr- = do { traceTc "About to run" (ppr expr)- ; recordThSpliceUse -- seems to be the best place to do this,- -- we catch all kinds of splices and annotations.-- -- Check that we've had no errors of any sort so far.- -- For example, if we found an error in an earlier defn f, but- -- recovered giving it type f :: forall a.a, it'd be very dodgy- -- to carry on. Mind you, the staging restrictions mean we won't- -- actually run f, but it still seems wrong. And, more concretely,- -- see #5358 for an example that fell over when trying to- -- reify a function with an unlifted kind in it. (These don't occur- -- in type-correct programs.)- ; failIfErrsM-- -- run plugins- ; hsc_env <- getTopEnv- ; expr' <- withPlugins (hsc_plugins hsc_env) spliceRunAction expr-- -- Desugar- ; (ds_msgs, mb_ds_expr) <- initDsTc (dsLExpr expr')-- -- Print any messages (even warnings) eagerly: they might be helpful if anything- -- goes wrong. See Note [Errors in desugaring a splice]. This happens in all- -- cases.- ; logger <- getLogger- ; diag_opts <- initDiagOpts <$> getDynFlags- ; liftIO $ printMessages logger diag_opts ds_msgs-- ; ds_expr <- case mb_ds_expr of- Nothing -> failM -- Case (a) from Note [Errors in desugaring a splice]- Just ds_expr -> -- There still might be a fatal warning or recoverable- -- Cases (b) and (c) from Note [Errors in desugaring a splice]- do { when (errorsOrFatalWarningsFound ds_msgs)- failM- ; return ds_expr }-- -- Compile and link it; might fail if linking fails- ; src_span <- getSrcSpanM- ; traceTc "About to run (desugared)" (ppr ds_expr)- ; either_hval <- tryM $ liftIO $- GHC.Driver.Main.hscCompileCoreExpr hsc_env src_span ds_expr- ; case either_hval of {- Left exn -> fail_with_exn "compile and link" exn ;- Right (hval, needed_mods, needed_pkgs) -> do-- { -- Coerce it to Q t, and run it-- -- Running might fail if it throws an exception of any kind (hence tryAllM)- -- including, say, a pattern-match exception in the code we are running- --- -- We also do the TH -> HS syntax conversion inside the same- -- exception-catching thing so that if there are any lurking- -- exceptions in the data structure returned by hval, we'll- -- encounter them inside the try- --- -- See Note [Exceptions in TH]- let expr_span = getLocA expr- ; recordThNeededRuntimeDeps needed_mods needed_pkgs- ; either_tval <- tryAllM $- setSrcSpan expr_span $ -- Set the span so that qLocation can- -- see where this splice is- do { mb_result <- run_and_convert expr_span hval- ; case mb_result of- Left err -> failWithTc (TcRnUnknownMessage $ mkPlainError noHints err)- Right result -> do { traceTc "Got HsSyn result:" (ppr_hs result)- ; return $! result } }-- ; case either_tval of- Right v -> return v- Left se -> case fromException se of- Just IOEnvFailure -> failM -- Error already in Tc monad- _ -> fail_with_exn "run" se -- Exception- }}}- where- -- see Note [Concealed TH exceptions]- fail_with_exn :: Exception e => String -> e -> TcM a- fail_with_exn phase exn = do- exn_msg <- liftIO $ Panic.safeShowException exn- let msg = vcat [text "Exception when trying to" <+> text phase <+> text "compile-time code:",- nest 2 (text exn_msg),- if show_code then text "Code:" <+> ppr expr else empty]- failWithTc (TcRnUnknownMessage $ mkPlainError noHints msg)--{--Note [Running typed splices in the zonker]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--See #15471 for the full discussion.--For many years typed splices were run immediately after they were type checked-however, this is too early as it means to zonk some type variables before-they can be unified with type variables in the surrounding context.--For example,--```-module A where--test_foo :: forall a . Q (TExp (a -> a))-test_foo = [|| id ||]--module B where--import A--qux = $$(test_foo)-```--We would expect `qux` to have inferred type `forall a . a -> a` but if-we run the splices too early the unified variables are zonked to `Any`. The-inferred type is the unusable `Any -> Any`.--To run the splice, we must compile `test_foo` all the way to byte code.-But at the moment when the type checker is looking at the splice, test_foo-has type `Q (TExp (alpha -> alpha))` and we-certainly can't compile code involving unification variables!--We could default `alpha` to `Any` but then we infer `qux :: Any -> Any`-which definitely is not what we want. Moreover, if we had- qux = [$$(test_foo), (\x -> x +1::Int)]-then `alpha` would have to be `Int`.--Conclusion: we must defer taking decisions about `alpha` until the-typechecker is done; and *then* we can run the splice. It's fine to do it-later, because we know it'll produce type-correct code.--Deferring running the splice until later, in the zonker, means that the-unification variables propagate upwards from the splice into the surrounding-context and are unified correctly.--This is implemented by storing the arguments we need for running the splice-in a `DelayedSplice`. In the zonker, the arguments are passed to-`GHC.Tc.Gen.Splice.runTopSplice` and the expression inserted into the AST as normal.----Note [Exceptions in TH]-~~~~~~~~~~~~~~~~~~~~~~~-Suppose we have something like this- $( f 4 )-where- f :: Int -> Q [Dec]- f n | n>3 = fail "Too many declarations"- | otherwise = ...--The 'fail' is a user-generated failure, and should be displayed as a-perfectly ordinary compiler error message, not a panic or anything-like that. Here's how it's processed:-- * 'fail' is the monad fail. The monad instance for Q in TH.Syntax- effectively transforms (fail s) to- qReport True s >> fail- where 'qReport' comes from the Quasi class and fail from its monad- superclass.-- * The TcM monad is an instance of Quasi (see GHC.Tc.Gen.Splice), and it implements- (qReport True s) by using addErr to add an error message to the bag of errors.- The 'fail' in TcM raises an IOEnvFailure exception-- * 'qReport' forces the message to ensure any exception hidden in unevaluated- thunk doesn't get into the bag of errors. Otherwise the following splice- will trigger panic (#8987):- $(fail undefined)- See also Note [Concealed TH exceptions]-- * So, when running a splice, we catch all exceptions; then for- - an IOEnvFailure exception, we assume the error is already- in the error-bag (above)- - other errors, we add an error to the bag- and then fail--Note [Concealed TH exceptions]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-When displaying the error message contained in an exception originated from TH-code, we need to make sure that the error message itself does not contain an-exception. For example, when executing the following splice:-- $( error ("foo " ++ error "bar") )--the message for the outer exception is a thunk which will throw the inner-exception when evaluated.--For this reason, we display the message of a TH exception using the-'safeShowException' function, which recursively catches any exception thrown-when showing an error message.---To call runQ in the Tc monad, we need to make TcM an instance of Quasi:--}--instance TH.Quasi TcM where- qNewName s = do { u <- newUnique- ; let i = toInteger (getKey u)- ; return (TH.mkNameU s i) }-- -- 'msg' is forced to ensure exceptions don't escape,- -- see Note [Exceptions in TH]- qReport True msg = seqList msg $ addErr $ TcRnUnknownMessage $ mkPlainError noHints (text msg)- qReport False msg = seqList msg $ addDiagnostic $ TcRnUnknownMessage $- mkPlainDiagnostic WarningWithoutFlag noHints (text msg)-- qLocation = do { m <- getModule- ; l <- getSrcSpanM- ; r <- case l of- UnhelpfulSpan _ -> pprPanic "qLocation: Unhelpful location"- (ppr l)- RealSrcSpan s _ -> return s- ; return (TH.Loc { TH.loc_filename = unpackFS (srcSpanFile r)- , TH.loc_module = moduleNameString (moduleName m)- , TH.loc_package = unitString (moduleUnit m)- , TH.loc_start = (srcSpanStartLine r, srcSpanStartCol r)- , TH.loc_end = (srcSpanEndLine r, srcSpanEndCol r) }) }-- qLookupName = lookupName- qReify = reify- qReifyFixity nm = lookupThName nm >>= reifyFixity- qReifyType = reifyTypeOfThing- qReifyInstances = reifyInstances- qReifyRoles = reifyRoles- qReifyAnnotations = reifyAnnotations- qReifyModule = reifyModule- qReifyConStrictness nm = do { nm' <- lookupThName nm- ; dc <- tcLookupDataCon nm'- ; let bangs = dataConImplBangs dc- ; return (map reifyDecidedStrictness bangs) }-- -- For qRecover, discard error messages if- -- the recovery action is chosen. Otherwise- -- we'll only fail higher up.- qRecover recover main = tryTcDiscardingErrs recover main-- qGetPackageRoot = do- dflags <- getDynFlags- return $ fromMaybe "." (workingDirectory dflags)-- qAddDependentFile fp = do- ref <- fmap tcg_dependent_files getGblEnv- dep_files <- readTcRef ref- writeTcRef ref (fp:dep_files)-- qAddTempFile suffix = do- dflags <- getDynFlags- logger <- getLogger- tmpfs <- hsc_tmpfs <$> getTopEnv- liftIO $ newTempName logger tmpfs (tmpDir dflags) TFL_GhcSession suffix-- qAddTopDecls thds = do- l <- getSrcSpanM- th_origin <- getThSpliceOrigin- let either_hval = convertToHsDecls th_origin l thds- ds <- case either_hval of- Left exn -> failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $- hang (text "Error in a declaration passed to addTopDecls:")- 2 exn- Right ds -> return ds- mapM_ (checkTopDecl . unLoc) ds- th_topdecls_var <- fmap tcg_th_topdecls getGblEnv- updTcRef th_topdecls_var (\topds -> ds ++ topds)- where- checkTopDecl :: HsDecl GhcPs -> TcM ()- checkTopDecl (ValD _ binds)- = mapM_ bindName (collectHsBindBinders CollNoDictBinders binds)- checkTopDecl (SigD _ _)- = return ()- checkTopDecl (AnnD _ _)- = return ()- checkTopDecl (ForD _ (ForeignImport { fd_name = L _ name }))- = bindName name- checkTopDecl _- = addErr $ TcRnUnknownMessage $ mkPlainError noHints $- text "Only function, value, annotation, and foreign import declarations may be added with addTopDecl"-- bindName :: RdrName -> TcM ()- bindName (Exact n)- = do { th_topnames_var <- fmap tcg_th_topnames getGblEnv- ; updTcRef th_topnames_var (\ns -> extendNameSet ns n)- }-- bindName name =- addErr $ TcRnUnknownMessage $ mkPlainError noHints $- hang (text "The binder" <+> quotes (ppr name) <+> text "is not a NameU.")- 2 (text "Probable cause: you used mkName instead of newName to generate a binding.")-- qAddForeignFilePath lang fp = do- var <- fmap tcg_th_foreign_files getGblEnv- updTcRef var ((lang, fp) :)-- qAddModFinalizer fin = do- r <- liftIO $ mkRemoteRef fin- fref <- liftIO $ mkForeignRef r (freeRemoteRef r)- addModFinalizerRef fref-- qAddCorePlugin plugin = do- hsc_env <- getTopEnv- let fc = hsc_FC hsc_env- let home_unit = hsc_home_unit hsc_env- let dflags = hsc_dflags hsc_env- let fopts = initFinderOpts dflags- r <- liftIO $ findHomeModule fc fopts home_unit (mkModuleName plugin)- let err = hang- (text "addCorePlugin: invalid plugin module "- <+> text (show plugin)- )- 2- (text "Plugins in the current package can't be specified.")- case r of- Found {} -> addErr $ TcRnUnknownMessage $ mkPlainError noHints err- FoundMultiple {} -> addErr $ TcRnUnknownMessage $ mkPlainError noHints err- _ -> return ()- th_coreplugins_var <- tcg_th_coreplugins <$> getGblEnv- updTcRef th_coreplugins_var (plugin:)-- qGetQ :: forall a. Typeable a => TcM (Maybe a)- qGetQ = do- th_state_var <- fmap tcg_th_state getGblEnv- th_state <- readTcRef th_state_var- -- See #10596 for why we use a scoped type variable here.- return (Map.lookup (typeRep (Proxy :: Proxy a)) th_state >>= fromDynamic)-- qPutQ x = do- th_state_var <- fmap tcg_th_state getGblEnv- updTcRef th_state_var (\m -> Map.insert (typeOf x) (toDyn x) m)-- qIsExtEnabled = xoptM-- qExtsEnabled =- EnumSet.toList . extensionFlags . hsc_dflags <$> getTopEnv-- qPutDoc doc_loc s = do- th_doc_var <- tcg_th_docs <$> getGblEnv- resolved_doc_loc <- resolve_loc doc_loc- is_local <- checkLocalName resolved_doc_loc- unless is_local $ failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $ text- "Can't add documentation to" <+> ppr_loc doc_loc <+>- text "as it isn't inside the current module"- let ds = mkGeneratedHsDocString s- hd = lexHsDoc parseIdentifier ds- hd' <- rnHsDoc hd- updTcRef th_doc_var (Map.insert resolved_doc_loc hd')- where- resolve_loc (TH.DeclDoc n) = DeclDoc <$> lookupThName n- resolve_loc (TH.ArgDoc n i) = ArgDoc <$> lookupThName n <*> pure i- resolve_loc (TH.InstDoc t) = InstDoc <$> fmap getName (lookupThInstName t)- resolve_loc TH.ModuleDoc = pure ModuleDoc-- ppr_loc (TH.DeclDoc n) = ppr_th n- ppr_loc (TH.ArgDoc n _) = ppr_th n- ppr_loc (TH.InstDoc t) = ppr_th t- ppr_loc TH.ModuleDoc = text "the module header"-- -- It doesn't make sense to add documentation to something not inside- -- the current module. So check for it!- checkLocalName (DeclDoc n) = nameIsLocalOrFrom <$> getModule <*> pure n- checkLocalName (ArgDoc n _) = nameIsLocalOrFrom <$> getModule <*> pure n- checkLocalName (InstDoc n) = nameIsLocalOrFrom <$> getModule <*> pure n- checkLocalName ModuleDoc = pure True--- qGetDoc (TH.DeclDoc n) = lookupThName n >>= lookupDeclDoc- qGetDoc (TH.InstDoc t) = lookupThInstName t >>= lookupDeclDoc- qGetDoc (TH.ArgDoc n i) = lookupThName n >>= lookupArgDoc i- qGetDoc TH.ModuleDoc = do- df <- getDynFlags- docs <- getGblEnv >>= extractDocs df- return (renderHsDocString . hsDocString <$> (docs_mod_hdr =<< docs))---- | Looks up documentation for a declaration in first the current module,--- otherwise tries to find it in another module via 'hscGetModuleInterface'.-lookupDeclDoc :: Name -> TcM (Maybe String)-lookupDeclDoc nm = do- df <- getDynFlags- Docs{docs_decls} <- fmap (fromMaybe emptyDocs) $ getGblEnv >>= extractDocs df- case lookupUniqMap docs_decls nm of- Just doc -> pure $ Just (renderHsDocStrings $ map hsDocString doc)- Nothing -> do- -- Wasn't in the current module. Try searching other external ones!- mIface <- getExternalModIface nm- case mIface of- Just ModIface { mi_docs = Just Docs{docs_decls = dmap} } ->- pure $ renderHsDocStrings . map hsDocString <$> lookupUniqMap dmap nm- _ -> pure Nothing---- | Like 'lookupDeclDoc', looks up documentation for a function argument. If--- it can't find any documentation for a function in this module, it tries to--- find it in another module.-lookupArgDoc :: Int -> Name -> TcM (Maybe String)-lookupArgDoc i nm = do- df <- getDynFlags- Docs{docs_args = argDocs} <- fmap (fromMaybe emptyDocs) $ getGblEnv >>= extractDocs df- case lookupUniqMap argDocs nm of- Just m -> pure $ renderHsDocString . hsDocString <$> IntMap.lookup i m- Nothing -> do- mIface <- getExternalModIface nm- case mIface of- Just ModIface { mi_docs = Just Docs{docs_args = amap} } ->- pure $ renderHsDocString . hsDocString <$> (lookupUniqMap amap nm >>= IntMap.lookup i)- _ -> pure Nothing---- | Returns the module a Name belongs to, if it is isn't local.-getExternalModIface :: Name -> TcM (Maybe ModIface)-getExternalModIface nm = do- isLocal <- nameIsLocalOrFrom <$> getModule <*> pure nm- if isLocal- then pure Nothing- else case nameModule_maybe nm of- Nothing -> pure Nothing- Just modNm -> do- hsc_env <- getTopEnv- iface <- liftIO $ hscGetModuleInterface hsc_env modNm- pure (Just iface)---- | Find the GHC name of the first instance that matches the TH type-lookupThInstName :: TH.Type -> TcM Name-lookupThInstName th_type = do- cls_name <- inst_cls_name th_type- insts <- reifyInstances' cls_name (inst_arg_types th_type)- case insts of -- This expands any type synonyms- Left (_, (inst:_)) -> return $ getName inst- Left (_, []) -> noMatches- Right (_, (inst:_)) -> return $ getName inst- Right (_, []) -> noMatches- where- noMatches = failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $- text "Couldn't find any instances of"- <+> ppr_th th_type- <+> text "to add documentation to"-- -- Get the name of the class for the instance we are documenting- -- > inst_cls_name (Monad Maybe) == Monad- -- > inst_cls_name C = C- inst_cls_name :: TH.Type -> TcM TH.Name- inst_cls_name (TH.AppT t _) = inst_cls_name t- inst_cls_name (TH.SigT n _) = inst_cls_name n- inst_cls_name (TH.VarT n) = pure n- inst_cls_name (TH.ConT n) = pure n- inst_cls_name (TH.PromotedT n) = pure n- inst_cls_name (TH.InfixT _ n _) = pure n- inst_cls_name (TH.UInfixT _ n _) = pure n- inst_cls_name (TH.PromotedInfixT _ n _) = pure n- inst_cls_name (TH.PromotedUInfixT _ n _) = pure n- inst_cls_name (TH.ParensT t) = inst_cls_name t-- inst_cls_name (TH.ForallT _ _ _) = inst_cls_name_err- inst_cls_name (TH.ForallVisT _ _) = inst_cls_name_err- inst_cls_name (TH.AppKindT _ _) = inst_cls_name_err- inst_cls_name (TH.TupleT _) = inst_cls_name_err- inst_cls_name (TH.UnboxedTupleT _) = inst_cls_name_err- inst_cls_name (TH.UnboxedSumT _) = inst_cls_name_err- inst_cls_name TH.ArrowT = inst_cls_name_err- inst_cls_name TH.MulArrowT = inst_cls_name_err- inst_cls_name TH.EqualityT = inst_cls_name_err- inst_cls_name TH.ListT = inst_cls_name_err- inst_cls_name (TH.PromotedTupleT _) = inst_cls_name_err- inst_cls_name TH.PromotedNilT = inst_cls_name_err- inst_cls_name TH.PromotedConsT = inst_cls_name_err- inst_cls_name TH.StarT = inst_cls_name_err- inst_cls_name TH.ConstraintT = inst_cls_name_err- inst_cls_name (TH.LitT _) = inst_cls_name_err- inst_cls_name TH.WildCardT = inst_cls_name_err- inst_cls_name (TH.ImplicitParamT _ _) = inst_cls_name_err-- inst_cls_name_err = failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $- text "Couldn't work out what instance"- <+> ppr_th th_type- <+> text "is supposed to be"-- -- Basically does the opposite of 'mkThAppTs'- -- > inst_arg_types (Monad Maybe) == [Maybe]- -- > inst_arg_types C == []- inst_arg_types :: TH.Type -> [TH.Type]- inst_arg_types (TH.AppT _ args) =- let go (TH.AppT t ts) = t:go ts- go t = [t]- in go args- inst_arg_types _ = []---- | Adds a mod finalizer reference to the local environment.-addModFinalizerRef :: ForeignRef (TH.Q ()) -> TcM ()-addModFinalizerRef finRef = do- th_stage <- getStage- case th_stage of- RunSplice th_modfinalizers_var -> updTcRef th_modfinalizers_var (finRef :)- -- This case happens only if a splice is executed and the caller does- -- not set the 'ThStage' to 'RunSplice' to collect finalizers.- -- See Note [Delaying modFinalizers in untyped splices] in GHC.Rename.Splice.- _ ->- pprPanic "addModFinalizer was called when no finalizers were collected"- (ppr th_stage)---- | Releases the external interpreter state.-finishTH :: TcM ()-finishTH = do- hsc_env <- getTopEnv- case interpInstance <$> hsc_interp hsc_env of- Nothing -> pure ()-#if defined(HAVE_INTERNAL_INTERPRETER)- Just InternalInterp -> pure ()-#endif- Just (ExternalInterp {}) -> do- tcg <- getGblEnv- writeTcRef (tcg_th_remote_state tcg) Nothing---runTHExp :: ForeignHValue -> TcM TH.Exp-runTHExp = runTH THExp--runTHPat :: ForeignHValue -> TcM TH.Pat-runTHPat = runTH THPat--runTHType :: ForeignHValue -> TcM TH.Type-runTHType = runTH THType--runTHDec :: ForeignHValue -> TcM [TH.Dec]-runTHDec = runTH THDec--runTH :: Binary a => THResultType -> ForeignHValue -> TcM a-runTH ty fhv = do- interp <- tcGetInterp- case interpInstance interp of-#if defined(HAVE_INTERNAL_INTERPRETER)- InternalInterp -> do- -- Run it in the local TcM- hv <- liftIO $ wormhole interp fhv- r <- runQuasi (unsafeCoerce hv :: TH.Q a)- return r-#endif-- ExternalInterp conf iserv ->- -- Run it on the server. For an overview of how TH works with- -- Remote GHCi, see Note [Remote Template Haskell] in- -- libraries/ghci/GHCi/TH.hs.- withIServ_ conf iserv $ \i -> do- rstate <- getTHState i- loc <- TH.qLocation- liftIO $- withForeignRef rstate $ \state_hv ->- withForeignRef fhv $ \q_hv ->- writeIServ i (putMessage (RunTH state_hv q_hv ty (Just loc)))- runRemoteTH i []- bs <- readQResult i- return $! runGet get (LB.fromStrict bs)----- | communicate with a remotely-running TH computation until it finishes.--- See Note [Remote Template Haskell] in libraries/ghci/GHCi/TH.hs.-runRemoteTH- :: IServInstance- -> [Messages TcRnMessage] -- saved from nested calls to qRecover- -> TcM ()-runRemoteTH iserv recovers = do- THMsg msg <- liftIO $ readIServ iserv getTHMessage- case msg of- RunTHDone -> return ()- StartRecover -> do -- Note [TH recover with -fexternal-interpreter]- v <- getErrsVar- msgs <- readTcRef v- writeTcRef v emptyMessages- runRemoteTH iserv (msgs : recovers)- EndRecover caught_error -> do- let (prev_msgs, rest) = case recovers of- [] -> panic "EndRecover"- a : b -> (a,b)- v <- getErrsVar- warn_msgs <- getWarningMessages <$> readTcRef v- -- keep the warnings only if there were no errors- writeTcRef v $ if caught_error- then prev_msgs- else mkMessages warn_msgs `unionMessages` prev_msgs- runRemoteTH iserv rest- _other -> do- r <- handleTHMessage msg- liftIO $ writeIServ iserv (put r)- runRemoteTH iserv recovers---- | Read a value of type QResult from the iserv-readQResult :: Binary a => IServInstance -> TcM a-readQResult i = do- qr <- liftIO $ readIServ i get- case qr of- QDone a -> return a- QException str -> liftIO $ throwIO (ErrorCall str)- QFail str -> fail str--{- Note [TH recover with -fexternal-interpreter]- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Recover is slightly tricky to implement.--The meaning of "recover a b" is- - Do a- - If it finished with no errors, then keep the warnings it generated- - If it failed, discard any messages it generated, and do b--Note that "failed" here can mean either- (1) threw an exception (failTc)- (2) generated an error message (addErrTcM)--The messages are managed by GHC in the TcM monad, whereas the-exception-handling is done in the ghc-iserv process, so we have to-coordinate between the two.--On the server:- - emit a StartRecover message- - run "a; FailIfErrs" inside a try- - emit an (EndRecover x) message, where x = True if "a; FailIfErrs" failed- - if "a; FailIfErrs" failed, run "b"--Back in GHC, when we receive:-- FailIfErrrs- failTc if there are any error messages (= failIfErrsM)- StartRecover- save the current messages and start with an empty set.- EndRecover caught_error- Restore the previous messages,- and merge in the new messages if caught_error is false.--}---- | Retrieve (or create, if it hasn't been created already), the--- remote TH state. The TH state is a remote reference to an IORef--- QState living on the server, and we have to pass this to each RunTH--- call we make.------ The TH state is stored in tcg_th_remote_state in the TcGblEnv.----getTHState :: IServInstance -> TcM (ForeignRef (IORef QState))-getTHState i = do- tcg <- getGblEnv- th_state <- readTcRef (tcg_th_remote_state tcg)- case th_state of- Just rhv -> return rhv- Nothing -> do- interp <- tcGetInterp- fhv <- liftIO $ mkFinalizedHValue interp =<< iservCall i StartTH- writeTcRef (tcg_th_remote_state tcg) (Just fhv)- return fhv--wrapTHResult :: TcM a -> TcM (THResult a)-wrapTHResult tcm = do- e <- tryM tcm -- only catch 'fail', treat everything else as catastrophic- case e of- Left e -> return (THException (show e))- Right a -> return (THComplete a)--handleTHMessage :: THMessage a -> TcM a-handleTHMessage msg = case msg of- NewName a -> wrapTHResult $ TH.qNewName a- Report b str -> wrapTHResult $ TH.qReport b str- LookupName b str -> wrapTHResult $ TH.qLookupName b str- Reify n -> wrapTHResult $ TH.qReify n- ReifyFixity n -> wrapTHResult $ TH.qReifyFixity n- ReifyType n -> wrapTHResult $ TH.qReifyType n- ReifyInstances n ts -> wrapTHResult $ TH.qReifyInstances n ts- ReifyRoles n -> wrapTHResult $ TH.qReifyRoles n- ReifyAnnotations lookup tyrep ->- wrapTHResult $ (map B.pack <$> getAnnotationsByTypeRep lookup tyrep)- ReifyModule m -> wrapTHResult $ TH.qReifyModule m- ReifyConStrictness nm -> wrapTHResult $ TH.qReifyConStrictness nm- GetPackageRoot -> wrapTHResult $ TH.qGetPackageRoot- AddDependentFile f -> wrapTHResult $ TH.qAddDependentFile f- AddTempFile s -> wrapTHResult $ TH.qAddTempFile s- AddModFinalizer r -> do- interp <- hscInterp <$> getTopEnv- wrapTHResult $ liftIO (mkFinalizedHValue interp r) >>= addModFinalizerRef- AddCorePlugin str -> wrapTHResult $ TH.qAddCorePlugin str- AddTopDecls decs -> wrapTHResult $ TH.qAddTopDecls decs- AddForeignFilePath lang str -> wrapTHResult $ TH.qAddForeignFilePath lang str- IsExtEnabled ext -> wrapTHResult $ TH.qIsExtEnabled ext- ExtsEnabled -> wrapTHResult $ TH.qExtsEnabled- PutDoc l s -> wrapTHResult $ TH.qPutDoc l s- GetDoc l -> wrapTHResult $ TH.qGetDoc l- FailIfErrs -> wrapTHResult failIfErrsM- _ -> panic ("handleTHMessage: unexpected message " ++ show msg)--getAnnotationsByTypeRep :: TH.AnnLookup -> TypeRep -> TcM [[Word8]]-getAnnotationsByTypeRep th_name tyrep- = do { name <- lookupThAnnLookup th_name- ; topEnv <- getTopEnv- ; epsHptAnns <- liftIO $ prepareAnnotations topEnv Nothing- ; tcg <- getGblEnv- ; let selectedEpsHptAnns = findAnnsByTypeRep epsHptAnns name tyrep- ; let selectedTcgAnns = findAnnsByTypeRep (tcg_ann_env tcg) name tyrep- ; return (selectedEpsHptAnns ++ selectedTcgAnns) }--{--************************************************************************-* *- Instance Testing-* *-************************************************************************--}--reifyInstances :: TH.Name -> [TH.Type] -> TcM [TH.Dec]-reifyInstances th_nm th_tys- = do { insts <- reifyInstances' th_nm th_tys- ; case insts of- Left (cls, cls_insts) ->- reifyClassInstances cls cls_insts- Right (tc, fam_insts) ->- reifyFamilyInstances tc fam_insts }--reifyInstances' :: TH.Name- -> [TH.Type]- -> TcM (Either (Class, [ClsInst]) (TyCon, [FamInst]))- -- ^ Returns 'Left' in the case that the instances were found to- -- be class instances, or 'Right' if they are family instances.-reifyInstances' th_nm th_tys- = addErrCtxt (text "In the argument of reifyInstances:"- <+> ppr_th th_nm <+> sep (map ppr_th th_tys)) $- do { loc <- getSrcSpanM- ; th_origin <- getThSpliceOrigin- ; rdr_ty <- cvt th_origin loc (mkThAppTs (TH.ConT th_nm) th_tys)- -- #9262 says to bring vars into scope, like in HsForAllTy case- -- of rnHsTyKi- ; let tv_rdrs = extractHsTyRdrTyVars rdr_ty- -- Rename to HsType Name- ; ((tv_names, rn_ty), _fvs)- <- checkNoErrs $ -- If there are out-of-scope Names here, then we- -- must error before proceeding to typecheck the- -- renamed type, as that will result in GHC- -- internal errors (#13837).- rnImplicitTvOccs Nothing tv_rdrs $ \ tv_names ->- do { (rn_ty, fvs) <- rnLHsType doc rdr_ty- ; return ((tv_names, rn_ty), fvs) }- ; skol_info <- mkSkolemInfo ReifySkol- ; (tclvl, wanted, (tvs, ty))- <- pushLevelAndSolveEqualitiesX "reifyInstances" $- bindImplicitTKBndrs_Skol skol_info tv_names $- tcInferLHsType rn_ty-- ; tvs <- zonkAndScopedSort tvs-- -- Avoid error cascade if there are unsolved- ; reportUnsolvedEqualities skol_info tvs tclvl wanted-- ; ty <- zonkTcTypeToType ty- -- Substitute out the meta type variables- -- In particular, the type might have kind- -- variables inside it (#7477)-- ; traceTc "reifyInstances'" (ppr ty $$ ppr (tcTypeKind ty))- ; case splitTyConApp_maybe ty of -- This expands any type synonyms- Just (tc, tys) -- See #7910- | Just cls <- tyConClass_maybe tc- -> do { inst_envs <- tcGetInstEnvs- ; let (matches, unifies, _) = lookupInstEnv False inst_envs cls tys- ; traceTc "reifyInstances'1" (ppr matches)- ; return $ Left (cls, map fst matches ++ getPotentialUnifiers unifies) }- | isOpenFamilyTyCon tc- -> do { inst_envs <- tcGetFamInstEnvs- ; let matches = lookupFamInstEnv inst_envs tc tys- ; traceTc "reifyInstances'2" (ppr matches)- ; return $ Right (tc, map fim_instance matches) }- _ -> bale_out $ TcRnUnknownMessage $ mkPlainError noHints $- (hang (text "reifyInstances:" <+> quotes (ppr ty))- 2 (text "is not a class constraint or type family application")) }- where- doc = ClassInstanceCtx- bale_out msg = failWithTc msg-- cvt :: Origin -> SrcSpan -> TH.Type -> TcM (LHsType GhcPs)- cvt origin loc th_ty = case convertToHsType origin loc th_ty of- Left msg -> failWithTc (TcRnUnknownMessage $ mkPlainError noHints msg)- Right ty -> return ty--{--************************************************************************-* *- Reification-* *-************************************************************************--}--lookupName :: Bool -- True <=> type namespace- -- False <=> value namespace- -> String -> TcM (Maybe TH.Name)-lookupName is_type_name s- = do { mb_nm <- lookupOccRn_maybe rdr_name- ; return (fmap reifyName mb_nm) }- where- th_name = TH.mkName s -- Parses M.x into a base of 'x' and a module of 'M'-- occ_fs :: FastString- occ_fs = mkFastString (TH.nameBase th_name)-- occ :: OccName- occ | is_type_name- = if isLexVarSym occ_fs || isLexCon occ_fs- then mkTcOccFS occ_fs- else mkTyVarOccFS occ_fs- | otherwise- = if isLexCon occ_fs then mkDataOccFS occ_fs- else mkVarOccFS occ_fs-- rdr_name = case TH.nameModule th_name of- Nothing -> mkRdrUnqual occ- Just mod -> mkRdrQual (mkModuleName mod) occ--getThing :: TH.Name -> TcM TcTyThing-getThing th_name- = do { name <- lookupThName th_name- ; traceIf (text "reify" <+> text (show th_name) <+> brackets (ppr_ns th_name) <+> ppr name)- ; tcLookupTh name }- -- ToDo: this tcLookup could fail, which would give a- -- rather unhelpful error message- where- ppr_ns (TH.Name _ (TH.NameG TH.DataName _pkg _mod)) = text "data"- ppr_ns (TH.Name _ (TH.NameG TH.TcClsName _pkg _mod)) = text "tc"- ppr_ns (TH.Name _ (TH.NameG TH.VarName _pkg _mod)) = text "var"- ppr_ns _ = panic "reify/ppr_ns"--reify :: TH.Name -> TcM TH.Info-reify th_name- = do { traceTc "reify 1" (text (TH.showName th_name))- ; thing <- getThing th_name- ; traceTc "reify 2" (ppr thing)- ; reifyThing thing }--lookupThName :: TH.Name -> TcM Name-lookupThName th_name = do- mb_name <- lookupThName_maybe th_name- case mb_name of- Nothing -> failWithTc (notInScope th_name)- Just name -> return name--lookupThName_maybe :: TH.Name -> TcM (Maybe Name)-lookupThName_maybe th_name- = do { names <- mapMaybeM lookupOccRn_maybe (thRdrNameGuesses th_name)- -- Pick the first that works- -- E.g. reify (mkName "A") will pick the class A in preference to the data constructor A- ; return (listToMaybe names) }--tcLookupTh :: Name -> TcM TcTyThing--- This is a specialised version of GHC.Tc.Utils.Env.tcLookup; specialised mainly in that--- it gives a reify-related error message on failure, whereas in the normal--- tcLookup, failure is a bug.-tcLookupTh name- = do { (gbl_env, lcl_env) <- getEnvs- ; case lookupNameEnv (tcl_env lcl_env) name of {- Just thing -> return thing;- Nothing ->-- case lookupNameEnv (tcg_type_env gbl_env) name of {- Just thing -> return (AGlobal thing);- Nothing ->-- -- EZY: I don't think this choice matters, no TH in signatures!- if nameIsLocalOrFrom (tcg_semantic_mod gbl_env) name- then -- It's defined in this module- failWithTc (notInEnv name)-- else- do { mb_thing <- tcLookupImported_maybe name- ; case mb_thing of- Succeeded thing -> return (AGlobal thing)- Failed msg -> failWithTc (TcRnUnknownMessage $ mkPlainError noHints msg)- }}}}--notInScope :: TH.Name -> TcRnMessage-notInScope th_name = TcRnUnknownMessage $ mkPlainError noHints $- quotes (text (TH.pprint th_name)) <+>- text "is not in scope at a reify"- -- Ugh! Rather an indirect way to display the name--notInEnv :: Name -> TcRnMessage-notInEnv name = TcRnUnknownMessage $ mkPlainError noHints $- quotes (ppr name) <+> text "is not in the type environment at a reify"---------------------------------reifyRoles :: TH.Name -> TcM [TH.Role]-reifyRoles th_name- = do { thing <- getThing th_name- ; case thing of- AGlobal (ATyCon tc) -> return (map reify_role (tyConRoles tc))- _ -> failWithTc (TcRnUnknownMessage $ mkPlainError noHints $ text "No roles associated with" <+> (ppr thing))- }- where- reify_role Nominal = TH.NominalR- reify_role Representational = TH.RepresentationalR- reify_role Phantom = TH.PhantomR---------------------------------reifyThing :: TcTyThing -> TcM TH.Info--- The only reason this is monadic is for error reporting,--- which in turn is mainly for the case when TH can't express--- some random GHC extension--reifyThing (AGlobal (AnId id))- = do { ty <- reifyType (idType id)- ; let v = reifyName id- ; case idDetails id of- ClassOpId cls -> return (TH.ClassOpI v ty (reifyName cls))- RecSelId{sel_tycon=RecSelData tc}- -> return (TH.VarI (reifySelector id tc) ty Nothing)- _ -> return (TH.VarI v ty Nothing)- }--reifyThing (AGlobal (ATyCon tc)) = reifyTyCon tc-reifyThing (AGlobal (AConLike (RealDataCon dc)))- = do { let name = dataConName dc- ; ty <- reifyType (idType (dataConWrapId dc))- ; return (TH.DataConI (reifyName name) ty- (reifyName (dataConOrigTyCon dc)))- }--reifyThing (AGlobal (AConLike (PatSynCon ps)))- = do { let name = reifyName ps- ; ty <- reifyPatSynType (patSynSigBndr ps)- ; return (TH.PatSynI name ty) }--reifyThing (ATcId {tct_id = id})- = do { ty1 <- zonkTcType (idType id) -- Make use of all the info we have, even- -- though it may be incomplete- ; ty2 <- reifyType ty1- ; return (TH.VarI (reifyName id) ty2 Nothing) }--reifyThing (ATyVar tv tv1)- = do { ty1 <- zonkTcTyVar tv1- ; ty2 <- reifyType ty1- ; return (TH.TyVarI (reifyName tv) ty2) }--reifyThing thing = pprPanic "reifyThing" (pprTcTyThingCategory thing)----------------------------------------------reifyAxBranch :: TyCon -> CoAxBranch -> TcM TH.TySynEqn-reifyAxBranch fam_tc (CoAxBranch { cab_tvs = tvs- , cab_lhs = lhs- , cab_rhs = rhs })- -- remove kind patterns (#8884)- = do { tvs' <- reifyTyVarsToMaybe tvs- ; let lhs_types_only = filterOutInvisibleTypes fam_tc lhs- ; lhs' <- reifyTypes lhs_types_only- ; annot_th_lhs <- zipWith3M annotThType (tyConArgsPolyKinded fam_tc)- lhs_types_only lhs'- ; let lhs_type = mkThAppTs (TH.ConT $ reifyName fam_tc) annot_th_lhs- ; rhs' <- reifyType rhs- ; return (TH.TySynEqn tvs' lhs_type rhs') }--reifyTyCon :: TyCon -> TcM TH.Info-reifyTyCon tc- | Just cls <- tyConClass_maybe tc- = reifyClass cls-- | isFunTyCon tc- = return (TH.PrimTyConI (reifyName tc) 2 False)-- | isPrimTyCon tc- = return (TH.PrimTyConI (reifyName tc) (length (tyConVisibleTyVars tc))- (isUnliftedTypeKind (tyConResKind tc)))-- | isTypeFamilyTyCon tc- = do { let tvs = tyConTyVars tc- res_kind = tyConResKind tc- resVar = famTcResVar tc-- ; kind' <- reifyKind res_kind- ; let (resultSig, injectivity) =- case resVar of- Nothing -> (TH.KindSig kind', Nothing)- Just name ->- let thName = reifyName name- injAnnot = tyConInjectivityInfo tc- sig = TH.TyVarSig (TH.KindedTV thName () kind')- inj = case injAnnot of- NotInjective -> Nothing- Injective ms ->- Just (TH.InjectivityAnn thName injRHS)- where- injRHS = map (reifyName . tyVarName)- (filterByList ms tvs)- in (sig, inj)- ; tvs' <- reifyTyVars (tyConVisibleTyVars tc)- ; let tfHead =- TH.TypeFamilyHead (reifyName tc) tvs' resultSig injectivity- ; if isOpenTypeFamilyTyCon tc- then do { fam_envs <- tcGetFamInstEnvs- ; instances <- reifyFamilyInstances tc- (familyInstances fam_envs tc)- ; return (TH.FamilyI (TH.OpenTypeFamilyD tfHead) instances) }- else do { eqns <-- case isClosedSynFamilyTyConWithAxiom_maybe tc of- Just ax -> mapM (reifyAxBranch tc) $- fromBranches $ coAxiomBranches ax- Nothing -> return []- ; return (TH.FamilyI (TH.ClosedTypeFamilyD tfHead eqns)- []) } }-- | isDataFamilyTyCon tc- = do { let res_kind = tyConResKind tc-- ; kind' <- fmap Just (reifyKind res_kind)-- ; tvs' <- reifyTyVars (tyConVisibleTyVars tc)- ; fam_envs <- tcGetFamInstEnvs- ; instances <- reifyFamilyInstances tc (familyInstances fam_envs tc)- ; return (TH.FamilyI- (TH.DataFamilyD (reifyName tc) tvs' kind') instances) }-- | Just (_, rhs) <- synTyConDefn_maybe tc -- Vanilla type synonym- = do { rhs' <- reifyType rhs- ; tvs' <- reifyTyVars (tyConVisibleTyVars tc)- ; return (TH.TyConI- (TH.TySynD (reifyName tc) tvs' rhs'))- }-- | otherwise- = do { cxt <- reifyCxt (tyConStupidTheta tc)- ; let tvs = tyConTyVars tc- dataCons = tyConDataCons tc- isGadt = isGadtSyntaxTyCon tc- ; cons <- mapM (reifyDataCon isGadt (mkTyVarTys tvs)) dataCons- ; r_tvs <- reifyTyVars (tyConVisibleTyVars tc)- ; let name = reifyName tc- deriv = [] -- Don't know about deriving- decl | isNewTyCon tc =- TH.NewtypeD cxt name r_tvs Nothing (head cons) deriv- | otherwise =- TH.DataD cxt name r_tvs Nothing cons deriv- ; return (TH.TyConI decl) }--reifyDataCon :: Bool -> [Type] -> DataCon -> TcM TH.Con-reifyDataCon isGadtDataCon tys dc- = do { let -- used for H98 data constructors- (ex_tvs, theta, arg_tys)- = dataConInstSig dc tys- -- used for GADTs data constructors- g_user_tvs' = dataConUserTyVarBinders dc- (g_univ_tvs, _, g_eq_spec, g_theta', g_arg_tys', g_res_ty')- = dataConFullSig dc- (srcUnpks, srcStricts)- = mapAndUnzip reifySourceBang (dataConSrcBangs dc)- dcdBangs = zipWith TH.Bang srcUnpks srcStricts- fields = dataConFieldLabels dc- name = reifyName dc- -- Universal tvs present in eq_spec need to be filtered out, as- -- they will not appear anywhere in the type.- eq_spec_tvs = mkVarSet (map eqSpecTyVar g_eq_spec)-- ; (univ_subst, _)- -- See Note [Freshen reified GADT constructors' universal tyvars]- <- freshenTyVarBndrs $- filterOut (`elemVarSet` eq_spec_tvs) g_univ_tvs- ; let (tvb_subst, g_user_tvs) = subst_tv_binders univ_subst g_user_tvs'- g_theta = substTys tvb_subst g_theta'- g_arg_tys = substTys tvb_subst (map scaledThing g_arg_tys')- g_res_ty = substTy tvb_subst g_res_ty'-- ; r_arg_tys <- reifyTypes (if isGadtDataCon then g_arg_tys else arg_tys)-- ; main_con <-- if | not (null fields) && not isGadtDataCon ->- return $ TH.RecC name (zip3 (map reifyFieldLabel fields)- dcdBangs r_arg_tys)- | not (null fields) -> do- { res_ty <- reifyType g_res_ty- ; return $ TH.RecGadtC [name]- (zip3 (map (reifyName . flSelector) fields)- dcdBangs r_arg_tys) res_ty }- -- We need to check not isGadtDataCon here because GADT- -- constructors can be declared infix.- -- See Note [Infix GADT constructors] in GHC.Tc.TyCl.- | dataConIsInfix dc && not isGadtDataCon ->- assert (r_arg_tys `lengthIs` 2) $ do- { let [r_a1, r_a2] = r_arg_tys- [s1, s2] = dcdBangs- ; return $ TH.InfixC (s1,r_a1) name (s2,r_a2) }- | isGadtDataCon -> do- { res_ty <- reifyType g_res_ty- ; return $ TH.GadtC [name] (dcdBangs `zip` r_arg_tys) res_ty }- | otherwise ->- return $ TH.NormalC name (dcdBangs `zip` r_arg_tys)-- ; let (ex_tvs', theta') | isGadtDataCon = (g_user_tvs, g_theta)- | otherwise = assert (all isTyVar ex_tvs)- -- no covars for haskell syntax- (map mk_specified ex_tvs, theta)- ret_con | null ex_tvs' && null theta' = return main_con- | otherwise = do- { cxt <- reifyCxt theta'- ; ex_tvs'' <- reifyTyVarBndrs ex_tvs'- ; return (TH.ForallC ex_tvs'' cxt main_con) }- ; assert (r_arg_tys `equalLength` dcdBangs)- ret_con }- where- mk_specified tv = Bndr tv SpecifiedSpec-- subst_tv_binders subst tv_bndrs =- let tvs = binderVars tv_bndrs- flags = map binderArgFlag tv_bndrs- (subst', tvs') = substTyVarBndrs subst tvs- tv_bndrs' = map (\(tv,fl) -> Bndr tv fl) (zip tvs' flags)- in (subst', tv_bndrs')--{--Note [Freshen reified GADT constructors' universal tyvars]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Suppose one were to reify this GADT:-- data a :~: b where- Refl :: forall a b. (a ~ b) => a :~: b--We ought to be careful here about the uniques we give to the occurrences of `a`-and `b` in this definition. That is because in the original DataCon, all uses-of `a` and `b` have the same unique, since `a` and `b` are both universally-quantified type variables--that is, they are used in both the (:~:) tycon as-well as in the constructor type signature. But when we turn the DataCon-definition into the reified one, the `a` and `b` in the constructor type-signature becomes differently scoped than the `a` and `b` in `data a :~: b`.--While it wouldn't technically be *wrong* per se to re-use the same uniques for-`a` and `b` across these two different scopes, it's somewhat annoying for end-users of Template Haskell, since they wouldn't be able to rely on the-assumption that all TH names have globally distinct uniques (#13885). For this-reason, we freshen the universally quantified tyvars that go into the reified-GADT constructor type signature to give them distinct uniques from their-counterparts in the tycon.--}---------------------------------reifyClass :: Class -> TcM TH.Info-reifyClass cls- = do { cxt <- reifyCxt theta- ; inst_envs <- tcGetInstEnvs- ; insts <- reifyClassInstances cls (InstEnv.classInstances inst_envs cls)- ; assocTys <- concatMapM reifyAT ats- ; ops <- concatMapM reify_op op_stuff- ; tvs' <- reifyTyVars (tyConVisibleTyVars (classTyCon cls))- ; let dec = TH.ClassD cxt (reifyName cls) tvs' fds' (assocTys ++ ops)- ; return (TH.ClassI dec insts) }- where- (_, fds, theta, _, ats, op_stuff) = classExtraBigSig cls- fds' = map reifyFunDep fds- reify_op (op, def_meth)- = do { let (_, _, ty) = tcSplitMethodTy (idType op)- -- Use tcSplitMethodTy to get rid of the extraneous class- -- variables and predicates at the beginning of op's type- -- (see #15551).- ; ty' <- reifyType ty- ; let nm' = reifyName op- ; case def_meth of- Just (_, GenericDM gdm_ty) ->- do { gdm_ty' <- reifyType gdm_ty- ; return [TH.SigD nm' ty', TH.DefaultSigD nm' gdm_ty'] }- _ -> return [TH.SigD nm' ty'] }-- reifyAT :: ClassATItem -> TcM [TH.Dec]- reifyAT (ATI tycon def) = do- tycon' <- reifyTyCon tycon- case tycon' of- TH.FamilyI dec _ -> do- let (tyName, tyArgs) = tfNames dec- (dec :) <$> maybe (return [])- (fmap (:[]) . reifyDefImpl tyName tyArgs . fst)- def- _ -> pprPanic "reifyAT" (text (show tycon'))-- reifyDefImpl :: TH.Name -> [TH.Name] -> Type -> TcM TH.Dec- reifyDefImpl n args ty =- TH.TySynInstD . TH.TySynEqn Nothing (mkThAppTs (TH.ConT n) (map TH.VarT args))- <$> reifyType ty-- tfNames :: TH.Dec -> (TH.Name, [TH.Name])- tfNames (TH.OpenTypeFamilyD (TH.TypeFamilyHead n args _ _))- = (n, map bndrName args)- tfNames d = pprPanic "tfNames" (text (show d))-- bndrName :: TH.TyVarBndr flag -> TH.Name- bndrName (TH.PlainTV n _) = n- bndrName (TH.KindedTV n _ _) = n----------------------------------- | Annotate (with TH.SigT) a type if the first parameter is True--- and if the type contains a free variable.--- This is used to annotate type patterns for poly-kinded tyvars in--- reifying class and type instances.--- See @Note [Reified instances and explicit kind signatures]@.-annotThType :: Bool -- True <=> annotate- -> TyCoRep.Type -> TH.Type -> TcM TH.Type- -- tiny optimization: if the type is annotated, don't annotate again.-annotThType _ _ th_ty@(TH.SigT {}) = return th_ty-annotThType True ty th_ty- | not $ isEmptyVarSet $ filterVarSet isTyVar $ tyCoVarsOfType ty- = do { let ki = tcTypeKind ty- ; th_ki <- reifyKind ki- ; return (TH.SigT th_ty th_ki) }-annotThType _ _ th_ty = return th_ty---- | For every argument type that a type constructor accepts,--- report whether or not the argument is poly-kinded. This is used to--- eventually feed into 'annotThType'.--- See @Note [Reified instances and explicit kind signatures]@.-tyConArgsPolyKinded :: TyCon -> [Bool]-tyConArgsPolyKinded tc =- map (is_poly_ty . tyVarKind) tc_vis_tvs- -- See "Wrinkle: Oversaturated data family instances" in- -- @Note [Reified instances and explicit kind signatures]@- ++ map (is_poly_ty . tyCoBinderType) tc_res_kind_vis_bndrs -- (1) in Wrinkle- ++ repeat True -- (2) in Wrinkle- where- is_poly_ty :: Type -> Bool- is_poly_ty ty = not $- isEmptyVarSet $- filterVarSet isTyVar $- tyCoVarsOfType ty-- tc_vis_tvs :: [TyVar]- tc_vis_tvs = tyConVisibleTyVars tc-- tc_res_kind_vis_bndrs :: [TyCoBinder]- tc_res_kind_vis_bndrs = filter isVisibleBinder $ fst $ splitPiTys $ tyConResKind tc--{--Note [Reified instances and explicit kind signatures]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Reified class instances and type family instances often include extra kind-information to disambiguate instances. Here is one such example that-illustrates this (#8953):-- type family Poly (a :: k) :: Type- type instance Poly (x :: Bool) = Int- type instance Poly (x :: Maybe k) = Double--If you're not careful, reifying these instances might yield this:-- type instance Poly x = Int- type instance Poly x = Double--To avoid this, we go through some care to annotate things with extra kind-information. Some functions which accomplish this feat include:--* annotThType: This annotates a type with a kind signature if the type contains- a free variable.-* tyConArgsPolyKinded: This checks every argument that a type constructor can- accept and reports if the type of the argument is poly-kinded. This- information is ultimately fed into annotThType.---------- Wrinkle: Oversaturated data family instances--------What constitutes an argument to a type constructor in the definition of-tyConArgsPolyKinded? For most type constructors, it's simply the visible-type variable binders (i.e., tyConVisibleTyVars). There is one corner case-we must keep in mind, however: data family instances can appear oversaturated-(#17296). For instance:-- data family Foo :: Type -> Type- data instance Foo x-- data family Bar :: k- data family Bar x--For these sorts of data family instances, tyConVisibleTyVars isn't enough,-as they won't give you the kinds of the oversaturated arguments. We must-also consult:--1. The kinds of the arguments in the result kind (i.e., the tyConResKind).- This will tell us, e.g., the kind of `x` in `Foo x` above.-2. If we go beyond the number of arguments in the result kind (like the- `x` in `Bar x`), then we conservatively assume that the argument's- kind is poly-kinded.---------- Wrinkle: data family instances with return kinds--------Another squirrelly corner case is this:-- data family Foo (a :: k)- data instance Foo :: Bool -> Type- data instance Foo :: Char -> Type--If you're not careful, reifying these instances might yield this:-- data instance Foo- data instance Foo--We can fix this ambiguity by reifying the instances' explicit return kinds. We-should only do this if necessary (see-Note [When does a tycon application need an explicit kind signature?] in GHC.Core.Type),-but more importantly, we *only* do this if either of the following are true:--1. The data family instance has no constructors.-2. The data family instance is declared with GADT syntax.--If neither of these are true, then reifying the return kind would yield-something like this:-- data instance (Bar a :: Type) = MkBar a--Which is not valid syntax.--}---------------------------------reifyClassInstances :: Class -> [ClsInst] -> TcM [TH.Dec]-reifyClassInstances cls insts- = mapM (reifyClassInstance (tyConArgsPolyKinded (classTyCon cls))) insts--reifyClassInstance :: [Bool] -- True <=> the corresponding tv is poly-kinded- -- includes only *visible* tvs- -> ClsInst -> TcM TH.Dec-reifyClassInstance is_poly_tvs i- = do { cxt <- reifyCxt theta- ; let vis_types = filterOutInvisibleTypes cls_tc types- ; thtypes <- reifyTypes vis_types- ; annot_thtypes <- zipWith3M annotThType is_poly_tvs vis_types thtypes- ; let head_ty = mkThAppTs (TH.ConT (reifyName cls)) annot_thtypes- ; return $ (TH.InstanceD over cxt head_ty []) }- where- (_tvs, theta, cls, types) = tcSplitDFunTy (idType dfun)- cls_tc = classTyCon cls- dfun = instanceDFunId i- over = case overlapMode (is_flag i) of- NoOverlap _ -> Nothing- Overlappable _ -> Just TH.Overlappable- Overlapping _ -> Just TH.Overlapping- Overlaps _ -> Just TH.Overlaps- Incoherent _ -> Just TH.Incoherent---------------------------------reifyFamilyInstances :: TyCon -> [FamInst] -> TcM [TH.Dec]-reifyFamilyInstances fam_tc fam_insts- = mapM (reifyFamilyInstance (tyConArgsPolyKinded fam_tc)) fam_insts--reifyFamilyInstance :: [Bool] -- True <=> the corresponding tv is poly-kinded- -- includes only *visible* tvs- -> FamInst -> TcM TH.Dec-reifyFamilyInstance is_poly_tvs (FamInst { fi_flavor = flavor- , fi_axiom = ax- , fi_fam = fam })- | let fam_tc = coAxiomTyCon ax- branch = coAxiomSingleBranch ax- , CoAxBranch { cab_tvs = tvs, cab_lhs = lhs, cab_rhs = rhs } <- branch- = case flavor of- SynFamilyInst ->- -- remove kind patterns (#8884)- do { th_tvs <- reifyTyVarsToMaybe tvs- ; let lhs_types_only = filterOutInvisibleTypes fam_tc lhs- ; th_lhs <- reifyTypes lhs_types_only- ; annot_th_lhs <- zipWith3M annotThType is_poly_tvs lhs_types_only- th_lhs- ; let lhs_type = mkThAppTs (TH.ConT $ reifyName fam) annot_th_lhs- ; th_rhs <- reifyType rhs- ; return (TH.TySynInstD (TH.TySynEqn th_tvs lhs_type th_rhs)) }-- DataFamilyInst rep_tc ->- do { let -- eta-expand lhs types, because sometimes data/newtype- -- instances are eta-reduced; See #9692- -- See Note [Eta reduction for data families] in GHC.Core.Coercion.Axiom- (ee_tvs, ee_lhs, _) = etaExpandCoAxBranch branch- fam' = reifyName fam- dataCons = tyConDataCons rep_tc- isGadt = isGadtSyntaxTyCon rep_tc- ; th_tvs <- reifyTyVarsToMaybe ee_tvs- ; cons <- mapM (reifyDataCon isGadt (mkTyVarTys ee_tvs)) dataCons- ; let types_only = filterOutInvisibleTypes fam_tc ee_lhs- ; th_tys <- reifyTypes types_only- ; annot_th_tys <- zipWith3M annotThType is_poly_tvs types_only th_tys- ; let lhs_type = mkThAppTs (TH.ConT fam') annot_th_tys- ; mb_sig <-- -- See "Wrinkle: data family instances with return kinds" in- -- Note [Reified instances and explicit kind signatures]- if (null cons || isGadtSyntaxTyCon rep_tc)- && tyConAppNeedsKindSig False fam_tc (length ee_lhs)- then do { let full_kind = tcTypeKind (mkTyConApp fam_tc ee_lhs)- ; th_full_kind <- reifyKind full_kind- ; pure $ Just th_full_kind }- else pure Nothing- ; return $- if isNewTyCon rep_tc- then TH.NewtypeInstD [] th_tvs lhs_type mb_sig (head cons) []- else TH.DataInstD [] th_tvs lhs_type mb_sig cons []- }---------------------------------reifyType :: TyCoRep.Type -> TcM TH.Type--- Monadic only because of failure-reifyType ty | tcIsLiftedTypeKind ty = return TH.StarT- -- Make sure to use tcIsLiftedTypeKind here, since we don't want to confuse it- -- with Constraint (#14869).-reifyType ty@(ForAllTy (Bndr _ argf) _)- = reify_for_all argf ty-reifyType (LitTy t) = do { r <- reifyTyLit t; return (TH.LitT r) }-reifyType (TyVarTy tv) = return (TH.VarT (reifyName tv))-reifyType (TyConApp tc tys) = reify_tc_app tc tys -- Do not expand type synonyms here-reifyType ty@(AppTy {}) = do- let (ty_head, ty_args) = splitAppTys ty- ty_head' <- reifyType ty_head- ty_args' <- reifyTypes (filter_out_invisible_args ty_head ty_args)- pure $ mkThAppTs ty_head' ty_args'- where- -- Make sure to filter out any invisible arguments. For instance, if you- -- reify the following:- --- -- newtype T (f :: forall a. a -> Type) = MkT (f Bool)- --- -- Then you should receive back `f Bool`, not `f Type Bool`, since the- -- `Type` argument is invisible (#15792).- filter_out_invisible_args :: Type -> [Type] -> [Type]- filter_out_invisible_args ty_head ty_args =- filterByList (map isVisibleArgFlag $ appTyArgFlags ty_head ty_args)- ty_args-reifyType ty@(FunTy { ft_af = af, ft_mult = Many, ft_arg = t1, ft_res = t2 })- | InvisArg <- af = reify_for_all Inferred ty -- Types like ((?x::Int) => Char -> Char)- | otherwise = do { [r1,r2] <- reifyTypes [t1,t2]- ; return (TH.ArrowT `TH.AppT` r1 `TH.AppT` r2) }-reifyType ty@(FunTy { ft_af = af, ft_mult = tm, ft_arg = t1, ft_res = t2 })- | InvisArg <- af = noTH (text "linear invisible argument") (ppr ty)- | otherwise = do { [rm,r1,r2] <- reifyTypes [tm,t1,t2]- ; return (TH.MulArrowT `TH.AppT` rm `TH.AppT` r1 `TH.AppT` r2) }-reifyType (CastTy t _) = reifyType t -- Casts are ignored in TH-reifyType ty@(CoercionTy {})= noTH (text "coercions in types") (ppr ty)--reify_for_all :: TyCoRep.ArgFlag -> TyCoRep.Type -> TcM TH.Type--- Arg of reify_for_all is always ForAllTy or a predicate FunTy-reify_for_all argf ty- | isVisibleArgFlag argf- = do let (req_bndrs, phi) = tcSplitForAllReqTVBinders ty- tvbndrs' <- reifyTyVarBndrs req_bndrs- phi' <- reifyType phi- pure $ TH.ForallVisT tvbndrs' phi'- | otherwise- = do let (inv_bndrs, phi) = tcSplitForAllInvisTVBinders ty- tvbndrs' <- reifyTyVarBndrs inv_bndrs- let (cxt, tau) = tcSplitPhiTy phi- cxt' <- reifyCxt cxt- tau' <- reifyType tau- pure $ TH.ForallT tvbndrs' cxt' tau'--reifyTyLit :: TyCoRep.TyLit -> TcM TH.TyLit-reifyTyLit (NumTyLit n) = return (TH.NumTyLit n)-reifyTyLit (StrTyLit s) = return (TH.StrTyLit (unpackFS s))-reifyTyLit (CharTyLit c) = return (TH.CharTyLit c)--reifyTypes :: [Type] -> TcM [TH.Type]-reifyTypes = mapM reifyType--reifyPatSynType- :: ([InvisTVBinder], ThetaType, [InvisTVBinder], ThetaType, [Scaled Type], Type) -> TcM TH.Type--- reifies a pattern synonym's type and returns its *complete* type--- signature; see NOTE [Pattern synonym signatures and Template--- Haskell]-reifyPatSynType (univTyVars, req, exTyVars, prov, argTys, resTy)- = do { univTyVars' <- reifyTyVarBndrs univTyVars- ; req' <- reifyCxt req- ; exTyVars' <- reifyTyVarBndrs exTyVars- ; prov' <- reifyCxt prov- ; tau' <- reifyType (mkVisFunTys argTys resTy)- ; return $ TH.ForallT univTyVars' req'- $ TH.ForallT exTyVars' prov' tau' }--reifyKind :: Kind -> TcM TH.Kind-reifyKind = reifyType--reifyCxt :: [PredType] -> TcM [TH.Pred]-reifyCxt = mapM reifyType--reifyFunDep :: ([TyVar], [TyVar]) -> TH.FunDep-reifyFunDep (xs, ys) = TH.FunDep (map reifyName xs) (map reifyName ys)--class ReifyFlag flag flag' | flag -> flag' where- reifyFlag :: flag -> flag'--instance ReifyFlag () () where- reifyFlag () = ()--instance ReifyFlag Specificity TH.Specificity where- reifyFlag SpecifiedSpec = TH.SpecifiedSpec- reifyFlag InferredSpec = TH.InferredSpec--reifyTyVars :: [TyVar] -> TcM [TH.TyVarBndr ()]-reifyTyVars = reifyTyVarBndrs . map mk_bndr- where- mk_bndr tv = Bndr tv ()--reifyTyVarBndrs :: ReifyFlag flag flag'- => [VarBndr TyVar flag] -> TcM [TH.TyVarBndr flag']-reifyTyVarBndrs = mapM reify_tvbndr- where- -- even if the kind is *, we need to include a kind annotation,- -- in case a poly-kind would be inferred without the annotation.- -- See #8953 or test th/T8953- reify_tvbndr (Bndr tv fl) = TH.KindedTV (reifyName tv)- (reifyFlag fl)- <$> reifyKind (tyVarKind tv)--reifyTyVarsToMaybe :: [TyVar] -> TcM (Maybe [TH.TyVarBndr ()])-reifyTyVarsToMaybe [] = pure Nothing-reifyTyVarsToMaybe tys = Just <$> reifyTyVars tys--reify_tc_app :: TyCon -> [Type.Type] -> TcM TH.Type-reify_tc_app tc tys- = do { tys' <- reifyTypes (filterOutInvisibleTypes tc tys)- ; maybe_sig_t (mkThAppTs r_tc tys') }- where- arity = tyConArity tc-- r_tc | isUnboxedSumTyCon tc = TH.UnboxedSumT (arity `div` 2)- | isUnboxedTupleTyCon tc = TH.UnboxedTupleT (arity `div` 2)- | isPromotedTupleTyCon tc = TH.PromotedTupleT (arity `div` 2)- -- See Note [Unboxed tuple RuntimeRep vars] in GHC.Core.TyCon- | isTupleTyCon tc = if isPromotedDataCon tc- then TH.PromotedTupleT arity- else TH.TupleT arity- | tc `hasKey` constraintKindTyConKey- = TH.ConstraintT- | tc `hasKey` unrestrictedFunTyConKey = TH.ArrowT- | tc `hasKey` listTyConKey = TH.ListT- | tc `hasKey` nilDataConKey = TH.PromotedNilT- | tc `hasKey` consDataConKey = TH.PromotedConsT- | tc `hasKey` heqTyConKey = TH.EqualityT- | tc `hasKey` eqPrimTyConKey = TH.EqualityT- | tc `hasKey` eqReprPrimTyConKey = TH.ConT (reifyName coercibleTyCon)- | isPromotedDataCon tc = TH.PromotedT (reifyName tc)- | otherwise = TH.ConT (reifyName tc)-- -- See Note [When does a tycon application need an explicit kind- -- signature?] in GHC.Core.TyCo.Rep- maybe_sig_t th_type- | tyConAppNeedsKindSig- False -- We don't reify types using visible kind applications, so- -- don't count specified binders as contributing towards- -- injective positions in the kind of the tycon.- tc (length tys)- = do { let full_kind = tcTypeKind (mkTyConApp tc tys)- ; th_full_kind <- reifyKind full_kind- ; return (TH.SigT th_type th_full_kind) }- | otherwise- = return th_type---------------------------------reifyName :: NamedThing n => n -> TH.Name-reifyName thing- | isExternalName name- = mk_varg pkg_str mod_str occ_str- | otherwise = TH.mkNameU occ_str (toInteger $ getKey (getUnique name))- -- Many of the things we reify have local bindings, and- -- NameL's aren't supposed to appear in binding positions, so- -- we use NameU. When/if we start to reify nested things, that- -- have free variables, we may need to generate NameL's for them.- where- name = getName thing- mod = assert (isExternalName name) $ nameModule name- pkg_str = unitString (moduleUnit mod)- mod_str = moduleNameString (moduleName mod)- occ_str = occNameString occ- occ = nameOccName name- mk_varg | OccName.isDataOcc occ = TH.mkNameG_d- | OccName.isVarOcc occ = TH.mkNameG_v- | OccName.isTcOcc occ = TH.mkNameG_tc- | otherwise = pprPanic "reifyName" (ppr name)---- See Note [Reifying field labels]-reifyFieldLabel :: FieldLabel -> TH.Name-reifyFieldLabel fl- | flIsOverloaded fl- = TH.Name (TH.mkOccName occ_str) (TH.NameQ (TH.mkModName mod_str))- | otherwise = TH.mkNameG_v pkg_str mod_str occ_str- where- name = flSelector fl- mod = assert (isExternalName name) $ nameModule name- pkg_str = unitString (moduleUnit mod)- mod_str = moduleNameString (moduleName mod)- occ_str = unpackFS (flLabel fl)--reifySelector :: Id -> TyCon -> TH.Name-reifySelector id tc- = case find ((idName id ==) . flSelector) (tyConFieldLabels tc) of- Just fl -> reifyFieldLabel fl- Nothing -> pprPanic "reifySelector: missing field" (ppr id $$ ppr tc)---------------------------------reifyFixity :: Name -> TcM (Maybe TH.Fixity)-reifyFixity name- = do { (found, fix) <- lookupFixityRn_help name- ; return (if found then Just (conv_fix fix) else Nothing) }- where- conv_fix (Hs.Fixity _ i d) = TH.Fixity i (conv_dir d)- conv_dir Hs.InfixR = TH.InfixR- conv_dir Hs.InfixL = TH.InfixL- conv_dir Hs.InfixN = TH.InfixN--reifyUnpackedness :: DataCon.SrcUnpackedness -> TH.SourceUnpackedness-reifyUnpackedness NoSrcUnpack = TH.NoSourceUnpackedness-reifyUnpackedness SrcNoUnpack = TH.SourceNoUnpack-reifyUnpackedness SrcUnpack = TH.SourceUnpack--reifyStrictness :: DataCon.SrcStrictness -> TH.SourceStrictness-reifyStrictness NoSrcStrict = TH.NoSourceStrictness-reifyStrictness SrcStrict = TH.SourceStrict-reifyStrictness SrcLazy = TH.SourceLazy--reifySourceBang :: DataCon.HsSrcBang- -> (TH.SourceUnpackedness, TH.SourceStrictness)-reifySourceBang (HsSrcBang _ u s) = (reifyUnpackedness u, reifyStrictness s)--reifyDecidedStrictness :: DataCon.HsImplBang -> TH.DecidedStrictness-reifyDecidedStrictness HsLazy = TH.DecidedLazy-reifyDecidedStrictness HsStrict = TH.DecidedStrict-reifyDecidedStrictness HsUnpack{} = TH.DecidedUnpack--reifyTypeOfThing :: TH.Name -> TcM TH.Type-reifyTypeOfThing th_name = do- thing <- getThing th_name- case thing of- AGlobal (AnId id) -> reifyType (idType id)- AGlobal (ATyCon tc) -> reifyKind (tyConKind tc)- AGlobal (AConLike (RealDataCon dc)) ->- reifyType (idType (dataConWrapId dc))- AGlobal (AConLike (PatSynCon ps)) ->- reifyPatSynType (patSynSigBndr ps)- ATcId{tct_id = id} -> zonkTcType (idType id) >>= reifyType- ATyVar _ tctv -> zonkTcTyVar tctv >>= reifyType- -- Impossible cases, supposedly:- AGlobal (ACoAxiom _) -> panic "reifyTypeOfThing: ACoAxiom"- ATcTyCon _ -> panic "reifyTypeOfThing: ATcTyCon"- APromotionErr _ -> panic "reifyTypeOfThing: APromotionErr"---------------------------------lookupThAnnLookup :: TH.AnnLookup -> TcM CoreAnnTarget-lookupThAnnLookup (TH.AnnLookupName th_nm) = fmap NamedTarget (lookupThName th_nm)-lookupThAnnLookup (TH.AnnLookupModule (TH.Module pn mn))- = return $ ModuleTarget $- mkModule (stringToUnit $ TH.pkgString pn) (mkModuleName $ TH.modString mn)--reifyAnnotations :: Data a => TH.AnnLookup -> TcM [a]-reifyAnnotations th_name- = do { name <- lookupThAnnLookup th_name- ; topEnv <- getTopEnv- ; epsHptAnns <- liftIO $ prepareAnnotations topEnv Nothing- ; tcg <- getGblEnv- ; let selectedEpsHptAnns = findAnns deserializeWithData epsHptAnns name- ; let selectedTcgAnns = findAnns deserializeWithData (tcg_ann_env tcg) name- ; return (selectedEpsHptAnns ++ selectedTcgAnns) }---------------------------------modToTHMod :: Module -> TH.Module-modToTHMod m = TH.Module (TH.PkgName $ unitString $ moduleUnit m)- (TH.ModName $ moduleNameString $ moduleName m)--reifyModule :: TH.Module -> TcM TH.ModuleInfo-reifyModule (TH.Module (TH.PkgName pkgString) (TH.ModName mString)) = do- this_mod <- getModule- let reifMod = mkModule (stringToUnit pkgString) (mkModuleName mString)- if (reifMod == this_mod) then reifyThisModule else reifyFromIface reifMod- where- reifyThisModule = do- usages <- fmap (map modToTHMod . moduleEnvKeys . imp_mods) getImports- return $ TH.ModuleInfo usages-- reifyFromIface reifMod = do- iface <- loadInterfaceForModule (text "reifying module from TH for" <+> ppr reifMod) reifMod- let usages = [modToTHMod m | usage <- mi_usages iface,- Just m <- [usageToModule (moduleUnit reifMod) usage] ]- return $ TH.ModuleInfo usages-- usageToModule :: Unit -> Usage -> Maybe Module- usageToModule _ (UsageFile {}) = Nothing- usageToModule this_pkg (UsageHomeModule { usg_mod_name = mn }) = Just $ mkModule this_pkg mn- usageToModule _ (UsagePackageModule { usg_mod = m }) = Just m- usageToModule _ (UsageMergedRequirement { usg_mod = m }) = Just m- usageToModule this_pkg (UsageHomeModuleInterface { usg_mod_name = mn }) = Just $ mkModule this_pkg mn---------------------------------mkThAppTs :: TH.Type -> [TH.Type] -> TH.Type-mkThAppTs fun_ty arg_tys = foldl' TH.AppT fun_ty arg_tys--noTH :: SDoc -> SDoc -> TcM a-noTH s d = failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $- (hsep [text "Can't represent" <+> s <+>- text "in Template Haskell:",- nest 2 d])+ tcTypedSplice, tcTypedBracket, tcUntypedBracket,+ runAnnotation,++ runMetaE, runMetaP, runMetaT, runMetaD, runQuasi,+ tcTopSpliceExpr, lookupThName_maybe,+ defaultRunMeta, runMeta', runRemoteModFinalizers,+ finishTH, runTopSplice+ ) where++import GHC.Prelude++import GHC.Driver.Errors+import GHC.Driver.Plugins+import GHC.Driver.Main+import GHC.Driver.Session+import GHC.Driver.Env+import GHC.Driver.Hooks+import GHC.Driver.Config.Diagnostic+import GHC.Driver.Config.Finder++import GHC.Hs++import GHC.Tc.Errors.Types+import GHC.Tc.Utils.Monad+import GHC.Tc.Utils.TcType+import GHC.Tc.Gen.Expr+import GHC.Tc.Utils.Unify+import GHC.Tc.Utils.Env+import GHC.Tc.Types.Origin+import GHC.Tc.Types.Evidence+import GHC.Tc.Utils.Zonk+import GHC.Tc.Solver+import GHC.Tc.Utils.TcMType+import GHC.Tc.Gen.HsType+import GHC.Tc.Instance.Family+import GHC.Tc.Utils.Instantiate++import GHC.Core.Multiplicity+import GHC.Core.Coercion( etaExpandCoAxBranch )+import GHC.Core.Type as Type+import GHC.Core.TyCo.Rep as TyCoRep+import GHC.Core.FamInstEnv+import GHC.Core.InstEnv as InstEnv++import GHC.Builtin.Names.TH+import GHC.Builtin.Names+import GHC.Builtin.Types++import GHC.ThToHs+import GHC.HsToCore.Docs+import GHC.HsToCore.Expr+import GHC.HsToCore.Monad+import GHC.IfaceToCore+import GHC.Iface.Load++import GHCi.Message+import GHCi.RemoteTypes+import GHC.Runtime.Interpreter++import GHC.Rename.Splice( traceSplice, SpliceInfo(..))+import GHC.Rename.Expr+import GHC.Rename.Env+import GHC.Rename.Fixity ( lookupFixityRn_help )+import GHC.Rename.HsType++import GHC.Core.Class+import GHC.Core.TyCon+import GHC.Core.Coercion.Axiom+import GHC.Core.PatSyn+import GHC.Core.ConLike+import GHC.Core.DataCon as DataCon++import GHC.Types.FieldLabel+import GHC.Types.SrcLoc+import GHC.Types.Name.Env+import GHC.Types.Name.Set+import GHC.Types.Name.Reader+import GHC.Types.Name.Occurrence as OccName+import GHC.Types.Var+import GHC.Types.Id+import GHC.Types.Id.Info+import GHC.Types.Unique+import GHC.Types.Var.Set+import GHC.Types.Meta+import GHC.Types.Basic hiding( SuccessFlag(..) )+import GHC.Types.Error+import GHC.Types.Fixity as Hs+import GHC.Types.Annotations+import GHC.Types.Name+import GHC.Types.Unique.Map+import GHC.Serialized++import GHC.Unit.Finder+import GHC.Unit.Module+import GHC.Unit.Module.ModIface+import GHC.Unit.Module.Deps++import GHC.Utils.Misc+import GHC.Utils.Panic as Panic+import GHC.Utils.Panic.Plain+import GHC.Utils.Lexeme+import GHC.Utils.Outputable+import GHC.Utils.Logger+import GHC.Utils.Exception (throwIO, ErrorCall(..), SomeException(..))++import GHC.Utils.TmpFs ( newTempName, TempFileLifetime(..) )++import GHC.Data.FastString+import GHC.Data.Maybe( MaybeErr(..) )+import qualified GHC.Data.EnumSet as EnumSet++import Language.Haskell.Syntax.Basic (FieldLabelString(..))+import qualified Language.Haskell.TH as TH+-- THSyntax gives access to internal functions and data types+import qualified Language.Haskell.TH.Syntax as TH++#if defined(HAVE_INTERNAL_INTERPRETER)+-- Because GHC.Desugar might not be in the base library of the bootstrapping compiler+import GHC.Desugar ( AnnotationWrapper(..) )+import Unsafe.Coerce ( unsafeCoerce )+#endif++import Control.Monad+import Data.Binary+import Data.Binary.Get+import Data.List ( find )+import Data.Maybe+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as LB+import Data.Dynamic ( fromDynamic, toDyn )+import qualified Data.IntMap as IntMap+import qualified Data.Map as Map+import Data.Typeable ( typeOf, Typeable, TypeRep, typeRep )+import Data.Data (Data)+import Data.Proxy ( Proxy (..) )+import GHC.Parser.HaddockLex (lexHsDoc)+import GHC.Parser (parseIdentifier)+import GHC.Rename.Doc (rnHsDoc)++++{-+Note [Template Haskell state diagram]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Here are the ThStages, s, their corresponding level numbers+(the result of (thLevel s)), and their state transitions.+The top level of the program is stage Comp:++ Start here+ |+ V+ ----------- $ ------------ $+ | Comp | ---------> | Splice | -----|+ | 1 | | 0 | <----|+ ----------- ------------+ ^ | ^ |+ $ | | [||] $ | | [||]+ | v | v+ -------------- ----------------+ | Brack Comp | | Brack Splice |+ | 2 | | 1 |+ -------------- ----------------++* Normal top-level declarations start in state Comp+ (which has level 1).+ Annotations start in state Splice, since they are+ treated very like a splice (only without a '$')++* Code compiled in state Splice (and only such code)+ will be *run at compile time*, with the result replacing+ the splice++* The original paper used level -1 instead of 0, etc.++* The original paper did not allow a splice within a+ splice, but there is no reason not to. This is the+ $ transition in the top right.++Note [Template Haskell levels]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+* Imported things are impLevel (= 0)++* However things at level 0 are not *necessarily* imported.+ eg $( \b -> ... ) here b is bound at level 0++* In GHCi, variables bound by a previous command are treated+ as impLevel, because we have bytecode for them.++* Variables are bound at the "current level"++* The current level starts off at outerLevel (= 1)++* The level is decremented by splicing $(..)+ incremented by brackets [| |]+ incremented by name-quoting 'f++* When a variable is used, checkWellStaged compares+ bind: binding level, and+ use: current level at usage site++ Generally+ bind > use Always error (bound later than used)+ [| \x -> $(f x) |]++ bind = use Always OK (bound same stage as used)+ [| \x -> $(f [| x |]) |]++ bind < use Inside brackets, it depends+ Inside splice, OK+ Inside neither, OK++ For (bind < use) inside brackets, there are three cases:+ - Imported things OK f = [| map |]+ - Top-level things OK g = [| f |]+ - Non-top-level Only if there is a liftable instance+ h = \(x:Int) -> [| x |]++ To track top-level-ness we use the ThBindEnv in TcLclEnv++ For example:+ f = ...+ g1 = $(map ...) is OK+ g2 = $(f ...) is not OK; because we haven't compiled f yet+++Note [How top-level splices are handled]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Top-level splices (those not inside a [| .. |] quotation bracket) are handled+very straightforwardly:++ 1. tcTopSpliceExpr: typecheck the body e of the splice $(e)++ 2. runMetaT: desugar, compile, run it, and convert result back to+ GHC.Hs syntax RdrName (of the appropriate flavour, eg HsType RdrName,+ HsExpr RdrName etc)++ 3. treat the result as if that's what you saw in the first place+ e.g for HsType, rename and kind-check+ for HsExpr, rename and type-check++ (The last step is different for decls, because they can *only* be+ top-level: we return the result of step 2.)++Note [Warnings for TH splices]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+We only produce warnings for TH splices when the user requests so+(-fenable-th-splice-warnings). There are multiple reasons:++ * It's not clear that the user that compiles a splice is the author of the code+ that produces the warning. Think of the situation where they just splice in+ code from a third-party library that produces incomplete pattern matches.+ In this scenario, the user isn't even able to fix that warning.+ * Gathering information for producing the warnings (pattern-match check+ warnings in particular) is costly. There's no point in doing so if the user+ is not interested in those warnings.++That's why we store Origin flags in the Haskell AST. The functions from ThToHs+take such a flag and depending on whether TH splice warnings were enabled or+not, we pass FromSource (if the user requests warnings) or Generated+(otherwise). This is implemented in getThSpliceOrigin.++For correct pattern-match warnings it's crucial that we annotate the Origin+consistently (#17270). In the future we could offer the Origin as part of the+TH AST. That would enable us to give quotes from the current module get+FromSource origin, and/or third library authors to tag certain parts of+generated code as FromSource to enable warnings.+That effort is tracked in #14838.++Note [The life cycle of a TH quotation]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+When desugaring a bracket (aka quotation), we want to produce Core+code that, when run, will produce the TH syntax tree for the quotation.+To that end, we want to desugar /renamed/ but not /typechecked/ code;+the latter is cluttered with the typechecker's elaboration that should+not appear in the TH syntax tree. So in (HsExpr GhcTc) tree, we must+have a (HsExpr GhcRn) for the quotation itself.++As such, when typechecking both typed and untyped brackets,+we keep a /renamed/ bracket in the extension field.++The HsBracketTc, the GhcTc ext field for both typed and untyped+brackets, contains:+ - The renamed quote :: HsQuote GhcRn -- for the desugarer+ - [PendingTcSplice]+ - The type of the quote+ - Maybe QuoteWrapper++Note that HsBracketTc stores the untyped (HsQuote GhcRn) for both typed and+untyped brackets. They are treated uniformly by the desugarer, and we can+easily construct untyped brackets from typed ones (with ExpBr).++See Note [Desugaring of brackets].++------------+Typed quotes+------------+Here is the life cycle of a /typed/ quote [|| e ||], whose datacon is+ HsTypedBracket (XTypedBracket p) (LHsExpr p)++ In pass p (XTypedBracket p) (LHsExpr p)+ -------------------------------------------+ GhcPs Annotations only LHsExpr GhcPs+ GhcRn Annotations only LHsExpr GhcRn+ GhcTc HsBracketTc LHsExpr GhcTc: unused!++Note that in the GhcTc tree, the second field (HsExpr GhcTc)+is entirely unused; the desugarer uses the (HsExpr GhcRn) from the+first field.++--------------+Untyped quotes+--------------+Here is the life cycle of an /untyped/ quote, whose datacon is+ HsUntypedBracket (XUntypedBracket p) (HsQuote p)++Here HsQuote is a sum-type of expressions [| e |], patterns [| p |],+types [| t |] etc.++ In pass p (XUntypedBracket p) (HsQuote p)+ -------------------------------------------------------+ GhcPs Annotations only HsQuote GhcPs+ GhcRn Annotations, [PendingRnSplice] HsQuote GhcRn+ GhcTc HsBracketTc HsQuote GhcTc: unused!++The difficulty is: the typechecker does not typecheck the body of an+untyped quote, so how do we make a (HsQuote GhcTc) to put in the+second field?++Answer: we use the extension constructor of HsQuote, namely XQuote,+and make all the other constructors into DataConCantHappen. That is,+the only non-bottom value of type (HsQuote GhcTc) is (XQuote noExtField).+Hence the instances++ type instance XExpBr GhcTc = DataConCantHappen+ ...etc...++See the related Note [How brackets and nested splices are handled]++Note [Typechecking Overloaded Quotes]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+The main function for typechecking untyped quotations is `tcUntypedBracket`.++Consider an expression quote, `[| e |]`, its type is `forall m . Quote m => m Exp`.+Note carefully that this is overloaded: its type is not `Q Exp` for some fixed Q.++When we typecheck it we therefore create a template of a metavariable+`m` applied to `Exp` and emit a constraint `Quote m`. All this is done+in the `brackTy` function. `brackTy` also selects the correct+contents type for the quotation (Exp, Type, Decs etc).++The meta variable and the constraint evidence variable are+returned together in a `QuoteWrapper` and then passed along to two further places+during compilation:++1. Typechecking nested splices (immediately in tcPendingSplice)+2. Desugaring quotations (see GHC.HsToCore.Quote)++`tcPendingSplice` takes the `m` type variable as an argument and+checks each nested splice against this variable `m`. During this+process the variable `m` can either be fixed to a specific value or+further constrained by the nested splices.++Once we have checked all the nested splices, the quote type is checked against+the expected return type.++The process is very simple and like typechecking a list where the quotation is+like the container and the splices are the elements of the list which must have+a specific type.++After the typechecking process is completed, the evidence variable for `Quote m`+and the type `m` is stored in a `QuoteWrapper` which is passed through the pipeline+and used when desugaring quotations.++Typechecking typed quotations is a similar idea but the `QuoteWrapper` is stored+in the `PendingStuff` as the nested splices are gathered up in a different way+to untyped splices. Untyped splices are found in the renamer but typed splices are+not typechecked and extracted until during typechecking.++Note [Lifecycle of an untyped splice, and PendingRnSplice]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Untyped splices $(f x) and quasiquotes [p| stuff |] have the following+life cycle. Remember, quasi-quotes are very like splices; see Note [Quasi-quote overview]).++The type structure is++ data HsExpr p = ...+ | HsUntypedSplice (XUntypedSplice p) (HsUntypedSplice p)++ data HsUntypedSplice p+ = HsUntypedSpliceExpr (XUntypedSpliceExpr p) (LHsExpr p)+ | HsQuasiQuote (XQuasiQuote p) (IdP id) (XRec p FastString)++Remember that untyped splices can occur in expressions, patterns,+types, and declarations. So we have a HsUntypedSplice data+constructor in all four of these types.++Untyped splices never occur in (HsExpr GhcTc), and similarly+patterns etc. So we have++ type instance XUntypedSplice GhcTc = DataConCantHappen++Top-level and nested splices are handled differently.++-------------------------------------+Nested untyped splices/quasiquotes+----------------------------------+When we rename an /untyped/ bracket, such as+ [| f $(g x) |]+we name and lift out all the nested splices, so that when the+typechecker hits the bracket, it can typecheck those nested splices+without having to walk over the untyped bracket code. Our example+[| f $(g x) |] parses as++ HsUntypedBracket _+ (HsApp (HsVar "f")+ (HsUntypedSplice _ (HsUntypedSpliceExpr _ (g x :: LHsExpr GhcPs)))++RENAMER (rnUntypedBracket):++* Set the ThStage to (Brack s (RnPendingUntyped ps_var))++* Rename the body++* Nested splices (which must be untyped) are renamed (rnUntypedSplice),+ and the results accumulated in ps_var. Each gets a fresh+ SplicePointName, 'spn'++* The SplicePointName connects the `PendingRnSplice` with the particular point+ in the syntax tree where that expression should be spliced in. That point+ in the tree is identified by `(HsUntypedSpliceNested spn)`. It is used by+ the desugarer, so that we ultimately generate something like+ let spn = g x+ in App (Var "f") spn++The result is+ HsUntypedBracket+ [PendingRnSplice UntypedExpSplice spn (g x :: LHsExpr GHcRn)]+ (HsApp (HsVar f) (HsUntypedSplice (HsUntypedSpliceNested spn)+ (HsUntypedSpliceExpr _ (g x :: LHsExpr GhcRn))))++Note that a nested splice, such as the `$(g x)` now appears twice:+ - In the PendingRnSplice: this is the version that will later be typechecked+ - In the HsUntypedSpliceExpr in the body of the bracket. This copy is used+ only for pretty printing.++NB: a single untyped bracket can contain many splices, each of a different+`UntypedSpliceFlavour`. For example++ [| let $e0 in (f :: $e1) $e2 (\ $e -> body ) |] + 1++Here $e0 is a declaration splice, $e1 is a type splice, $e2 is an+expression splice, and $e3 is a pattern splice. The `PendingRnSplice`+keeps track of which is which through its `UntypedSpliceFlavour`+field.++TYPECHECKER (tcUntypedBracket): see also Note [Typechecking Overloaded Quotes]++* Typecheck the [PendingRnSplice] individually, to give [PendingTcSplice]+ So PendingTcSplice is used for both typed and untyped splices.++* Ignore the body of the bracket; just check that the context+ expects a bracket of that type (e.g. a [p| pat |] bracket should+ be in a context needing a (m Pat)++* Stash the whole lot inside a HsBracketTc++Result is:+ HsUntypedBracket+ (HsBracketTc { hsb_splices = [PendingTcSplice spn (g x :: LHsExpr GHcTc)]+ , hsb_quote = HsApp (HsVar f)+ (HsUntypedSplice (HsUntypedSpliceNested spn)+ (HsUntypedSpliceExpr _ (g x :: LHsExpr GhcRn)))+ })+ (XQuote noExtField)++NB in the typechecker output, the original payload (which would now+have type (HsQuote GhcTc) is stubbed off with (XQuote noExtField). The payload+is now in the hsb_quote field of the HsBracketTc.+++-------------------------------------+Top-level untyped splices/quasiquotes+-------------------------------------+A top-level splice (not inside a bracket) does not need a SpliceName,+nor does a top-level splice ever end up inside a PendingRnSplice;+hence HsUntypedSpliceTop does not have a SplicePointName field.++Example $(g x). This is parsed as++ HsUntypedSplice _ (HsUntypedSpliceExpr _ ((g x) :: LHsExpr GhcPs))++Renamer: the renamer runs the splice, so the output of the renamer looks like++ HsUntypedSplice (HsUntypedSpliceTop fins (e2 :: LHsExpr GhcRn))+ (HsUntypedSpliceExpr ((g x) :: LHsExpr GhcRn))++where 'e2' is the result of running (g x) to+ produce the syntax tree for 'e2'+ 'fins' is a bunch of TH finalisers, to be run later.++Typechecker: the typechecker simply adds the finalisers, and+typechecks e2, discarding the HsUntypedSplice altogether.+++Note [Lifecycle of an typed splice, and PendingTcSplice]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++----------------------+Nested, typed splices+----------------------+When we typecheck a /typed/ bracket, we lift nested splices out as+`PendingTcSplice`, very similar to Note [PendingRnSplice]. Again, the+splice needs a SplicePointName, for the desguarer to use to connect+the splice expression with the point in the syntax tree where it is+used. Example:+ [|| f $$(g 2)||]++Parser: this is parsed as++ HsTypedBracket _ (HsApp (HsVar "f")+ (HsTypedSplice _ (g 2 :: LHsExpr GhcPs)))++RENAMER (rnTypedSplice): the renamer adds a SplicePointName, spn:++ HsTypedBracket _ (HsApp (HsVar "f")+ (HsTypedSplice spn (g x :: LHsExpr GhcRn)))++TYPECHECKER (tcTypedBracket):++* Set the ThStage to (Brack s (TcPending ps_var lie_var))++* Typecheck the body, and keep the elaborated result (despite never using it!)++* Nested splices (which must be typed) are typechecked by tcNestedSplice, and+ the results accumulated in ps_var; their constraints accumulate in lie_var++* Result is a HsTypedBracket (HsBracketTc rn_brack ty quote_wrapper pending_splices) tc_brack+ where rn_brack is the untyped renamed exp quote constructed from the typed renamed expression :: HsQuote GhcRn++Just like untyped brackets, dump the output into a HsBracketTc.++ HsTypedBracket+ (HsBracketTc { hsb_splices = [PendingTcSplice spn (g x :: LHsExpr GHcTc)]+ , hsb_quote = HsApp (HsVar f)+ (HsUntypedSplice (HsUntypedSpliceNested spn)+ (HsUntypedSpliceExpr _ (g x :: LHsExpr GhcRn)))+ })+ (panic "should never be looked at")++NB: we never need to represent typed /nested/ splices in phase GhcTc.++There are only typed expression splices so `PendingTcSplice` doesn't have a+flavour field.+++--------------------------------+Top-level, typed splices $$(f x)+--------------------------------+Typed splices are renamed and typechecked, but only actually run in+the zonker, after typechecking. See Note [Running typed splices in the zonker]++* Output of parser:+ HsTypedSplice _ (e :: HsExpr GhcPs)++* Output of renamer:+ HsTypedSplice (n :: SplicePointName) (e :: HsExpr GhcRn)++* Output of typechecker: (top-level splices only)+ HsTypedSplice (del_splice :: DelayedSplice) (e :: HsExpr GhcTc)+ where 'del_splice' is something the zonker can run to produce+ the syntax tree to splice in.+ See Note [Running typed splices in the zonker]++Note [Desugaring of brackets]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+In both cases, desugaring happens like this:+ * Hs*Bracket is desugared by GHC.HsToCore.Quote.dsBracket using the renamed+ expression held in `HsBracketTc` (`type instance X*Bracket GhcTc = HsBracketTc`). It++ a) Extends the ds_meta environment with the PendingSplices+ attached to the bracket++ b) Converts the quoted (HsExpr Name) to a CoreExpr that, when+ run, will produce a suitable TH expression/type/decl. This+ is why we leave the *renamed* expression attached to the bracket:+ the quoted expression should not be decorated with all the goop+ added by the type checker++ * Each splice carries a unique Name, called a "splice point", thus+ ${n}(e). The name is initialised to an (Unqual "splice") when the+ splice is created; the renamer gives it a unique.++ * When GHC.HsToCore.Quote (used to desugar the body of the bracket) comes across+ a splice, it looks up the splice's Name, n, in the ds_meta envt,+ to find an (HsExpr Id) that should be substituted for the splice;+ it just desugars it to get a CoreExpr (GHC.HsToCore.Quote.repSplice).++Example:+ Source: f = [| Just $(g 3) |]+ The [| |] part is a HsUntypedBracket GhcPs++ Typechecked: f = [| Just ${s7}(g 3) |]{s7 = g Int 3}+ The [| |] part is a HsUntypedBracket GhcTc, containing *renamed*+ (not typechecked) expression (see Note [The life cycle of a TH quotation])+ The "s7" is the "splice point"; the (g Int 3) part+ is a typechecked expression++ Desugared: f = do { s7 <- g Int 3+ ; return (ConE "Data.Maybe.Just" s7) }++-}++{-+************************************************************************+* *+\subsection{Main interface + stubs for the non-GHCI case+* *+************************************************************************+-}++tcTypedBracket :: HsExpr GhcRn -> LHsExpr GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)+tcUntypedBracket :: HsExpr GhcRn -> HsQuote GhcRn -> [PendingRnSplice] -> ExpRhoType+ -> TcM (HsExpr GhcTc)+tcTypedSplice :: Name -> LHsExpr GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)+ -- None of these functions add constraints to the LIE++runAnnotation :: CoreAnnTarget -> LHsExpr GhcRn -> TcM Annotation+{-+************************************************************************+* *+\subsection{Quoting an expression}+* *+************************************************************************+-}++-- See Note [How brackets and nested splices are handled]+tcTypedBracket rn_expr expr res_ty+ = addErrCtxt (quotationCtxtDoc expr) $+ do { cur_stage <- getStage+ ; ps_ref <- newMutVar []+ ; lie_var <- getConstraintVar -- Any constraints arising from nested splices+ -- should get thrown into the constraint set+ -- from outside the bracket++ -- Make a new type variable for the type of the overall quote+ ; m_var <- mkTyVarTy <$> mkMetaTyVar+ -- Make sure the type variable satisfies Quote+ ; ev_var <- emitQuoteWanted m_var+ -- Bundle them together so they can be used in GHC.HsToCore.Quote for desugaring+ -- brackets.+ ; let wrapper = QuoteWrapper ev_var m_var+ -- Typecheck expr to make sure it is valid.+ -- The typechecked expression won't be used, so we just discard it+ -- (See Note [The life cycle of a TH quotation] in GHC.Hs.Expr)+ -- We'll typecheck it again when we splice it in somewhere+ ; (tc_expr, expr_ty) <- setStage (Brack cur_stage (TcPending ps_ref lie_var wrapper)) $+ tcScalingUsage ManyTy $+ -- Scale by Many, TH lifting is currently nonlinear (#18465)+ tcInferRhoNC expr+ -- NC for no context; tcBracket does that+ ; let rep = getRuntimeRep expr_ty+ ; meta_ty <- tcTExpTy m_var expr_ty+ ; ps' <- readMutVar ps_ref+ ; codeco <- tcLookupId unsafeCodeCoerceName+ ; bracket_ty <- mkAppTy m_var <$> tcMetaTy expTyConName+ ; let brack_tc = HsBracketTc { hsb_quote = ExpBr noExtField expr, hsb_ty = bracket_ty+ , hsb_wrap = Just wrapper, hsb_splices = ps' }+ -- The tc_expr is stored here so that the expression can be used in HIE files.+ brack_expr = HsTypedBracket brack_tc tc_expr+ ; tcWrapResultO (Shouldn'tHappenOrigin "TH typed bracket expression")+ rn_expr+ (unLoc (mkHsApp (mkLHsWrap (applyQuoteWrapper wrapper)+ (nlHsTyApp codeco [rep, expr_ty]))+ (noLocA brack_expr)))+ meta_ty res_ty }++-- See Note [Typechecking Overloaded Quotes]+tcUntypedBracket rn_expr brack ps res_ty+ = do { traceTc "tc_bracket untyped" (ppr brack $$ ppr ps)++ -- Create the type m Exp for expression bracket, m Type for a type+ -- bracket and so on. The brack_info is a Maybe because the+ -- VarBracket ('a) isn't overloaded, but also shouldn't contain any+ -- splices.+ ; (brack_info, expected_type) <- brackTy brack++ -- Match the expected type with the type of all the internal+ -- splices. They might have further constrained types and if they do+ -- we want to reflect that in the overall type of the bracket.+ ; ps' <- case quoteWrapperTyVarTy <$> brack_info of+ Just m_var -> mapM (tcPendingSplice m_var) ps+ Nothing -> assert (null ps) $ return []++ -- Notice that we don't attempt to typecheck the body+ -- of the bracket, which is in brack.+ ; traceTc "tc_bracket done untyped" (ppr expected_type)++ -- Unify the overall type of the bracket with the expected result type+ ; tcWrapResultO BracketOrigin rn_expr+ (HsUntypedBracket (HsBracketTc { hsb_quote = brack, hsb_ty = expected_type+ , hsb_wrap = brack_info, hsb_splices = ps' })+ (XQuote noExtField))+ -- (XQuote noExtField): see Note [The life cycle of a TH quotation] in GHC.Hs.Expr+ expected_type res_ty++ }++-- | A type variable with kind * -> * named "m"+mkMetaTyVar :: TcM TyVar+mkMetaTyVar =+ newNamedFlexiTyVar (fsLit "m") (mkVisFunTyMany liftedTypeKind liftedTypeKind)+++-- | For a type 'm', emit the constraint 'Quote m'.+emitQuoteWanted :: Type -> TcM EvVar+emitQuoteWanted m_var = do+ quote_con <- tcLookupTyCon quoteClassName+ emitWantedEvVar BracketOrigin $+ mkTyConApp quote_con [m_var]++---------------+-- | Compute the expected type of a quotation, and also the QuoteWrapper in+-- the case where it is an overloaded quotation. All quotation forms are+-- overloaded aprt from Variable quotations ('foo)+brackTy :: HsQuote GhcRn -> TcM (Maybe QuoteWrapper, Type)+brackTy b =+ let mkTy n = do+ -- New polymorphic type variable for the bracket+ m_var <- mkTyVarTy <$> mkMetaTyVar+ -- Emit a Quote constraint for the bracket+ ev_var <- emitQuoteWanted m_var+ -- Construct the final expected type of the quote, for example+ -- m Exp or m Type+ final_ty <- mkAppTy m_var <$> tcMetaTy n+ -- Return the evidence variable and metavariable to be used during+ -- desugaring.+ let wrapper = QuoteWrapper ev_var m_var+ return (Just wrapper, final_ty)+ in+ case b of+ (VarBr {}) -> (Nothing,) <$> tcMetaTy nameTyConName+ -- Result type is Var (not Quote-monadic)+ (ExpBr {}) -> mkTy expTyConName -- Result type is m Exp+ (TypBr {}) -> mkTy typeTyConName -- Result type is m Type+ (DecBrG {}) -> mkTy decsTyConName -- Result type is m [Dec]+ (PatBr {}) -> mkTy patTyConName -- Result type is m Pat+ (DecBrL {}) -> panic "tcBrackTy: Unexpected DecBrL"++---------------+-- | Typechecking a pending splice from a untyped bracket+tcPendingSplice :: TcType -- Metavariable for the expected overall type of the+ -- quotation.+ -> PendingRnSplice+ -> TcM PendingTcSplice+tcPendingSplice m_var (PendingRnSplice flavour splice_name expr)+ -- See Note [Typechecking Overloaded Quotes]+ = do { meta_ty <- tcMetaTy meta_ty_name+ -- Expected type of splice, e.g. m Exp+ ; let expected_type = mkAppTy m_var meta_ty+ ; expr' <- tcScalingUsage ManyTy $ tcCheckPolyExpr expr expected_type+ -- Scale by Many, TH lifting is currently nonlinear (#18465)+ ; return (PendingTcSplice splice_name expr') }+ where+ meta_ty_name = case flavour of+ UntypedExpSplice -> expTyConName+ UntypedPatSplice -> patTyConName+ UntypedTypeSplice -> typeTyConName+ UntypedDeclSplice -> decsTyConName++---------------+-- Takes a m and tau and returns the type m (TExp tau)+tcTExpTy :: TcType -> TcType -> TcM TcType+tcTExpTy m_ty exp_ty+ = do { unless (isTauTy exp_ty) $ addErr (TcRnTypedTHWithPolyType exp_ty)+ ; codeCon <- tcLookupTyCon codeTyConName+ ; let rep = getRuntimeRep exp_ty+ ; return (mkTyConApp codeCon [rep, m_ty, exp_ty]) }++quotationCtxtDoc :: LHsExpr GhcRn -> SDoc+quotationCtxtDoc br_body+ = hang (text "In the Template Haskell quotation")+ 2 (thTyBrackets . ppr $ br_body)+++ -- The whole of the rest of the file is the else-branch (ie stage2 only)+++{-+************************************************************************+* *+\subsection{Splicing an expression}+* *+************************************************************************+-}++tcTypedSplice splice_name expr res_ty+ = addErrCtxt (typedSpliceCtxtDoc splice_name expr) $+ setSrcSpan (getLocA expr) $ do+ { stage <- getStage+ ; case stage of+ Splice {} -> tcTopSplice expr res_ty+ Brack pop_stage pend -> tcNestedSplice pop_stage pend splice_name expr res_ty+ RunSplice _ ->+ -- See Note [RunSplice ThLevel] in "GHC.Tc.Types".+ pprPanic ("tcSpliceExpr: attempted to typecheck a splice when " +++ "running another splice") (pprTypedSplice (Just splice_name) expr)+ Comp -> tcTopSplice expr res_ty+ }++{- Note [Collecting modFinalizers in typed splices]+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+'qAddModFinalizer' of the @Quasi TcM@ instance adds finalizers in the local+environment (see Note [Delaying modFinalizers in untyped splices] in+GHC.Rename.Splice). Thus after executing the splice, we move the finalizers to the+finalizer list in the global environment and set them to use the current local+environment (with 'addModFinalizersWithLclEnv').++-}++------------------+tcTopSplice :: LHsExpr GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)+tcTopSplice expr res_ty+ = do { -- Typecheck the expression,+ -- making sure it has type Q (T res_ty)+ res_ty <- expTypeToType res_ty+ ; q_type <- tcMetaTy qTyConName+ -- Top level splices must still be of type Q (TExp a)+ ; meta_exp_ty <- tcTExpTy q_type res_ty+ ; q_expr <- tcTopSpliceExpr Typed $+ tcCheckMonoExpr expr meta_exp_ty+ ; lcl_env <- getLclEnv+ ; let delayed_splice+ = DelayedSplice lcl_env expr res_ty q_expr+ ; return (HsTypedSplice delayed_splice q_expr)++ }++-------------------+tcTopSpliceExpr :: SpliceType -> TcM (LHsExpr GhcTc) -> TcM (LHsExpr GhcTc)+-- Note [How top-level splices are handled]+-- Type check an expression that is the body of a top-level splice+-- (the caller will compile and run it)+-- Note that set the level to Splice, regardless of the original level,+-- before typechecking the expression. For example:+-- f x = $( ...$(g 3) ... )+-- The recursive call to tcCheckPolyExpr will simply expand the+-- inner escape before dealing with the outer one++tcTopSpliceExpr isTypedSplice tc_action+ = checkNoErrs $ -- checkNoErrs: must not try to run the thing+ -- if the type checker fails!+ setStage (Splice isTypedSplice) $+ do { -- Typecheck the expression+ (mb_expr', wanted) <- tryCaptureConstraints tc_action+ -- If tc_action fails (perhaps because of insoluble constraints)+ -- we want to capture and report those constraints, else we may+ -- just get a silent failure (#20179). Hence the 'try' part.++ ; const_binds <- simplifyTop wanted++ ; case mb_expr' of+ Nothing -> failM -- In this case simplifyTop should have+ -- reported some errors+ Just expr' -> return $ mkHsDictLet (EvBinds const_binds) expr' }++------------------+tcNestedSplice :: ThStage -> PendingStuff -> Name+ -> LHsExpr GhcRn -> ExpRhoType -> TcM (HsExpr GhcTc)+ -- See Note [How brackets and nested splices are handled]+ -- A splice inside brackets+tcNestedSplice pop_stage (TcPending ps_var lie_var q@(QuoteWrapper _ m_var)) splice_name expr res_ty+ = do { res_ty <- expTypeToType res_ty+ ; let rep = getRuntimeRep res_ty+ ; meta_exp_ty <- tcTExpTy m_var res_ty+ ; expr' <- setStage pop_stage $+ setConstraintVar lie_var $+ tcCheckMonoExpr expr meta_exp_ty+ ; untype_code <- tcLookupId unTypeCodeName+ ; let expr'' = mkHsApp+ (mkLHsWrap (applyQuoteWrapper q)+ (nlHsTyApp untype_code [rep, res_ty])) expr'+ ; ps <- readMutVar ps_var+ ; writeMutVar ps_var (PendingTcSplice splice_name expr'' : ps)++ -- The returned expression is ignored; it's in the pending splices+ ; return stubNestedSplice }++tcNestedSplice _ _ splice_name _ _+ = pprPanic "tcNestedSplice: rename stage found" (ppr splice_name)+++------------------+-- This is called in the zonker+-- See Note [Running typed splices in the zonker]+runTopSplice :: DelayedSplice -> TcM (HsExpr GhcTc)+runTopSplice (DelayedSplice lcl_env orig_expr res_ty q_expr)+ = restoreLclEnv lcl_env $+ do { zonked_ty <- zonkTcType res_ty+ ; zonked_q_expr <- zonkTopLExpr q_expr+ -- See Note [Collecting modFinalizers in typed splices].+ ; modfinalizers_ref <- newTcRef []+ -- Run the expression+ ; expr2 <- setStage (RunSplice modfinalizers_ref) $+ runMetaE zonked_q_expr+ ; mod_finalizers <- readTcRef modfinalizers_ref+ ; addModFinalizersWithLclEnv $ ThModFinalizers mod_finalizers+ -- We use orig_expr here and not q_expr when tracing as a call to+ -- unsafeCodeCoerce is added to the original expression by the+ -- typechecker when typed quotes are type checked.+ ; traceSplice (SpliceInfo { spliceDescription = "expression"+ , spliceIsDecl = False+ , spliceSource = Just orig_expr+ , spliceGenerated = ppr expr2 })+ -- Rename and typecheck the spliced-in expression,+ -- making sure it has type res_ty+ -- These steps should never fail; this is a *typed* splice+ ; (res, wcs) <-+ captureConstraints $+ addErrCtxt (spliceResultDoc zonked_q_expr) $ do+ { (exp3, _fvs) <- rnLExpr expr2+ ; tcCheckMonoExpr exp3 zonked_ty }+ ; ev <- simplifyTop wcs+ ; return $ unLoc (mkHsDictLet (EvBinds ev) res)+ }+++{-+************************************************************************+* *++* *+************************************************************************+-}++typedSpliceCtxtDoc :: SplicePointName -> LHsExpr GhcRn -> SDoc+typedSpliceCtxtDoc n splice+ = hang (text "In the Template Haskell splice")+ 2 (pprTypedSplice (Just n) splice)++spliceResultDoc :: LHsExpr GhcTc -> SDoc+spliceResultDoc expr+ = sep [ text "In the result of the splice:"+ , nest 2 (char '$' <> ppr expr)+ , text "To see what the splice expanded to, use -ddump-splices"]++stubNestedSplice :: HsExpr GhcTc+-- Used when we need a (LHsExpr GhcTc) that we are never going+-- to look at. We could use "panic" but that's confusing if we ever+-- do a debug-print. The warning is because this should never happen+-- /except/ when doing debug prints.+stubNestedSplice = warnPprTrace True "stubNestedSplice" empty $+ HsLit noComments (mkHsString "stubNestedSplice")+++{-+************************************************************************+* *+ Annotations+* *+************************************************************************+-}++runAnnotation target expr = do+ -- Find the classes we want instances for in order to call toAnnotationWrapper+ loc <- getSrcSpanM+ data_class <- tcLookupClass dataClassName+ to_annotation_wrapper_id <- tcLookupId toAnnotationWrapperName++ -- Check the instances we require live in another module (we want to execute it..)+ -- and check identifiers live in other modules using TH stage checks. tcSimplifyStagedExpr+ -- also resolves the LIE constraints to detect e.g. instance ambiguity+ zonked_wrapped_expr' <- zonkTopLExpr =<< tcTopSpliceExpr Untyped (+ do { (expr', expr_ty) <- tcInferRhoNC expr+ -- We manually wrap the typechecked expression in a call to toAnnotationWrapper+ -- By instantiating the call >here< it gets registered in the+ -- LIE consulted by tcTopSpliceExpr+ -- and hence ensures the appropriate dictionary is bound by const_binds+ ; wrapper <- instCall AnnOrigin [expr_ty] [mkClassPred data_class [expr_ty]]+ ; let loc' = noAnnSrcSpan loc+ ; let specialised_to_annotation_wrapper_expr+ = L loc' (mkHsWrap wrapper+ (HsVar noExtField (L (noAnnSrcSpan loc) to_annotation_wrapper_id)))+ ; return (L loc' (HsApp noComments+ specialised_to_annotation_wrapper_expr expr'))+ })++ -- Run the appropriately wrapped expression to get the value of+ -- the annotation and its dictionaries. The return value is of+ -- type AnnotationWrapper by construction, so this conversion is+ -- safe+ serialized <- runMetaAW zonked_wrapped_expr'+ return Annotation {+ ann_target = target,+ ann_value = serialized+ }++convertAnnotationWrapper :: ForeignHValue -> TcM Serialized+convertAnnotationWrapper fhv = do+ interp <- tcGetInterp+ case interpInstance interp of+ ExternalInterp {} -> runTH THAnnWrapper fhv+#if defined(HAVE_INTERNAL_INTERPRETER)+ InternalInterp -> do+ annotation_wrapper <- liftIO $ wormhole interp fhv+ return $+ case unsafeCoerce annotation_wrapper of+ AnnotationWrapper value | let serialized = toSerialized serializeWithData value ->+ -- Got the value and dictionaries: build the serialized value and+ -- call it a day. We ensure that we seq the entire serialized value+ -- in order that any errors in the user-written code for the+ -- annotation are exposed at this point. This is also why we are+ -- doing all this stuff inside the context of runMeta: it has the+ -- facilities to deal with user error in a meta-level expression+ seqSerialized serialized `seq` serialized++-- | Force the contents of the Serialized value so weknow it doesn't contain any bottoms+seqSerialized :: Serialized -> ()+seqSerialized (Serialized the_type bytes) = the_type `seq` bytes `seqList` ()++#endif++{-+************************************************************************+* *+\subsection{Running an expression}+* *+************************************************************************+-}++runQuasi :: TH.Q a -> TcM a+runQuasi act = TH.runQ act++runRemoteModFinalizers :: ThModFinalizers -> TcM ()+runRemoteModFinalizers (ThModFinalizers finRefs) = do+ let withForeignRefs [] f = f []+ withForeignRefs (x : xs) f = withForeignRef x $ \r ->+ withForeignRefs xs $ \rs -> f (r : rs)+ interp <- tcGetInterp+ case interpInstance interp of+#if defined(HAVE_INTERNAL_INTERPRETER)+ InternalInterp -> do+ qs <- liftIO (withForeignRefs finRefs $ mapM localRef)+ runQuasi $ sequence_ qs+#endif++ ExternalInterp conf iserv -> withIServ_ conf iserv $ \i -> do+ tcg <- getGblEnv+ th_state <- readTcRef (tcg_th_remote_state tcg)+ case th_state of+ Nothing -> return () -- TH was not started, nothing to do+ Just fhv -> do+ liftIO $ withForeignRef fhv $ \st ->+ withForeignRefs finRefs $ \qrefs ->+ writeIServ i (putMessage (RunModFinalizers st qrefs))+ () <- runRemoteTH i []+ readQResult i++runQResult+ :: (a -> String)+ -> (Origin -> SrcSpan -> a -> b)+ -> (ForeignHValue -> TcM a)+ -> SrcSpan+ -> ForeignHValue {- TH.Q a -}+ -> TcM b+runQResult show_th f runQ expr_span hval+ = do { th_result <- runQ hval+ ; th_origin <- getThSpliceOrigin+ ; traceTc "Got TH result:" (text (show_th th_result))+ ; return (f th_origin expr_span th_result) }+++-----------------+runMeta :: (MetaHook TcM -> LHsExpr GhcTc -> TcM hs_syn)+ -> LHsExpr GhcTc+ -> TcM hs_syn+runMeta unwrap e = do+ hooks <- getHooks+ case runMetaHook hooks of+ Nothing -> unwrap defaultRunMeta e+ Just h -> unwrap h e++defaultRunMeta :: MetaHook TcM+defaultRunMeta (MetaE r)+ = fmap r . runMeta' True ppr (runQResult TH.pprint convertToHsExpr runTHExp)+defaultRunMeta (MetaP r)+ = fmap r . runMeta' True ppr (runQResult TH.pprint convertToPat runTHPat)+defaultRunMeta (MetaT r)+ = fmap r . runMeta' True ppr (runQResult TH.pprint convertToHsType runTHType)+defaultRunMeta (MetaD r)+ = fmap r . runMeta' True ppr (runQResult TH.pprint convertToHsDecls runTHDec)+defaultRunMeta (MetaAW r)+ = fmap r . runMeta' False (const empty) (const $ fmap Right . convertAnnotationWrapper)+ -- We turn off showing the code in meta-level exceptions because doing so exposes+ -- the toAnnotationWrapper function that we slap around the user's code++----------------+runMetaAW :: LHsExpr GhcTc -- Of type AnnotationWrapper+ -> TcM Serialized+runMetaAW = runMeta metaRequestAW++runMetaE :: LHsExpr GhcTc -- Of type (Q Exp)+ -> TcM (LHsExpr GhcPs)+runMetaE = runMeta metaRequestE++runMetaP :: LHsExpr GhcTc -- Of type (Q Pat)+ -> TcM (LPat GhcPs)+runMetaP = runMeta metaRequestP++runMetaT :: LHsExpr GhcTc -- Of type (Q Type)+ -> TcM (LHsType GhcPs)+runMetaT = runMeta metaRequestT++runMetaD :: LHsExpr GhcTc -- Of type Q [Dec]+ -> TcM [LHsDecl GhcPs]+runMetaD = runMeta metaRequestD++{- Note [Errors in desugaring a splice]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+What should we do if there are errors when desugaring a splice? We should+abort. There are several cases to consider:++(a) The desugarer hits an unrecoverable error and fails in the monad.+(b) The desugarer hits a recoverable error, reports it, and continues.+(c) The desugarer reports a fatal warning (with -Werror), reports it, and continues.+(d) The desugarer reports a non-fatal warning, and continues.++Each case is tested in th/T19709[abcd].++General principle: we wish to report all messages from dealing with a splice+eagerly, as these messages arise during an earlier stage than type-checking+generally. It's also likely that a compile-time warning from spliced code+will be easier to understand then an error that arises from processing the+code the splice produces. (Rationale: the warning will be about the code the+user actually wrote, not what is generated.)++Case (a): We have no choice but to abort here, but we must make sure that+the messages are printed or logged before aborting. Logging them is annoying,+because we're in the type-checker, and the messages are DsMessages, from the+desugarer. So we report and then fail in the monad. This case is detected+by the fact that initDsTc returns Nothing.++Case (b): We detect this case by looking for errors in the messages returned+from initDsTc and aborting if we spot any (after printing, of course). Note+that initDsTc will return a Just ds_expr in this case, but we don't wish to+use the (likely very bogus) expression.++Case (c): This is functionally the same as (b), except that the expression+isn't bogus. We still don't wish to use it, as the user's request for -Werror+tells us not to.++Case (d): We report the warnings and then carry on with the expression.+This might result in warnings printed out of source order, but this is+appropriate, as the warnings from the splice arise from an earlier stage+of compilation.++Previously, we failed to abort in cases (b) and (c), leading to #19709.+-}++---------------+runMeta' :: Bool -- Whether code should be printed in the exception message+ -> (hs_syn -> SDoc) -- how to print the code+ -> (SrcSpan -> ForeignHValue -> TcM (Either RunSpliceFailReason hs_syn)) -- How to run x+ -> LHsExpr GhcTc -- Of type x; typically x = Q TH.Exp, or+ -- something like that+ -> TcM hs_syn -- Of type t+runMeta' show_code ppr_hs run_and_convert expr+ = do { traceTc "About to run" (ppr expr)+ ; recordThSpliceUse -- seems to be the best place to do this,+ -- we catch all kinds of splices and annotations.++ -- Check that we've had no errors of any sort so far.+ -- For example, if we found an error in an earlier defn f, but+ -- recovered giving it type f :: forall a.a, it'd be very dodgy+ -- to carry on. Mind you, the staging restrictions mean we won't+ -- actually run f, but it still seems wrong. And, more concretely,+ -- see #5358 for an example that fell over when trying to+ -- reify a function with an unlifted kind in it. (These don't occur+ -- in type-correct programs.)+ ; failIfErrsM++ -- run plugins+ ; hsc_env <- getTopEnv+ ; expr' <- withPlugins (hsc_plugins hsc_env) spliceRunAction expr++ -- Desugar+ ; (ds_msgs, mb_ds_expr) <- initDsTc (dsLExpr expr')++ -- Print any messages (even warnings) eagerly: they might be helpful if anything+ -- goes wrong. See Note [Errors in desugaring a splice]. This happens in all+ -- cases.+ ; logger <- getLogger+ ; diag_opts <- initDiagOpts <$> getDynFlags+ ; print_config <- initDsMessageOpts <$> getDynFlags+ ; liftIO $ printMessages logger print_config diag_opts ds_msgs++ ; ds_expr <- case mb_ds_expr of+ Nothing -> failM -- Case (a) from Note [Errors in desugaring a splice]+ Just ds_expr -> -- There still might be a fatal warning or recoverable+ -- Cases (b) and (c) from Note [Errors in desugaring a splice]+ do { when (errorsOrFatalWarningsFound ds_msgs)+ failM+ ; return ds_expr }++ -- Compile and link it; might fail if linking fails+ ; src_span <- getSrcSpanM+ ; traceTc "About to run (desugared)" (ppr ds_expr)+ ; either_hval <- tryM $ liftIO $+ GHC.Driver.Main.hscCompileCoreExpr hsc_env src_span ds_expr+ ; case either_hval of {+ Left exn -> fail_with_exn SplicePhase_CompileAndLink exn ;+ Right (hval, needed_mods, needed_pkgs) -> do++ { -- Coerce it to Q t, and run it++ -- Running might fail if it throws an exception of any kind (hence tryAllM)+ -- including, say, a pattern-match exception in the code we are running+ --+ -- We also do the TH -> HS syntax conversion inside the same+ -- exception-catching thing so that if there are any lurking+ -- exceptions in the data structure returned by hval, we'll+ -- encounter them inside the try+ --+ -- See Note [Exceptions in TH]+ let expr_span = getLocA expr+ ; recordThNeededRuntimeDeps needed_mods needed_pkgs+ ; either_tval <- tryAllM $+ setSrcSpan expr_span $ -- Set the span so that qLocation can+ -- see where this splice is+ do { mb_result <- run_and_convert expr_span hval+ ; case mb_result of+ Left err -> failWithTc (TcRnRunSpliceFailure Nothing err)+ Right result -> do { traceTc "Got HsSyn result:" (ppr_hs result)+ ; return $! result } }++ ; case either_tval of+ Right v -> return v+ Left se -> case fromException se of+ Just IOEnvFailure -> failM -- Error already in Tc monad+ _ -> fail_with_exn SplicePhase_Run se -- Exception+ }}}+ where+ -- see Note [Concealed TH exceptions]+ fail_with_exn :: Exception e => SplicePhase -> e -> TcM a+ fail_with_exn phase exn = do+ exn_msg <- liftIO $ Panic.safeShowException exn+ failWithTc+ $ TcRnSpliceThrewException phase (SomeException exn) exn_msg expr show_code++{-+Note [Running typed splices in the zonker]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++See #15471 for the full discussion.++For many years typed splices were run immediately after they were type checked+however, this is too early as it means to zonk some type variables before+they can be unified with type variables in the surrounding context.++For example,++```+module A where++test_foo :: forall a . Q (TExp (a -> a))+test_foo = [|| id ||]++module B where++import A++qux = $$(test_foo)+```++We would expect `qux` to have inferred type `forall a . a -> a` but if+we run the splices too early the unified variables are zonked to `Any`. The+inferred type is the unusable `Any -> Any`.++To run the splice, we must compile `test_foo` all the way to byte code.+But at the moment when the type checker is looking at the splice, test_foo+has type `Q (TExp (alpha -> alpha))` and we+certainly can't compile code involving unification variables!++We could default `alpha` to `Any` but then we infer `qux :: Any -> Any`+which definitely is not what we want. Moreover, if we had+ qux = [$$(test_foo), (\x -> x +1::Int)]+then `alpha` would have to be `Int`.++Conclusion: we must defer taking decisions about `alpha` until the+typechecker is done; and *then* we can run the splice. It's fine to do it+later, because we know it'll produce type-correct code.++Deferring running the splice until later, in the zonker, means that the+unification variables propagate upwards from the splice into the surrounding+context and are unified correctly.++This is implemented by storing the arguments we need for running the splice+in a `DelayedSplice`. In the zonker, the arguments are passed to+`GHC.Tc.Gen.Splice.runTopSplice` and the expression inserted into the AST as normal.++++Note [Exceptions in TH]+~~~~~~~~~~~~~~~~~~~~~~~+Suppose we have something like this+ $( f 4 )+where+ f :: Int -> Q [Dec]+ f n | n>3 = fail "Too many declarations"+ | otherwise = ...++The 'fail' is a user-generated failure, and should be displayed as a+perfectly ordinary compiler error message, not a panic or anything+like that. Here's how it's processed:++ * 'fail' is the monad fail. The monad instance for Q in TH.Syntax+ effectively transforms (fail s) to+ qReport True s >> fail+ where 'qReport' comes from the Quasi class and fail from its monad+ superclass.++ * The TcM monad is an instance of Quasi (see GHC.Tc.Gen.Splice), and it implements+ (qReport True s) by using addErr to add an error message to the bag of errors.+ The 'fail' in TcM raises an IOEnvFailure exception++ * 'qReport' forces the message to ensure any exception hidden in unevaluated+ thunk doesn't get into the bag of errors. Otherwise the following splice+ will trigger panic (#8987):+ $(fail undefined)+ See also Note [Concealed TH exceptions]++ * So, when running a splice, we catch all exceptions; then for+ - an IOEnvFailure exception, we assume the error is already+ in the error-bag (above)+ - other errors, we add an error to the bag+ and then fail++Note [Concealed TH exceptions]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+When displaying the error message contained in an exception originated from TH+code, we need to make sure that the error message itself does not contain an+exception. For example, when executing the following splice:++ $( error ("foo " ++ error "bar") )++the message for the outer exception is a thunk which will throw the inner+exception when evaluated.++For this reason, we display the message of a TH exception using the+'safeShowException' function, which recursively catches any exception thrown+when showing an error message.+++To call runQ in the Tc monad, we need to make TcM an instance of Quasi:+-}++instance TH.Quasi TcM where+ qNewName s = do { u <- newUnique+ ; let i = toInteger (getKey u)+ ; return (TH.mkNameU s i) }++ -- 'msg' is forced to ensure exceptions don't escape,+ -- see Note [Exceptions in TH]+ qReport True msg = seqList msg $ addErr $ TcRnReportCustomQuasiError True msg+ qReport False msg = seqList msg $ addDiagnostic $ TcRnReportCustomQuasiError False msg++ qLocation :: TcM TH.Loc+ qLocation = do { m <- getModule+ ; l <- getSrcSpanM+ ; r <- case l of+ UnhelpfulSpan _ -> pprPanic "qLocation: Unhelpful location"+ (ppr l)+ RealSrcSpan s _ -> return s+ ; return (TH.Loc { TH.loc_filename = unpackFS (srcSpanFile r)+ , TH.loc_module = moduleNameString (moduleName m)+ , TH.loc_package = unitString (moduleUnit m)+ , TH.loc_start = (srcSpanStartLine r, srcSpanStartCol r)+ , TH.loc_end = (srcSpanEndLine r, srcSpanEndCol r) }) }++ qLookupName = lookupName+ qReify = reify+ qReifyFixity nm = lookupThName nm >>= reifyFixity+ qReifyType = reifyTypeOfThing+ qReifyInstances = reifyInstances+ qReifyRoles = reifyRoles+ qReifyAnnotations = reifyAnnotations+ qReifyModule = reifyModule+ qReifyConStrictness nm = do { nm' <- lookupThName nm+ ; dc <- tcLookupDataCon nm'+ ; let bangs = dataConImplBangs dc+ ; return (map reifyDecidedStrictness bangs) }++ -- For qRecover, discard error messages if+ -- the recovery action is chosen. Otherwise+ -- we'll only fail higher up.+ qRecover recover main = tryTcDiscardingErrs recover main++ qGetPackageRoot = do+ dflags <- getDynFlags+ return $ fromMaybe "." (workingDirectory dflags)++ qAddDependentFile fp = do+ ref <- fmap tcg_dependent_files getGblEnv+ dep_files <- readTcRef ref+ writeTcRef ref (fp:dep_files)++ qAddTempFile suffix = do+ dflags <- getDynFlags+ logger <- getLogger+ tmpfs <- hsc_tmpfs <$> getTopEnv+ liftIO $ newTempName logger tmpfs (tmpDir dflags) TFL_GhcSession suffix++ qAddTopDecls thds = do+ l <- getSrcSpanM+ th_origin <- getThSpliceOrigin+ let either_hval = convertToHsDecls th_origin l thds+ ds <- case either_hval of+ Left exn -> failWithTc+ $ TcRnRunSpliceFailure (Just "addTopDecls") exn+ Right ds -> return ds+ mapM_ (checkTopDecl . unLoc) ds+ th_topdecls_var <- fmap tcg_th_topdecls getGblEnv+ updTcRef th_topdecls_var (\topds -> ds ++ topds)+ where+ checkTopDecl :: HsDecl GhcPs -> TcM ()+ checkTopDecl (ValD _ binds)+ = mapM_ bindName (collectHsBindBinders CollNoDictBinders binds)+ checkTopDecl (SigD _ _)+ = return ()+ checkTopDecl (AnnD _ _)+ = return ()+ checkTopDecl (ForD _ (ForeignImport { fd_name = L _ name }))+ = bindName name+ checkTopDecl d+ = addErr $ TcRnInvalidTopDecl d++ bindName :: RdrName -> TcM ()+ bindName (Exact n)+ = do { th_topnames_var <- fmap tcg_th_topnames getGblEnv+ ; updTcRef th_topnames_var (\ns -> extendNameSet ns n)+ }++ bindName name = addErr $ TcRnNonExactName name++ qAddForeignFilePath lang fp = do+ var <- fmap tcg_th_foreign_files getGblEnv+ updTcRef var ((lang, fp) :)++ qAddModFinalizer fin = do+ r <- liftIO $ mkRemoteRef fin+ fref <- liftIO $ mkForeignRef r (freeRemoteRef r)+ addModFinalizerRef fref++ qAddCorePlugin plugin = do+ hsc_env <- getTopEnv+ let fc = hsc_FC hsc_env+ let home_unit = hsc_home_unit hsc_env+ let dflags = hsc_dflags hsc_env+ let fopts = initFinderOpts dflags+ r <- liftIO $ findHomeModule fc fopts home_unit (mkModuleName plugin)+ let err = TcRnAddInvalidCorePlugin plugin+ case r of+ Found {} -> addErr err+ FoundMultiple {} -> addErr err+ _ -> return ()+ th_coreplugins_var <- tcg_th_coreplugins <$> getGblEnv+ updTcRef th_coreplugins_var (plugin:)++ qGetQ :: forall a. Typeable a => TcM (Maybe a)+ qGetQ = do+ th_state_var <- fmap tcg_th_state getGblEnv+ th_state <- readTcRef th_state_var+ -- See #10596 for why we use a scoped type variable here.+ return (Map.lookup (typeRep (Proxy :: Proxy a)) th_state >>= fromDynamic)++ qPutQ x = do+ th_state_var <- fmap tcg_th_state getGblEnv+ updTcRef th_state_var (\m -> Map.insert (typeOf x) (toDyn x) m)++ qIsExtEnabled = xoptM++ qExtsEnabled =+ EnumSet.toList . extensionFlags . hsc_dflags <$> getTopEnv++ qPutDoc doc_loc s = do+ th_doc_var <- tcg_th_docs <$> getGblEnv+ resolved_doc_loc <- resolve_loc doc_loc+ is_local <- checkLocalName resolved_doc_loc+ unless is_local $ failWithTc $ TcRnAddDocToNonLocalDefn doc_loc+ let ds = mkGeneratedHsDocString s+ hd = lexHsDoc parseIdentifier ds+ hd' <- rnHsDoc hd+ updTcRef th_doc_var (Map.insert resolved_doc_loc hd')+ where+ resolve_loc (TH.DeclDoc n) = DeclDoc <$> lookupThName n+ resolve_loc (TH.ArgDoc n i) = ArgDoc <$> lookupThName n <*> pure i+ resolve_loc (TH.InstDoc t) = InstDoc <$> fmap getName (lookupThInstName t)+ resolve_loc TH.ModuleDoc = pure ModuleDoc++ -- It doesn't make sense to add documentation to something not inside+ -- the current module. So check for it!+ checkLocalName (DeclDoc n) = nameIsLocalOrFrom <$> getModule <*> pure n+ checkLocalName (ArgDoc n _) = nameIsLocalOrFrom <$> getModule <*> pure n+ checkLocalName (InstDoc n) = nameIsLocalOrFrom <$> getModule <*> pure n+ checkLocalName ModuleDoc = pure True+++ qGetDoc (TH.DeclDoc n) = lookupThName n >>= lookupDeclDoc+ qGetDoc (TH.InstDoc t) = lookupThInstName t >>= lookupDeclDoc+ qGetDoc (TH.ArgDoc n i) = lookupThName n >>= lookupArgDoc i+ qGetDoc TH.ModuleDoc = do+ df <- getDynFlags+ docs <- getGblEnv >>= extractDocs df+ return (renderHsDocString . hsDocString <$> (docs_mod_hdr =<< docs))++-- | Looks up documentation for a declaration in first the current module,+-- otherwise tries to find it in another module via 'hscGetModuleInterface'.+lookupDeclDoc :: Name -> TcM (Maybe String)+lookupDeclDoc nm = do+ df <- getDynFlags+ Docs{docs_decls} <- fmap (fromMaybe emptyDocs) $ getGblEnv >>= extractDocs df+ case lookupUniqMap docs_decls nm of+ Just doc -> pure $ Just (renderHsDocStrings $ map hsDocString doc)+ Nothing -> do+ -- Wasn't in the current module. Try searching other external ones!+ mIface <- getExternalModIface nm+ case mIface of+ Just ModIface { mi_docs = Just Docs{docs_decls = dmap} } ->+ pure $ renderHsDocStrings . map hsDocString <$> lookupUniqMap dmap nm+ _ -> pure Nothing++-- | Like 'lookupDeclDoc', looks up documentation for a function argument. If+-- it can't find any documentation for a function in this module, it tries to+-- find it in another module.+lookupArgDoc :: Int -> Name -> TcM (Maybe String)+lookupArgDoc i nm = do+ df <- getDynFlags+ Docs{docs_args = argDocs} <- fmap (fromMaybe emptyDocs) $ getGblEnv >>= extractDocs df+ case lookupUniqMap argDocs nm of+ Just m -> pure $ renderHsDocString . hsDocString <$> IntMap.lookup i m+ Nothing -> do+ mIface <- getExternalModIface nm+ case mIface of+ Just ModIface { mi_docs = Just Docs{docs_args = amap} } ->+ pure $ renderHsDocString . hsDocString <$> (lookupUniqMap amap nm >>= IntMap.lookup i)+ _ -> pure Nothing++-- | Returns the module a Name belongs to, if it is isn't local.+getExternalModIface :: Name -> TcM (Maybe ModIface)+getExternalModIface nm = do+ isLocal <- nameIsLocalOrFrom <$> getModule <*> pure nm+ if isLocal+ then pure Nothing+ else case nameModule_maybe nm of+ Nothing -> pure Nothing+ Just modNm -> do+ hsc_env <- getTopEnv+ iface <- liftIO $ hscGetModuleInterface hsc_env modNm+ pure (Just iface)++-- | Find the GHC name of the first instance that matches the TH type+lookupThInstName :: TH.Type -> TcM Name+lookupThInstName th_type = do+ cls_name <- inst_cls_name th_type+ insts <- reifyInstances' cls_name (inst_arg_types th_type)+ case insts of -- This expands any type synonyms+ Left (_, (inst:_)) -> return $ getName inst+ Left (_, []) -> noMatches+ Right (_, (inst:_)) -> return $ getName inst+ Right (_, []) -> noMatches+ where+ noMatches = failWithTc $+ TcRnFailedToLookupThInstName th_type NoMatchesFound++ -- Get the name of the class for the instance we are documenting+ -- > inst_cls_name (Monad Maybe) == Monad+ -- > inst_cls_name C = C+ inst_cls_name :: TH.Type -> TcM TH.Name+ inst_cls_name (TH.AppT t _) = inst_cls_name t+ inst_cls_name (TH.SigT n _) = inst_cls_name n+ inst_cls_name (TH.VarT n) = pure n+ inst_cls_name (TH.ConT n) = pure n+ inst_cls_name (TH.PromotedT n) = pure n+ inst_cls_name (TH.InfixT _ n _) = pure n+ inst_cls_name (TH.UInfixT _ n _) = pure n+ inst_cls_name (TH.PromotedInfixT _ n _) = pure n+ inst_cls_name (TH.PromotedUInfixT _ n _) = pure n+ inst_cls_name (TH.ParensT t) = inst_cls_name t++ inst_cls_name (TH.ForallT _ _ _) = inst_cls_name_err+ inst_cls_name (TH.ForallVisT _ _) = inst_cls_name_err+ inst_cls_name (TH.AppKindT _ _) = inst_cls_name_err+ inst_cls_name (TH.TupleT _) = inst_cls_name_err+ inst_cls_name (TH.UnboxedTupleT _) = inst_cls_name_err+ inst_cls_name (TH.UnboxedSumT _) = inst_cls_name_err+ inst_cls_name TH.ArrowT = inst_cls_name_err+ inst_cls_name TH.MulArrowT = inst_cls_name_err+ inst_cls_name TH.EqualityT = inst_cls_name_err+ inst_cls_name TH.ListT = inst_cls_name_err+ inst_cls_name (TH.PromotedTupleT _) = inst_cls_name_err+ inst_cls_name TH.PromotedNilT = inst_cls_name_err+ inst_cls_name TH.PromotedConsT = inst_cls_name_err+ inst_cls_name TH.StarT = inst_cls_name_err+ inst_cls_name TH.ConstraintT = inst_cls_name_err+ inst_cls_name (TH.LitT _) = inst_cls_name_err+ inst_cls_name TH.WildCardT = inst_cls_name_err+ inst_cls_name (TH.ImplicitParamT _ _) = inst_cls_name_err++ inst_cls_name_err = failWithTc $+ TcRnFailedToLookupThInstName th_type CouldNotDetermineInstance++ -- Basically does the opposite of 'mkThAppTs'+ -- > inst_arg_types (Monad Maybe) == [Maybe]+ -- > inst_arg_types C == []+ inst_arg_types :: TH.Type -> [TH.Type]+ inst_arg_types (TH.AppT _ args) =+ let go (TH.AppT t ts) = t:go ts+ go t = [t]+ in go args+ inst_arg_types _ = []++-- | Adds a mod finalizer reference to the local environment.+addModFinalizerRef :: ForeignRef (TH.Q ()) -> TcM ()+addModFinalizerRef finRef = do+ th_stage <- getStage+ case th_stage of+ RunSplice th_modfinalizers_var -> updTcRef th_modfinalizers_var (finRef :)+ -- This case happens only if a splice is executed and the caller does+ -- not set the 'ThStage' to 'RunSplice' to collect finalizers.+ -- See Note [Delaying modFinalizers in untyped splices] in GHC.Rename.Splice.+ _ ->+ pprPanic "addModFinalizer was called when no finalizers were collected"+ (ppr th_stage)++-- | Releases the external interpreter state.+finishTH :: TcM ()+finishTH = do+ hsc_env <- getTopEnv+ case interpInstance <$> hsc_interp hsc_env of+ Nothing -> pure ()+#if defined(HAVE_INTERNAL_INTERPRETER)+ Just InternalInterp -> pure ()+#endif+ Just (ExternalInterp {}) -> do+ tcg <- getGblEnv+ writeTcRef (tcg_th_remote_state tcg) Nothing+++runTHExp :: ForeignHValue -> TcM TH.Exp+runTHExp = runTH THExp++runTHPat :: ForeignHValue -> TcM TH.Pat+runTHPat = runTH THPat++runTHType :: ForeignHValue -> TcM TH.Type+runTHType = runTH THType++runTHDec :: ForeignHValue -> TcM [TH.Dec]+runTHDec = runTH THDec++runTH :: Binary a => THResultType -> ForeignHValue -> TcM a+runTH ty fhv = do+ interp <- tcGetInterp+ case interpInstance interp of+#if defined(HAVE_INTERNAL_INTERPRETER)+ InternalInterp -> do+ -- Run it in the local TcM+ hv <- liftIO $ wormhole interp fhv+ r <- runQuasi (unsafeCoerce hv :: TH.Q a)+ return r+#endif++ ExternalInterp conf iserv ->+ -- Run it on the server. For an overview of how TH works with+ -- Remote GHCi, see Note [Remote Template Haskell] in+ -- libraries/ghci/GHCi/TH.hs.+ withIServ_ conf iserv $ \i -> do+ rstate <- getTHState i+ loc <- TH.qLocation+ liftIO $+ withForeignRef rstate $ \state_hv ->+ withForeignRef fhv $ \q_hv ->+ writeIServ i (putMessage (RunTH state_hv q_hv ty (Just loc)))+ runRemoteTH i []+ bs <- readQResult i+ return $! runGet get (LB.fromStrict bs)+++-- | communicate with a remotely-running TH computation until it finishes.+-- See Note [Remote Template Haskell] in libraries/ghci/GHCi/TH.hs.+runRemoteTH+ :: IServInstance+ -> [Messages TcRnMessage] -- saved from nested calls to qRecover+ -> TcM ()+runRemoteTH iserv recovers = do+ THMsg msg <- liftIO $ readIServ iserv getTHMessage+ case msg of+ RunTHDone -> return ()+ StartRecover -> do -- Note [TH recover with -fexternal-interpreter]+ v <- getErrsVar+ msgs <- readTcRef v+ writeTcRef v emptyMessages+ runRemoteTH iserv (msgs : recovers)+ EndRecover caught_error -> do+ let (prev_msgs, rest) = case recovers of+ [] -> panic "EndRecover"+ a : b -> (a,b)+ v <- getErrsVar+ warn_msgs <- getWarningMessages <$> readTcRef v+ -- keep the warnings only if there were no errors+ writeTcRef v $ if caught_error+ then prev_msgs+ else mkMessages warn_msgs `unionMessages` prev_msgs+ runRemoteTH iserv rest+ _other -> do+ r <- handleTHMessage msg+ liftIO $ writeIServ iserv (put r)+ runRemoteTH iserv recovers++-- | Read a value of type QResult from the iserv+readQResult :: Binary a => IServInstance -> TcM a+readQResult i = do+ qr <- liftIO $ readIServ i get+ case qr of+ QDone a -> return a+ QException str -> liftIO $ throwIO (ErrorCall str)+ QFail str -> fail str++{- Note [TH recover with -fexternal-interpreter]+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Recover is slightly tricky to implement.++The meaning of "recover a b" is+ - Do a+ - If it finished with no errors, then keep the warnings it generated+ - If it failed, discard any messages it generated, and do b++Note that "failed" here can mean either+ (1) threw an exception (failTc)+ (2) generated an error message (addErrTcM)++The messages are managed by GHC in the TcM monad, whereas the+exception-handling is done in the ghc-iserv process, so we have to+coordinate between the two.++On the server:+ - emit a StartRecover message+ - run "a; FailIfErrs" inside a try+ - emit an (EndRecover x) message, where x = True if "a; FailIfErrs" failed+ - if "a; FailIfErrs" failed, run "b"++Back in GHC, when we receive:++ FailIfErrrs+ failTc if there are any error messages (= failIfErrsM)+ StartRecover+ save the current messages and start with an empty set.+ EndRecover caught_error+ Restore the previous messages,+ and merge in the new messages if caught_error is false.+-}++-- | Retrieve (or create, if it hasn't been created already), the+-- remote TH state. The TH state is a remote reference to an IORef+-- QState living on the server, and we have to pass this to each RunTH+-- call we make.+--+-- The TH state is stored in tcg_th_remote_state in the TcGblEnv.+--+getTHState :: IServInstance -> TcM (ForeignRef (IORef QState))+getTHState i = do+ tcg <- getGblEnv+ th_state <- readTcRef (tcg_th_remote_state tcg)+ case th_state of+ Just rhv -> return rhv+ Nothing -> do+ interp <- tcGetInterp+ fhv <- liftIO $ mkFinalizedHValue interp =<< iservCall i StartTH+ writeTcRef (tcg_th_remote_state tcg) (Just fhv)+ return fhv++wrapTHResult :: TcM a -> TcM (THResult a)+wrapTHResult tcm = do+ e <- tryM tcm -- only catch 'fail', treat everything else as catastrophic+ case e of+ Left e -> return (THException (show e))+ Right a -> return (THComplete a)++handleTHMessage :: THMessage a -> TcM a+handleTHMessage msg = case msg of+ NewName a -> wrapTHResult $ TH.qNewName a+ Report b str -> wrapTHResult $ TH.qReport b str+ LookupName b str -> wrapTHResult $ TH.qLookupName b str+ Reify n -> wrapTHResult $ TH.qReify n+ ReifyFixity n -> wrapTHResult $ TH.qReifyFixity n+ ReifyType n -> wrapTHResult $ TH.qReifyType n+ ReifyInstances n ts -> wrapTHResult $ TH.qReifyInstances n ts+ ReifyRoles n -> wrapTHResult $ TH.qReifyRoles n+ ReifyAnnotations lookup tyrep ->+ wrapTHResult $ (map B.pack <$> getAnnotationsByTypeRep lookup tyrep)+ ReifyModule m -> wrapTHResult $ TH.qReifyModule m+ ReifyConStrictness nm -> wrapTHResult $ TH.qReifyConStrictness nm+ GetPackageRoot -> wrapTHResult $ TH.qGetPackageRoot+ AddDependentFile f -> wrapTHResult $ TH.qAddDependentFile f+ AddTempFile s -> wrapTHResult $ TH.qAddTempFile s+ AddModFinalizer r -> do+ interp <- hscInterp <$> getTopEnv+ wrapTHResult $ liftIO (mkFinalizedHValue interp r) >>= addModFinalizerRef+ AddCorePlugin str -> wrapTHResult $ TH.qAddCorePlugin str+ AddTopDecls decs -> wrapTHResult $ TH.qAddTopDecls decs+ AddForeignFilePath lang str -> wrapTHResult $ TH.qAddForeignFilePath lang str+ IsExtEnabled ext -> wrapTHResult $ TH.qIsExtEnabled ext+ ExtsEnabled -> wrapTHResult $ TH.qExtsEnabled+ PutDoc l s -> wrapTHResult $ TH.qPutDoc l s+ GetDoc l -> wrapTHResult $ TH.qGetDoc l+ FailIfErrs -> wrapTHResult failIfErrsM+ _ -> panic ("handleTHMessage: unexpected message " ++ show msg)++getAnnotationsByTypeRep :: TH.AnnLookup -> TypeRep -> TcM [[Word8]]+getAnnotationsByTypeRep th_name tyrep+ = do { name <- lookupThAnnLookup th_name+ ; topEnv <- getTopEnv+ ; epsHptAnns <- liftIO $ prepareAnnotations topEnv Nothing+ ; tcg <- getGblEnv+ ; let selectedEpsHptAnns = findAnnsByTypeRep epsHptAnns name tyrep+ ; let selectedTcgAnns = findAnnsByTypeRep (tcg_ann_env tcg) name tyrep+ ; return (selectedEpsHptAnns ++ selectedTcgAnns) }++{-+************************************************************************+* *+ Instance Testing+* *+************************************************************************+-}++reifyInstances :: TH.Name -> [TH.Type] -> TcM [TH.Dec]+reifyInstances th_nm th_tys+ = do { insts <- reifyInstances' th_nm th_tys+ ; case insts of+ Left (cls, cls_insts) ->+ reifyClassInstances cls cls_insts+ Right (tc, fam_insts) ->+ reifyFamilyInstances tc fam_insts }++reifyInstances' :: TH.Name+ -> [TH.Type]+ -> TcM (Either (Class, [ClsInst]) (TyCon, [FamInst]))+ -- ^ Returns 'Left' in the case that the instances were found to+ -- be class instances, or 'Right' if they are family instances.+reifyInstances' th_nm th_tys+ = addErrCtxt (text "In the argument of reifyInstances:"+ <+> ppr_th th_nm <+> sep (map ppr_th th_tys)) $+ do { loc <- getSrcSpanM+ ; th_origin <- getThSpliceOrigin+ ; rdr_ty <- cvt th_origin loc (mkThAppTs (TH.ConT th_nm) th_tys)+ -- #9262 says to bring vars into scope, like in HsForAllTy case+ -- of rnHsTyKi+ ; let tv_rdrs = extractHsTyRdrTyVars rdr_ty+ -- Rename to HsType Name+ ; ((tv_names, rn_ty), _fvs)+ <- checkNoErrs $ -- If there are out-of-scope Names here, then we+ -- must error before proceeding to typecheck the+ -- renamed type, as that will result in GHC+ -- internal errors (#13837).+ rnImplicitTvOccs Nothing tv_rdrs $ \ tv_names ->+ do { (rn_ty, fvs) <- rnLHsType doc rdr_ty+ ; return ((tv_names, rn_ty), fvs) }+ ; skol_info <- mkSkolemInfo ReifySkol+ ; (tclvl, wanted, (tvs, ty))+ <- pushLevelAndSolveEqualitiesX "reifyInstances" $+ bindImplicitTKBndrs_Skol skol_info tv_names $+ tcInferLHsType rn_ty++ ; tvs <- zonkAndScopedSort tvs++ -- Avoid error cascade if there are unsolved+ ; reportUnsolvedEqualities skol_info tvs tclvl wanted++ ; ty <- zonkTcTypeToType ty+ -- Substitute out the meta type variables+ -- In particular, the type might have kind+ -- variables inside it (#7477)++ ; traceTc "reifyInstances'" (ppr ty $$ ppr (typeKind ty))+ ; case splitTyConApp_maybe ty of -- This expands any type synonyms+ Just (tc, tys) -- See #7910+ | Just cls <- tyConClass_maybe tc+ -> do { inst_envs <- tcGetInstEnvs+ ; let (matches, unifies, _) = lookupInstEnv False inst_envs cls tys+ ; traceTc "reifyInstances'1" (ppr matches)+ ; return $ Left (cls, map fst matches ++ getPotentialUnifiers unifies) }+ | isOpenFamilyTyCon tc+ -> do { inst_envs <- tcGetFamInstEnvs+ ; let matches = lookupFamInstEnv inst_envs tc tys+ ; traceTc "reifyInstances'2" (ppr matches)+ ; return $ Right (tc, map fim_instance matches) }+ _ -> bale_out $ TcRnCannotReifyInstance ty }+ where+ doc = ClassInstanceCtx+ bale_out msg = failWithTc msg++ cvt :: Origin -> SrcSpan -> TH.Type -> TcM (LHsType GhcPs)+ cvt origin loc th_ty = case convertToHsType origin loc th_ty of+ Left msg -> failWithTc (TcRnRunSpliceFailure Nothing msg)+ Right ty -> return ty++{-+************************************************************************+* *+ Reification+* *+************************************************************************+-}++lookupName :: Bool -- True <=> type namespace+ -- False <=> value namespace+ -> String -> TcM (Maybe TH.Name)+lookupName is_type_name s+ = do { mb_nm <- lookupOccRn_maybe rdr_name+ ; return (fmap reifyName mb_nm) }+ where+ th_name = TH.mkName s -- Parses M.x into a base of 'x' and a module of 'M'++ occ_fs :: FastString+ occ_fs = mkFastString (TH.nameBase th_name)++ occ :: OccName+ occ | is_type_name+ = if isLexVarSym occ_fs || isLexCon occ_fs+ then mkTcOccFS occ_fs+ else mkTyVarOccFS occ_fs+ | otherwise+ = if isLexCon occ_fs then mkDataOccFS occ_fs+ else mkVarOccFS occ_fs++ rdr_name = case TH.nameModule th_name of+ Nothing -> mkRdrUnqual occ+ Just mod -> mkRdrQual (mkModuleName mod) occ++-- | We only want to produce warnings for TH-splices if the user requests so.+-- See Note [Warnings for TH splices].+getThSpliceOrigin :: TcM Origin+getThSpliceOrigin = do+ warn <- goptM Opt_EnableThSpliceWarnings+ if warn then return FromSource else return Generated+++getThing :: TH.Name -> TcM TcTyThing+getThing th_name+ = do { name <- lookupThName th_name+ ; traceIf (text "reify" <+> text (show th_name) <+> brackets (ppr_ns th_name) <+> ppr name)+ ; tcLookupTh name }+ -- ToDo: this tcLookup could fail, which would give a+ -- rather unhelpful error message+ where+ ppr_ns (TH.Name _ (TH.NameG TH.DataName _pkg _mod)) = text "data"+ ppr_ns (TH.Name _ (TH.NameG TH.TcClsName _pkg _mod)) = text "tc"+ ppr_ns (TH.Name _ (TH.NameG TH.VarName _pkg _mod)) = text "var"+ ppr_ns _ = panic "reify/ppr_ns"++reify :: TH.Name -> TcM TH.Info+reify th_name+ = do { traceTc "reify 1" (text (TH.showName th_name))+ ; thing <- getThing th_name+ ; traceTc "reify 2" (ppr thing)+ ; reifyThing thing }++lookupThName :: TH.Name -> TcM Name+lookupThName th_name = do+ mb_name <- lookupThName_maybe th_name+ case mb_name of+ Nothing -> failWithTc (notInScope th_name)+ Just name -> return name++lookupThName_maybe :: TH.Name -> TcM (Maybe Name)+lookupThName_maybe th_name+ = do { names <- mapMaybeM lookupOccRn_maybe (thRdrNameGuesses th_name)+ -- Pick the first that works+ -- E.g. reify (mkName "A") will pick the class A in preference to the data constructor A+ ; return (listToMaybe names) }++tcLookupTh :: Name -> TcM TcTyThing+-- This is a specialised version of GHC.Tc.Utils.Env.tcLookup; specialised mainly in that+-- it gives a reify-related error message on failure, whereas in the normal+-- tcLookup, failure is a bug.+tcLookupTh name+ = do { (gbl_env, lcl_env) <- getEnvs+ ; case lookupNameEnv (tcl_env lcl_env) name of {+ Just thing -> return thing;+ Nothing ->++ case lookupNameEnv (tcg_type_env gbl_env) name of {+ Just thing -> return (AGlobal thing);+ Nothing ->++ -- EZY: I don't think this choice matters, no TH in signatures!+ if nameIsLocalOrFrom (tcg_semantic_mod gbl_env) name+ then -- It's defined in this module+ failWithTc (notInEnv name)++ else+ do { mb_thing <- tcLookupImported_maybe name+ ; case mb_thing of+ Succeeded thing -> return (AGlobal thing)+ Failed msg -> failWithTc (TcRnInterfaceLookupError name msg)+ }}}}++notInScope :: TH.Name -> TcRnMessage+notInScope th_name =+ TcRnCannotReifyOutOfScopeThing th_name++notInEnv :: Name -> TcRnMessage+notInEnv name = TcRnCannotReifyThingNotInTypeEnv name++------------------------------+reifyRoles :: TH.Name -> TcM [TH.Role]+reifyRoles th_name+ = do { thing <- getThing th_name+ ; case thing of+ AGlobal (ATyCon tc) -> return (map reify_role (tyConRoles tc))+ _ -> failWithTc (TcRnNoRolesAssociatedWithThing thing)+ }+ where+ reify_role Nominal = TH.NominalR+ reify_role Representational = TH.RepresentationalR+ reify_role Phantom = TH.PhantomR++------------------------------+reifyThing :: TcTyThing -> TcM TH.Info+-- The only reason this is monadic is for error reporting,+-- which in turn is mainly for the case when TH can't express+-- some random GHC extension++reifyThing (AGlobal (AnId id))+ = do { ty <- reifyType (idType id)+ ; let v = reifyName id+ ; case idDetails id of+ ClassOpId cls -> return (TH.ClassOpI v ty (reifyName cls))+ RecSelId{sel_tycon=RecSelData tc}+ -> return (TH.VarI (reifySelector id tc) ty Nothing)+ _ -> return (TH.VarI v ty Nothing)+ }++reifyThing (AGlobal (ATyCon tc)) = reifyTyCon tc+reifyThing (AGlobal (AConLike (RealDataCon dc)))+ = mkDataConI dc++reifyThing (AGlobal (AConLike (PatSynCon ps)))+ = do { let name = reifyName ps+ ; ty <- reifyPatSynType (patSynSigBndr ps)+ ; return (TH.PatSynI name ty) }++reifyThing (ATcId {tct_id = id})+ = do { ty1 <- zonkTcType (idType id) -- Make use of all the info we have, even+ -- though it may be incomplete+ ; ty2 <- reifyType ty1+ ; return (TH.VarI (reifyName id) ty2 Nothing) }++reifyThing (ATyVar tv tv1)+ = do { ty1 <- zonkTcTyVar tv1+ ; ty2 <- reifyType ty1+ ; return (TH.TyVarI (reifyName tv) ty2) }++reifyThing thing = pprPanic "reifyThing" (pprTcTyThingCategory thing)++-------------------------------------------+reifyAxBranch :: TyCon -> CoAxBranch -> TcM TH.TySynEqn+reifyAxBranch fam_tc (CoAxBranch { cab_tvs = tvs+ , cab_lhs = lhs+ , cab_rhs = rhs })+ -- remove kind patterns (#8884)+ = do { tvs' <- reifyTyVarsToMaybe tvs+ ; let lhs_types_only = filterOutInvisibleTypes fam_tc lhs+ ; lhs' <- reifyTypes lhs_types_only+ ; annot_th_lhs <- zipWith3M annotThType (tyConArgsPolyKinded fam_tc)+ lhs_types_only lhs'+ ; let lhs_type = mkThAppTs (TH.ConT $ reifyName fam_tc) annot_th_lhs+ ; rhs' <- reifyType rhs+ ; return (TH.TySynEqn tvs' lhs_type rhs') }++reifyTyCon :: TyCon -> TcM TH.Info+reifyTyCon tc+ | Just cls <- tyConClass_maybe tc+ = reifyClass cls++{- Seems to be just a short cut for the next equation -- omit+ | tc `hasKey` fUNTyConKey -- I'm not quite sure what is happening here+ = return (TH.PrimTyConI (reifyName tc) 2 False)+-}++ | isPrimTyCon tc+ = return (TH.PrimTyConI (reifyName tc) (length (tyConVisibleTyVars tc))+ (isUnliftedTypeKind (tyConResKind tc)))++ | isTypeFamilyTyCon tc+ = do { let tvs = tyConTyVars tc+ res_kind = tyConResKind tc+ resVar = tyConFamilyResVar_maybe tc++ ; kind' <- reifyKind res_kind+ ; let (resultSig, injectivity) =+ case resVar of+ Nothing -> (TH.KindSig kind', Nothing)+ Just name ->+ let thName = reifyName name+ injAnnot = tyConInjectivityInfo tc+ sig = TH.TyVarSig (TH.KindedTV thName () kind')+ inj = case injAnnot of+ NotInjective -> Nothing+ Injective ms ->+ Just (TH.InjectivityAnn thName injRHS)+ where+ injRHS = map (reifyName . tyVarName)+ (filterByList ms tvs)+ in (sig, inj)+ ; tvs' <- reifyTyVars (tyConVisibleTyVars tc)+ ; let tfHead =+ TH.TypeFamilyHead (reifyName tc) tvs' resultSig injectivity+ ; if isOpenTypeFamilyTyCon tc+ then do { fam_envs <- tcGetFamInstEnvs+ ; instances <- reifyFamilyInstances tc+ (familyInstances fam_envs tc)+ ; return (TH.FamilyI (TH.OpenTypeFamilyD tfHead) instances) }+ else do { eqns <-+ case isClosedSynFamilyTyConWithAxiom_maybe tc of+ Just ax -> mapM (reifyAxBranch tc) $+ fromBranches $ coAxiomBranches ax+ Nothing -> return []+ ; return (TH.FamilyI (TH.ClosedTypeFamilyD tfHead eqns)+ []) } }++ | isDataFamilyTyCon tc+ = do { let res_kind = tyConResKind tc++ ; kind' <- fmap Just (reifyKind res_kind)++ ; tvs' <- reifyTyVars (tyConVisibleTyVars tc)+ ; fam_envs <- tcGetFamInstEnvs+ ; instances <- reifyFamilyInstances tc (familyInstances fam_envs tc)+ ; return (TH.FamilyI+ (TH.DataFamilyD (reifyName tc) tvs' kind') instances) }++ | Just (_, rhs) <- synTyConDefn_maybe tc -- Vanilla type synonym+ = do { rhs' <- reifyType rhs+ ; tvs' <- reifyTyVars (tyConVisibleTyVars tc)+ ; return (TH.TyConI+ (TH.TySynD (reifyName tc) tvs' rhs'))+ }++ -- Special case for `type data` data constructors, which are reified as+ -- `ATyCon`s rather than `ADataCon`s (#22818).+ -- See Note [Type data declarations] in GHC.Rename.Module.+ | Just dc <- isPromotedDataCon_maybe tc+ , isTypeDataCon dc+ = mkDataConI dc++ | otherwise+ = do { cxt <- reifyCxt (tyConStupidTheta tc)+ ; let tvs = tyConTyVars tc+ dataCons = tyConDataCons tc+ isGadt = isGadtSyntaxTyCon tc+ ; cons <- mapM (reifyDataCon isGadt (mkTyVarTys tvs)) dataCons+ ; r_tvs <- reifyTyVars (tyConVisibleTyVars tc)+ ; let name = reifyName tc+ deriv = [] -- Don't know about deriving+ decl | isTypeDataTyCon tc =+ -- `type data` declarations have a special `Dec`,+ -- separate from other `DataD`s. See+ -- [Type data declarations] in GHC.Rename.Module.+ TH.TypeDataD name r_tvs Nothing cons+ | isNewTyCon tc =+ TH.NewtypeD cxt name r_tvs Nothing (head cons) deriv+ | otherwise =+ TH.DataD cxt name r_tvs Nothing cons deriv+ ; return (TH.TyConI decl) }++reifyDataCon :: Bool -> [Type] -> DataCon -> TcM TH.Con+reifyDataCon isGadtDataCon tys dc+ = do { let -- used for H98 data constructors+ (ex_tvs, theta, arg_tys)+ = dataConInstSig dc tys+ -- used for GADTs data constructors+ g_user_tvs' = dataConUserTyVarBinders dc+ (g_univ_tvs, _, g_eq_spec, g_theta', g_arg_tys', g_res_ty')+ = dataConFullSig dc+ (srcUnpks, srcStricts)+ = mapAndUnzip reifySourceBang (dataConSrcBangs dc)+ dcdBangs = zipWith TH.Bang srcUnpks srcStricts+ fields = dataConFieldLabels dc+ name = reifyName dc+ -- Universal tvs present in eq_spec need to be filtered out, as+ -- they will not appear anywhere in the type.+ eq_spec_tvs = mkVarSet (map eqSpecTyVar g_eq_spec)++ ; (univ_subst, _)+ -- See Note [Freshen reified GADT constructors' universal tyvars]+ <- freshenTyVarBndrs $+ filterOut (`elemVarSet` eq_spec_tvs) g_univ_tvs+ ; let (tvb_subst, g_user_tvs) = subst_tv_binders univ_subst g_user_tvs'+ g_theta = substTys tvb_subst g_theta'+ g_arg_tys = substTys tvb_subst (map scaledThing g_arg_tys')+ g_res_ty = substTy tvb_subst g_res_ty'++ ; r_arg_tys <- reifyTypes (if isGadtDataCon then g_arg_tys else arg_tys)++ ; main_con <-+ if | not (null fields) && not isGadtDataCon ->+ return $ TH.RecC name (zip3 (map reifyFieldLabel fields)+ dcdBangs r_arg_tys)+ | not (null fields) -> do+ { res_ty <- reifyType g_res_ty+ ; return $ TH.RecGadtC [name]+ (zip3 (map (reifyName . flSelector) fields)+ dcdBangs r_arg_tys) res_ty }+ -- We need to check not isGadtDataCon here because GADT+ -- constructors can be declared infix.+ -- See Note [Infix GADT constructors] in GHC.Tc.TyCl.+ | dataConIsInfix dc && not isGadtDataCon ->+ assert (r_arg_tys `lengthIs` 2) $ do+ { let [r_a1, r_a2] = r_arg_tys+ [s1, s2] = dcdBangs+ ; return $ TH.InfixC (s1,r_a1) name (s2,r_a2) }+ | isGadtDataCon -> do+ { res_ty <- reifyType g_res_ty+ ; return $ TH.GadtC [name] (dcdBangs `zip` r_arg_tys) res_ty }+ | otherwise ->+ return $ TH.NormalC name (dcdBangs `zip` r_arg_tys)++ ; let (ex_tvs', theta') | isGadtDataCon = (g_user_tvs, g_theta)+ | otherwise = assert (all isTyVar ex_tvs)+ -- no covars for haskell syntax+ (map mk_specified ex_tvs, theta)+ ret_con | null ex_tvs' && null theta' = return main_con+ | otherwise = do+ { cxt <- reifyCxt theta'+ ; ex_tvs'' <- reifyTyVarBndrs ex_tvs'+ ; return (TH.ForallC ex_tvs'' cxt main_con) }+ ; assert (r_arg_tys `equalLength` dcdBangs)+ ret_con }+ where+ mk_specified tv = Bndr tv SpecifiedSpec++ subst_tv_binders subst tv_bndrs =+ let tvs = binderVars tv_bndrs+ flags = binderFlags tv_bndrs+ (subst', tvs') = substTyVarBndrs subst tvs+ tv_bndrs' = map (\(tv,fl) -> Bndr tv fl) (zip tvs' flags)+ in (subst', tv_bndrs')++mkDataConI :: DataCon -> TcM TH.Info+mkDataConI dc+ = do { let name = dataConName dc+ ; ty <- reifyType (idType (dataConWrapId dc))+ ; return (TH.DataConI (reifyName name) ty+ (reifyName (dataConOrigTyCon dc)))+ }++{-+Note [Freshen reified GADT constructors' universal tyvars]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Suppose one were to reify this GADT:++ data a :~: b where+ Refl :: forall a b. (a ~ b) => a :~: b++We ought to be careful here about the uniques we give to the occurrences of `a`+and `b` in this definition. That is because in the original DataCon, all uses+of `a` and `b` have the same unique, since `a` and `b` are both universally+quantified type variables--that is, they are used in both the (:~:) tycon as+well as in the constructor type signature. But when we turn the DataCon+definition into the reified one, the `a` and `b` in the constructor type+signature becomes differently scoped than the `a` and `b` in `data a :~: b`.++While it wouldn't technically be *wrong* per se to re-use the same uniques for+`a` and `b` across these two different scopes, it's somewhat annoying for end+users of Template Haskell, since they wouldn't be able to rely on the+assumption that all TH names have globally distinct uniques (#13885). For this+reason, we freshen the universally quantified tyvars that go into the reified+GADT constructor type signature to give them distinct uniques from their+counterparts in the tycon.+-}++------------------------------+reifyClass :: Class -> TcM TH.Info+reifyClass cls+ = do { cxt <- reifyCxt theta+ ; inst_envs <- tcGetInstEnvs+ ; insts <- reifyClassInstances cls (InstEnv.classInstances inst_envs cls)+ ; assocTys <- concatMapM reifyAT ats+ ; ops <- concatMapM reify_op op_stuff+ ; tvs' <- reifyTyVars (tyConVisibleTyVars (classTyCon cls))+ ; let dec = TH.ClassD cxt (reifyName cls) tvs' fds' (assocTys ++ ops)+ ; return (TH.ClassI dec insts) }+ where+ (_, fds, theta, _, ats, op_stuff) = classExtraBigSig cls+ fds' = map reifyFunDep fds+ reify_op (op, def_meth)+ = do { let (_, _, ty) = tcSplitMethodTy (idType op)+ -- Use tcSplitMethodTy to get rid of the extraneous class+ -- variables and predicates at the beginning of op's type+ -- (see #15551).+ ; ty' <- reifyType ty+ ; let nm' = reifyName op+ ; case def_meth of+ Just (_, GenericDM gdm_ty) ->+ do { gdm_ty' <- reifyType gdm_ty+ ; return [TH.SigD nm' ty', TH.DefaultSigD nm' gdm_ty'] }+ _ -> return [TH.SigD nm' ty'] }++ reifyAT :: ClassATItem -> TcM [TH.Dec]+ reifyAT (ATI tycon def) = do+ tycon' <- reifyTyCon tycon+ case tycon' of+ TH.FamilyI dec _ -> do+ let (tyName, tyArgs) = tfNames dec+ (dec :) <$> maybe (return [])+ (fmap (:[]) . reifyDefImpl tyName tyArgs . fst)+ def+ _ -> pprPanic "reifyAT" (text (show tycon'))++ reifyDefImpl :: TH.Name -> [TH.Name] -> Type -> TcM TH.Dec+ reifyDefImpl n args ty =+ TH.TySynInstD . TH.TySynEqn Nothing (mkThAppTs (TH.ConT n) (map TH.VarT args))+ <$> reifyType ty++ tfNames :: TH.Dec -> (TH.Name, [TH.Name])+ tfNames (TH.OpenTypeFamilyD (TH.TypeFamilyHead n args _ _))+ = (n, map bndrName args)+ tfNames d = pprPanic "tfNames" (text (show d))++ bndrName :: TH.TyVarBndr flag -> TH.Name+ bndrName (TH.PlainTV n _) = n+ bndrName (TH.KindedTV n _ _) = n++------------------------------+-- | Annotate (with TH.SigT) a type if the first parameter is True+-- and if the type contains a free variable.+-- This is used to annotate type patterns for poly-kinded tyvars in+-- reifying class and type instances.+-- See @Note [Reified instances and explicit kind signatures]@.+annotThType :: Bool -- True <=> annotate+ -> TyCoRep.Type -> TH.Type -> TcM TH.Type+ -- tiny optimization: if the type is annotated, don't annotate again.+annotThType _ _ th_ty@(TH.SigT {}) = return th_ty+annotThType True ty th_ty+ | not $ isEmptyVarSet $ filterVarSet isTyVar $ tyCoVarsOfType ty+ = do { let ki = typeKind ty+ ; th_ki <- reifyKind ki+ ; return (TH.SigT th_ty th_ki) }+annotThType _ _ th_ty = return th_ty++-- | For every argument type that a type constructor accepts,+-- report whether or not the argument is poly-kinded. This is used to+-- eventually feed into 'annotThType'.+-- See @Note [Reified instances and explicit kind signatures]@.+tyConArgsPolyKinded :: TyCon -> [Bool]+tyConArgsPolyKinded tc =+ map (is_poly_ty . tyVarKind) tc_vis_tvs+ -- See "Wrinkle: Oversaturated data family instances" in+ -- @Note [Reified instances and explicit kind signatures]@+ ++ map (is_poly_ty . piTyBinderType) tc_res_kind_vis_bndrs -- (1) in Wrinkle+ ++ repeat True -- (2) in Wrinkle+ where+ is_poly_ty :: Type -> Bool+ is_poly_ty ty = not $+ isEmptyVarSet $+ filterVarSet isTyVar $+ tyCoVarsOfType ty++ tc_vis_tvs :: [TyVar]+ tc_vis_tvs = tyConVisibleTyVars tc++ tc_res_kind_vis_bndrs :: [PiTyBinder]+ tc_res_kind_vis_bndrs = filter isVisiblePiTyBinder $ fst $ splitPiTys $ tyConResKind tc++{-+Note [Reified instances and explicit kind signatures]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Reified class instances and type family instances often include extra kind+information to disambiguate instances. Here is one such example that+illustrates this (#8953):++ type family Poly (a :: k) :: Type+ type instance Poly (x :: Bool) = Int+ type instance Poly (x :: Maybe k) = Double++If you're not careful, reifying these instances might yield this:++ type instance Poly x = Int+ type instance Poly x = Double++To avoid this, we go through some care to annotate things with extra kind+information. Some functions which accomplish this feat include:++* annotThType: This annotates a type with a kind signature if the type contains+ a free variable.+* tyConArgsPolyKinded: This checks every argument that a type constructor can+ accept and reports if the type of the argument is poly-kinded. This+ information is ultimately fed into annotThType.++-----+-- Wrinkle: Oversaturated data family instances+-----++What constitutes an argument to a type constructor in the definition of+tyConArgsPolyKinded? For most type constructors, it's simply the visible+type variable binders (i.e., tyConVisibleTyVars). There is one corner case+we must keep in mind, however: data family instances can appear oversaturated+(#17296). For instance:++ data family Foo :: Type -> Type+ data instance Foo x++ data family Bar :: k+ data family Bar x++For these sorts of data family instances, tyConVisibleTyVars isn't enough,+as they won't give you the kinds of the oversaturated arguments. We must+also consult:++1. The kinds of the arguments in the result kind (i.e., the tyConResKind).+ This will tell us, e.g., the kind of `x` in `Foo x` above.+2. If we go beyond the number of arguments in the result kind (like the+ `x` in `Bar x`), then we conservatively assume that the argument's+ kind is poly-kinded.++-----+-- Wrinkle: data family instances with return kinds+-----++Another squirrelly corner case is this:++ data family Foo (a :: k)+ data instance Foo :: Bool -> Type+ data instance Foo :: Char -> Type++If you're not careful, reifying these instances might yield this:++ data instance Foo+ data instance Foo++We can fix this ambiguity by reifying the instances' explicit return kinds. We+should only do this if necessary (see+Note [When does a tycon application need an explicit kind signature?] in GHC.Core.Type),+but more importantly, we *only* do this if either of the following are true:++1. The data family instance has no constructors.+2. The data family instance is declared with GADT syntax.++If neither of these are true, then reifying the return kind would yield+something like this:++ data instance (Bar a :: Type) = MkBar a++Which is not valid syntax.+-}++------------------------------+reifyClassInstances :: Class -> [ClsInst] -> TcM [TH.Dec]+reifyClassInstances cls insts+ = mapM (reifyClassInstance (tyConArgsPolyKinded (classTyCon cls))) insts++reifyClassInstance :: [Bool] -- True <=> the corresponding tv is poly-kinded+ -- includes only *visible* tvs+ -> ClsInst -> TcM TH.Dec+reifyClassInstance is_poly_tvs i+ = do { cxt <- reifyCxt theta+ ; let vis_types = filterOutInvisibleTypes cls_tc types+ ; thtypes <- reifyTypes vis_types+ ; annot_thtypes <- zipWith3M annotThType is_poly_tvs vis_types thtypes+ ; let head_ty = mkThAppTs (TH.ConT (reifyName cls)) annot_thtypes+ ; return $ (TH.InstanceD over cxt head_ty []) }+ where+ (_tvs, theta, cls, types) = tcSplitDFunTy (idType dfun)+ cls_tc = classTyCon cls+ dfun = instanceDFunId i+ over = case overlapMode (is_flag i) of+ NoOverlap _ -> Nothing+ Overlappable _ -> Just TH.Overlappable+ Overlapping _ -> Just TH.Overlapping+ Overlaps _ -> Just TH.Overlaps+ Incoherent _ -> Just TH.Incoherent++------------------------------+reifyFamilyInstances :: TyCon -> [FamInst] -> TcM [TH.Dec]+reifyFamilyInstances fam_tc fam_insts+ = mapM (reifyFamilyInstance (tyConArgsPolyKinded fam_tc)) fam_insts++reifyFamilyInstance :: [Bool] -- True <=> the corresponding tv is poly-kinded+ -- includes only *visible* tvs+ -> FamInst -> TcM TH.Dec+reifyFamilyInstance is_poly_tvs (FamInst { fi_flavor = flavor+ , fi_axiom = ax+ , fi_fam = fam })+ | let fam_tc = coAxiomTyCon ax+ branch = coAxiomSingleBranch ax+ , CoAxBranch { cab_tvs = tvs, cab_lhs = lhs, cab_rhs = rhs } <- branch+ = case flavor of+ SynFamilyInst ->+ -- remove kind patterns (#8884)+ do { th_tvs <- reifyTyVarsToMaybe tvs+ ; let lhs_types_only = filterOutInvisibleTypes fam_tc lhs+ ; th_lhs <- reifyTypes lhs_types_only+ ; annot_th_lhs <- zipWith3M annotThType is_poly_tvs lhs_types_only+ th_lhs+ ; let lhs_type = mkThAppTs (TH.ConT $ reifyName fam) annot_th_lhs+ ; th_rhs <- reifyType rhs+ ; return (TH.TySynInstD (TH.TySynEqn th_tvs lhs_type th_rhs)) }++ DataFamilyInst rep_tc ->+ do { let -- eta-expand lhs types, because sometimes data/newtype+ -- instances are eta-reduced; See #9692+ -- See Note [Eta reduction for data families] in GHC.Core.Coercion.Axiom+ (ee_tvs, ee_lhs, _) = etaExpandCoAxBranch branch+ fam' = reifyName fam+ dataCons = tyConDataCons rep_tc+ isGadt = isGadtSyntaxTyCon rep_tc+ ; th_tvs <- reifyTyVarsToMaybe ee_tvs+ ; cons <- mapM (reifyDataCon isGadt (mkTyVarTys ee_tvs)) dataCons+ ; let types_only = filterOutInvisibleTypes fam_tc ee_lhs+ ; th_tys <- reifyTypes types_only+ ; annot_th_tys <- zipWith3M annotThType is_poly_tvs types_only th_tys+ ; let lhs_type = mkThAppTs (TH.ConT fam') annot_th_tys+ ; mb_sig <-+ -- See "Wrinkle: data family instances with return kinds" in+ -- Note [Reified instances and explicit kind signatures]+ if (null cons || isGadtSyntaxTyCon rep_tc)+ && tyConAppNeedsKindSig False fam_tc (length ee_lhs)+ then do { let full_kind = typeKind (mkTyConApp fam_tc ee_lhs)+ ; th_full_kind <- reifyKind full_kind+ ; pure $ Just th_full_kind }+ else pure Nothing+ ; return $+ if isNewTyCon rep_tc+ then TH.NewtypeInstD [] th_tvs lhs_type mb_sig (head cons) []+ else TH.DataInstD [] th_tvs lhs_type mb_sig cons []+ }++------------------------------+reifyType :: TyCoRep.Type -> TcM TH.Type+-- Monadic only because of failure+reifyType ty | tcIsLiftedTypeKind ty = return TH.StarT+ -- Make sure to use tcIsLiftedTypeKind here, since we don't want to confuse it+ -- with Constraint (#14869).+reifyType ty@(ForAllTy (Bndr _ argf) _)+ = reify_for_all argf ty+reifyType (LitTy t) = do { r <- reifyTyLit t; return (TH.LitT r) }+reifyType (TyVarTy tv) = return (TH.VarT (reifyName tv))+reifyType (TyConApp tc tys) = reify_tc_app tc tys -- Do not expand type synonyms here+reifyType ty@(AppTy {}) = do+ let (ty_head, ty_args) = splitAppTys ty+ ty_head' <- reifyType ty_head+ ty_args' <- reifyTypes (filter_out_invisible_args ty_head ty_args)+ pure $ mkThAppTs ty_head' ty_args'+ where+ -- Make sure to filter out any invisible arguments. For instance, if you+ -- reify the following:+ --+ -- newtype T (f :: forall a. a -> Type) = MkT (f Bool)+ --+ -- Then you should receive back `f Bool`, not `f Type Bool`, since the+ -- `Type` argument is invisible (#15792).+ filter_out_invisible_args :: Type -> [Type] -> [Type]+ filter_out_invisible_args ty_head ty_args =+ filterByList (map isVisibleForAllTyFlag $ appTyForAllTyFlags ty_head ty_args)+ ty_args+reifyType ty@(FunTy { ft_af = af, ft_mult = ManyTy, ft_arg = t1, ft_res = t2 })+ | isInvisibleFunArg af = reify_for_all Inferred ty -- Types like ((?x::Int) => Char -> Char)+ | otherwise = do { [r1,r2] <- reifyTypes [t1,t2]+ ; return (TH.ArrowT `TH.AppT` r1 `TH.AppT` r2) }+reifyType ty@(FunTy { ft_af = af, ft_mult = tm, ft_arg = t1, ft_res = t2 })+ | isInvisibleFunArg af = noTH LinearInvisibleArgument ty+ | otherwise = do { [rm,r1,r2] <- reifyTypes [tm,t1,t2]+ ; return (TH.MulArrowT `TH.AppT` rm `TH.AppT` r1 `TH.AppT` r2) }+reifyType (CastTy t _) = reifyType t -- Casts are ignored in TH+reifyType ty@(CoercionTy {})= noTH CoercionsInTypes ty++reify_for_all :: TyCoRep.ForAllTyFlag -> TyCoRep.Type -> TcM TH.Type+-- Arg of reify_for_all is always ForAllTy or a predicate FunTy+reify_for_all argf ty+ | isVisibleForAllTyFlag argf+ = do let (req_bndrs, phi) = tcSplitForAllReqTVBinders ty+ tvbndrs' <- reifyTyVarBndrs req_bndrs+ phi' <- reifyType phi+ pure $ TH.ForallVisT tvbndrs' phi'+ | otherwise+ = do let (inv_bndrs, phi) = tcSplitForAllInvisTVBinders ty+ tvbndrs' <- reifyTyVarBndrs inv_bndrs+ let (cxt, tau) = tcSplitPhiTy phi+ cxt' <- reifyCxt cxt+ tau' <- reifyType tau+ pure $ TH.ForallT tvbndrs' cxt' tau'++reifyTyLit :: TyCoRep.TyLit -> TcM TH.TyLit+reifyTyLit (NumTyLit n) = return (TH.NumTyLit n)+reifyTyLit (StrTyLit s) = return (TH.StrTyLit (unpackFS s))+reifyTyLit (CharTyLit c) = return (TH.CharTyLit c)++reifyTypes :: [Type] -> TcM [TH.Type]+reifyTypes = mapM reifyType++reifyPatSynType+ :: ([InvisTVBinder], ThetaType, [InvisTVBinder], ThetaType, [Scaled Type], Type) -> TcM TH.Type+-- reifies a pattern synonym's type and returns its *complete* type+-- signature; see Note [Pattern synonym type signatures and Template+-- Haskell] in GHC.ThToHs+reifyPatSynType (univTyVars, req, exTyVars, prov, argTys, resTy)+ = do { univTyVars' <- reifyTyVarBndrs univTyVars+ ; req' <- reifyCxt req+ ; exTyVars' <- reifyTyVarBndrs exTyVars+ ; prov' <- reifyCxt prov+ ; tau' <- reifyType (mkScaledFunTys argTys resTy)+ ; return $ TH.ForallT univTyVars' req'+ $ TH.ForallT exTyVars' prov' tau' }++reifyKind :: Kind -> TcM TH.Kind+reifyKind = reifyType++reifyCxt :: [PredType] -> TcM [TH.Pred]+reifyCxt = mapM reifyType++reifyFunDep :: ([TyVar], [TyVar]) -> TH.FunDep+reifyFunDep (xs, ys) = TH.FunDep (map reifyName xs) (map reifyName ys)++class ReifyFlag flag flag' | flag -> flag' where+ reifyFlag :: flag -> flag'++instance ReifyFlag () () where+ reifyFlag () = ()++instance ReifyFlag Specificity TH.Specificity where+ reifyFlag SpecifiedSpec = TH.SpecifiedSpec+ reifyFlag InferredSpec = TH.InferredSpec++reifyTyVars :: [TyVar] -> TcM [TH.TyVarBndr ()]+reifyTyVars = reifyTyVarBndrs . map mk_bndr+ where+ mk_bndr tv = Bndr tv ()++reifyTyVarBndrs :: ReifyFlag flag flag'+ => [VarBndr TyVar flag] -> TcM [TH.TyVarBndr flag']+reifyTyVarBndrs = mapM reify_tvbndr+ where+ -- even if the kind is *, we need to include a kind annotation,+ -- in case a poly-kind would be inferred without the annotation.+ -- See #8953 or test th/T8953+ reify_tvbndr (Bndr tv fl) = TH.KindedTV (reifyName tv)+ (reifyFlag fl)+ <$> reifyKind (tyVarKind tv)++reifyTyVarsToMaybe :: [TyVar] -> TcM (Maybe [TH.TyVarBndr ()])+reifyTyVarsToMaybe [] = pure Nothing+reifyTyVarsToMaybe tys = Just <$> reifyTyVars tys++reify_tc_app :: TyCon -> [Type.Type] -> TcM TH.Type+reify_tc_app tc tys+ = do { tys' <- reifyTypes (filterOutInvisibleTypes tc tys)+ ; maybe_sig_t (mkThAppTs r_tc tys') }+ where+ arity = tyConArity tc++ r_tc | isUnboxedSumTyCon tc = TH.UnboxedSumT (arity `div` 2)+ | isUnboxedTupleTyCon tc = TH.UnboxedTupleT (arity `div` 2)+ | isPromotedTupleTyCon tc = TH.PromotedTupleT (arity `div` 2)+ -- See Note [Unboxed tuple RuntimeRep vars] in GHC.Core.TyCon+ | isTupleTyCon tc = if isPromotedDataCon tc+ then TH.PromotedTupleT arity+ else TH.TupleT arity+ | tc `hasKey` constraintKindTyConKey+ = TH.ConstraintT+ | tc `hasKey` unrestrictedFunTyConKey = TH.ArrowT+ | tc `hasKey` listTyConKey = TH.ListT+ | tc `hasKey` nilDataConKey = TH.PromotedNilT+ | tc `hasKey` consDataConKey = TH.PromotedConsT+ | tc `hasKey` heqTyConKey = TH.EqualityT+ | tc `hasKey` eqPrimTyConKey = TH.EqualityT+ | tc `hasKey` eqReprPrimTyConKey = TH.ConT (reifyName coercibleTyCon)+ | isDataKindsPromotedDataCon tc = TH.PromotedT (reifyName tc)+ | otherwise = TH.ConT (reifyName tc)++ -- See Note [When does a tycon application need an explicit kind+ -- signature?] in GHC.Core.TyCo.Rep+ maybe_sig_t th_type+ | tyConAppNeedsKindSig+ False -- We don't reify types using visible kind applications, so+ -- don't count specified binders as contributing towards+ -- injective positions in the kind of the tycon.+ tc (length tys)+ = do { let full_kind = typeKind (mkTyConApp tc tys)+ ; th_full_kind <- reifyKind full_kind+ ; return (TH.SigT th_type th_full_kind) }+ | otherwise+ = return th_type++------------------------------+reifyName :: NamedThing n => n -> TH.Name+reifyName thing+ | isExternalName name+ = mk_varg pkg_str mod_str occ_str+ | otherwise = TH.mkNameU occ_str (toInteger $ getKey (getUnique name))+ -- Many of the things we reify have local bindings, and+ -- NameL's aren't supposed to appear in binding positions, so+ -- we use NameU. When/if we start to reify nested things, that+ -- have free variables, we may need to generate NameL's for them.+ where+ name = getName thing+ mod = assert (isExternalName name) $ nameModule name+ pkg_str = unitString (moduleUnit mod)+ mod_str = moduleNameString (moduleName mod)+ occ_str = occNameString occ+ occ = nameOccName name+ mk_varg | OccName.isDataOcc occ = TH.mkNameG_d+ | OccName.isVarOcc occ = TH.mkNameG_v+ | OccName.isTcOcc occ = TH.mkNameG_tc+ | otherwise = pprPanic "reifyName" (ppr name)++-- See Note [Reifying field labels]+reifyFieldLabel :: FieldLabel -> TH.Name+reifyFieldLabel fl+ | flIsOverloaded fl+ = TH.Name (TH.mkOccName occ_str) (TH.NameQ (TH.mkModName mod_str))+ | otherwise = TH.mkNameG_v pkg_str mod_str occ_str+ where+ name = flSelector fl+ mod = assert (isExternalName name) $ nameModule name+ pkg_str = unitString (moduleUnit mod)+ mod_str = moduleNameString (moduleName mod)+ occ_str = unpackFS (field_label $ flLabel fl)++reifySelector :: Id -> TyCon -> TH.Name+reifySelector id tc+ = case find ((idName id ==) . flSelector) (tyConFieldLabels tc) of+ Just fl -> reifyFieldLabel fl+ Nothing -> pprPanic "reifySelector: missing field" (ppr id $$ ppr tc)++------------------------------+reifyFixity :: Name -> TcM (Maybe TH.Fixity)+reifyFixity name+ = do { (found, fix) <- lookupFixityRn_help name+ ; return (if found then Just (conv_fix fix) else Nothing) }+ where+ conv_fix (Hs.Fixity _ i d) = TH.Fixity i (conv_dir d)+ conv_dir Hs.InfixR = TH.InfixR+ conv_dir Hs.InfixL = TH.InfixL+ conv_dir Hs.InfixN = TH.InfixN++reifyUnpackedness :: DataCon.SrcUnpackedness -> TH.SourceUnpackedness+reifyUnpackedness NoSrcUnpack = TH.NoSourceUnpackedness+reifyUnpackedness SrcNoUnpack = TH.SourceNoUnpack+reifyUnpackedness SrcUnpack = TH.SourceUnpack++reifyStrictness :: DataCon.SrcStrictness -> TH.SourceStrictness+reifyStrictness NoSrcStrict = TH.NoSourceStrictness+reifyStrictness SrcStrict = TH.SourceStrict+reifyStrictness SrcLazy = TH.SourceLazy++reifySourceBang :: DataCon.HsSrcBang+ -> (TH.SourceUnpackedness, TH.SourceStrictness)+reifySourceBang (HsSrcBang _ u s) = (reifyUnpackedness u, reifyStrictness s)++reifyDecidedStrictness :: DataCon.HsImplBang -> TH.DecidedStrictness+reifyDecidedStrictness HsLazy = TH.DecidedLazy+reifyDecidedStrictness (HsStrict _) = TH.DecidedStrict+reifyDecidedStrictness HsUnpack{} = TH.DecidedUnpack++reifyTypeOfThing :: TH.Name -> TcM TH.Type+reifyTypeOfThing th_name = do+ thing <- getThing th_name+ case thing of+ AGlobal (AnId id) -> reifyType (idType id)+ AGlobal (ATyCon tc) -> reifyKind (tyConKind tc)+ AGlobal (AConLike (RealDataCon dc)) ->+ reifyType (idType (dataConWrapId dc))+ AGlobal (AConLike (PatSynCon ps)) ->+ reifyPatSynType (patSynSigBndr ps)+ ATcId{tct_id = id} -> zonkTcType (idType id) >>= reifyType+ ATyVar _ tctv -> zonkTcTyVar tctv >>= reifyType+ -- Impossible cases, supposedly:+ AGlobal (ACoAxiom _) -> panic "reifyTypeOfThing: ACoAxiom"+ ATcTyCon _ -> panic "reifyTypeOfThing: ATcTyCon"+ APromotionErr _ -> panic "reifyTypeOfThing: APromotionErr"++------------------------------+lookupThAnnLookup :: TH.AnnLookup -> TcM CoreAnnTarget+lookupThAnnLookup (TH.AnnLookupName th_nm) = fmap NamedTarget (lookupThName th_nm)+lookupThAnnLookup (TH.AnnLookupModule (TH.Module pn mn))+ = return $ ModuleTarget $+ mkModule (stringToUnit $ TH.pkgString pn) (mkModuleName $ TH.modString mn)++reifyAnnotations :: Data a => TH.AnnLookup -> TcM [a]+reifyAnnotations th_name+ = do { name <- lookupThAnnLookup th_name+ ; topEnv <- getTopEnv+ ; epsHptAnns <- liftIO $ prepareAnnotations topEnv Nothing+ ; tcg <- getGblEnv+ ; let selectedEpsHptAnns = findAnns deserializeWithData epsHptAnns name+ ; let selectedTcgAnns = findAnns deserializeWithData (tcg_ann_env tcg) name+ ; return (selectedEpsHptAnns ++ selectedTcgAnns) }++------------------------------+modToTHMod :: Module -> TH.Module+modToTHMod m = TH.Module (TH.PkgName $ unitString $ moduleUnit m)+ (TH.ModName $ moduleNameString $ moduleName m)++reifyModule :: TH.Module -> TcM TH.ModuleInfo+reifyModule (TH.Module (TH.PkgName pkgString) (TH.ModName mString)) = do+ this_mod <- getModule+ let reifMod = mkModule (stringToUnit pkgString) (mkModuleName mString)+ if (reifMod == this_mod) then reifyThisModule else reifyFromIface reifMod+ where+ reifyThisModule = do+ usages <- fmap (map modToTHMod . moduleEnvKeys . imp_mods) getImports+ return $ TH.ModuleInfo usages++ reifyFromIface reifMod = do+ iface <- loadInterfaceForModule (text "reifying module from TH for" <+> ppr reifMod) reifMod+ let usages = [modToTHMod m | usage <- mi_usages iface,+ Just m <- [usageToModule (moduleUnit reifMod) usage] ]+ return $ TH.ModuleInfo usages++ usageToModule :: Unit -> Usage -> Maybe Module+ usageToModule _ (UsageFile {}) = Nothing+ usageToModule this_pkg (UsageHomeModule { usg_mod_name = mn }) = Just $ mkModule this_pkg mn+ usageToModule _ (UsagePackageModule { usg_mod = m }) = Just m+ usageToModule _ (UsageMergedRequirement { usg_mod = m }) = Just m+ usageToModule this_pkg (UsageHomeModuleInterface { usg_mod_name = mn }) = Just $ mkModule this_pkg mn++------------------------------+mkThAppTs :: TH.Type -> [TH.Type] -> TH.Type+mkThAppTs fun_ty arg_tys = foldl' TH.AppT fun_ty arg_tys++noTH :: UnrepresentableTypeDescr -> Type -> TcM a+noTH s d = failWithTc $ TcRnCannotRepresentType s d ppr_th :: TH.Ppr a => a -> SDoc ppr_th x = text (TH.pprint x)
compiler/GHC/Tc/Gen/Splice.hs-boot view
@@ -10,13 +10,13 @@ import GHC.Types.Annotations ( Annotation, CoreAnnTarget ) import GHC.Hs.Extension ( GhcRn, GhcPs, GhcTc ) -import GHC.Hs ( HsSplice, HsQuote, HsExpr, LHsExpr, LHsType,- LPat, LHsDecl, ThModFinalizers )+import GHC.Hs ( HsQuote, HsExpr, LHsExpr, LHsType, LPat, LHsDecl, ThModFinalizers ) import qualified Language.Haskell.TH as TH -tcSpliceExpr :: HsSplice GhcRn- -> ExpRhoType- -> TcM (HsExpr GhcTc)+tcTypedSplice :: Name+ -> LHsExpr GhcRn+ -> ExpRhoType+ -> TcM (HsExpr GhcTc) tcTypedBracket :: HsExpr GhcRn -> LHsExpr GhcRn
compiler/GHC/Tc/Instance/Class.hs view
@@ -28,14 +28,17 @@ import GHC.Builtin.Types.Prim import GHC.Builtin.Names +import GHC.Types.FieldLabel import GHC.Types.Name.Reader( lookupGRE_FieldLabel, greMangledName ) import GHC.Types.SafeHaskell import GHC.Types.Name ( Name, pprDefinedAt ) import GHC.Types.Var.Env ( VarEnv ) import GHC.Types.Id+import GHC.Types.Id.Make ( nospecId ) import GHC.Types.Var import GHC.Core.Predicate+import GHC.Core.Coercion import GHC.Core.InstEnv import GHC.Core.Type import GHC.Core.Make ( mkCharExpr, mkNaturalExpr, mkStringExprFS, mkCoreLams )@@ -43,15 +46,14 @@ import GHC.Core.TyCon import GHC.Core.Class -import GHC.Core ( Expr(Var, App, Cast, Let), Bind (NonRec) )-import GHC.Types.Basic+import GHC.Core ( Expr(Var, App, Cast, Type) ) import GHC.Utils.Outputable import GHC.Utils.Panic import GHC.Utils.Misc( splitAtList, fstOf3 ) import GHC.Data.FastString -import Data.Maybe+import Language.Haskell.Syntax.Basic (FieldLabelString(..)) {- ******************************************************************* * *@@ -151,20 +153,17 @@ -- See Note [Shortcut solving: overlap] -> Class -> [Type] -> TcM ClsInstResult matchGlobalInst dflags short_cut clas tys- | cls_name == knownNatClassName- = matchKnownNat dflags short_cut clas tys- | cls_name == knownSymbolClassName- = matchKnownSymbol dflags short_cut clas tys- | cls_name == knownCharClassName- = matchKnownChar dflags short_cut clas tys- | isCTupleClass clas = matchCTuple clas tys- | cls_name == typeableClassName = matchTypeable clas tys- | cls_name == withDictClassName = matchWithDict tys- | clas `hasKey` heqTyConKey = matchHeteroEquality tys- | clas `hasKey` eqTyConKey = matchHomoEquality tys- | clas `hasKey` coercibleTyConKey = matchCoercible tys- | cls_name == hasFieldClassName = matchHasField dflags short_cut clas tys- | otherwise = matchInstEnv dflags short_cut clas tys+ | cls_name == knownNatClassName = matchKnownNat dflags short_cut clas tys+ | cls_name == knownSymbolClassName = matchKnownSymbol dflags short_cut clas tys+ | cls_name == knownCharClassName = matchKnownChar dflags short_cut clas tys+ | isCTupleClass clas = matchCTuple clas tys+ | cls_name == typeableClassName = matchTypeable clas tys+ | cls_name == withDictClassName = matchWithDict tys+ | clas `hasKey` heqTyConKey = matchHeteroEquality tys+ | clas `hasKey` eqTyConKey = matchHomoEquality tys+ | clas `hasKey` coercibleTyConKey = matchCoercible tys+ | cls_name == hasFieldClassName = matchHasField dflags short_cut clas tys+ | otherwise = matchInstEnv dflags short_cut clas tys where cls_name = className clas @@ -190,7 +189,7 @@ -- Nothing matches ([], NoUnifiers, _)- -> do { traceTc "matchClass not matching" (ppr pred)+ -> do { traceTc "matchClass not matching" (ppr pred $$ ppr (ie_local instEnvs)) ; return NoInstance } -- A single match (& no safe haskell failure)@@ -424,7 +423,7 @@ -- then tcRep is SNat , Just (_, co_rep) <- tcInstNewTyCon_maybe tcRep [ty] -- SNat n ~ Integer- , let ev_tm = mkEvCast et (mkTcSymCo (mkTcTransCo co_dict co_rep))+ , let ev_tm = mkEvCast et (mkSymCo (mkTransCo co_dict co_rep)) = return $ OneInst { cir_new_theta = [] , cir_mk_ev = \_ -> ev_tm , cir_what = BuiltinInstance }@@ -451,25 +450,29 @@ -- and in that case let -- co :: C t1 ..tn ~R# inst_meth_ty , Just (inst_meth_ty, co) <- tcInstNewTyCon_maybe dict_tc dict_args- = do { sv <- mkSysLocalM (fsLit "withDict_s") Many mty- ; k <- mkSysLocalM (fsLit "withDict_k") Many (mkInvisFunTyMany cls openAlphaTy)-- ; let evWithDict_type = mkSpecForAllTys [runtimeRep1TyVar, openAlphaTyVar] $- mkVisFunTysMany [mty, mkInvisFunTyMany cls openAlphaTy] openAlphaTy-- ; wd_id <- mkSysLocalM (fsLit "withDict_wd") Many evWithDict_type- ; let wd_id' = wd_id `setInlinePragma` neverInlinePragma- -- Inlining withDict can cause the specialiser to incorrectly common up- -- distinct evidence terms. See (WD6) in Note [withDict].+ = do { sv <- mkSysLocalM (fsLit "withDict_s") ManyTy mty+ ; k <- mkSysLocalM (fsLit "withDict_k") ManyTy (mkInvisFunTy cls openAlphaTy) -- Given co2 : mty ~N# inst_meth_ty, construct the method of -- the WithDict dictionary:- -- \@(r : RuntimeRep) @(a :: TYPE r) (sv : mty) (k :: cls => a) -> k (sv |> (sub co; sym co2))+ --+ -- \@(r :: RuntimeRep) @(a :: TYPE r) (sv :: mty) (k :: cls => a) ->+ -- nospec @(cls => a) k (sv |> (sub co ; sym co2))+ --+ -- where nospec :: forall a. a -> a ensures that the typeclass specialiser+ -- doesn't attempt to common up this evidence term with other evidence terms+ -- of the same type.+ --+ -- See (WD6) in Note [withDict], and Note [nospecId magic] in GHC.Types.Id.Make. ; let evWithDict co2 =- let wd_rhs = mkCoreLams [ runtimeRep1TyVar, openAlphaTyVar, sv, k ] $- Var k `App` Cast (Var sv) (mkTcTransCo (mkTcSubCo co2) (mkTcSymCo co))- in Let (NonRec wd_id' wd_rhs) (Var wd_id')- -- Why a Let? See (WD6) in Note [withDict]+ mkCoreLams [ runtimeRep1TyVar, openAlphaTyVar, sv, k ] $+ Var nospecId+ `App`+ (Type $ mkInvisFunTy cls openAlphaTy)+ `App`+ Var k+ `App`+ (Var sv `Cast` mkTransCo (mkSubCo co2) (mkSymCo co)) ; tc <- tcLookupTyCon withDictClassName ; let Just withdict_data_con@@ -531,7 +534,7 @@ instance (mty ~# inst_meth_ty) => WithDict (C t1..tn) mty where withDict = \@{rr} @(r :: TYPE rr) (sv :: mty) (k :: C t1..tn => r) ->- k (sv |> (sub co2; sym co))+ k (sv |> (sub co2 ; sym co)) That is, it matches on the first (constraint) argument of C; if C is a single-method class, the instance "fires" and emits an equality@@ -564,50 +567,69 @@ (WD3) As an alternative to `withDict`, one could define functions like `withT` above in terms of `unsafeCoerce`. This is more error-prone, however. -(WD4) In order to define things like `reifySymbol` below:+(WD4) In order to define things like `withKnownNat` below: - reifySymbol :: forall r. String -> (forall (n :: Symbol). KnownSymbol n => r) -> r+ withKnownNat :: SNat n -> (KnownNat n => r) -> r `withDict` needs to be instantiated with `Any`, like so: - reifySymbol n k = withDict @(KnownSymbol Any) @String @r n (k @Any)+ withKnownNat = withDict @(KnownNat Any) @(SNat Any) @r - The use of `Any` is explained in Note [NOINLINE someNatVal] in+ The use of `Any` is explained in Note [NOINLINE withSomeSNat] in base:GHC.TypeNats. (WD5) In earlier implementations, `withDict` was implemented as an identifier with special handling during either constant-folding or desugaring.- The current approach is more robust, previously the type of `withDict`+ The current approach is more robust: previously, the type of `withDict` did not have a type-class constraint and was overly polymorphic. See #19915. -(WD6) In fact we desugar `withDict @(C t_1 ... t_n) @mty @{rr} @r` to+(WD6) In fact, we desugar `withDict @cls @mty @{rr} @r` to - let wd = \sv k -> k (sv |> co)- {-# NOINLINE wd #-}- in wd+ \@(r :: RuntimeRep) @(a :: TYPE r) (sv :: mty) (k :: cls => a) ->+ nospec @(cls => a) k (sv |> (sub co2 ; sym co))) - The local `let` and NOINLINE pragma ensure that the type-class specialiser- doesn't wrongly common up distinct evidence terms. This is super important!- Suppose we have calls+ That is, we cast the method using a coercion, and apply k to it.+ However, we use the 'nospec' magicId (see Note [nospecId magic] in GHC.Types.Id.Make)+ to ensure that the typeclass specialiser doesn't incorrectly common-up distinct+ evidence terms. This is super important! Suppose we have calls+ withDict A k withDict B k- where k1, k2 :: C T -> blah. If we inline those withDict calls we'll get++ where k1, k2 :: C T -> blah. If we desugared withDict naively, we'd get+ k (A |> co1) k (B |> co2)- and the Specialiser will assume that those arguments (of type `C T`) are- the same, will specialise `k` for that type, and will call the same,++ and the Specialiser would assume that those arguments (of type `C T`) are+ the same. It would then specialise `k` for that type, and then call the same, specialised function from both call sites. #21575 is a concrete case in point. - Solution: never inline `withDict`. Note that it is not sufficient to delay- inlining until after the specialiser (that is, until Phase 2), because if- we inline withDict in module A but import it in module B, the specialiser- will try to common up the two distinct evidence terms.- See test case T21575b.+ To avoid this, we need to stop the typeclass specialiser from seeing this+ structure, by using nospec. This function is inlined only in CorePrep; crucially+ this means that it still appears in interface files, so that the desugaring of+ withDict remains opaque to the typeclass specialiser across modules.+ This means the specialiser will always see instead: - This solution is unsatisfactory, as it imposes a performance overhead- on uses of withDict.+ nospec @(cls => a) k (A |> co1)+ nospec @(cls => a) k (B |> co2) + Why does this work? Recall that nospec is not an overloaded function;+ it has the type++ nospec :: forall a. a -> a++ This means that there is nothing for the specialiser to do with function calls+ such as++ nospec @(cls => a) k (A |> co)++ as the specialiser only looks at calls of the form `f dict` for an+ overloaded function `f` (e.g. with a type such as `f :: Eq a => ...`).++ See test-case T21575b.+ -} {- ********************************************************************@@ -620,18 +642,29 @@ -- and it was applied to the correct argument. matchTypeable :: Class -> [Type] -> TcM ClsInstResult matchTypeable clas [k,t] -- clas = Typeable- -- For the first two cases, See Note [No Typeable for polytypes or qualified types]- | isForAllTy k = return NoInstance -- Polytype- | isJust (tcSplitPredFunTy_maybe t) = return NoInstance -- Qualified type+ -- Forall types: see Note [No Typeable for polytypes or qualified types]+ | isForAllTy k = return NoInstance + -- Functions; but only with a visible argment+ | Just (af,mult,arg,ret) <- splitFunTy_maybe t+ = if isVisibleFunArg af+ then doFunTy clas t mult arg ret+ else return NoInstance+ -- 'else' case: qualified types like (Num a => blah) are not typeable+ -- see Note [No Typeable for polytypes or qualified types]+ -- Now cases that do work- | k `eqType` naturalTy = doTyLit knownNatClassName t- | k `eqType` typeSymbolKind = doTyLit knownSymbolClassName t- | k `eqType` charTy = doTyLit knownCharClassName t- | tcIsConstraintKind t = doTyConApp clas t constraintKindTyCon []- | Just (mult,arg,ret) <- splitFunTy_maybe t = doFunTy clas t mult arg ret+ | k `eqType` naturalTy = doTyLit knownNatClassName t+ | k `eqType` typeSymbolKind = doTyLit knownSymbolClassName t+ | k `eqType` charTy = doTyLit knownCharClassName t++ -- TyCon applied to its kind args+ -- No special treatment of Type and Constraint; they get distinct TypeReps+ -- see wrinkle (W4) of Note [Type and Constraint are not apart]+ -- in GHC.Builtin.Types.Prim. | Just (tc, ks) <- splitTyConApp_maybe t -- See Note [Typeable (T a b c)] , onlyNamedBndrsApplied tc ks = doTyConApp clas t tc ks+ | Just (f,kt) <- splitAppTy_maybe t = doTyApp clas t f kt matchTypeable _ _ = return NoInstance@@ -655,10 +688,9 @@ doTyConApp :: Class -> Type -> TyCon -> [Kind] -> TcM ClsInstResult doTyConApp clas ty tc kind_args | tyConIsTypeable tc- = do- return $ OneInst { cir_new_theta = (map (mk_typeable_pred clas) kind_args)- , cir_mk_ev = mk_ev- , cir_what = BuiltinTypeableInstance tc }+ = return $ OneInst { cir_new_theta = map (mk_typeable_pred clas) kind_args+ , cir_mk_ev = mk_ev+ , cir_what = BuiltinTypeableInstance tc } | otherwise = return NoInstance where@@ -684,7 +716,7 @@ -- (Typeable f, Typeable Int, Typeable Char) --> (after some simp. steps) -- Typeable f doTyApp clas ty f tk- | isForAllTy (tcTypeKind f)+ | isForAllTy (typeKind f) = return NoInstance -- We can't solve until we know the ctr. | otherwise = return $ OneInst { cir_new_theta = map (mk_typeable_pred clas) [f, tk]@@ -697,7 +729,7 @@ -- Emit a `Typeable` constraint for the given type. mk_typeable_pred :: Class -> Type -> PredType-mk_typeable_pred clas ty = mkClassPred clas [ tcTypeKind ty, ty ]+mk_typeable_pred clas ty = mkClassPred clas [ typeKind ty, ty ] -- Typeable is implied by KnownNat/KnownSymbol. In the case of a type literal -- we generate a sub-goal for the appropriate class.@@ -713,15 +745,32 @@ {- Note [Typeable (T a b c)] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ For type applications we always decompose using binary application,-via doTyApp, until we get to a *kind* instantiation. Example- Proxy :: forall k. k -> *+via doTyApp (building a TrApp), until we get to a *kind* instantiation+(building a TrTyCon). We detect a pure kind instantiation using+`onlyNamedBndrsApplied`. -To solve Typeable (Proxy (* -> *) Maybe) we- - First decompose with doTyApp,- to get (Typeable (Proxy (* -> *))) and Typeable Maybe- - Then solve (Typeable (Proxy (* -> *))) with doTyConApp+Example: Proxy :: forall k. k -> * + To solve Typeable (Proxy @(* -> *) Maybe) we++ - First decompose with doTyApp (onlyNamedBndrsApplied is False)+ to get (Typeable (Proxy @(* -> *))) and Typeable Maybe.+ This step returns a TrApp.++ - Then solve (Typeable (Proxy @(* -> *))) with doTyConApp+ (onlyNamedBndrsApplied is True).+ This step returns a TrTyCon++ So the TypeRep we build is+ TrApp (TrTyCon ("Proxy" @(*->*))) (TrTyCon "Maybe")++Notice also that TYPE and CONSTRAINT are distinct so, in effect, we+allow (Typeable TYPE) and (Typeable CONSTRAINT), giving disinct TypeReps.+This is very important: we may want to get a TypeRep for a kind like+ Type -> Constraint+ If we attempt to short-cut by solving it all at once, via doTyConApp @@ -894,7 +943,7 @@ -- use representation tycon (if data family); it has the fields , let r_tc = fstOf3 (tcLookupDataFamInst fam_inst_envs tc args) -- x should be a field of r- , Just fl <- lookupTyConFieldLabel x r_tc+ , Just fl <- lookupTyConFieldLabel (FieldLabelString x) r_tc -- the field selector should be in scope , Just gre <- lookupGRE_FieldLabel rdr_env fl @@ -913,8 +962,8 @@ -- it to a HasField dictionary. mk_ev (ev1:evs) = evSelector sel_id tvs evs `evCast` co where- co = mkTcSubCo (evTermCoercion (EvExpr ev1))- `mkTcTransCo` mkTcSymCo co2+ co = mkSubCo (evTermCoercion (EvExpr ev1))+ `mkTransCo` mkSymCo co2 mk_ev [] = panic "matchHasField.mk_ev" Just (_, co2) = tcInstNewTyCon_maybe (classTyCon clas)
compiler/GHC/Tc/Instance/Family.hs view
@@ -364,7 +364,7 @@ -- We could, but doing so means one of two things: -- -- 1. When looping over the cartesian product we convert- -- a set into a non-deterministicly ordered list. Which+ -- a set into a non-deterministically ordered list. Which -- happens to be fine for interface file determinism -- in this case, today, because the order only -- determines the order of deferred checks. But such@@ -519,16 +519,16 @@ = Nothing -- | 'tcTopNormaliseNewTypeTF_maybe' gets rid of top-level newtypes,--- potentially looking through newtype /instances/.+-- potentially looking through newtype /instances/ and type synonyms. -- -- It is only used by the type inference engine (specifically, when -- solving representational equality), and hence it is careful to unwrap -- only if the relevant data constructor is in scope. That's why -- it gets a GlobalRdrEnv argument. ----- It is careful not to unwrap data/newtype instances if it can't--- continue unwrapping. Such care is necessary for proper error--- messages.+-- It is careful not to unwrap data/newtype instances nor synonyms+-- if it can't continue unwrapping. Such care is necessary for proper+-- error messages. -- -- It does not look through type families. -- It does not normalise arguments to a tycon.@@ -558,8 +558,7 @@ -- which would lead to terrible error messages unwrap_newtype_instance rec_nts tc tys | Just (tc', tys', co) <- tcLookupDataFamInst_maybe faminsts tc tys- = mapStepResult (\(gres, co1) -> (gres, co `mkTransCo` co1)) $- unwrap_newtype rec_nts tc' tys'+ = fmap (mkTransCo co) <$> unwrap_newtype rec_nts tc' tys' | otherwise = NS_Done unwrap_newtype rec_nts tc tys@@ -567,8 +566,7 @@ , Just gre <- lookupGRE_Name rdr_env (dataConName con) -- This is where we check that the -- data constructor is in scope- = mapStepResult (\co -> (unitBag gre, co)) $- unwrapNewTypeStepper rec_nts tc tys+ = (,) (unitBag gre) <$> unwrapNewTypeStepper rec_nts tc tys | otherwise = NS_Done
compiler/GHC/Tc/Instance/FunDeps.hs view
@@ -29,14 +29,18 @@ import GHC.Core.Class import GHC.Core.Predicate import GHC.Core.Type-import GHC.Tc.Utils.TcType( transSuperClasses )+import GHC.Core.RoughMap( RoughMatchTc(..) ) import GHC.Core.Coercion.Axiom( TypeEqn ) import GHC.Core.Unify import GHC.Core.InstEnv-import GHC.Types.Var.Set-import GHC.Types.Var.Env import GHC.Core.TyCo.FVs+import GHC.Core.TyCo.Compare( eqTypes, eqType ) import GHC.Core.TyCo.Ppr( pprWithExplicitKindsWhen )++import GHC.Tc.Utils.TcType( transSuperClasses )++import GHC.Types.Var.Set+import GHC.Types.Var.Env import GHC.Types.SrcLoc import GHC.Utils.Outputable@@ -57,7 +61,6 @@ * * ************************************************************************ - Each functional dependency with one variable in the RHS is responsible for generating a single equality. For instance: class C a b | a -> b@@ -83,31 +86,57 @@ INVARIANT: Corresponding types aren't already equal That is, there exists at least one non-identity equality in FDEqs. +Note [Improving against instances]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Assume:- class C a b c | a -> b c- instance C Int x x-And: [Wanted] C Int Bool alpha-We will /match/ the LHS of fundep equations, producing a matching substitution-and create equations for the RHS sides. In our last example we'd have generated:- ({x}, [fd1,fd2])-where- fd1 = FDEq 1 Bool x- fd2 = FDEq 2 alpha x-To ``execute'' the equation, make fresh type variable for each tyvar in the set,-instantiate the two types with these fresh variables, and then unify or generate-a new constraint. In the above example we would generate a new unification-variable 'beta' for x and produce the following constraints:- [Wanted] (Bool ~ beta)- [Wanted] (alpha ~ beta)+ class C a b | a -> b+ instance C Int Bool+ [W] C Int ty -Notice the subtle difference between the above class declaration and:- class C a b c | a -> b, a -> c-where we would generate:- ({x},[fd1]),({x},[fd2])-This means that the template variable would be instantiated to different-unification variables when producing the FD constraints.+Then `improveFromInstEnv` should return a FDEqn with+ FDEqn { fd_qtvs = [], fd_eqs = [Pair Bool ty] } -Finally, the position parameters will help us rewrite the wanted constraint ``on the spot''+describing an equality (Int ~ ty). To do this we /match/ the instance head+against the [W], using just the LHS of the fundep; if we match, we return+an equality for the RHS.++Wrinkles:++(1) meta_tvs: sometimes the instance mentions variables in the RHS that+ are not bound in the LHS. For example++ class C a b | a -> b+ instance C Int (Maybe x)+ [W] C Int ty++ Note that although the `Int` parts match, that does not fix what `x` is.+ So we just make up a fresh unification variable (a meta_tv), to give the+ "shape" of the RHS. So we emit the FDEqun+ FDEqn { fd_qtvs = [x], fd_eqs = [Pair (Maybe x) ty] }++ Note that the fd_qtvs can be free in the /first/ component of the Pair,++ but not in the seconde (which comes from the [W] constraint.++(2) Multi-range fundeps. When these meta_tvs are involved, there is a subtle+ difference between the fundep (a -> b c) and the two fundeps (a->b, a->c).+ Consider+ class D a b c | a -> b c+ instance D Int x (Maybe x)+ [W] D Int Bool ty++ Then we'll generate+ FDEqn { fd_qtvs = [x0], fd_eqs = [ x0 ~ Bool, Maybe x0 ~ ty] }+ which generates one fresh unification variable x0++ But if the fundeps had been (a->b, a->c) we'd generate two FDEqns+ FDEqn { fd_qtvs = [x1], fd_eqs = [ x1 ~ Bool ] }+ FDEqn { fd_qtvs = [x2], fd_eqs = [ Maybe x2 ~ ty ] }+ with two FDEqns, generating two separate unification variables.++(3) improveFromInstEnv doesn't return any equations that already hold.+ Reason: then we know if any actual improvement has happened, in+ which case we need to iterate the solver -} data FunDepEqn loc@@ -116,11 +145,24 @@ -- Non-empty only for FunDepEqns arising from instance decls , fd_eqs :: [TypeEqn] -- Make these pairs of types equal+ -- Invariant: In each (Pair ty1 ty2), the fd_qtvs may be+ -- free in ty1 but not in ty2. See Wrinkle (1) of+ -- Note [Improving against instances]+ , fd_pred1 :: PredType -- The FunDepEqn arose from , fd_pred2 :: PredType -- combining these two constraints , fd_loc :: loc } deriving Functor +instance Outputable (FunDepEqn a) where+ ppr = pprEquation++pprEquation :: FunDepEqn a -> SDoc+pprEquation (FDEqn { fd_qtvs = qtvs, fd_eqs = pairs })+ = vcat [text "forall" <+> braces (pprWithCommas ppr qtvs),+ nest 2 (vcat [ ppr t1 <+> text "~" <+> ppr t2+ | Pair t1 t2 <- pairs])]+ {- Given a bunch of predicates that must hold, such as @@ -195,20 +237,12 @@ -- Improve a class constraint from instance declarations -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -instance Outputable (FunDepEqn a) where- ppr = pprEquation--pprEquation :: FunDepEqn a -> SDoc-pprEquation (FDEqn { fd_qtvs = qtvs, fd_eqs = pairs })- = vcat [text "forall" <+> braces (pprWithCommas ppr qtvs),- nest 2 (vcat [ ppr t1 <+> text "~" <+> ppr t2- | Pair t1 t2 <- pairs])]- improveFromInstEnv :: InstEnvs -> (PredType -> SrcSpan -> loc) -> Class -> [Type] -> [FunDepEqn loc] -- Needs to be a FunDepEqn because -- of quantified variables+-- See Note [Improving against instances] -- Post: Equations oriented from the template (matching instance) to the workitem! improveFromInstEnv inst_env mk_loc cls tys = [ FDEqn { fd_qtvs = meta_tvs, fd_eqs = eqs@@ -232,37 +266,16 @@ rough_tcs = RM_KnownTc (className cls) : roughMatchTcs tys pred = mkClassPred cls tys --- improveClsFD :: [TyVar] -> FunDep TyVar -- One functional dependency from the class -> ClsInst -- An instance template -> [Type] -> [RoughMatchTc] -- Arguments of this (C tys) predicate -> [([TyCoVar], [TypeEqn])] -- Empty or singleton+-- See Note [Improving against instances] improveClsFD clas_tvs fd (ClsInst { is_tvs = qtvs, is_tys = tys_inst, is_tcs = rough_tcs_inst }) tys_actual rough_tcs_actual --- Compare instance {a,b} C sx sp sy sq--- with wanted [W] C tx tp ty tq--- for fundep (x,y -> p,q) from class (C x p y q)--- If (sx,sy) unifies with (tx,ty), take the subst S---- 'qtvs' are the quantified type variables, the ones which can be instantiated--- to make the types match. For example, given--- class C a b | a->b where ...--- instance C (Maybe x) (Tree x) where ..------ and a wanted constraint of form (C (Maybe t1) t2),--- then we will call checkClsFD with------ is_qtvs = {x}, is_tys = [Maybe x, Tree x]--- tys_actual = [Maybe t1, t2]------ We can instantiate x to t1, and then we want to force--- (Tree x) [t1/x] ~ t2- | instanceCantMatch rough_tcs_inst rough_tcs_actual = [] -- Filter out ones that can't possibly match, @@ -271,12 +284,11 @@ equalLength tys_inst clas_tvs) (ppr tys_inst <+> ppr tys_actual) $ - case tcMatchTyKis ltys1 ltys2 of+ case tcMatchTyKisX init_subst ltys1 ltys2 of Nothing -> [] Just subst | isJust (tcMatchTyKisX subst rtys1 rtys2) -- Don't include any equations that already hold.- -- Reason: then we know if any actual improvement has happened,- -- in which case we need to iterate the solver+ -- See Note [Improving against instances] wrinkle (3) -- In making this check we must taking account of the fact that any -- qtvs that aren't already instantiated can be instantiated to anything -- at all@@ -307,7 +319,7 @@ -- executed. What we're doing instead is recording the partial -- work of the ls1/ls2 unification leaving a smaller unification problem where- rtys1' = map (substTyUnchecked subst) rtys1+ rtys1' = map (substTy subst) rtys1 fdeqs = zipAndComputeFDEqs (\_ _ -> False) rtys1' rtys2 -- Don't discard anything!@@ -315,8 +327,10 @@ -- eqType again, since we know for sure that /at least one/ -- equation in there is useful) - meta_tvs = [ setVarType tv (substTyUnchecked subst (varType tv))- | tv <- qtvs, tv `notElemTCvSubst` subst ]+ meta_tvs = [ setVarType tv (substTy subst (varType tv))+ | tv <- qtvs+ , tv `notElemSubst` subst+ , tv `elemVarSet` rtys1_tvs ] -- meta_tvs are the quantified type variables -- that have not been substituted out --@@ -331,11 +345,14 @@ -- type variables' kinds -- (b) we must apply 'subst' to the kinds, in case we have -- matched out a kind variable, but not a type variable- -- whose kind mentions that kind variable!- -- #6015, #6068+ -- whose kind mentions that kind variable! #6015, #6068+ -- (c) no need to include tyvars not in rtys1 where+ init_subst = mkEmptySubst $ mkInScopeSet $+ mkVarSet qtvs `unionVarSet` tyCoVarsOfTypes ltys2 (ltys1, rtys1) = instFD fd clas_tvs tys_inst (ltys2, rtys2) = instFD fd clas_tvs tys_actual+ rtys1_tvs = tyCoVarsOfTypes rtys1 {- %************************************************************************@@ -393,7 +410,7 @@ where (ls,rs) = instFD fd tyvars inst_taus ls_tvs = tyCoVarsOfTypes ls- rs_tvs = splitVisVarsOfTypes rs+ rs_tvs = visVarsOfTypes rs undetermined_tvs | be_liberal = liberal_undet_tvs | otherwise = conserv_undet_tvs@@ -621,7 +638,7 @@ ClsInsts in the environment. The bogus aspect is discussed in #10675. Currently it if the two-types are *contradicatory*, using (isNothing . tcUnifyTys). But all+types are *contradictory*, using (isNothing . tcUnifyTys). But all the papers say we should check if the two types are *equal* thus not (substTys subst rtys1 `eqTypes` substTys subst rtys2) For now I'm leaving the bogus form because that's the way it has
compiler/GHC/Tc/Instance/Typeable.hs view
@@ -13,7 +13,7 @@ import GHC.Prelude import GHC.Platform -import GHC.Types.Basic ( Boxity(..), neverInlinePragma )+import GHC.Types.Basic ( Boxity(..), TypeOrConstraint(..), neverInlinePragma ) import GHC.Types.SourceText ( SourceText(..) ) import GHC.Iface.Env( newGlobalBinder ) import GHC.Core.TyCo.Rep( Type(..), TyLit(..) )@@ -106,7 +106,7 @@ * GHC.Prim doesn't have any associated object code, so we need to put the representations for types defined in this module elsewhere. We chose this place to be GHC.Types. GHC.Tc.Instance.Typeable.mkPrimTypeableBinds is responsible for- injecting the bindings for the GHC.Prim representions when compiling+ injecting the bindings for the GHC.Prim representations when compiling GHC.Types. * TyCon.tyConRepModOcc is responsible for determining where to find@@ -190,7 +190,7 @@ mkModIdBindings = do { mod <- getModule ; loc <- getSrcSpanM- ; mod_nm <- newGlobalBinder mod (mkVarOcc "$trModule") loc+ ; mod_nm <- newGlobalBinder mod (mkVarOccFS (fsLit "$trModule")) loc ; trModuleTyCon <- tcLookupTyCon trModuleTyConName ; let mod_id = mkExportedVanillaId mod_nm (mkTyConApp trModuleTyCon []) ; mod_bind <- mkVarBind mod_id <$> mkModIdRHS mod@@ -330,9 +330,11 @@ -- Build TypeRepTodos for built-in KindReps ; todo1 <- todoForExportedKindReps builtInKindReps+ -- Build TypeRepTodos for types in GHC.Prim ; todo2 <- todoForTyCons gHC_PRIM ghc_prim_module_id ghcPrimTypeableTyCons+ ; tcg_env <- getGblEnv ; let mod_id = case tcg_tr_module tcg_env of -- Should be set by now Just mod_id -> mod_id@@ -419,7 +421,7 @@ -> TypeableTyCon -> KindRepM (LHsBinds GhcTc) mkTyConRepBinds stuff todo (TypeableTyCon {..}) = do -- Make a KindRep- let (bndrs, kind) = splitForAllTyCoVarBinders (tyConKind tycon)+ let (bndrs, kind) = splitForAllForAllTyBinders (tyConKind tycon) liftTc $ traceTc "mkTyConKindRepBinds" (ppr tycon $$ ppr (tyConKind tycon) $$ ppr kind) let ctx = mkDeBruijnContext (map binderVar bndrs)@@ -433,9 +435,8 @@ -- | Is a particular 'TyCon' representable by @Typeable@?. These exclude type -- families and polytypes. tyConIsTypeable :: TyCon -> Bool-tyConIsTypeable tc =- isJust (tyConRepName_maybe tc)- && kindIsTypeable (dropForAlls $ tyConKind tc)+tyConIsTypeable tc = isJust (tyConRepName_maybe tc)+ && kindIsTypeable (dropForAlls $ tyConKind tc) -- | Is a particular 'Kind' representable by @Typeable@? Here we look for -- polytypes and types containing casts (which may be, for instance, a type@@ -477,12 +478,14 @@ liftTc :: TcRn a -> KindRepM a liftTc = KindRepM . lift --- | We generate @KindRep@s for a few common kinds in @GHC.Types@ so that they+-- | We generate `KindRep`s for a few common kinds, so that they -- can be reused across modules.+-- These definitions are generated in `ghc-prim:GHC.Types`. builtInKindReps :: [(Kind, Name)] builtInKindReps =- [ (star, starKindRepName)- , (mkVisFunTyMany star star, starArrStarKindRepName)+ [ (star, starKindRepName)+ , (constraintKind, constraintKindRepName)+ , (mkVisFunTyMany star star, starArrStarKindRepName) , (mkVisFunTysMany [star, star] star, starArrStarArrStarKindRepName) ] where@@ -494,6 +497,7 @@ add_kind_rep acc (k,n) = do id <- tcLookupId n return $! extendTypeMap acc k (id, Nothing)+ -- The TypeMap looks through type synonyms -- | Performed while compiling "GHC.Types" to generate the built-in 'KindRep's. mkExportedKindReps :: TypeableStuff@@ -509,6 +513,7 @@ -- since the latter would find the built-in 'KindRep's in the -- 'KindRepEnv' (by virtue of being in 'initialKindRepEnv'). rhs <- mkKindRepRhs stuff empty_scope kind+ liftTc (traceTc "mkExport" (ppr kind $$ ppr rep_bndr $$ ppr rhs)) addKindRepBind empty_scope kind rep_bndr rhs addKindRepBind :: CmEnv -> Kind -> Id -> LHsExpr GhcTc -> KindRepM ()@@ -541,10 +546,8 @@ go' :: Kind -> KindRepEnv -> TcRn (LHsExpr GhcTc, KindRepEnv) go' k env- -- Look through type synonyms- | Just k' <- tcView k = go' k' env- -- We've already generated the needed KindRep+ -- This lookup looks through synonyms | Just (id, _) <- lookupTypeMapWithScope env in_scope k = return (nlHsVar id, env) @@ -553,7 +556,7 @@ = do -- Place a NOINLINE pragma on KindReps since they tend to be quite -- large and bloat interface files. rep_bndr <- (`setInlinePragma` neverInlinePragma)- <$> newSysLocalId (fsLit "$krep") Many (mkTyConTy kindRepTyCon)+ <$> newSysLocalId (fsLit "$krep") ManyTy (mkTyConTy kindRepTyCon) -- do we need to tie a knot here? flip runStateT env $ unKindRepM $ do@@ -573,24 +576,27 @@ -- We handle (TYPE LiftedRep) etc separately to make it -- clear to consumers (e.g. serializers) that there is -- a loop here (as TYPE :: RuntimeRep -> TYPE 'LiftedRep)- | not (tcIsConstraintKind k)+ | Just (TypeLike, rep) <- sORTKind_maybe k -- Typeable respects the Constraint/Type distinction -- so do not follow the special case here- , Just arg <- kindRep_maybe k- = case splitTyConApp_maybe arg of- Just (tc, [])+ = -- Here k = TYPE <something>+ case splitTyConApp_maybe rep of+ Just (tc, []) -- TYPE IntRep, TYPE FloatRep etc | Just dc <- isPromotedDataCon_maybe tc -> return $ nlHsDataCon kindRepTYPEDataCon `nlHsApp` nlHsDataCon dc - Just (rep, [levArg])- | Just dcRep <- isPromotedDataCon_maybe rep- , Just (lev, []) <- splitTyConApp_maybe levArg- , Just dcLev <- isPromotedDataCon_maybe lev+ Just (rep_tc, [levArg]) -- TYPE (BoxedRep lev)+ | Just dcRep <- isPromotedDataCon_maybe rep_tc+ , Just (lev_tc, []) <- splitTyConApp_maybe levArg+ , Just dcLev <- isPromotedDataCon_maybe lev_tc -> return $ nlHsDataCon kindRepTYPEDataCon `nlHsApp` (nlHsDataCon dcRep `nlHsApp` nlHsDataCon dcLev) _ -> new_kind_rep k | otherwise = new_kind_rep k + new_kind_rep ki -- Expand synonyms+ | Just ki' <- coreView ki+ = new_kind_rep ki' new_kind_rep (TyVarTy v) | Just idx <- lookupCME in_scope v@@ -662,7 +668,7 @@ where n_kind_vars = length $ filter isNamedTyConBinder (tyConBinders tycon) tycon_str = add_tick (occNameString (getOccName tycon))- add_tick s | isPromotedDataCon tycon = '\'' : s+ add_tick s | isDataKindsPromotedDataCon tycon = '\'' : s | otherwise = s -- This must match the computation done in
compiler/GHC/Tc/Module.hs view
@@ -112,7 +112,6 @@ import GHC.Core.PatSyn ( pprPatSynType ) import GHC.Core.Predicate ( classMethodTy )-import GHC.Core.FVs ( orphNamesOfFamInst ) import GHC.Core.InstEnv import GHC.Core.TyCon import GHC.Core.ConLike@@ -121,9 +120,10 @@ import GHC.Core.Class import GHC.Core.Coercion.Axiom import GHC.Core.Reduction ( Reduction(..) )-import GHC.Core.Unify( RoughMatchTc(..) )+import GHC.Core.RoughMap( RoughMatchTc(..) )+import GHC.Core.TyCo.Ppr( debugPprType ) import GHC.Core.FamInstEnv- ( FamInst, pprFamInst, famInstsRepTyCons+ ( FamInst, pprFamInst, famInstsRepTyCons, orphNamesOfFamInst , famInstEnvElts, extendFamInstEnvList, normaliseType ) import GHC.Parser.Header ( mkPrelImports )@@ -176,7 +176,10 @@ import GHC.Data.Bag import qualified GHC.Data.BooleanFormula as BF +import Data.Functor.Classes ( liftEq ) import Data.List ( sortBy, sort )+import Data.List.NonEmpty ( NonEmpty (..) )+import qualified Data.List.NonEmpty as NE import Data.Ord import Data.Data ( Data ) import qualified Data.Set as S@@ -242,9 +245,8 @@ tcRnModuleTcRnM hsc_env mod_sum (HsParsedModule { hpm_module =- (L loc (HsModule _ _ maybe_mod export_ies- import_decls local_decls mod_deprec- maybe_doc_hdr)),+ (L loc (HsModule (XModulePs _ _ mod_deprec maybe_doc_hdr)+ maybe_mod export_ies import_decls local_decls)), hpm_src_files = src_files }) (this_mod, prel_imp_loc)@@ -265,7 +267,7 @@ implicit_prelude import_decls } ; when (notNull prel_imports) $ do- let msg = TcRnUnknownMessage $+ let msg = mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag Opt_WarnImplicitPrelude) noHints (implicitPreludeWarn) addDiagnostic msg @@ -283,7 +285,7 @@ ++ import_decls)) ; let { mkImport mod_name = noLocA $ (simpleImportDecl mod_name)- { ideclHiding = Just (False, noLocA [])}}+ { ideclImportList = Just (Exactly, noLocA [])}} ; let { withReason t imps = map (,text t) imps } ; let { all_imports = withReason "is implicitly imported" prel_imports ++ withReason "is directly imported" import_decls@@ -628,7 +630,7 @@ { Nothing -> return () ; Just (SpliceDecl _ (L loc _) _, _) -> setSrcSpanA loc- $ addErr (TcRnUnknownMessage $ mkPlainError noHints $ text+ $ addErr (mkTcRnUnknownMessage $ mkPlainError noHints $ text ("Declaration splices are not " ++ "permitted inside top-level " ++ "declarations added with addTopDecls"))@@ -750,7 +752,7 @@ badBootDecl :: HscSource -> String -> LocatedA decl -> TcM () badBootDecl hsc_src what (L loc _)- = addErrAt (locA loc) $ TcRnUnknownMessage $ mkPlainError noHints $+ = addErrAt (locA loc) $ mkTcRnUnknownMessage $ mkPlainError noHints $ (char 'A' <+> text what <+> text "declaration is not (currently) allowed in a" <+> (case hsc_src of@@ -889,9 +891,9 @@ | name `elem` boot_dfun_names = return () -- Check that the actual module exports the same thing- | not (null missing_names)- = addErrAt (nameSrcSpan (head missing_names))- (missingBootThing True (head missing_names) "exported by")+ | missing_name:_ <- missing_names+ = addErrAt (nameSrcSpan missing_name)+ (missingBootThing True missing_name "exported by") -- If the boot module does not *define* the thing, we are done -- (it simply re-exports it, and names match, so nothing further to do)@@ -1066,7 +1068,7 @@ (text "The types of" <+> pname1 <+> text "are different") `andThenCheck` if is_boot- then check (eqMaybeBy eqDM def_meth1 def_meth2)+ then check (liftEq eqDM def_meth1 def_meth2) (text "The default methods associated with" <+> pname1 <+> text "are different") else check (subDM op_ty1 def_meth1 def_meth2)@@ -1093,6 +1095,7 @@ -- Order of pattern matching matters. subDM _ Nothing _ = True subDM _ _ Nothing = False+ -- If the hsig wrote: -- -- f :: a -> a@@ -1100,11 +1103,14 @@ -- -- this should be validly implementable using an old-fashioned -- vanilla default method.- subDM t1 (Just (_, GenericDM t2)) (Just (_, VanillaDM))- = eqTypeX env t1 t2+ subDM t1 (Just (_, GenericDM gdm_t1)) (Just (_, VanillaDM))+ = eqType t1 gdm_t1 -- Take care (#22476). Both t1 and gdm_t1 come+ -- from tc1, so use eqType, and /not/ eqTypeX+ -- This case can occur when merging signatures subDM t1 (Just (_, VanillaDM)) (Just (_, GenericDM t2)) = eqTypeX env t1 t2+ subDM _ (Just (_, VanillaDM)) (Just (_, VanillaDM)) = True subDM _ (Just (_, GenericDM t1)) (Just (_, GenericDM t2)) = eqTypeX env t1 t2@@ -1115,15 +1121,15 @@ eqATDef _ _ = False eqFD (as1,bs1) (as2,bs2) =- eqListBy (eqTypeX env) (mkTyVarTys as1) (mkTyVarTys as2) &&- eqListBy (eqTypeX env) (mkTyVarTys bs1) (mkTyVarTys bs2)+ liftEq (eqTypeX env) (mkTyVarTys as1) (mkTyVarTys as2) &&+ liftEq (eqTypeX env) (mkTyVarTys bs1) (mkTyVarTys bs2) in checkRoles roles1 roles2 `andThenCheck` -- Checks kind of class- check (eqListBy eqFD clas_fds1 clas_fds2)+ check (liftEq eqFD clas_fds1 clas_fds2) (text "The functional dependencies do not match") `andThenCheck` checkUnless (isAbstractTyCon tc1) $- check (eqListBy (eqTypeX env) sc_theta1 sc_theta2)+ check (liftEq (eqTypeX env) sc_theta1 sc_theta2) (text "The class constraints do not match") `andThenCheck` checkListBy eqSig op_stuff1 op_stuff2 (text "methods") `andThenCheck` checkListBy eqAT ats1 ats2 (text "associated types") `andThenCheck`@@ -1191,7 +1197,7 @@ , Just env <- eqVarBndrs emptyRnEnv2 (tyConTyVars tc1) (tyConTyVars tc2) = assert (tc1 == tc2) $ checkRoles roles1 roles2 `andThenCheck`- check (eqListBy (eqTypeX env)+ check (liftEq (eqTypeX env) (tyConStupidTheta tc1) (tyConStupidTheta tc2)) (text "The datatype contexts do not match") `andThenCheck` eqAlgRhs tc1 (algTyConRhs tc1) (algTyConRhs tc2)@@ -1250,7 +1256,7 @@ -- data T a = MkT -- -- If you write this, we'll treat T as injective, and make inferences- -- like T a ~R T b ==> a ~N b (mkNthCo). But if we can+ -- like T a ~R T b ==> a ~N b (mkSelCo). But if we can -- subsequently replace T with one at phantom role, we would then be able to -- infer things like T Int ~R T Bool which is bad news. --@@ -1276,7 +1282,7 @@ -- but ONLY if the type synonym is nullary and has no type family -- applications. This arises from two properties of skolem abstract data: --- -- For any T (with some number of paramaters),+ -- For any T (with some number of parameters), -- -- 1. T is a valid type (it is "curryable"), and --@@ -1337,7 +1343,7 @@ check (dataConIsInfix c1 == dataConIsInfix c2) (text "The fixities of" <+> pname1 <+> text "differ") `andThenCheck`- check (eqListBy eqHsBang (dataConImplBangs c1) (dataConImplBangs c2))+ check (liftEq eqHsBang (dataConImplBangs c1) (dataConImplBangs c2)) (text "The strictness annotations for" <+> pname1 <+> text "differ") `andThenCheck` check (map flSelector (dataConFieldLabels c1) == map flSelector (dataConFieldLabels c2))@@ -1368,7 +1374,7 @@ , cab_lhs = lhs2, cab_rhs = rhs2 }) | Just env1 <- eqVarBndrs emptyRnEnv2 tvs1 tvs2 , Just env <- eqVarBndrs env1 cvs1 cvs2- = eqListBy (eqTypeX env) lhs1 lhs2 &&+ = liftEq (eqTypeX env) lhs1 lhs2 && eqTypeX env rhs1 rhs2 | otherwise = False@@ -1379,7 +1385,7 @@ ---------------- missingBootThing :: Bool -> Name -> String -> TcRnMessage missingBootThing is_boot name what- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ quotes (ppr name) <+> text "is exported by the" <+> (if is_boot then text "hs-boot" else text "hsig") <+> text "file, but not"@@ -1387,7 +1393,7 @@ badReexportedBootThing :: Bool -> Name -> Name -> TcRnMessage badReexportedBootThing is_boot name name'- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ withUserStyle alwaysQualify AllTheWay $ vcat [ text "The" <+> (if is_boot then text "hs-boot" else text "hsig") <+> text "file (re)exports" <+> quotes (ppr name)@@ -1396,7 +1402,7 @@ bootMisMatch :: Bool -> SDoc -> TyThing -> TyThing -> TcRnMessage bootMisMatch is_boot extra_info real_thing boot_thing- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ pprBootMisMatch is_boot extra_info real_thing real_doc boot_doc where to_doc@@ -1427,7 +1433,7 @@ instMisMatch :: DFunId -> TcRnMessage instMisMatch dfun- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "instance" <+> ppr (idType dfun)) 2 (text "is defined in the hs-boot file, but not in the module itself") @@ -1620,7 +1626,7 @@ (hang (ppr name) 4 (sep [ppr clashingElts])) ; let warn_msg x = addDiagnosticAt (nameSrcSpan (greMangledName x)) $- TcRnUnknownMessage $+ mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag warnFlag) noHints $ (hsep [ text "Local definition of" , (quotes . ppr . nameOccName . greMangledName) x@@ -1650,7 +1656,7 @@ -- Implicit (Prelude) import? isImplicit :: ImportDecl GhcRn -> Bool- isImplicit = ideclImplicit+ isImplicit = ideclImplicit . ideclExt -- Unqualified import? isUnqualified :: ImportDecl GhcRn -> Bool@@ -1660,17 +1666,17 @@ -- Nothing -> No explicit imports -- Just (False, <names>) -> Explicit import list of <names> -- Just (True , <names>) -> Explicit hiding of <names>- importListOf :: ImportDecl GhcRn -> Maybe (Bool, [Name])- importListOf = fmap toImportList . ideclHiding+ importListOf :: ImportDecl GhcRn -> Maybe (ImportListInterpretation, [Name])+ importListOf = fmap toImportList . ideclImportList where toImportList (h, loc) = (h, map (ieName . unLoc) (unLoc loc)) isExplicit :: ImportDecl GhcRn -> Bool isExplicit x = case importListOf x of Nothing -> False- Just (False, explicit)+ Just (Exactly, explicit) -> nameOccName name `elem` map nameOccName explicit- Just (True, hidden)+ Just (EverythingBut, hidden) -> nameOccName name `notElem` map nameOccName hidden -- Check whether the given name would be imported (unqualified) from@@ -1733,7 +1739,7 @@ ; let instLoc = srcLocSpan . nameSrcLoc $ getName isInst warnMsg (RM_KnownTc name:_) = addDiagnosticAt instLoc $- TcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag warnFlag) noHints $+ mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag warnFlag) noHints $ hsep [ (quotes . ppr . nameOccName) name , text "is an instance of" , (ppr . nameOccName . className) isClass@@ -1871,7 +1877,7 @@ -- in other modes, add error message and go on with typechecking. noMainMsg main_mod main_occ- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ text "The" <+> ppMainFn main_occ <+> text "is not" <+> text defOrExp <+> text "module" <+> quotes (ppr main_mod)@@ -1913,7 +1919,7 @@ ; (ev_binds, main_expr) <- setMainCtxt main_name io_ty $ tcCheckMonoExpr main_expr_rn io_ty - -- See Note [Root-main id]+ -- See Note [Root-main Id] -- Construct the binding -- :Main.main :: IO res_ty = runMainIO res_ty main ; run_main_id <- tcLookupId runMainIOName@@ -1983,7 +1989,7 @@ - check that the export list does indeed export something called 'foo' - generateMainBinding: generate the root-main binding :Main.main = runMainIO M.foo- See Note [Root-main id]+ See Note [Root-main Id] An annoying consequence of having both checkMainType and checkMain is that, when (but only when) -fdefer-type-errors is on, we may report an@@ -2040,7 +2046,15 @@ module. Tiresomely, we must filter it out again in GHC.Iface.Make, less we get two defns for 'main' in the interface file! +When using `-fwrite-if-simplified-core` the root_main_id can end up in an interface file.+When the interface is read back in we have to add a special case when creating the+Id because otherwise we would go looking for the :Main module which obviously doesn't+exist. For this logic see GHC.IfaceToCore.mk_top_id. +There is also some similar (probably dead) logic in GHC.Rename.Env which says it+was added for External Core which faced a similar issue.++ ********************************************************* * * GHCi stuff@@ -2189,7 +2203,7 @@ return (global_ids, zonked_expr, fix_env) } where- bad_unboxed id = addErr $ TcRnUnknownMessage $ mkPlainError noHints $+ bad_unboxed id = addErr $ mkTcRnUnknownMessage $ mkPlainError noHints $ (sep [text "GHCi can't bind a variable of unlifted type:", nest 2 (pprPrefixOcc id <+> dcolon <+> ppr (idType id))]) @@ -2223,10 +2237,8 @@ -- | Try the plans in order. If one fails (by raising an exn), try the next. -- If one succeeds, take it.-runPlans :: [Plan] -> TcM PlanResult-runPlans [] = panic "runPlans"-runPlans [p] = p-runPlans (p:ps) = tryTcDiscardingErrs (runPlans ps) p+runPlans :: NonEmpty Plan -> Plan+runPlans = foldr1 (flip tryTcDiscardingErrs) -- | Typecheck (and 'lift') a stmt entered by the user in GHCi into the -- GHCi 'environment'.@@ -2298,30 +2310,31 @@ -- See Note [GHCi Plans] - it_plans = [+ it_plans = -- Plan A do { stuff@([it_id], _) <- tcGhciStmts [bind_stmt, print_it] ; it_ty <- zonkTcType (idType it_id)- ; when (isUnitTy $ it_ty) failM- ; return stuff },+ ; when (isUnitTy it_ty) failM+ ; return stuff } :| -- Plan B; a naked bind statement- tcGhciStmts [bind_stmt],+ [ tcGhciStmts [bind_stmt] -- Plan C; check that the let-binding is typeable all by itself. -- If not, fail; if so, try to print it. -- The two-step process avoids getting two errors: one from -- the expression itself, and one from the 'print it' part -- This two-step story is very clunky, alas- do { _ <- checkNoErrs (tcGhciStmts [let_stmt])+ , do { _ <- checkNoErrs (tcGhciStmts [let_stmt]) --- checkNoErrs defeats the error recovery of let-bindings ; tcGhciStmts [let_stmt, print_it] } ] -- Plans where we don't bind "it"- no_it_plans = [- tcGhciStmts [no_it_a] ,- tcGhciStmts [no_it_b] ,- tcGhciStmts [no_it_c] ]+ no_it_plans =+ tcGhciStmts [no_it_a] :|+ tcGhciStmts [no_it_b] :+ tcGhciStmts [no_it_c] :+ [] ; generate_it <- goptM Opt_NoIt @@ -2413,13 +2426,13 @@ ; let print_result_plan | opt_pr_flag -- The flag says "print result" , [v] <- collectLStmtBinders CollNoDictBinders gi_stmt -- One binder- = [mk_print_result_plan gi_stmt v]- | otherwise = []+ = Just $ mk_print_result_plan gi_stmt v+ | otherwise = Nothing -- The plans are: -- [stmt; print v] if one binder and not v::() -- [stmt] otherwise- ; plan <- runPlans (print_result_plan ++ [tcGhciStmts [gi_stmt]])+ ; plan <- runPlans $ maybe id (NE.<|) print_result_plan $ NE.singleton $ tcGhciStmts [gi_stmt] ; return (plan, fix_env) } where mk_print_result_plan stmt v@@ -2544,8 +2557,8 @@ _ <- tcLookupInstance ghciClass [userTy] return name - Just _ -> failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $ text "Ambiguous type!"- Nothing -> failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $ text ("Can't find type:" ++ ty)+ Just _ -> failWithTc $ mkTcRnUnknownMessage $ mkPlainError noHints $ text "Ambiguous type!"+ Nothing -> failWithTc $ mkTcRnUnknownMessage $ mkPlainError noHints $ text ("Can't find type:" ++ ty) -- | How should we infer a type? See Note [TcRnExprMode] data TcRnExprMode = TM_Inst -- ^ Instantiate inferred quantifiers only (:type)@@ -2669,8 +2682,15 @@ tcRnType hsc_env flexi normalise rdr_type = runTcInteractive hsc_env $ setXOptM LangExt.PolyKinds $ -- See Note [Kind-generalise in tcRnType]- do { (HsWC { hswc_ext = wcs, hswc_body = rn_type }, _fvs)- <- rnHsWcType GHCiCtx (mkHsWildCardBndrs rdr_type)+ do { (HsWC { hswc_ext = wcs, hswc_body = rn_sig_type@(L _ (HsSig{sig_bndrs = outer_bndrs, sig_body = body })) }, _fvs)+ -- we are using 'rnHsSigWcType' to bind the unbound type variables+ -- and in combination with 'tcOuterTKBndrs' we are able to+ -- implicitly quantify them as if the user wrote 'forall' by+ -- hand (see #19217). This allows kind check to work in presence+ -- of free type variables :+ -- ghci> :k [a]+ -- [a] :: *+ <- rnHsSigWcType GHCiCtx (mkHsWildCardBndrs $ noLocA (mkHsImplicitSigType rdr_type)) -- The type can have wild cards, but no implicit -- generalisation; e.g. :kind (T _) ; failIfErrsM@@ -2680,14 +2700,14 @@ -- Now kind-check the type -- It can have any rank or kind -- First bring into scope any wildcards- ; traceTc "tcRnType" (vcat [ppr wcs, ppr rn_type])- ; ((ty, kind), wanted)+ ; traceTc "tcRnType" (vcat [ppr wcs, ppr rn_sig_type])+ ; si <- mkSkolemInfo $ SigTypeSkol (GhciCtxt True)+ ; ((_, (ty, kind)), wanted) <- captureTopConstraints $ pushTcLevelM_ $ bindNamedWildCardBinders wcs $ \ wcs' -> do { mapM_ emitNamedTypeHole wcs'- ; tcInferLHsTypeUnsaturated rn_type }-+ ; tcOuterTKBndrs si outer_bndrs $ tcInferLHsTypeUnsaturated body } -- Since all the wanteds are equalities, the returned bindings will be empty ; empty_binds <- simplifyTop wanted ; massertPpr (isEmptyBag empty_binds) (ppr empty_binds)@@ -2709,7 +2729,8 @@ normaliseType fam_envs Nominal ty | otherwise = ty - ; return (ty', mkInfForAllTys kvs (tcTypeKind ty')) }+ ; traceTc "tcRnExpr" (debugPprType ty $$ debugPprType ty')+ ; return (ty', mkInfForAllTys kvs (typeKind ty')) } {- Note [TcRnExprMode]@@ -2721,7 +2742,7 @@ :type / TM_Inst - In this mode, we report the type obained by instantiating only the+ In this mode, we report the type obtained by instantiating only the /inferred/ quantifiers of e's type, solving constraints, and re-generalising, as discussed in #11376. @@ -2841,7 +2862,7 @@ let rdr_names = dataTcOccs rdr_name ; names_s <- mapM lookupInfoOccRn rdr_names ; let names = concat names_s- ; when (null names) (addErrTc $ TcRnUnknownMessage $ mkPlainError noHints $+ ; when (null names) (addErrTc $ mkTcRnUnknownMessage $ mkPlainError noHints $ (text "Not in scope:" <+> quotes (ppr rdr_name))) ; return names }
+ compiler/GHC/Tc/Plugin.hs view
@@ -0,0 +1,194 @@++-- | This module provides an interface for typechecker plugins to+-- access select functions of the 'TcM', principally those to do with+-- reading parts of the state.+module GHC.Tc.Plugin (+ -- * Basic TcPluginM functionality+ TcPluginM,+ tcPluginIO,+ tcPluginTrace,+ unsafeTcPluginTcM,++ -- * Finding Modules and Names+ Finder.FindResult(..),+ findImportedModule,+ lookupOrig,++ -- * Looking up Names in the typechecking environment+ tcLookupGlobal,+ tcLookupTyCon,+ tcLookupDataCon,+ tcLookupClass,+ tcLookup,+ tcLookupId,++ -- * Getting the TcM state+ getTopEnv,+ getTargetPlatform,+ getEnvs,+ getInstEnvs,+ getFamInstEnvs,+ matchFam,++ -- * Type variables+ newUnique,+ newFlexiTyVar,+ isTouchableTcPluginM,++ -- * Zonking+ zonkTcType,+ zonkCt,++ -- * Creating constraints+ newWanted,+ newGiven,+ newCoercionHole,++ -- * Manipulating evidence bindings+ newEvVar,+ setEvBind,+ ) where++import GHC.Prelude++import GHC.Platform (Platform)++import qualified GHC.Tc.Utils.Monad as TcM+import qualified GHC.Tc.Solver.Monad as TcS+import qualified GHC.Tc.Utils.Env as TcM+import qualified GHC.Tc.Utils.TcMType as TcM+import qualified GHC.Tc.Instance.Family as TcM+import qualified GHC.Iface.Env as IfaceEnv+import qualified GHC.Unit.Finder as Finder++import GHC.Core.FamInstEnv ( FamInstEnv )+import GHC.Tc.Utils.Monad ( TcGblEnv, TcLclEnv, TcPluginM+ , unsafeTcPluginTcM+ , liftIO, traceTc )+import GHC.Tc.Types.Constraint ( Ct, CtLoc, CtEvidence(..) )+import GHC.Tc.Utils.TcMType ( TcTyVar, TcType )+import GHC.Tc.Utils.Env ( TcTyThing )+import GHC.Tc.Types.Evidence ( CoercionHole, EvTerm(..)+ , EvExpr, EvBindsVar, EvBind, mkGivenEvBind )+import GHC.Types.Var ( EvVar )++import GHC.Unit.Module ( ModuleName, Module )+import GHC.Types.Name ( OccName, Name )+import GHC.Types.TyThing ( TyThing )+import GHC.Core.Reduction ( Reduction )+import GHC.Core.TyCon ( TyCon )+import GHC.Core.DataCon ( DataCon )+import GHC.Core.Class ( Class )+import GHC.Driver.Env ( HscEnv(..) )+import GHC.Utils.Outputable ( SDoc )+import GHC.Core.Type ( Kind, Type, PredType )+import GHC.Types.Id ( Id )+import GHC.Core.InstEnv ( InstEnvs )+import GHC.Types.Unique ( Unique )+import GHC.Types.PkgQual ( PkgQual )+++-- | Perform some IO, typically to interact with an external tool.+tcPluginIO :: IO a -> TcPluginM a+tcPluginIO a = unsafeTcPluginTcM (liftIO a)++-- | Output useful for debugging the compiler.+tcPluginTrace :: String -> SDoc -> TcPluginM ()+tcPluginTrace a b = unsafeTcPluginTcM (traceTc a b)+++findImportedModule :: ModuleName -> PkgQual -> TcPluginM Finder.FindResult+findImportedModule mod_name mb_pkg = do+ hsc_env <- getTopEnv+ tcPluginIO $ Finder.findImportedModule hsc_env mod_name mb_pkg++lookupOrig :: Module -> OccName -> TcPluginM Name+lookupOrig mod = unsafeTcPluginTcM . IfaceEnv.lookupOrig mod+++tcLookupGlobal :: Name -> TcPluginM TyThing+tcLookupGlobal = unsafeTcPluginTcM . TcM.tcLookupGlobal++tcLookupTyCon :: Name -> TcPluginM TyCon+tcLookupTyCon = unsafeTcPluginTcM . TcM.tcLookupTyCon++tcLookupDataCon :: Name -> TcPluginM DataCon+tcLookupDataCon = unsafeTcPluginTcM . TcM.tcLookupDataCon++tcLookupClass :: Name -> TcPluginM Class+tcLookupClass = unsafeTcPluginTcM . TcM.tcLookupClass++tcLookup :: Name -> TcPluginM TcTyThing+tcLookup = unsafeTcPluginTcM . TcM.tcLookup++tcLookupId :: Name -> TcPluginM Id+tcLookupId = unsafeTcPluginTcM . TcM.tcLookupId+++getTopEnv :: TcPluginM HscEnv+getTopEnv = unsafeTcPluginTcM TcM.getTopEnv++getTargetPlatform :: TcPluginM Platform+getTargetPlatform = unsafeTcPluginTcM TcM.getPlatform+++getEnvs :: TcPluginM (TcGblEnv, TcLclEnv)+getEnvs = unsafeTcPluginTcM TcM.getEnvs++getInstEnvs :: TcPluginM InstEnvs+getInstEnvs = unsafeTcPluginTcM TcM.tcGetInstEnvs++getFamInstEnvs :: TcPluginM (FamInstEnv, FamInstEnv)+getFamInstEnvs = unsafeTcPluginTcM TcM.tcGetFamInstEnvs++matchFam :: TyCon -> [Type]+ -> TcPluginM (Maybe Reduction)+matchFam tycon args = unsafeTcPluginTcM $ TcS.matchFamTcM tycon args++newUnique :: TcPluginM Unique+newUnique = unsafeTcPluginTcM TcM.newUnique++newFlexiTyVar :: Kind -> TcPluginM TcTyVar+newFlexiTyVar = unsafeTcPluginTcM . TcM.newFlexiTyVar++isTouchableTcPluginM :: TcTyVar -> TcPluginM Bool+isTouchableTcPluginM = unsafeTcPluginTcM . TcM.isTouchableTcM++-- Confused by zonking? See Note [What is zonking?] in GHC.Tc.Utils.TcMType.+zonkTcType :: TcType -> TcPluginM TcType+zonkTcType = unsafeTcPluginTcM . TcM.zonkTcType++zonkCt :: Ct -> TcPluginM Ct+zonkCt = unsafeTcPluginTcM . TcM.zonkCt++-- | Create a new Wanted constraint with the given 'CtLoc'.+newWanted :: CtLoc -> PredType -> TcPluginM CtEvidence+newWanted loc pty+ = unsafeTcPluginTcM (TcM.newWantedWithLoc loc pty)++-- | Create a new given constraint, with the supplied evidence.+--+-- This should only be invoked within 'tcPluginSolve'.+newGiven :: EvBindsVar -> CtLoc -> PredType -> EvExpr -> TcPluginM CtEvidence+newGiven tc_evbinds loc pty evtm = do+ new_ev <- newEvVar pty+ setEvBind tc_evbinds $ mkGivenEvBind new_ev (EvExpr evtm)+ return CtGiven { ctev_pred = pty, ctev_evar = new_ev, ctev_loc = loc }++-- | Create a fresh evidence variable.+--+-- This should only be invoked within 'tcPluginSolve'.+newEvVar :: PredType -> TcPluginM EvVar+newEvVar = unsafeTcPluginTcM . TcM.newEvVar++-- | Create a fresh coercion hole.+-- This should only be invoked within 'tcPluginSolve'.+newCoercionHole :: PredType -> TcPluginM CoercionHole+newCoercionHole = unsafeTcPluginTcM . TcM.newCoercionHole++-- | Bind an evidence variable.+--+-- This should only be invoked within 'tcPluginSolve'.+setEvBind :: EvBindsVar -> EvBind -> TcPluginM ()+setEvBind tc_evbinds ev_bind = do+ unsafeTcPluginTcM $ TcM.addTcEvBind tc_evbinds ev_bind
compiler/GHC/Tc/Solver.hs view
@@ -58,7 +58,7 @@ import GHC.Core.Type import GHC.Core.Ppr import GHC.Core.TyCon ( TyConBinder, isTypeFamilyTyCon )-import GHC.Builtin.Types ( liftedRepTy, manyDataConTy, liftedDataConTy )+import GHC.Builtin.Types ( liftedRepTy, liftedDataConTy ) import GHC.Core.Unify ( tcMatchTyKi ) import GHC.Utils.Misc import GHC.Utils.Panic@@ -73,7 +73,7 @@ import Data.Foldable ( toList ) import Data.List ( partition ) import Data.List.NonEmpty ( NonEmpty(..) )-import GHC.Data.Maybe ( mapMaybe )+import GHC.Data.Maybe ( mapMaybe, isJust ) {- *********************************************************************************@@ -438,7 +438,7 @@ We re-emit the implication rather than reporting the errors right now,-so that the error mesages are improved by other solving and defaulting.+so that the error messages are improved by other solving and defaulting. e.g. we prefer Cannot match 'Type->Type' with 'Type' to Cannot match 'Type->Type' with 'TYPE r0'@@ -1013,7 +1013,7 @@ the recursive case, we would go on forever in the common case where the constraints /are/ satisfiable (#10592 comment:12!). -For stratightforard situations without type functions the try_harder+For straightforward situations without type functions the try_harder step does nothing. Note [tcNormalise]@@ -1199,10 +1199,12 @@ -- NB: bound_theta are constraints we want to quantify over, -- including the psig_theta, which we always quantify over -- NB: bound_theta are fully zonked+ -- rec {..}: see Note [Keeping SkolemInfo inside a SkolemTv]+ -- in GHC.Tc.Utils.TcType ; rec { (qtvs, bound_theta, co_vars) <- decideQuantification skol_info infer_mode rhs_tclvl name_taus partial_sigs quant_pred_candidates- ; bound_theta_vars <- mapM TcM.newEvVar bound_theta+ ; bound_theta_vars <- mapM TcM.newEvVar bound_theta ; let full_theta = map idType bound_theta_vars ; skol_info <- mkSkolemInfo (InferSkol [ (name, mkSigmaTy [] full_theta ty)@@ -1668,13 +1670,13 @@ At least for single functions we would like to quantify f over precisely the same theta as <quant-theta>, so that we get to take the short-cut path in GHC.Tc.Gen.Bind.mkExport, and avoid calling-tcSubTypeSigma for impedence matching. Why avoid? Because it falls+tcSubTypeSigma for impedance matching. Why avoid? Because it falls over for ambiguous types (#20921). We can get precisely the same theta by using the same algorithm, findInferredDiff. -All of this goes wrong if we have (a) mutual recursion, (b) mutiple+All of this goes wrong if we have (a) mutual recursion, (b) multiple partial type signatures, (c) with different constraints, and (d) ambiguous types. Something like f :: forall a. Eq a => F a -> _@@ -1848,7 +1850,7 @@ -- NB: only pass 'DefaultKindVars' when we know we're dealing with a kind variable. tv - -- this common case (no inferred contraints) should be fast+ -- this common case (no inferred constraints) should be fast simplify_cand [] = return [] -- see Note [Unconditionally resimplify constraints when quantifying] simplify_cand candidates@@ -2490,18 +2492,7 @@ ; bad_telescope <- checkBadTelescope implic - ; let (used_givens, unused_givens)- | warnRedundantGivens info- = partition (`elemVarSet` need_inner) givens- | otherwise = (givens, []) -- None to report-- minimal_used_givens = mkMinimalBySCs evVarPred used_givens- is_minimal = (`elemVarSet` mkVarSet minimal_used_givens)-- warn_givens- | not (null unused_givens) = unused_givens- | warnRedundantGivens info = filterOut is_minimal used_givens- | otherwise = []+ ; let warn_givens = findUnnecessaryGivens info need_inner givens discard_entire_implication -- Can we discard the entire implication? = null warn_givens -- No warning from this implication@@ -2541,6 +2532,67 @@ | otherwise = True -- Otherwise, keep it +findUnnecessaryGivens :: SkolemInfoAnon -> VarSet -> [EvVar] -> [EvVar]+findUnnecessaryGivens info need_inner givens+ | not (warnRedundantGivens info) -- Don't report redundant constraints at all+ = []++ | not (null unused_givens) -- Some givens are literally unused+ = unused_givens++ | otherwise -- All givens are used, but some might+ = redundant_givens -- still be redundant e.g. (Eq a, Ord a)++ where+ in_instance_decl = case info of { InstSkol {} -> True; _ -> False }+ -- See Note [Redundant constraints in instance decls]++ unused_givens = filterOut is_used givens++ is_used given = is_type_error given+ || (given `elemVarSet` need_inner)+ || (in_instance_decl && is_improving (idType given))++ minimal_givens = mkMinimalBySCs evVarPred givens+ is_minimal = (`elemVarSet` mkVarSet minimal_givens)+ redundant_givens+ | in_instance_decl = []+ | otherwise = filterOut is_minimal givens++ -- See #15232+ is_type_error = isJust . userTypeError_maybe . idType++ is_improving pred -- (transSuperClasses p) does not include p+ = any isImprovementPred (pred : transSuperClasses pred)++{- Note [Redundant constraints in instance decls]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Instance declarations are special in two ways:++* We don't report unused givens if they can give rise to improvement.+ Example (#10100):+ class Add a b ab | a b -> ab, a ab -> b+ instance Add Zero b b+ instance Add a b ab => Add (Succ a) b (Succ ab)+ The context (Add a b ab) for the instance is clearly unused in terms+ of evidence, since the dictionary has no fields. But it is still+ needed! With the context, a wanted constraint+ Add (Succ Zero) beta (Succ Zero)+ we will reduce to (Add Zero beta Zero), and thence we get beta := Zero.+ But without the context we won't find beta := Zero.++ This only matters in instance declarations.++* We don't report givens that are a superclass of another given. E.g.+ class Ord r => UserOfRegs r a where ...+ instance (Ord r, UserOfRegs r CmmReg) => UserOfRegs r CmmExpr where+ The (Ord r) is not redundant, even though it is a superclass of+ (UserOfRegs r CmmReg). See Note [Recursive superclasses] in GHC.Tc.TyCl.Instance.++ Again this is specific to instance declarations.+-}++ checkBadTelescope :: Implication -> TcS Bool -- True <=> the skolems form a bad telescope -- See Note [Checking telescopes] in GHC.Tc.Types.Constraint@@ -2689,7 +2741,7 @@ Moreover, if we simplify this implication more than once (e.g. because we can't solve it completely on the first iteration- of simpl_looop), we'll generate all the same bindings AGAIN!+ of simpl_loop), we'll generate all the same bindings AGAIN! Easy solution: take advantage of the work we are doing to track dead (unused) Givens, and use it to prune the Given bindings too. This is@@ -2895,7 +2947,7 @@ ; return True } | isMultiplicityVar the_tv = do { traceTcS "defaultTyVarTcS Multiplicity" (ppr the_tv)- ; unifyTyVar the_tv manyDataConTy+ ; unifyTyVar the_tv ManyTy ; return True } | otherwise = return False -- the common case@@ -2996,7 +3048,7 @@ foo x = show (\_ -> True) Then we'll get a constraint (Show (p ->q)) where p has kind (TYPE r),-and that won't match the tcTypeKind (*) in the instance decl. See tests+and that won't match the typeKind (*) in the instance decl. See tests tc217 and tc175. We look only at touchable type variables. No further constraints@@ -3153,7 +3205,7 @@ | Just (cls,tys) <- getClassPredTys_maybe (ctPred cc) , [ty] <- filterOutInvisibleTypes (classTyCon cls) tys -- Ignore invisible arguments for this purpose- , Just tv <- tcGetTyVar_maybe ty+ , Just tv <- getTyVar_maybe ty , isMetaTyVar tv -- We might have runtime-skolems in GHCi, and -- we definitely don't want to try to assign to those! = Left (cc, cls, tv)
compiler/GHC/Tc/Solver/Canonical.hs view
@@ -1,3144 +1,3420 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE MultiWayIf #-}--module GHC.Tc.Solver.Canonical(- canonicalize,- unifyWanted,- makeSuperClasses,- StopOrContinue(..), stopWith, continueWith, andWhenContinue,- rewriteEqEvidence,- solveCallStack -- For GHC.Tc.Solver- ) where--import GHC.Prelude--import GHC.Tc.Types.Constraint-import GHC.Core.Predicate-import GHC.Tc.Types.Origin-import GHC.Tc.Utils.Unify-import GHC.Tc.Utils.TcType-import GHC.Core.Type-import GHC.Tc.Solver.Rewrite-import GHC.Tc.Solver.Monad-import GHC.Tc.Solver.InertSet-import GHC.Tc.Types.Evidence-import GHC.Tc.Types.EvTerm-import GHC.Core.Class-import GHC.Core.DataCon ( dataConName )-import GHC.Core.TyCon-import GHC.Core.Multiplicity-import GHC.Core.TyCo.Rep -- cleverly decomposes types, good for completeness checking-import GHC.Core.Coercion-import GHC.Core.Coercion.Axiom-import GHC.Core.Reduction-import GHC.Core-import GHC.Types.Id( mkTemplateLocals )-import GHC.Core.FamInstEnv ( FamInstEnvs )-import GHC.Tc.Instance.Family ( tcTopNormaliseNewTypeTF_maybe )-import GHC.Types.Var-import GHC.Types.Var.Env( mkInScopeSet )-import GHC.Types.Var.Set( delVarSetList, anyVarSet )-import GHC.Utils.Outputable-import GHC.Utils.Panic-import GHC.Utils.Panic.Plain-import GHC.Builtin.Types ( anyTypeOfKind )-import GHC.Types.Name.Set-import GHC.Types.Name.Reader-import GHC.Hs.Type( HsIPName(..) )-import GHC.Types.Unique ( hasKey )-import GHC.Builtin.Names ( coercibleTyConKey )--import GHC.Data.Pair-import GHC.Utils.Misc-import GHC.Data.Bag-import GHC.Utils.Monad-import Control.Monad-import Data.Maybe ( isJust, isNothing )-import Data.List ( zip4 )-import GHC.Types.Basic--import qualified Data.Semigroup as S-import Data.Bifunctor ( bimap )-import Data.Foldable ( traverse_ )--{--************************************************************************-* *-* The Canonicaliser *-* *-************************************************************************--Note [Canonicalization]-~~~~~~~~~~~~~~~~~~~~~~~--Canonicalization converts a simple constraint to a canonical form. It is-unary (i.e. treats individual constraints one at a time).--Constraints originating from user-written code come into being as-CNonCanonicals. We know nothing about these constraints. So, first:-- Classify CNonCanoncal constraints, depending on whether they- are equalities, class predicates, or other.--Then proceed depending on the shape of the constraint. Generally speaking,-each constraint gets rewritten and then decomposed into one of several forms-(see type Ct in GHC.Tc.Types).--When an already-canonicalized constraint gets kicked out of the inert set,-it must be recanonicalized. But we know a bit about its shape from the-last time through, so we can skip the classification step.---}---- Top-level canonicalization--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~--canonicalize :: Ct -> TcS (StopOrContinue Ct)-canonicalize (CNonCanonical { cc_ev = ev })- = {-# SCC "canNC" #-}- canNC ev--canonicalize (CQuantCan (QCI { qci_ev = ev, qci_pend_sc = pend_sc }))- = canForAll ev pend_sc--canonicalize (CIrredCan { cc_ev = ev })- = canNC ev- -- Instead of rewriting the evidence before classifying, it's possible we- -- can make progress without the rewrite. Try this first.- -- For insolubles (all of which are equalities), do /not/ rewrite the arguments- -- In #14350 doing so led entire-unnecessary and ridiculously large- -- type function expansion. Instead, canEqNC just applies- -- the substitution to the predicate, and may do decomposition;- -- e.g. a ~ [a], where [G] a ~ [Int], can decompose--canonicalize (CDictCan { cc_ev = ev, cc_class = cls- , cc_tyargs = xis, cc_pend_sc = pend_sc- , cc_fundeps = fds })- = {-# SCC "canClass" #-}- canClass ev cls xis pend_sc fds--canonicalize (CEqCan { cc_ev = ev- , cc_lhs = lhs- , cc_rhs = rhs- , cc_eq_rel = eq_rel })- = {-# SCC "canEqLeafTyVarEq" #-}- canEqNC ev eq_rel (canEqLHSType lhs) rhs--canNC :: CtEvidence -> TcS (StopOrContinue Ct)-canNC ev =- case classifyPredType pred of- ClassPred cls tys -> do traceTcS "canEvNC:cls" (ppr cls <+> ppr tys)- canClassNC ev cls tys- EqPred eq_rel ty1 ty2 -> do traceTcS "canEvNC:eq" (ppr ty1 $$ ppr ty2)- canEqNC ev eq_rel ty1 ty2- IrredPred {} -> do traceTcS "canEvNC:irred" (ppr pred)- canIrred ev- ForAllPred tvs th p -> do traceTcS "canEvNC:forall" (ppr pred)- canForAllNC ev tvs th p-- where- pred = ctEvPred ev--{--************************************************************************-* *-* Class Canonicalization-* *-************************************************************************--}--canClassNC :: CtEvidence -> Class -> [Type] -> TcS (StopOrContinue Ct)--- "NC" means "non-canonical"; that is, we have got here--- from a NonCanonical constraint, not from a CDictCan--- Precondition: EvVar is class evidence-canClassNC ev cls tys- | isGiven ev -- See Note [Eagerly expand given superclasses]- = do { sc_cts <- mkStrictSuperClasses ev [] [] cls tys- ; emitWork sc_cts- ; canClass ev cls tys False fds }-- | CtWanted { ctev_rewriters = rewriters } <- ev- , Just ip_name <- isCallStackPred cls tys- , isPushCallStackOrigin orig- -- If we're given a CallStack constraint that arose from a function- -- call, we need to push the current call-site onto the stack instead- -- of solving it directly from a given.- -- See Note [Overview of implicit CallStacks] in GHC.Tc.Types.Evidence- -- and Note [Solving CallStack constraints] in GHC.Tc.Solver.Types- = do { -- First we emit a new constraint that will capture the- -- given CallStack.- ; let new_loc = setCtLocOrigin loc (IPOccOrigin (HsIPName ip_name))- -- We change the origin to IPOccOrigin so- -- this rule does not fire again.- -- See Note [Overview of implicit CallStacks]- -- in GHC.Tc.Types.Evidence-- ; new_ev <- newWantedEvVarNC new_loc rewriters pred-- -- Then we solve the wanted by pushing the call-site- -- onto the newly emitted CallStack- ; let ev_cs = EvCsPushCall (callStackOriginFS orig)- (ctLocSpan loc) (ctEvExpr new_ev)- ; solveCallStack ev ev_cs-- ; canClass new_ev cls tys- False -- No superclasses- False -- No top level instances for fundeps- }-- | otherwise- = canClass ev cls tys (has_scs cls) fds-- where- has_scs cls = not (null (classSCTheta cls))- loc = ctEvLoc ev- orig = ctLocOrigin loc- pred = ctEvPred ev- fds = classHasFds cls--solveCallStack :: CtEvidence -> EvCallStack -> TcS ()--- Also called from GHC.Tc.Solver when defaulting call stacks-solveCallStack ev ev_cs = do- -- We're given ev_cs :: CallStack, but the evidence term should be a- -- dictionary, so we have to coerce ev_cs to a dictionary for- -- `IP ip CallStack`. See Note [Overview of implicit CallStacks]- cs_tm <- evCallStack ev_cs- let ev_tm = mkEvCast cs_tm (wrapIP (ctEvPred ev))- setEvBindIfWanted ev ev_tm--canClass :: CtEvidence- -> Class -> [Type]- -> Bool -- True <=> un-explored superclasses- -> Bool -- True <=> unexploited fundep(s)- -> TcS (StopOrContinue Ct)--- Precondition: EvVar is class evidence--canClass ev cls tys pend_sc fds- = -- all classes do *nominal* matching- assertPpr (ctEvRole ev == Nominal) (ppr ev $$ ppr cls $$ ppr tys) $- do { (redns@(Reductions _ xis), rewriters) <- rewriteArgsNom ev cls_tc tys- ; let redn@(Reduction _ xi) = mkClassPredRedn cls redns- mk_ct new_ev = CDictCan { cc_ev = new_ev- , cc_tyargs = xis- , cc_class = cls- , cc_pend_sc = pend_sc- , cc_fundeps = fds }- ; mb <- rewriteEvidence rewriters ev redn- ; traceTcS "canClass" (vcat [ ppr ev- , ppr xi, ppr mb ])- ; return (fmap mk_ct mb) }- where- cls_tc = classTyCon cls--{- Note [The superclass story]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We need to add superclass constraints for two reasons:--* For givens [G], they give us a route to proof. E.g.- f :: Ord a => a -> Bool- f x = x == x- We get a Wanted (Eq a), which can only be solved from the superclass- of the Given (Ord a).--* For wanteds [W], they may give useful- functional dependencies. E.g.- class C a b | a -> b where ...- class C a b => D a b where ...- Now a [W] constraint (D Int beta) has (C Int beta) as a superclass- and that might tell us about beta, via C's fundeps. We can get this- by generating a [W] (C Int beta) constraint. We won't use the evidence,- but it may lead to unification.--See Note [Why adding superclasses can help].--For these reasons we want to generate superclass constraints for both-Givens and Wanteds. But:--* (Minor) they are often not needed, so generating them aggressively- is a waste of time.--* (Major) if we want recursive superclasses, there would be an infinite- number of them. Here is a real-life example (#10318);-- class (Frac (Frac a) ~ Frac a,- Fractional (Frac a),- IntegralDomain (Frac a))- => IntegralDomain a where- type Frac a :: *-- Notice that IntegralDomain has an associated type Frac, and one- of IntegralDomain's superclasses is another IntegralDomain constraint.--So here's the plan:--1. Eagerly generate superclasses for given (but not wanted)- constraints; see Note [Eagerly expand given superclasses].- This is done using mkStrictSuperClasses in canClassNC, when- we take a non-canonical Given constraint and cannonicalise it.-- However stop if you encounter the same class twice. That is,- mkStrictSuperClasses expands eagerly, but has a conservative- termination condition: see Note [Expanding superclasses] in GHC.Tc.Utils.TcType.--2. Solve the wanteds as usual, but do no further expansion of- superclasses for canonical CDictCans in solveSimpleGivens or- solveSimpleWanteds; Note [Danger of adding superclasses during solving]-- However, /do/ continue to eagerly expand superclasses for new /given/- /non-canonical/ constraints (canClassNC does this). As #12175- showed, a type-family application can expand to a class constraint,- and we want to see its superclasses for just the same reason as- Note [Eagerly expand given superclasses].--3. If we have any remaining unsolved wanteds- (see Note [When superclasses help] in GHC.Tc.Types.Constraint)- try harder: take both the Givens and Wanteds, and expand- superclasses again. See the calls to expandSuperClasses in- GHC.Tc.Solver.simpl_loop and solveWanteds.-- This may succeed in generating (a finite number of) extra Givens,- and extra Wanteds. Both may help the proof.--3a An important wrinkle: only expand Givens from the current level.- Two reasons:- - We only want to expand it once, and that is best done at- the level it is bound, rather than repeatedly at the leaves- of the implication tree- - We may be inside a type where we can't create term-level- evidence anyway, so we can't superclass-expand, say,- (a ~ b) to get (a ~# b). This happened in #15290.--4. Go round to (2) again. This loop (2,3,4) is implemented- in GHC.Tc.Solver.simpl_loop.--The cc_pend_sc flag in a CDictCan records whether the superclasses of-this constraint have been expanded. Specifically, in Step 3 we only-expand superclasses for constraints with cc_pend_sc set to true (i.e.-isPendingScDict holds).--Why do we do this? Two reasons:--* To avoid repeated work, by repeatedly expanding the superclasses of- same constraint,--* To terminate the above loop, at least in the -XNoRecursiveSuperClasses- case. If there are recursive superclasses we could, in principle,- expand forever, always encountering new constraints.--When we take a CNonCanonical or CIrredCan, but end up classifying it-as a CDictCan, we set the cc_pend_sc flag to False.--Note [Superclass loops]-~~~~~~~~~~~~~~~~~~~~~~~-Suppose we have- class C a => D a- class D a => C a--Then, when we expand superclasses, we'll get back to the self-same-predicate, so we have reached a fixpoint in expansion and there is no-point in fruitlessly expanding further. This case just falls out from-our strategy. Consider- f :: C a => a -> Bool- f x = x==x-Then canClassNC gets the [G] d1: C a constraint, and eager emits superclasses-G] d2: D a, [G] d3: C a (psc). (The "psc" means it has its sc_pend flag set.)-When processing d3 we find a match with d1 in the inert set, and we always-keep the inert item (d1) if possible: see Note [Replacement vs keeping] in-GHC.Tc.Solver.Interact. So d3 dies a quick, happy death.--Note [Eagerly expand given superclasses]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-In step (1) of Note [The superclass story], why do we eagerly expand-Given superclasses by one layer? (By "one layer" we mean expand transitively-until you meet the same class again -- the conservative criterion embodied-in expandSuperClasses. So a "layer" might be a whole stack of superclasses.)-We do this eagerly for Givens mainly because of some very obscure-cases like this:-- instance Bad a => Eq (T a)-- f :: (Ord (T a)) => blah- f x = ....needs Eq (T a), Ord (T a)....--Here if we can't satisfy (Eq (T a)) from the givens we'll use the-instance declaration; but then we are stuck with (Bad a). Sigh.-This is really a case of non-confluent proofs, but to stop our users-complaining we expand one layer in advance.--Note [Instance and Given overlap] in GHC.Tc.Solver.Interact.--We also want to do this if we have-- f :: F (T a) => blah--where- type instance F (T a) = Ord (T a)--So we may need to do a little work on the givens to expose the-class that has the superclasses. That's why the superclass-expansion for Givens happens in canClassNC.--This same scenario happens with quantified constraints, whose superclasses-are also eagerly expanded. Test case: typecheck/should_compile/T16502b-These are handled in canForAllNC, analogously to canClassNC.--Note [Why adding superclasses can help]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Examples of how adding superclasses can help:-- --- Example 1- class C a b | a -> b- Suppose we want to solve- [G] C a b- [W] C a beta- Then adding [W] beta~b will let us solve it.-- -- Example 2 (similar but using a type-equality superclass)- class (F a ~ b) => C a b- And try to sllve:- [G] C a b- [W] C a beta- Follow the superclass rules to add- [G] F a ~ b- [W] F a ~ beta- Now we get [W] beta ~ b, and can solve that.-- -- Example (tcfail138)- class L a b | a -> b- class (G a, L a b) => C a b-- instance C a b' => G (Maybe a)- instance C a b => C (Maybe a) a- instance L (Maybe a) a-- When solving the superclasses of the (C (Maybe a) a) instance, we get- [G] C a b, and hance by superclasses, [G] G a, [G] L a b- [W] G (Maybe a)- Use the instance decl to get- [W] C a beta- Generate its superclass- [W] L a beta. Now using fundeps, combine with [G] L a b to get- [W] beta ~ b- which is what we want.--Note [Danger of adding superclasses during solving]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Here's a serious, but now out-dated example, from #4497:-- class Num (RealOf t) => Normed t- type family RealOf x--Assume the generated wanted constraint is:- [W] RealOf e ~ e- [W] Normed e--If we were to be adding the superclasses during simplification we'd get:- [W] RealOf e ~ e- [W] Normed e- [W] RealOf e ~ fuv- [W] Num fuv-==>- e := fuv, Num fuv, Normed fuv, RealOf fuv ~ fuv--While looks exactly like our original constraint. If we add the-superclass of (Normed fuv) again we'd loop. By adding superclasses-definitely only once, during canonicalisation, this situation can't-happen.--Note [Nested quantified constraint superclasses]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider (typecheck/should_compile/T17202)-- class C1 a- class (forall c. C1 c) => C2 a- class (forall b. (b ~ F a) => C2 a) => C3 a--Elsewhere in the code, we get a [G] g1 :: C3 a. We expand its superclass-to get [G] g2 :: (forall b. (b ~ F a) => C2 a). This constraint has a-superclass, as well. But we now must be careful: we cannot just add-(forall c. C1 c) as a Given, because we need to remember g2's context.-That new constraint is Given only when forall b. (b ~ F a) is true.--It's tempting to make the new Given be (forall b. (b ~ F a) => forall c. C1 c),-but that's problematic, because it's nested, and ForAllPred is not capable-of representing a nested quantified constraint. (We could change ForAllPred-to allow this, but the solution in this Note is much more local and simpler.)--So, we swizzle it around to get (forall b c. (b ~ F a) => C1 c).--More generally, if we are expanding the superclasses of- g0 :: forall tvs. theta => cls tys-and find a superclass constraint- forall sc_tvs. sc_theta => sc_inner_pred-we must have a selector- sel_id :: forall cls_tvs. cls cls_tvs -> forall sc_tvs. sc_theta => sc_inner_pred-and thus build- g_sc :: forall tvs sc_tvs. theta => sc_theta => sc_inner_pred- g_sc = /\ tvs. /\ sc_tvs. \ theta_ids. \ sc_theta_ids.- sel_id tys (g0 tvs theta_ids) sc_tvs sc_theta_ids--Actually, we cheat a bit by eta-reducing: note that sc_theta_ids are both the-last bound variables and the last arguments. This avoids the need to produce-the sc_theta_ids at all. So our final construction is-- g_sc = /\ tvs. /\ sc_tvs. \ theta_ids.- sel_id tys (g0 tvs theta_ids) sc_tvs-- -}--makeSuperClasses :: [Ct] -> TcS [Ct]--- Returns strict superclasses, transitively, see Note [The superclass story]--- See Note [The superclass story]--- The loop-breaking here follows Note [Expanding superclasses] in GHC.Tc.Utils.TcType--- Specifically, for an incoming (C t) constraint, we return all of (C t)'s--- superclasses, up to /and including/ the first repetition of C------ Example: class D a => C a--- class C [a] => D a--- makeSuperClasses (C x) will return (D x, C [x])------ NB: the incoming constraints have had their cc_pend_sc flag already--- flipped to False, by isPendingScDict, so we are /obliged/ to at--- least produce the immediate superclasses-makeSuperClasses cts = concatMapM go cts- where- go (CDictCan { cc_ev = ev, cc_class = cls, cc_tyargs = tys })- = mkStrictSuperClasses ev [] [] cls tys- go (CQuantCan (QCI { qci_pred = pred, qci_ev = ev }))- = assertPpr (isClassPred pred) (ppr pred) $ -- The cts should all have- -- class pred heads- mkStrictSuperClasses ev tvs theta cls tys- where- (tvs, theta, cls, tys) = tcSplitDFunTy (ctEvPred ev)- go ct = pprPanic "makeSuperClasses" (ppr ct)--mkStrictSuperClasses- :: CtEvidence- -> [TyVar] -> ThetaType -- These two args are non-empty only when taking- -- superclasses of a /quantified/ constraint- -> Class -> [Type] -> TcS [Ct]--- Return constraints for the strict superclasses of--- ev :: forall as. theta => cls tys-mkStrictSuperClasses ev tvs theta cls tys- = mk_strict_superclasses (unitNameSet (className cls))- ev tvs theta cls tys--mk_strict_superclasses :: NameSet -> CtEvidence- -> [TyVar] -> ThetaType- -> Class -> [Type] -> TcS [Ct]--- Always return the immediate superclasses of (cls tys);--- and expand their superclasses, provided none of them are in rec_clss--- nor are repeated-mk_strict_superclasses rec_clss (CtGiven { ctev_evar = evar, ctev_loc = loc })- tvs theta cls tys- = concatMapM do_one_given $- classSCSelIds cls- where- dict_ids = mkTemplateLocals theta- size = sizeTypes tys-- do_one_given sel_id- | isUnliftedType sc_pred- -- NB: class superclasses are never representation-polymorphic,- -- so isUnliftedType is OK here.- , not (null tvs && null theta)- = -- See Note [Equality superclasses in quantified constraints]- return []- | otherwise- = do { given_ev <- newGivenEvVar sc_loc $- mk_given_desc sel_id sc_pred- ; mk_superclasses rec_clss given_ev tvs theta sc_pred }- where- sc_pred = classMethodInstTy sel_id tys-- -- See Note [Nested quantified constraint superclasses]- mk_given_desc :: Id -> PredType -> (PredType, EvTerm)- mk_given_desc sel_id sc_pred- = (swizzled_pred, swizzled_evterm)- where- (sc_tvs, sc_rho) = splitForAllTyCoVars sc_pred- (sc_theta, sc_inner_pred) = splitFunTys sc_rho-- all_tvs = tvs `chkAppend` sc_tvs- all_theta = theta `chkAppend` (map scaledThing sc_theta)- swizzled_pred = mkInfSigmaTy all_tvs all_theta sc_inner_pred-- -- evar :: forall tvs. theta => cls tys- -- sel_id :: forall cls_tvs. cls cls_tvs- -- -> forall sc_tvs. sc_theta => sc_inner_pred- -- swizzled_evterm :: forall tvs sc_tvs. theta => sc_theta => sc_inner_pred- swizzled_evterm = EvExpr $- mkLams all_tvs $- mkLams dict_ids $- Var sel_id- `mkTyApps` tys- `App` (evId evar `mkVarApps` (tvs ++ dict_ids))- `mkVarApps` sc_tvs-- sc_loc- | isCTupleClass cls- = loc -- For tuple predicates, just take them apart, without- -- adding their (large) size into the chain. When we- -- get down to a base predicate, we'll include its size.- -- #10335-- | isEqPredClass cls- || cls `hasKey` coercibleTyConKey- = loc -- The only superclasses of ~, ~~, and Coercible are primitive- -- equalities, and they don't use the InstSCOrigin mechanism- -- detailed in Note [Solving superclass constraints] in- -- GHC.Tc.TyCl.Instance. Skip for a tiny performance win.-- -- See Note [Solving superclass constraints] in GHC.Tc.TyCl.Instance- -- for explantation of InstSCOrigin and Note [Replacement vs keeping] in- -- GHC.Tc.Solver.Interact for why we need OtherSCOrigin and depths- | otherwise- = loc { ctl_origin = new_orig }- where- new_orig = case ctLocOrigin loc of- -- these cases are when we have something that's already a superclass constraint- InstSCOrigin sc_depth n -> InstSCOrigin (sc_depth + 1) (n `max` size)- OtherSCOrigin sc_depth si -> OtherSCOrigin (sc_depth + 1) si-- -- these cases do not already have a superclass constraint: depth starts at 1- GivenOrigin InstSkol -> InstSCOrigin 1 size- GivenOrigin other_skol -> OtherSCOrigin 1 other_skol-- other_orig -> pprPanic "Given constraint without given origin" $- ppr evar $$ ppr other_orig--mk_strict_superclasses rec_clss ev tvs theta cls tys- | all noFreeVarsOfType tys- = return [] -- Wanteds with no variables yield no superclass constraints.- -- See Note [Improvement from Ground Wanteds]-- | otherwise -- Wanted case, just add Wanted superclasses- -- that can lead to improvement.- = assertPpr (null tvs && null theta) (ppr tvs $$ ppr theta) $- concatMapM do_one (immSuperClasses cls tys)- where- loc = ctEvLoc ev `updateCtLocOrigin` WantedSuperclassOrigin (ctEvPred ev)-- do_one sc_pred- = do { traceTcS "mk_strict_superclasses Wanted" (ppr (mkClassPred cls tys) $$ ppr sc_pred)- ; sc_ev <- newWantedNC loc (ctEvRewriters ev) sc_pred- ; mk_superclasses rec_clss sc_ev [] [] sc_pred }--{- Note [Improvement from Ground Wanteds]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Suppose class C b a => D a b-and consider- [W] D Int Bool-Is there any point in emitting [W] C Bool Int? No! The only point of-emitting superclass constraints for W constraints is to get-improvement, extra unifications that result from functional-dependencies. See Note [Why adding superclasses can help] above.--But no variables means no improvement; case closed.--}--mk_superclasses :: NameSet -> CtEvidence- -> [TyVar] -> ThetaType -> PredType -> TcS [Ct]--- Return this constraint, plus its superclasses, if any-mk_superclasses rec_clss ev tvs theta pred- | ClassPred cls tys <- classifyPredType pred- = mk_superclasses_of rec_clss ev tvs theta cls tys-- | otherwise -- Superclass is not a class predicate- = return [mkNonCanonical ev]--mk_superclasses_of :: NameSet -> CtEvidence- -> [TyVar] -> ThetaType -> Class -> [Type]- -> TcS [Ct]--- Always return this class constraint,--- and expand its superclasses-mk_superclasses_of rec_clss ev tvs theta cls tys- | loop_found = do { traceTcS "mk_superclasses_of: loop" (ppr cls <+> ppr tys)- ; return [this_ct] } -- cc_pend_sc of this_ct = True- | otherwise = do { traceTcS "mk_superclasses_of" (vcat [ ppr cls <+> ppr tys- , ppr (isCTupleClass cls)- , ppr rec_clss- ])- ; sc_cts <- mk_strict_superclasses rec_clss' ev tvs theta cls tys- ; return (this_ct : sc_cts) }- -- cc_pend_sc of this_ct = False- where- cls_nm = className cls- loop_found = not (isCTupleClass cls) && cls_nm `elemNameSet` rec_clss- -- Tuples never contribute to recursion, and can be nested- rec_clss' = rec_clss `extendNameSet` cls_nm-- this_ct | null tvs, null theta- = CDictCan { cc_ev = ev, cc_class = cls, cc_tyargs = tys- , cc_pend_sc = loop_found, cc_fundeps = classHasFds cls }- -- NB: If there is a loop, we cut off, so we have not- -- added the superclasses, hence cc_pend_sc = True- | otherwise- = CQuantCan (QCI { qci_tvs = tvs, qci_pred = mkClassPred cls tys- , qci_ev = ev- , qci_pend_sc = loop_found })---{- Note [Equality superclasses in quantified constraints]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider (#15359, #15593, #15625)- f :: (forall a. theta => a ~ b) => stuff--It's a bit odd to have a local, quantified constraint for `(a~b)`,-but some people want such a thing (see the tickets). And for-Coercible it is definitely useful- f :: forall m. (forall p q. Coercible p q => Coercible (m p) (m q)))- => stuff--Moreover it's not hard to arrange; we just need to look up /equality/-constraints in the quantified-constraint environment, which we do in-GHC.Tc.Solver.Interact.doTopReactOther.--There is a wrinkle though, in the case where 'theta' is empty, so-we have- f :: (forall a. a~b) => stuff--Now, potentially, the superclass machinery kicks in, in-makeSuperClasses, giving us a a second quantified constraint- (forall a. a ~# b)-BUT this is an unboxed value! And nothing has prepared us for-dictionary "functions" that are unboxed. Actually it does just-about work, but the simplifier ends up with stuff like- case (/\a. eq_sel d) of df -> ...(df @Int)...-and fails to simplify that any further. And it doesn't satisfy-isPredTy any more.--So for now we simply decline to take superclasses in the quantified-case. Instead we have a special case in GHC.Tc.Solver.Interact.doTopReactOther,-which looks for primitive equalities specially in the quantified-constraints.--See also Note [Evidence for quantified constraints] in GHC.Core.Predicate.---************************************************************************-* *-* Irreducibles canonicalization-* *-************************************************************************--}--canIrred :: CtEvidence -> TcS (StopOrContinue Ct)--- Precondition: ty not a tuple and no other evidence form-canIrred ev- = do { let pred = ctEvPred ev- ; traceTcS "can_pred" (text "IrredPred = " <+> ppr pred)- ; (redn, rewriters) <- rewrite ev pred- ; rewriteEvidence rewriters ev redn `andWhenContinue` \ new_ev ->-- do { -- Re-classify, in case rewriting has improved its shape- -- Code is like the canNC, except- -- that the IrredPred branch stops work- ; case classifyPredType (ctEvPred new_ev) of- ClassPred cls tys -> canClassNC new_ev cls tys- EqPred eq_rel ty1 ty2 -> -- IrredPreds have kind Constraint, so- -- cannot become EqPreds- pprPanic "canIrred: EqPred"- (ppr ev $$ ppr eq_rel $$ ppr ty1 $$ ppr ty2)- ForAllPred tvs th p -> -- this is highly suspect; Quick Look- -- should never leave a meta-var filled- -- in with a polytype. This is #18987.- do traceTcS "canEvNC:forall" (ppr pred)- canForAllNC ev tvs th p- IrredPred {} -> continueWith $- mkIrredCt IrredShapeReason new_ev } }--{- *********************************************************************-* *-* Quantified predicates-* *-********************************************************************* -}--{- Note [Quantified constraints]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-The -XQuantifiedConstraints extension allows type-class contexts like this:-- data Rose f x = Rose x (f (Rose f x))-- instance (Eq a, forall b. Eq b => Eq (f b))- => Eq (Rose f a) where- (Rose x1 rs1) == (Rose x2 rs2) = x1==x2 && rs1 == rs2--Note the (forall b. Eq b => Eq (f b)) in the instance contexts.-This quantified constraint is needed to solve the- [W] (Eq (f (Rose f x)))-constraint which arises form the (==) definition.--The wiki page is- https://gitlab.haskell.org/ghc/ghc/wikis/quantified-constraints-which in turn contains a link to the GHC Proposal where the change-is specified, and a Haskell Symposium paper about it.--We implement two main extensions to the design in the paper:-- 1. We allow a variable in the instance head, e.g.- f :: forall m a. (forall b. m b) => D (m a)- Notice the 'm' in the head of the quantified constraint, not- a class.-- 2. We support superclasses to quantified constraints.- For example (contrived):- f :: (Ord b, forall b. Ord b => Ord (m b)) => m a -> m a -> Bool- f x y = x==y- Here we need (Eq (m a)); but the quantified constraint deals only- with Ord. But we can make it work by using its superclass.--Here are the moving parts- * Language extension {-# LANGUAGE QuantifiedConstraints #-}- and add it to ghc-boot-th:GHC.LanguageExtensions.Type.Extension-- * A new form of evidence, EvDFun, that is used to discharge- such wanted constraints-- * checkValidType gets some changes to accept forall-constraints- only in the right places.-- * Predicate.Pred gets a new constructor ForAllPred, and- and classifyPredType analyses a PredType to decompose- the new forall-constraints-- * GHC.Tc.Solver.Monad.InertCans gets an extra field, inert_insts,- which holds all the Given forall-constraints. In effect,- such Given constraints are like local instance decls.-- * When trying to solve a class constraint, via- GHC.Tc.Solver.Interact.matchInstEnv, use the InstEnv from inert_insts- so that we include the local Given forall-constraints- in the lookup. (See GHC.Tc.Solver.Monad.getInstEnvs.)-- * GHC.Tc.Solver.Canonical.canForAll deals with solving a- forall-constraint. See- Note [Solving a Wanted forall-constraint]-- * We augment the kick-out code to kick out an inert- forall constraint if it can be rewritten by a new- type equality; see GHC.Tc.Solver.Monad.kick_out_rewritable--Note that a quantified constraint is never /inferred/-(by GHC.Tc.Solver.simplifyInfer). A function can only have a-quantified constraint in its type if it is given an explicit-type signature.---}--canForAllNC :: CtEvidence -> [TyVar] -> TcThetaType -> TcPredType- -> TcS (StopOrContinue Ct)-canForAllNC ev tvs theta pred- | isGiven ev -- See Note [Eagerly expand given superclasses]- , Just (cls, tys) <- cls_pred_tys_maybe- = do { sc_cts <- mkStrictSuperClasses ev tvs theta cls tys- ; emitWork sc_cts- ; canForAll ev False }-- | otherwise- = canForAll ev (isJust cls_pred_tys_maybe)-- where- cls_pred_tys_maybe = getClassPredTys_maybe pred--canForAll :: CtEvidence -> Bool -> TcS (StopOrContinue Ct)--- We have a constraint (forall as. blah => C tys)-canForAll ev pend_sc- = do { -- First rewrite it to apply the current substitution- let pred = ctEvPred ev- ; (redn, rewriters) <- rewrite ev pred- ; rewriteEvidence rewriters ev redn `andWhenContinue` \ new_ev ->-- do { -- Now decompose into its pieces and solve it- -- (It takes a lot less code to rewrite before decomposing.)- ; case classifyPredType (ctEvPred new_ev) of- ForAllPred tvs theta pred- -> solveForAll new_ev tvs theta pred pend_sc- _ -> pprPanic "canForAll" (ppr new_ev)- } }--solveForAll :: CtEvidence -> [TyVar] -> TcThetaType -> PredType -> Bool- -> TcS (StopOrContinue Ct)-solveForAll ev@(CtWanted { ctev_dest = dest, ctev_rewriters = rewriters, ctev_loc = loc })- tvs theta pred _pend_sc- = -- See Note [Solving a Wanted forall-constraint]- setLclEnv (ctLocEnv loc) $- -- This setLclEnv is important: the emitImplicationTcS uses that- -- TcLclEnv for the implication, and that in turn sets the location- -- for the Givens when solving the constraint (#21006)- do { skol_info <- mkSkolemInfo QuantCtxtSkol- ; let empty_subst = mkEmptyTCvSubst $ mkInScopeSet $- tyCoVarsOfTypes (pred:theta) `delVarSetList` tvs- ; (subst, skol_tvs) <- tcInstSkolTyVarsX skol_info empty_subst tvs- ; given_ev_vars <- mapM newEvVar (substTheta subst theta)-- ; (lvl, (w_id, wanteds))- <- pushLevelNoWorkList (ppr skol_info) $- do { wanted_ev <- newWantedEvVarNC loc rewriters $- substTy subst pred- ; return ( ctEvEvId wanted_ev- , unitBag (mkNonCanonical wanted_ev)) }-- ; ev_binds <- emitImplicationTcS lvl (getSkolemInfo skol_info) skol_tvs- given_ev_vars wanteds-- ; setWantedEvTerm dest $- EvFun { et_tvs = skol_tvs, et_given = given_ev_vars- , et_binds = ev_binds, et_body = w_id }-- ; stopWith ev "Wanted forall-constraint" }-- -- See Note [Solving a Given forall-constraint]-solveForAll ev@(CtGiven {}) tvs _theta pred pend_sc- = do { addInertForAll qci- ; stopWith ev "Given forall-constraint" }- where- qci = QCI { qci_ev = ev, qci_tvs = tvs- , qci_pred = pred, qci_pend_sc = pend_sc }--{- Note [Solving a Wanted forall-constraint]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Solving a wanted forall (quantified) constraint- [W] df :: forall ab. (Eq a, Ord b) => C x a b-is delightfully easy. Just build an implication constraint- forall ab. (g1::Eq a, g2::Ord b) => [W] d :: C x a-and discharge df thus:- df = /\ab. \g1 g2. let <binds> in d-where <binds> is filled in by solving the implication constraint.-All the machinery is to hand; there is little to do.--Note [Solving a Given forall-constraint]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-For a Given constraint- [G] df :: forall ab. (Eq a, Ord b) => C x a b-we just add it to TcS's local InstEnv of known instances,-via addInertForall. Then, if we look up (C x Int Bool), say,-we'll find a match in the InstEnv.--************************************************************************-* *-* Equalities-* *-************************************************************************--Note [Canonicalising equalities]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-In order to canonicalise an equality, we look at the structure of the-two types at hand, looking for similarities. A difficulty is that the-types may look dissimilar before rewriting but similar after rewriting.-However, we don't just want to jump in and rewrite right away, because-this might be wasted effort. So, after looking for similarities and failing,-we rewrite and then try again. Of course, we don't want to loop, so we-track whether or not we've already rewritten.--It is conceivable to do a better job at tracking whether or not a type-is rewritten, but this is left as future work. (Mar '15)--Note [Decomposing FunTy]-~~~~~~~~~~~~~~~~~~~~~~~~-can_eq_nc' may attempt to decompose a FunTy that is un-zonked. This-means that we may very well have a FunTy containing a type of some-unknown kind. For instance, we may have,-- FunTy (a :: k) Int--Where k is a unification variable. So the calls to getRuntimeRep_maybe may-fail (returning Nothing). In that case we'll fall through, zonk, and try again.-Zonking should fill the variable k, meaning that decomposition will succeed the-second time around.--Also note that we require the AnonArgFlag to match. This will stop-us decomposing- (Int -> Bool) ~ (Show a => blah)-It's as if we treat (->) and (=>) as different type constructors.--}--canEqNC :: CtEvidence -> EqRel -> Type -> Type -> TcS (StopOrContinue Ct)-canEqNC ev eq_rel ty1 ty2- = do { result <- zonk_eq_types ty1 ty2- ; case result of- Left (Pair ty1' ty2') -> can_eq_nc False ev eq_rel ty1' ty1 ty2' ty2- Right ty -> canEqReflexive ev eq_rel ty }--can_eq_nc- :: Bool -- True => both types are rewritten- -> CtEvidence- -> EqRel- -> Type -> Type -- LHS, after and before type-synonym expansion, resp- -> Type -> Type -- RHS, after and before type-synonym expansion, resp- -> TcS (StopOrContinue Ct)-can_eq_nc rewritten ev eq_rel ty1 ps_ty1 ty2 ps_ty2- = do { traceTcS "can_eq_nc" $- vcat [ ppr rewritten, ppr ev, ppr eq_rel, ppr ty1, ppr ps_ty1, ppr ty2, ppr ps_ty2 ]- ; rdr_env <- getGlobalRdrEnvTcS- ; fam_insts <- getFamInstEnvs- ; can_eq_nc' rewritten rdr_env fam_insts ev eq_rel ty1 ps_ty1 ty2 ps_ty2 }--can_eq_nc'- :: Bool -- True => both input types are rewritten- -> GlobalRdrEnv -- needed to see which newtypes are in scope- -> FamInstEnvs -- needed to unwrap data instances- -> CtEvidence- -> EqRel- -> Type -> Type -- LHS, after and before type-synonym expansion, resp- -> Type -> Type -- RHS, after and before type-synonym expansion, resp- -> TcS (StopOrContinue Ct)---- See Note [Comparing nullary type synonyms] in GHC.Core.Type.-can_eq_nc' _flat _rdr_env _envs ev eq_rel ty1@(TyConApp tc1 []) _ps_ty1 (TyConApp tc2 []) _ps_ty2- | tc1 == tc2- = canEqReflexive ev eq_rel ty1---- Expand synonyms first; see Note [Type synonyms and canonicalization]-can_eq_nc' rewritten rdr_env envs ev eq_rel ty1 ps_ty1 ty2 ps_ty2- | Just ty1' <- tcView ty1 = can_eq_nc' rewritten rdr_env envs ev eq_rel ty1' ps_ty1 ty2 ps_ty2- | Just ty2' <- tcView ty2 = can_eq_nc' rewritten rdr_env envs ev eq_rel ty1 ps_ty1 ty2' ps_ty2---- need to check for reflexivity in the ReprEq case.--- See Note [Eager reflexivity check]--- Check only when rewritten because the zonk_eq_types check in canEqNC takes--- care of the non-rewritten case.-can_eq_nc' True _rdr_env _envs ev ReprEq ty1 _ ty2 _- | ty1 `tcEqType` ty2- = canEqReflexive ev ReprEq ty1---- When working with ReprEq, unwrap newtypes.--- See Note [Unwrap newtypes first]--- This must be above the TyVarTy case, in order to guarantee (TyEq:N)-can_eq_nc' _rewritten rdr_env envs ev eq_rel ty1 ps_ty1 ty2 ps_ty2- | ReprEq <- eq_rel- , Just stuff1 <- tcTopNormaliseNewTypeTF_maybe envs rdr_env ty1- = can_eq_newtype_nc ev NotSwapped ty1 stuff1 ty2 ps_ty2-- | ReprEq <- eq_rel- , Just stuff2 <- tcTopNormaliseNewTypeTF_maybe envs rdr_env ty2- = can_eq_newtype_nc ev IsSwapped ty2 stuff2 ty1 ps_ty1---- Then, get rid of casts-can_eq_nc' rewritten _rdr_env _envs ev eq_rel (CastTy ty1 co1) _ ty2 ps_ty2- | isNothing (canEqLHS_maybe ty2) -- See (3) in Note [Equalities with incompatible kinds]- = canEqCast rewritten ev eq_rel NotSwapped ty1 co1 ty2 ps_ty2-can_eq_nc' rewritten _rdr_env _envs ev eq_rel ty1 ps_ty1 (CastTy ty2 co2) _- | isNothing (canEqLHS_maybe ty1) -- See (3) in Note [Equalities with incompatible kinds]- = canEqCast rewritten ev eq_rel IsSwapped ty2 co2 ty1 ps_ty1--------------------------- Otherwise try to decompose--------------------------- Literals-can_eq_nc' _rewritten _rdr_env _envs ev eq_rel ty1@(LitTy l1) _ (LitTy l2) _- | l1 == l2- = do { setEvBindIfWanted ev (evCoercion $ mkReflCo (eqRelRole eq_rel) ty1)- ; stopWith ev "Equal LitTy" }---- Decompose FunTy: (s -> t) and (c => t)--- NB: don't decompose (Int -> blah) ~ (Show a => blah)-can_eq_nc' _rewritten _rdr_env _envs ev eq_rel- (FunTy { ft_mult = am1, ft_af = af1, ft_arg = ty1a, ft_res = ty1b }) _ps_ty1- (FunTy { ft_mult = am2, ft_af = af2, ft_arg = ty2a, ft_res = ty2b }) _ps_ty2- | af1 == af2 -- Don't decompose (Int -> blah) ~ (Show a => blah)- , Just ty1a_rep <- getRuntimeRep_maybe ty1a -- getRutimeRep_maybe:- , Just ty1b_rep <- getRuntimeRep_maybe ty1b -- see Note [Decomposing FunTy]- , Just ty2a_rep <- getRuntimeRep_maybe ty2a- , Just ty2b_rep <- getRuntimeRep_maybe ty2b- = canDecomposableTyConAppOK ev eq_rel funTyCon- [am1, ty1a_rep, ty1b_rep, ty1a, ty1b]- [am2, ty2a_rep, ty2b_rep, ty2a, ty2b]---- Decompose type constructor applications--- NB: we have expanded type synonyms already-can_eq_nc' _rewritten _rdr_env _envs ev eq_rel ty1 _ ty2 _- | Just (tc1, tys1) <- tcSplitTyConApp_maybe ty1- , Just (tc2, tys2) <- tcSplitTyConApp_maybe ty2- -- we want to catch e.g. Maybe Int ~ (Int -> Int) here for better- -- error messages rather than decomposing into AppTys;- -- hence no direct match on TyConApp- , not (isTypeFamilyTyCon tc1)- , not (isTypeFamilyTyCon tc2)- = canTyConApp ev eq_rel tc1 tys1 tc2 tys2--can_eq_nc' _rewritten _rdr_env _envs ev eq_rel- s1@(ForAllTy (Bndr _ vis1) _) _- s2@(ForAllTy (Bndr _ vis2) _) _- | vis1 `sameVis` vis2 -- Note [ForAllTy and typechecker equality]- = can_eq_nc_forall ev eq_rel s1 s2---- See Note [Canonicalising type applications] about why we require rewritten types--- Use tcSplitAppTy, not matching on AppTy, to catch oversaturated type families--- NB: Only decompose AppTy for nominal equality. See Note [Decomposing equality]-can_eq_nc' True _rdr_env _envs ev NomEq ty1 _ ty2 _- | Just (t1, s1) <- tcSplitAppTy_maybe ty1- , Just (t2, s2) <- tcSplitAppTy_maybe ty2- = can_eq_app ev t1 s1 t2 s2------------------------ Can't decompose.------------------------ No similarity in type structure detected. Rewrite and try again.-can_eq_nc' False rdr_env envs ev eq_rel _ ps_ty1 _ ps_ty2- = do { (redn1@(Reduction _ xi1), rewriters1) <- rewrite ev ps_ty1- ; (redn2@(Reduction _ xi2), rewriters2) <- rewrite ev ps_ty2- ; new_ev <- rewriteEqEvidence (rewriters1 S.<> rewriters2) ev NotSwapped redn1 redn2- ; can_eq_nc' True rdr_env envs new_ev eq_rel xi1 xi1 xi2 xi2 }--------------------------------- Look for a canonical LHS. See Note [Canonical LHS].--- Only rewritten types end up below here.--------------------------------- NB: pattern match on True: we want only rewritten types sent to canEqLHS--- This means we've rewritten any variables and reduced any type family redexes--- See also Note [No top-level newtypes on RHS of representational equalities]-can_eq_nc' True _rdr_env _envs ev eq_rel ty1 ps_ty1 ty2 ps_ty2- | Just can_eq_lhs1 <- canEqLHS_maybe ty1- = canEqCanLHS ev eq_rel NotSwapped can_eq_lhs1 ps_ty1 ty2 ps_ty2-- | Just can_eq_lhs2 <- canEqLHS_maybe ty2- = canEqCanLHS ev eq_rel IsSwapped can_eq_lhs2 ps_ty2 ty1 ps_ty1-- -- If the type is TyConApp tc1 args1, then args1 really can't be less- -- than tyConArity tc1. It could be *more* than tyConArity, but then we- -- should have handled the case as an AppTy. That case only fires if- -- _both_ sides of the equality are AppTy-like... but if one side is- -- AppTy-like and the other isn't (and it also isn't a variable or- -- saturated type family application, both of which are handled by- -- can_eq_nc'), we're in a failure mode and can just fall through.--------------------------------- Fall-through. Give up.--------------------------------- We've rewritten and the types don't match. Give up.-can_eq_nc' True _rdr_env _envs ev eq_rel _ ps_ty1 _ ps_ty2- = do { traceTcS "can_eq_nc' catch-all case" (ppr ps_ty1 $$ ppr ps_ty2)- ; case eq_rel of -- See Note [Unsolved equalities]- ReprEq -> continueWith (mkIrredCt ReprEqReason ev)- NomEq -> continueWith (mkIrredCt ShapeMismatchReason ev) }- -- No need to call canEqFailure/canEqHardFailure because they- -- rewrite, and the types involved here are already rewritten--{- Note [Unsolved equalities]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-If we have an unsolved equality like- (a b ~R# Int)-that is not necessarily insoluble! Maybe 'a' will turn out to be a newtype.-So we want to make it a potentially-soluble Irred not an insoluble one.-Missing this point is what caused #15431--Note [ForAllTy and typechecker equality]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Should GHC type-check the following program (adapted from #15740)?-- {-# LANGUAGE PolyKinds, ... #-}- data D a- type family F :: forall k. k -> Type- type instance F = D--Due to the way F is declared, any instance of F must have a right-hand side-whose kind is equal to `forall k. k -> Type`. The kind of D is-`forall {k}. k -> Type`, which is very close, but technically uses distinct-Core:-- ------------------------------------------------------------ | Source Haskell | Core |- ------------------------------------------------------------ | forall k. <...> | ForAllTy (Bndr k Specified) (<...>) |- | forall {k}. <...> | ForAllTy (Bndr k Inferred) (<...>) |- -------------------------------------------------------------We could deem these kinds to be unequal, but that would imply rejecting-programs like the one above. Whether a kind variable binder ends up being-specified or inferred can be somewhat subtle, however, especially for kinds-that aren't explicitly written out in the source code (like in D above).-For now, we decide to not make the specified/inferred status of an invisible-type variable binder affect GHC's notion of typechecker equality-(see Note [Typechecker equality vs definitional equality] in-GHC.Tc.Utils.TcType). That is, we have the following:-- --------------------------------------------------- | Type 1 | Type 2 | Equal? |- --------------------|------------------------------ | forall k. <...> | forall k. <...> | Yes |- | | forall {k}. <...> | Yes |- | | forall k -> <...> | No |- --------------------------------------------------- | forall {k}. <...> | forall k. <...> | Yes |- | | forall {k}. <...> | Yes |- | | forall k -> <...> | No |- --------------------------------------------------- | forall k -> <...> | forall k. <...> | No |- | | forall {k}. <...> | No |- | | forall k -> <...> | Yes |- ----------------------------------------------------We implement this nuance by using the GHC.Types.Var.sameVis function in-GHC.Tc.Solver.Canonical.canEqNC and GHC.Tc.Utils.TcType.tcEqType, which-respect typechecker equality. sameVis puts both forms of invisible type-variable binders into the same equivalence class.--Note that we do /not/ use sameVis in GHC.Core.Type.eqType, which implements-/definitional/ equality, a slighty more coarse-grained notion of equality-(see Note [Non-trivial definitional equality] in GHC.Core.TyCo.Rep) that does-not consider the ArgFlag of ForAllTys at all. That is, eqType would equate all-of forall k. <...>, forall {k}. <...>, and forall k -> <...>.--}------------------------------------can_eq_nc_forall :: CtEvidence -> EqRel- -> Type -> Type -- LHS and RHS- -> TcS (StopOrContinue Ct)--- (forall as. phi1) ~ (forall bs. phi2)--- Check for length match of as, bs--- Then build an implication constraint: forall as. phi1 ~ phi2[as/bs]--- But remember also to unify the kinds of as and bs--- (this is the 'go' loop), and actually substitute phi2[as |> cos / bs]--- Remember also that we might have forall z (a:z). blah--- so we must proceed one binder at a time (#13879)--can_eq_nc_forall ev eq_rel s1 s2- | CtWanted { ctev_loc = loc, ctev_dest = orig_dest, ctev_rewriters = rewriters } <- ev- = do { let free_tvs = tyCoVarsOfTypes [s1,s2]- (bndrs1, phi1) = tcSplitForAllTyVarBinders s1- (bndrs2, phi2) = tcSplitForAllTyVarBinders s2- ; if not (equalLength bndrs1 bndrs2)- then do { traceTcS "Forall failure" $- vcat [ ppr s1, ppr s2, ppr bndrs1, ppr bndrs2- , ppr (map binderArgFlag bndrs1)- , ppr (map binderArgFlag bndrs2) ]- ; canEqHardFailure ev s1 s2 }- else- do { traceTcS "Creating implication for polytype equality" $ ppr ev- ; let empty_subst1 = mkEmptyTCvSubst $ mkInScopeSet free_tvs- ; skol_info <- mkSkolemInfo (UnifyForAllSkol phi1)- ; (subst1, skol_tvs) <- tcInstSkolTyVarsX skol_info empty_subst1 $- binderVars bndrs1-- ; let phi1' = substTy subst1 phi1-- -- Unify the kinds, extend the substitution- go :: [TcTyVar] -> TCvSubst -> [TyVarBinder]- -> TcS (TcCoercion, Cts)- go (skol_tv:skol_tvs) subst (bndr2:bndrs2)- = do { let tv2 = binderVar bndr2- ; (kind_co, wanteds1) <- unify loc rewriters Nominal (tyVarKind skol_tv)- (substTy subst (tyVarKind tv2))- ; let subst' = extendTvSubstAndInScope subst tv2- (mkCastTy (mkTyVarTy skol_tv) kind_co)- -- skol_tv is already in the in-scope set, but the- -- free vars of kind_co are not; hence "...AndInScope"- ; (co, wanteds2) <- go skol_tvs subst' bndrs2- ; return ( mkTcForAllCo skol_tv kind_co co- , wanteds1 `unionBags` wanteds2 ) }-- -- Done: unify phi1 ~ phi2- go [] subst bndrs2- = assert (null bndrs2) $- unify loc rewriters (eqRelRole eq_rel) phi1' (substTyUnchecked subst phi2)-- go _ _ _ = panic "cna_eq_nc_forall" -- case (s:ss) []-- empty_subst2 = mkEmptyTCvSubst (getTCvInScope subst1)-- ; (lvl, (all_co, wanteds)) <- pushLevelNoWorkList (ppr skol_info) $- go skol_tvs empty_subst2 bndrs2- ; emitTvImplicationTcS lvl (getSkolemInfo skol_info) skol_tvs wanteds-- ; setWantedEq orig_dest all_co- ; stopWith ev "Deferred polytype equality" } }-- | otherwise- = do { traceTcS "Omitting decomposition of given polytype equality" $- pprEq s1 s2 -- See Note [Do not decompose Given polytype equalities]- ; stopWith ev "Discard given polytype equality" }-- where- unify :: CtLoc -> RewriterSet -> Role -> TcType -> TcType -> TcS (TcCoercion, Cts)- -- This version returns the wanted constraint rather- -- than putting it in the work list- unify loc rewriters role ty1 ty2- | ty1 `tcEqType` ty2- = return (mkTcReflCo role ty1, emptyBag)- | otherwise- = do { (wanted, co) <- newWantedEq loc rewriters role ty1 ty2- ; return (co, unitBag (mkNonCanonical wanted)) }-------------------------------------- | Compare types for equality, while zonking as necessary. Gives up--- as soon as it finds that two types are not equal.--- This is quite handy when some unification has made two--- types in an inert Wanted to be equal. We can discover the equality without--- rewriting, which is sometimes very expensive (in the case of type functions).--- In particular, this function makes a ~20% improvement in test case--- perf/compiler/T5030.------ Returns either the (partially zonked) types in the case of--- inequality, or the one type in the case of equality. canEqReflexive is--- a good next step in the 'Right' case. Returning 'Left' is always safe.------ NB: This does *not* look through type synonyms. In fact, it treats type--- synonyms as rigid constructors. In the future, it might be convenient--- to look at only those arguments of type synonyms that actually appear--- in the synonym RHS. But we're not there yet.-zonk_eq_types :: TcType -> TcType -> TcS (Either (Pair TcType) TcType)-zonk_eq_types = go- where- go (TyVarTy tv1) (TyVarTy tv2) = tyvar_tyvar tv1 tv2- go (TyVarTy tv1) ty2 = tyvar NotSwapped tv1 ty2- go ty1 (TyVarTy tv2) = tyvar IsSwapped tv2 ty1-- -- We handle FunTys explicitly here despite the fact that they could also be- -- treated as an application. Why? Well, for one it's cheaper to just look- -- at two types (the argument and result types) than four (the argument,- -- result, and their RuntimeReps). Also, we haven't completely zonked yet,- -- so we may run into an unzonked type variable while trying to compute the- -- RuntimeReps of the argument and result types. This can be observed in- -- testcase tc269.- go ty1 ty2- | Just (Scaled w1 arg1, res1) <- split1- , Just (Scaled w2 arg2, res2) <- split2- , eqType w1 w2- = do { res_a <- go arg1 arg2- ; res_b <- go res1 res2- ; return $ combine_rev (mkVisFunTy w1) res_b res_a- }- | isJust split1 || isJust split2- = bale_out ty1 ty2- where- split1 = tcSplitFunTy_maybe ty1- split2 = tcSplitFunTy_maybe ty2-- go ty1 ty2- | Just (tc1, tys1) <- tcRepSplitTyConApp_maybe ty1- , Just (tc2, tys2) <- tcRepSplitTyConApp_maybe ty2- = if tc1 == tc2 && tys1 `equalLength` tys2- -- Crucial to check for equal-length args, because- -- we cannot assume that the two args to 'go' have- -- the same kind. E.g go (Proxy * (Maybe Int))- -- (Proxy (*->*) Maybe)- -- We'll call (go (Maybe Int) Maybe)- -- See #13083- then tycon tc1 tys1 tys2- else bale_out ty1 ty2-- go ty1 ty2- | Just (ty1a, ty1b) <- tcRepSplitAppTy_maybe ty1- , Just (ty2a, ty2b) <- tcRepSplitAppTy_maybe ty2- = do { res_a <- go ty1a ty2a- ; res_b <- go ty1b ty2b- ; return $ combine_rev mkAppTy res_b res_a }-- go ty1@(LitTy lit1) (LitTy lit2)- | lit1 == lit2- = return (Right ty1)-- go ty1 ty2 = bale_out ty1 ty2- -- We don't handle more complex forms here-- bale_out ty1 ty2 = return $ Left (Pair ty1 ty2)-- tyvar :: SwapFlag -> TcTyVar -> TcType- -> TcS (Either (Pair TcType) TcType)- -- Try to do as little as possible, as anything we do here is redundant- -- with rewriting. In particular, no need to zonk kinds. That's why- -- we don't use the already-defined zonking functions- tyvar swapped tv ty- = case tcTyVarDetails tv of- MetaTv { mtv_ref = ref }- -> do { cts <- readTcRef ref- ; case cts of- Flexi -> give_up- Indirect ty' -> do { trace_indirect tv ty'- ; unSwap swapped go ty' ty } }- _ -> give_up- where- give_up = return $ Left $ unSwap swapped Pair (mkTyVarTy tv) ty-- tyvar_tyvar tv1 tv2- | tv1 == tv2 = return (Right (mkTyVarTy tv1))- | otherwise = do { (ty1', progress1) <- quick_zonk tv1- ; (ty2', progress2) <- quick_zonk tv2- ; if progress1 || progress2- then go ty1' ty2'- else return $ Left (Pair (TyVarTy tv1) (TyVarTy tv2)) }-- trace_indirect tv ty- = traceTcS "Following filled tyvar (zonk_eq_types)"- (ppr tv <+> equals <+> ppr ty)-- quick_zonk tv = case tcTyVarDetails tv of- MetaTv { mtv_ref = ref }- -> do { cts <- readTcRef ref- ; case cts of- Flexi -> return (TyVarTy tv, False)- Indirect ty' -> do { trace_indirect tv ty'- ; return (ty', True) } }- _ -> return (TyVarTy tv, False)-- -- This happens for type families, too. But recall that failure- -- here just means to try harder, so it's OK if the type function- -- isn't injective.- tycon :: TyCon -> [TcType] -> [TcType]- -> TcS (Either (Pair TcType) TcType)- tycon tc tys1 tys2- = do { results <- zipWithM go tys1 tys2- ; return $ case combine_results results of- Left tys -> Left (mkTyConApp tc <$> tys)- Right tys -> Right (mkTyConApp tc tys) }-- combine_results :: [Either (Pair TcType) TcType]- -> Either (Pair [TcType]) [TcType]- combine_results = bimap (fmap reverse) reverse .- foldl' (combine_rev (:)) (Right [])-- -- combine (in reverse) a new result onto an already-combined result- combine_rev :: (a -> b -> c)- -> Either (Pair b) b- -> Either (Pair a) a- -> Either (Pair c) c- combine_rev f (Left list) (Left elt) = Left (f <$> elt <*> list)- combine_rev f (Left list) (Right ty) = Left (f <$> pure ty <*> list)- combine_rev f (Right tys) (Left elt) = Left (f <$> elt <*> pure tys)- combine_rev f (Right tys) (Right ty) = Right (f ty tys)--{- See Note [Unwrap newtypes first]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider- newtype N m a = MkN (m a)-Then N will get a conservative, Nominal role for its second parameter 'a',-because it appears as an argument to the unknown 'm'. Now consider- [W] N Maybe a ~R# N Maybe b--If we decompose, we'll get- [W] a ~N# b--But if instead we unwrap we'll get- [W] Maybe a ~R# Maybe b-which in turn gives us- [W] a ~R# b-which is easier to satisfy.--Bottom line: unwrap newtypes before decomposing them!-c.f. #9123 comment:52,53 for a compelling example.--Note [Newtypes can blow the stack]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Suppose we have-- newtype X = MkX (Int -> X)- newtype Y = MkY (Int -> Y)--and now wish to prove-- [W] X ~R Y--This Wanted will loop, expanding out the newtypes ever deeper looking-for a solid match or a solid discrepancy. Indeed, there is something-appropriate to this looping, because X and Y *do* have the same representation,-in the limit -- they're both (Fix ((->) Int)). However, no finitely-sized-coercion will ever witness it. This loop won't actually cause GHC to hang,-though, because we check our depth when unwrapping newtypes.--Note [Eager reflexivity check]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Suppose we have-- newtype X = MkX (Int -> X)--and-- [W] X ~R X--Naively, we would start unwrapping X and end up in a loop. Instead,-we do this eager reflexivity check. This is necessary only for representational-equality because the rewriter technology deals with the similar case-(recursive type families) for nominal equality.--Note that this check does not catch all cases, but it will catch the cases-we're most worried about, types like X above that are actually inhabited.--Here's another place where this reflexivity check is key:-Consider trying to prove (f a) ~R (f a). The AppTys in there can't-be decomposed, because representational equality isn't congruent with respect-to AppTy. So, when canonicalising the equality above, we get stuck and-would normally produce a CIrredCan. However, we really do want to-be able to solve (f a) ~R (f a). So, in the representational case only,-we do a reflexivity check.--(This would be sound in the nominal case, but unnecessary, and I [Richard-E.] am worried that it would slow down the common case.)--}----------------------------- | We're able to unwrap a newtype. Update the bits accordingly.-can_eq_newtype_nc :: CtEvidence -- ^ :: ty1 ~ ty2- -> SwapFlag- -> TcType -- ^ ty1- -> ((Bag GlobalRdrElt, TcCoercion), TcType) -- ^ :: ty1 ~ ty1'- -> TcType -- ^ ty2- -> TcType -- ^ ty2, with type synonyms- -> TcS (StopOrContinue Ct)-can_eq_newtype_nc ev swapped ty1 ((gres, co1), ty1') ty2 ps_ty2- = do { traceTcS "can_eq_newtype_nc" $- vcat [ ppr ev, ppr swapped, ppr co1, ppr gres, ppr ty1', ppr ty2 ]-- -- check for blowing our stack:- -- See Note [Newtypes can blow the stack]- ; checkReductionDepth (ctEvLoc ev) ty1-- -- Next, we record uses of newtype constructors, since coercing- -- through newtypes is tantamount to using their constructors.- ; addUsedGREs gre_list- -- If a newtype constructor was imported, don't warn about not- -- importing it...- ; traverse_ keepAlive $ map greMangledName gre_list- -- ...and similarly, if a newtype constructor was defined in the same- -- module, don't warn about it being unused.- -- See Note [Tracking unused binding and imports] in GHC.Tc.Utils.-- ; let redn1 = mkReduction co1 ty1'-- ; new_ev <- rewriteEqEvidence emptyRewriterSet ev swapped- redn1- (mkReflRedn Representational ps_ty2)- ; can_eq_nc False new_ev ReprEq ty1' ty1' ty2 ps_ty2 }- where- gre_list = bagToList gres-------------- ^ Decompose a type application.--- All input types must be rewritten. See Note [Canonicalising type applications]--- Nominal equality only!-can_eq_app :: CtEvidence -- :: s1 t1 ~N s2 t2- -> Xi -> Xi -- s1 t1- -> Xi -> Xi -- s2 t2- -> TcS (StopOrContinue Ct)---- AppTys only decompose for nominal equality, so this case just leads--- to an irreducible constraint; see typecheck/should_compile/T10494--- See Note [Decomposing AppTy at representational role]-can_eq_app ev s1 t1 s2 t2- | CtWanted { ctev_dest = dest, ctev_rewriters = rewriters } <- ev- = do { co_s <- unifyWanted rewriters loc Nominal s1 s2- ; let arg_loc- | isNextArgVisible s1 = loc- | otherwise = updateCtLocOrigin loc toInvisibleOrigin- ; co_t <- unifyWanted rewriters arg_loc Nominal t1 t2- ; let co = mkAppCo co_s co_t- ; setWantedEq dest co- ; stopWith ev "Decomposed [W] AppTy" }-- -- If there is a ForAll/(->) mismatch, the use of the Left coercion- -- below is ill-typed, potentially leading to a panic in splitTyConApp- -- Test case: typecheck/should_run/Typeable1- -- We could also include this mismatch check above (for W and D), but it's slow- -- and we'll get a better error message not doing it- | s1k `mismatches` s2k- = canEqHardFailure ev (s1 `mkAppTy` t1) (s2 `mkAppTy` t2)-- | CtGiven { ctev_evar = evar } <- ev- = do { let co = mkTcCoVarCo evar- co_s = mkTcLRCo CLeft co- co_t = mkTcLRCo CRight co- ; evar_s <- newGivenEvVar loc ( mkTcEqPredLikeEv ev s1 s2- , evCoercion co_s )- ; evar_t <- newGivenEvVar loc ( mkTcEqPredLikeEv ev t1 t2- , evCoercion co_t )- ; emitWorkNC [evar_t]- ; canEqNC evar_s NomEq s1 s2 }-- where- loc = ctEvLoc ev-- s1k = tcTypeKind s1- s2k = tcTypeKind s2-- k1 `mismatches` k2- = isForAllTy k1 && not (isForAllTy k2)- || not (isForAllTy k1) && isForAllTy k2---------------------------- | Break apart an equality over a casted type--- looking like (ty1 |> co1) ~ ty2 (modulo a swap-flag)-canEqCast :: Bool -- are both types rewritten?- -> CtEvidence- -> EqRel- -> SwapFlag- -> TcType -> Coercion -- LHS (res. RHS), ty1 |> co1- -> TcType -> TcType -- RHS (res. LHS), ty2 both normal and pretty- -> TcS (StopOrContinue Ct)-canEqCast rewritten ev eq_rel swapped ty1 co1 ty2 ps_ty2- = do { traceTcS "Decomposing cast" (vcat [ ppr ev- , ppr ty1 <+> text "|>" <+> ppr co1- , ppr ps_ty2 ])- ; new_ev <- rewriteEqEvidence emptyRewriterSet ev swapped- (mkGReflLeftRedn role ty1 co1)- (mkReflRedn role ps_ty2)- ; can_eq_nc rewritten new_ev eq_rel ty1 ty1 ty2 ps_ty2 }- where- role = eqRelRole eq_rel---------------------------canTyConApp :: CtEvidence -> EqRel- -> TyCon -> [TcType]- -> TyCon -> [TcType]- -> TcS (StopOrContinue Ct)--- See Note [Decomposing TyConApps]--- Neither tc1 nor tc2 is a saturated funTyCon-canTyConApp ev eq_rel tc1 tys1 tc2 tys2- | tc1 == tc2- , tys1 `equalLength` tys2- = do { inerts <- getTcSInerts- ; if can_decompose inerts- then canDecomposableTyConAppOK ev eq_rel tc1 tys1 tys2- else canEqFailure ev eq_rel ty1 ty2 }-- -- See Note [Skolem abstract data] in GHC.Core.Tycon- | tyConSkolem tc1 || tyConSkolem tc2- = do { traceTcS "canTyConApp: skolem abstract" (ppr tc1 $$ ppr tc2)- ; continueWith (mkIrredCt AbstractTyConReason ev) }-- -- Fail straight away for better error messages- -- See Note [Use canEqFailure in canDecomposableTyConApp]- | eq_rel == ReprEq && not (isGenerativeTyCon tc1 Representational &&- isGenerativeTyCon tc2 Representational)- = canEqFailure ev eq_rel ty1 ty2- | otherwise- = canEqHardFailure ev ty1 ty2- where- -- Reconstruct the types for error messages. This would do- -- the wrong thing (from a pretty printing point of view)- -- for functions, because we've lost the AnonArgFlag; but- -- in fact we never call canTyConApp on a saturated FunTyCon- ty1 = mkTyConApp tc1 tys1- ty2 = mkTyConApp tc2 tys2-- loc = ctEvLoc ev- pred = ctEvPred ev-- -- See Note [Decomposing equality]- can_decompose inerts- = isInjectiveTyCon tc1 (eqRelRole eq_rel)- || (ctEvFlavour ev /= Given && isEmptyBag (matchableGivens loc pred inerts))--{--Note [Use canEqFailure in canDecomposableTyConApp]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We must use canEqFailure, not canEqHardFailure here, because there is-the possibility of success if working with a representational equality.-Here is one case:-- type family TF a where TF Char = Bool- data family DF a- newtype instance DF Bool = MkDF Int--Suppose we are canonicalising (Int ~R DF (TF a)), where we don't yet-know `a`. This is *not* a hard failure, because we might soon learn-that `a` is, in fact, Char, and then the equality succeeds.--Here is another case:-- [G] Age ~R Int--where Age's constructor is not in scope. We don't want to report-an "inaccessible code" error in the context of this Given!--For example, see typecheck/should_compile/T10493, repeated here:-- import Data.Ord (Down) -- no constructor-- foo :: Coercible (Down Int) Int => Down Int -> Int- foo = coerce--That should compile, but only because we use canEqFailure and not-canEqHardFailure.--Note [Decomposing equality]-~~~~~~~~~~~~~~~~~~~~~~~~~~~-If we have a constraint (of any flavour and role) that looks like-T tys1 ~ T tys2, what can we conclude about tys1 and tys2? The answer,-of course, is "it depends". This Note spells it all out.--In this Note, "decomposition" refers to taking the constraint- [fl] (T tys1 ~X T tys2)-(for some flavour fl and some role X) and replacing it with- [fls'] (tys1 ~Xs' tys2)-where that notation indicates a list of new constraints, where the-new constraints may have different flavours and different roles.--The key property to consider is injectivity. When decomposing a Given, the-decomposition is sound if and only if T is injective in all of its type-arguments. When decomposing a Wanted, the decomposition is sound (assuming the-correct roles in the produced equality constraints), but it may be a guess ---that is, an unforced decision by the constraint solver. Decomposing Wanteds-over injective TyCons does not entail guessing. But sometimes we want to-decompose a Wanted even when the TyCon involved is not injective! (See below.)--So, in broad strokes, we want this rule:--(*) Decompose a constraint (T tys1 ~X T tys2) if and only if T is injective-at role X.--Pursuing the details requires exploring three axes:-* Flavour: Given vs. Wanted-* Role: Nominal vs. Representational-* TyCon species: datatype vs. newtype vs. data family vs. type family vs. type variable--(A type variable isn't a TyCon, of course, but it's convenient to put the AppTy case-in the same table.)--Here is a table (discussion following) detailing where decomposition of- (T s1 ... sn) ~r (T t1 .. tn)-is allowed. The first four lines (Data types ... type family) refer-to TyConApps with various TyCons T; the last line is for AppTy, covering-both where there is a type variable at the head and the case for an over--saturated type family.--NOMINAL GIVEN WANTED WHERE--Datatype YES YES canTyConApp-Newtype YES YES canTyConApp-Data family YES YES canTyConApp-Type family NO{1} YES, in injective args{1} canEqCanLHS2-AppTy YES YES can_eq_app--REPRESENTATIONAL GIVEN WANTED--Datatype YES YES canTyConApp-Newtype NO{2} MAYBE{2} canTyConApp(can_decompose)-Data family NO{3} MAYBE{3} canTyConApp(can_decompose)-Type family NO NO canEqCanLHS2-AppTy NO{4} NO{4} can_eq_nc'--{1}: Type families can be injective in some, but not all, of their arguments,-so we want to do partial decomposition. This is quite different than the way-other decomposition is done, where the decomposed equalities replace the original-one. We thus proceed much like we do with superclasses, emitting new Wanteds-when "decomposing" a partially-injective type family Wanted. Injective type-families have no corresponding evidence of their injectivity, so we cannot-decompose an injective-type-family Given.--{2}: See Note [Decomposing newtypes at representational role]--{3}: Because of the possibility of newtype instances, we must treat-data families like newtypes. See also-Note [Decomposing newtypes at representational role]. See #10534 and-test case typecheck/should_fail/T10534.--{4}: See Note [Decomposing AppTy at representational role]-- Because type variables can stand in for newtypes, we conservatively do not- decompose AppTys over representational equality. Here are two examples that- demonstrate why we can't:-- 4a: newtype Phant a = MkPhant Int- [W] alpha Int ~R beta Bool-- If we eventually solve alpha := Phant and beta := Phant, then we can solve- this equality by unwrapping. But it would have been disastrous to decompose- the wanted to produce Int ~ Bool, which is definitely insoluble.-- 4b: newtype Age = MkAge Int- [W] alpha Age ~R Maybe Int-- First, a question: if we know that ty1 ~R ty2, can we conclude that- a ty1 ~R a ty2? Not for all a. This is precisely why we need role annotations- on type constructors. So, if we were to decompose, we would need to- decompose to [W] alpha ~R Maybe and [W] Age ~ Int. On the other hand, if we- later solve alpha := Maybe, then we would decompose to [W] Age ~R Int, and- that would be soluble.--In the implementation of can_eq_nc and friends, we don't directly pattern-match using lines like in the tables above, as those tables don't cover-all cases (what about PrimTyCon? tuples?). Instead we just ask about injectivity,-boiling the tables above down to rule (*). The exceptions to rule (*) are for-injective type families, which are handled separately from other decompositions,-and the MAYBE entries above.--Note [Decomposing newtypes at representational role]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-This note discusses the 'newtype' line in the REPRESENTATIONAL table-in Note [Decomposing equality]. (At nominal role, newtypes are fully-decomposable.)--Here is a representative example of why representational equality over-newtypes is tricky:-- newtype Nt a = Mk Bool -- NB: a is not used in the RHS,- type role Nt representational -- but the user gives it an R role anyway--If we have [W] Nt alpha ~R Nt beta, we *don't* want to decompose to-[W] alpha ~R beta, because it's possible that alpha and beta aren't-representationally equal. Here's another example.-- newtype Nt a = MkNt (Id a)- type family Id a where Id a = a-- [W] Nt Int ~R Nt Age--Because of its use of a type family, Nt's parameter will get inferred to have-a nominal role. Thus, decomposing the wanted will yield [W] Int ~N Age, which-is unsatisfiable. Unwrapping, though, leads to a solution.--Conclusion:- * Unwrap newtypes before attempting to decompose them.- This is done in can_eq_nc'.--It all comes from the fact that newtypes aren't necessarily injective-w.r.t. representational equality.--Furthermore, as explained in Note [NthCo and newtypes] in GHC.Core.TyCo.Rep, we can't use-NthCo on representational coercions over newtypes. NthCo comes into play-only when decomposing givens.--Conclusion:- * Do not decompose [G] N s ~R N t--Is it sensible to decompose *Wanted* constraints over newtypes? Yes!-It's the only way we could ever prove (IO Int ~R IO Age), recalling-that IO is a newtype.--However we must be careful. Consider-- type role Nt representational-- [G] Nt a ~R Nt b (1)- [W] NT alpha ~R Nt b (2)- [W] alpha ~ a (3)--If we focus on (3) first, we'll substitute in (2), and now it's-identical to the given (1), so we succeed. But if we focus on (2)-first, and decompose it, we'll get (alpha ~R b), which is not soluble.-This is exactly like the question of overlapping Givens for class-constraints: see Note [Instance and Given overlap] in GHC.Tc.Solver.Interact.--Conclusion:- * Decompose [W] N s ~R N t iff there no given constraint that could- later solve it.--Note [Decomposing AppTy at representational role]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We never decompose AppTy at a representational role. For Givens, doing-so is simply unsound: the LRCo coercion former requires a nominal-roled-arguments. (See (1) for an example of why.) For Wanteds, decomposing-would be sound, but it would be a guess, and a non-confluent one at that.--Here is an example:-- [G] g1 :: a ~R b- [W] w1 :: Maybe b ~R alpha a- [W] w2 :: alpha ~ Maybe--Suppose we see w1 before w2. If we were to decompose, we would decompose-this to become-- [W] w3 :: Maybe ~R alpha- [W] w4 :: b ~ a--Note that w4 is *nominal*. A nominal role here is necessary because AppCo-requires a nominal role on its second argument. (See (2) for an example of-why.) If we decomposed w1 to w3,w4, we would then get stuck, because w4-is insoluble. On the other hand, if we see w2 first, setting alpha := Maybe,-all is well, as we can decompose Maybe b ~R Maybe a into b ~R a.--Another example:-- newtype Phant x = MkPhant Int-- [W] w1 :: Phant Int ~R alpha Bool- [W] w2 :: alpha ~ Phant--If we see w1 first, decomposing would be disastrous, as we would then try-to solve Int ~ Bool. Instead, spotting w2 allows us to simplify w1 to become-- [W] w1' :: Phant Int ~R Phant Bool--which can then (assuming MkPhant is in scope) be simplified to Int ~R Int,-and all will be well. See also Note [Unwrap newtypes first].--Bottom line: never decompose AppTy with representational roles.--(1) Decomposing a Given AppTy over a representational role is simply-unsound. For example, if we have co1 :: Phant Int ~R a Bool (for-the newtype Phant, above), then we surely don't want any relationship-between Int and Bool, lest we also have co2 :: Phant ~ a around.--(2) The role on the AppCo coercion is a conservative choice, because we don't-know the role signature of the function. For example, let's assume we could-have a representational role on the second argument of AppCo. Then, consider-- data G a where -- G will have a nominal role, as G is a GADT- MkG :: G Int- newtype Age = MkAge Int-- co1 :: G ~R a -- by assumption- co2 :: Age ~R Int -- by newtype axiom- co3 = AppCo co1 co2 :: G Age ~R a Int -- by our broken AppCo--and now co3 can be used to cast MkG to have type G Age, in violation of-the way GADTs are supposed to work (which is to use nominal equality).---}--canDecomposableTyConAppOK :: CtEvidence -> EqRel- -> TyCon -> [TcType] -> [TcType]- -> TcS (StopOrContinue Ct)--- Precondition: tys1 and tys2 are the same length, hence "OK"-canDecomposableTyConAppOK ev eq_rel tc tys1 tys2- = assert (tys1 `equalLength` tys2) $- do { traceTcS "canDecomposableTyConAppOK"- (ppr ev $$ ppr eq_rel $$ ppr tc $$ ppr tys1 $$ ppr tys2)- ; case ev of- CtWanted { ctev_dest = dest, ctev_rewriters = rewriters }- -- new_locs and tc_roles are both infinite, so- -- we are guaranteed that cos has the same length- -- as tys1 and tys2- -> do { cos <- zipWith4M (unifyWanted rewriters) new_locs tc_roles tys1 tys2- ; setWantedEq dest (mkTyConAppCo role tc cos) }-- CtGiven { ctev_evar = evar }- -> do { let ev_co = mkCoVarCo evar- ; given_evs <- newGivenEvVars loc $- [ ( mkPrimEqPredRole r ty1 ty2- , evCoercion $ mkNthCo r i ev_co )- | (r, ty1, ty2, i) <- zip4 tc_roles tys1 tys2 [0..]- , r /= Phantom- , not (isCoercionTy ty1) && not (isCoercionTy ty2) ]- ; emitWorkNC given_evs }-- ; stopWith ev "Decomposed TyConApp" }-- where- loc = ctEvLoc ev- role = eqRelRole eq_rel-- -- infinite, as tyConRolesX returns an infinite tail of Nominal- tc_roles = tyConRolesX role tc-- -- Add nuances to the location during decomposition:- -- * if the argument is a kind argument, remember this, so that error- -- messages say "kind", not "type". This is determined based on whether- -- the corresponding tyConBinder is named (that is, dependent)- -- * if the argument is invisible, note this as well, again by- -- looking at the corresponding binder- -- For oversaturated tycons, we need the (repeat loc) tail, which doesn't- -- do either of these changes. (Forgetting to do so led to #16188)- --- -- NB: infinite in length- new_locs = [ new_loc- | bndr <- tyConBinders tc- , let new_loc0 | isNamedTyConBinder bndr = toKindLoc loc- | otherwise = loc- new_loc | isInvisibleTyConBinder bndr- = updateCtLocOrigin new_loc0 toInvisibleOrigin- | otherwise- = new_loc0 ]- ++ repeat loc---- | Call when canonicalizing an equality fails, but if the equality is--- representational, there is some hope for the future.--- Examples in Note [Use canEqFailure in canDecomposableTyConApp]-canEqFailure :: CtEvidence -> EqRel- -> TcType -> TcType -> TcS (StopOrContinue Ct)-canEqFailure ev NomEq ty1 ty2- = canEqHardFailure ev ty1 ty2-canEqFailure ev ReprEq ty1 ty2- = do { (redn1, rewriters1) <- rewrite ev ty1- ; (redn2, rewriters2) <- rewrite ev ty2- -- We must rewrite the types before putting them in the- -- inert set, so that we are sure to kick them out when- -- new equalities become available- ; traceTcS "canEqFailure with ReprEq" $- vcat [ ppr ev, ppr redn1, ppr redn2 ]- ; new_ev <- rewriteEqEvidence (rewriters1 S.<> rewriters2) ev NotSwapped redn1 redn2- ; continueWith (mkIrredCt ReprEqReason new_ev) }---- | Call when canonicalizing an equality fails with utterly no hope.-canEqHardFailure :: CtEvidence- -> TcType -> TcType -> TcS (StopOrContinue Ct)--- See Note [Make sure that insolubles are fully rewritten]-canEqHardFailure ev ty1 ty2- = do { traceTcS "canEqHardFailure" (ppr ty1 $$ ppr ty2)- ; (redn1, rewriters1) <- rewrite ev ty1- ; (redn2, rewriters2) <- rewrite ev ty2- ; new_ev <- rewriteEqEvidence (rewriters1 S.<> rewriters2) ev NotSwapped redn1 redn2- ; continueWith (mkIrredCt ShapeMismatchReason new_ev) }--{--Note [Decomposing TyConApps]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~-If we see (T s1 t1 ~ T s2 t2), then we can just decompose to- (s1 ~ s2, t1 ~ t2)-and push those back into the work list. But if- s1 = K k1 s2 = K k2-then we will just decomopose s1~s2, and it might be better to-do so on the spot. An important special case is where s1=s2,-and we get just Refl.--So canDecomposableTyCon is a fast-path decomposition that uses-unifyWanted etc to short-cut that work.--Note [Canonicalising type applications]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Given (s1 t1) ~ ty2, how should we proceed?-The simple thing is to see if ty2 is of form (s2 t2), and-decompose.--However, over-eager decomposition gives bad error messages-for things like- a b ~ Maybe c- e f ~ p -> q-Suppose (in the first example) we already know a~Array. Then if we-decompose the application eagerly, yielding- a ~ Maybe- b ~ c-we get an error "Can't match Array ~ Maybe",-but we'd prefer to get "Can't match Array b ~ Maybe c".--So instead can_eq_wanted_app rewrites the LHS and RHS, in the hope of-replacing (a b) by (Array b), before using try_decompose_app to-decompose it.--Note [Make sure that insolubles are fully rewritten]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-When an equality fails, we still want to rewrite the equality-all the way down, so that it accurately reflects- (a) the mutable reference substitution in force at start of solving- (b) any ty-binds in force at this point in solving-See Note [Rewrite insolubles] in GHC.Tc.Solver.InertSet.-And if we don't do this there is a bad danger that-GHC.Tc.Solver.applyTyVarDefaulting will find a variable-that has in fact been substituted.--Note [Do not decompose Given polytype equalities]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider [G] (forall a. t1 ~ forall a. t2). Can we decompose this?-No -- what would the evidence look like? So instead we simply discard-this given evidence.--Note [No top-level newtypes on RHS of representational equalities]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Suppose we're in this situation:-- work item: [W] c1 : a ~R b- inert: [G] c2 : b ~R Id a--where- newtype Id a = Id a--We want to make sure canEqCanLHS sees [W] a ~R a, after b is rewritten-and the Id newtype is unwrapped. This is assured by requiring only rewritten-types in canEqCanLHS *and* having the newtype-unwrapping check above-the tyvar check in can_eq_nc.--Note [Put touchable variables on the left]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Ticket #10009, a very nasty example:-- f :: (UnF (F b) ~ b) => F b -> ()-- g :: forall a. (UnF (F a) ~ a) => a -> ()- g _ = f (undefined :: F a)--For g we get [G] g1 : UnF (F a) ~ a- [W] w1 : UnF (F beta) ~ beta- [W] w2 : F a ~ F beta--g1 is canonical (CEqCan). It is oriented as above because a is not touchable.-See canEqTyVarFunEq.--w1 is similarly canonical, though the occurs-check in canEqTyVarFunEq is key-here.--w2 is canonical. But which way should it be oriented? As written, we'll be-stuck. When w2 is added to the inert set, nothing gets kicked out: g1 is-a Given (and Wanteds don't rewrite Givens), and w2 doesn't mention the LHS-of w2. We'll thus lose.--But if w2 is swapped around, to-- [W] w3 : F beta ~ F a--then we'll kick w1 out of the inert-set (it mentions the LHS of w3). We then rewrite w1 to-- [W] w4 : UnF (F a) ~ beta--and then, using g1, to-- [W] w5 : a ~ beta--at which point we can unify and go on to glory. (This rewriting actually-happens all at once, in the call to rewrite during canonicalisation.)--But what about the new LHS makes it better? It mentions a variable (beta)-that can appear in a Wanted -- a touchable metavariable never appears-in a Given. On the other hand, the original LHS mentioned only variables-that appear in Givens. We thus choose to put variables that can appear-in Wanteds on the left.--Ticket #12526 is another good example of this in action.---}------------------------canEqCanLHS :: CtEvidence -- ev :: lhs ~ rhs- -> EqRel -> SwapFlag- -> CanEqLHS -- lhs (or, if swapped, rhs)- -> TcType -- lhs: pretty lhs, already rewritten- -> TcType -> TcType -- rhs: already rewritten- -> TcS (StopOrContinue Ct)-canEqCanLHS ev eq_rel swapped lhs1 ps_xi1 xi2 ps_xi2- | k1 `tcEqType` k2- = canEqCanLHSHomo ev eq_rel swapped lhs1 ps_xi1 xi2 ps_xi2-- | otherwise- = canEqCanLHSHetero ev eq_rel swapped lhs1 k1 xi2 k2-- where- k1 = canEqLHSKind lhs1- k2 = tcTypeKind xi2--canEqCanLHSHetero :: CtEvidence -- :: (xi1 :: ki1) ~ (xi2 :: ki2)- -> EqRel -> SwapFlag- -> CanEqLHS -- xi1- -> TcKind -- ki1- -> TcType -- xi2- -> TcKind -- ki2- -> TcS (StopOrContinue Ct)-canEqCanLHSHetero ev eq_rel swapped lhs1 ki1 xi2 ki2- -- See Note [Equalities with incompatible kinds]- = do { (kind_ev, kind_co) <- mk_kind_eq -- :: ki2 ~N ki1-- ; let -- kind_co :: (ki2 :: *) ~N (ki1 :: *) (whether swapped or not)- lhs_redn = mkReflRedn role xi1- rhs_redn = mkGReflRightRedn role xi2 kind_co-- -- See Note [Equalities with incompatible kinds], Wrinkle (1)- -- This will be ignored in rewriteEqEvidence if the work item is a Given- rewriters = rewriterSetFromCo kind_co-- ; traceTcS "Hetero equality gives rise to kind equality"- (ppr kind_co <+> dcolon <+> sep [ ppr ki2, text "~#", ppr ki1 ])- ; type_ev <- rewriteEqEvidence rewriters ev swapped lhs_redn rhs_redn-- ; emitWorkNC [type_ev] -- delay the type equality until after we've finished- -- the kind equality, which may unlock things- -- See Note [Equalities with incompatible kinds]-- ; canEqNC kind_ev NomEq ki2 ki1 }- where- mk_kind_eq :: TcS (CtEvidence, CoercionN)- mk_kind_eq = case ev of- CtGiven { ctev_evar = evar }- -> do { let kind_co = maybe_sym $ mkTcKindCo (mkTcCoVarCo evar) -- :: k2 ~ k1- ; kind_ev <- newGivenEvVar kind_loc (kind_pty, evCoercion kind_co)- ; return (kind_ev, ctEvCoercion kind_ev) }-- CtWanted { ctev_rewriters = rewriters }- -> newWantedEq kind_loc rewriters Nominal ki2 ki1-- xi1 = canEqLHSType lhs1- loc = ctev_loc ev- role = eqRelRole eq_rel- kind_loc = mkKindLoc xi1 xi2 loc- kind_pty = mkHeteroPrimEqPred liftedTypeKind liftedTypeKind ki2 ki1-- maybe_sym = case swapped of- IsSwapped -> id -- if the input is swapped, then we already- -- will have k2 ~ k1- NotSwapped -> mkTcSymCo---- guaranteed that tcTypeKind lhs == tcTypeKind rhs-canEqCanLHSHomo :: CtEvidence- -> EqRel -> SwapFlag- -> CanEqLHS -- lhs (or, if swapped, rhs)- -> TcType -- pretty lhs- -> TcType -> TcType -- rhs, pretty rhs- -> TcS (StopOrContinue Ct)-canEqCanLHSHomo ev eq_rel swapped lhs1 ps_xi1 xi2 ps_xi2- | (xi2', mco) <- split_cast_ty xi2- , Just lhs2 <- canEqLHS_maybe xi2'- = canEqCanLHS2 ev eq_rel swapped lhs1 ps_xi1 lhs2 (ps_xi2 `mkCastTyMCo` mkTcSymMCo mco) mco-- | otherwise- = canEqCanLHSFinish ev eq_rel swapped lhs1 ps_xi2-- where- split_cast_ty (CastTy ty co) = (ty, MCo co)- split_cast_ty other = (other, MRefl)---- This function deals with the case that both LHS and RHS are potential--- CanEqLHSs.-canEqCanLHS2 :: CtEvidence -- lhs ~ (rhs |> mco)- -- or, if swapped: (rhs |> mco) ~ lhs- -> EqRel -> SwapFlag- -> CanEqLHS -- lhs (or, if swapped, rhs)- -> TcType -- pretty lhs- -> CanEqLHS -- rhs- -> TcType -- pretty rhs- -> MCoercion -- :: kind(rhs) ~N kind(lhs)- -> TcS (StopOrContinue Ct)-canEqCanLHS2 ev eq_rel swapped lhs1 ps_xi1 lhs2 ps_xi2 mco- | lhs1 `eqCanEqLHS` lhs2- -- It must be the case that mco is reflexive- = canEqReflexive ev eq_rel (canEqLHSType lhs1)-- | TyVarLHS tv1 <- lhs1- , TyVarLHS tv2 <- lhs2- , swapOverTyVars (isGiven ev) tv1 tv2- = do { traceTcS "canEqLHS2 swapOver" (ppr tv1 $$ ppr tv2 $$ ppr swapped)- ; new_ev <- do_swap- ; canEqCanLHSFinish new_ev eq_rel IsSwapped (TyVarLHS tv2)- (ps_xi1 `mkCastTyMCo` sym_mco) }-- | TyVarLHS tv1 <- lhs1- , TyFamLHS fun_tc2 fun_args2 <- lhs2- = canEqTyVarFunEq ev eq_rel swapped tv1 ps_xi1 fun_tc2 fun_args2 ps_xi2 mco-- | TyFamLHS fun_tc1 fun_args1 <- lhs1- , TyVarLHS tv2 <- lhs2- = do { new_ev <- do_swap- ; canEqTyVarFunEq new_ev eq_rel IsSwapped tv2 ps_xi2- fun_tc1 fun_args1 ps_xi1 sym_mco }-- | TyFamLHS fun_tc1 fun_args1 <- lhs1- , TyFamLHS fun_tc2 fun_args2 <- lhs2- = do { traceTcS "canEqCanLHS2 two type families" (ppr lhs1 $$ ppr lhs2)-- -- emit wanted equalities for injective type families- ; let inj_eqns :: [TypeEqn] -- TypeEqn = Pair Type- inj_eqns- | ReprEq <- eq_rel = [] -- injectivity applies only for nom. eqs.- | fun_tc1 /= fun_tc2 = [] -- if the families don't match, stop.-- | Injective inj <- tyConInjectivityInfo fun_tc1- = [ Pair arg1 arg2- | (arg1, arg2, True) <- zip3 fun_args1 fun_args2 inj ]-- -- built-in synonym families don't have an entry point- -- for this use case. So, we just use sfInteractInert- -- and pass two equal RHSs. We *could* add another entry- -- point, but then there would be a burden to make- -- sure the new entry point and existing ones were- -- internally consistent. This is slightly distasteful,- -- but it works well in practice and localises the- -- problem.- | Just ops <- isBuiltInSynFamTyCon_maybe fun_tc1- = let ki1 = canEqLHSKind lhs1- ki2 | MRefl <- mco- = ki1 -- just a small optimisation- | otherwise- = canEqLHSKind lhs2-- fake_rhs1 = anyTypeOfKind ki1- fake_rhs2 = anyTypeOfKind ki2- in- sfInteractInert ops fun_args1 fake_rhs1 fun_args2 fake_rhs2-- | otherwise -- ordinary, non-injective type family- = []-- ; case ev of- CtWanted { ctev_rewriters = rewriters } ->- mapM_ (\ (Pair t1 t2) -> unifyWanted rewriters (ctEvLoc ev) Nominal t1 t2) inj_eqns- CtGiven {} -> return ()- -- See Note [No Given/Given fundeps] in GHC.Tc.Solver.Interact-- ; tclvl <- getTcLevel- ; let tvs1 = tyCoVarsOfTypes fun_args1- tvs2 = tyCoVarsOfTypes fun_args2-- swap_for_rewriting = anyVarSet (isTouchableMetaTyVar tclvl) tvs2 &&- -- swap 'em: Note [Put touchable variables on the left]- not (anyVarSet (isTouchableMetaTyVar tclvl) tvs1)- -- this check is just to avoid unfruitful swapping-- -- If we have F a ~ F (F a), we want to swap.- swap_for_occurs- | cterHasNoProblem $ checkTyFamEq fun_tc2 fun_args2- (mkTyConApp fun_tc1 fun_args1)- , cterHasOccursCheck $ checkTyFamEq fun_tc1 fun_args1- (mkTyConApp fun_tc2 fun_args2)- = True-- | otherwise- = False-- ; if swap_for_rewriting || swap_for_occurs- then do { new_ev <- do_swap- ; canEqCanLHSFinish new_ev eq_rel IsSwapped lhs2 (ps_xi1 `mkCastTyMCo` sym_mco) }- else finish_without_swapping }-- -- that's all the special cases. Now we just figure out which non-special case- -- to continue to.- | otherwise- = finish_without_swapping-- where- sym_mco = mkTcSymMCo mco-- do_swap = rewriteCastedEquality ev eq_rel swapped (canEqLHSType lhs1) (canEqLHSType lhs2) mco- finish_without_swapping = canEqCanLHSFinish ev eq_rel swapped lhs1 (ps_xi2 `mkCastTyMCo` mco)----- This function handles the case where one side is a tyvar and the other is--- a type family application. Which to put on the left?--- If the tyvar is a touchable meta-tyvar, put it on the left, as this may--- be our only shot to unify.--- Otherwise, put the function on the left, because it's generally better to--- rewrite away function calls. This makes types smaller. And it seems necessary:--- [W] F alpha ~ alpha--- [W] F alpha ~ beta--- [W] G alpha beta ~ Int ( where we have type instance G a a = a )--- If we end up with a stuck alpha ~ F alpha, we won't be able to solve this.--- Test case: indexed-types/should_compile/CEqCanOccursCheck-canEqTyVarFunEq :: CtEvidence -- :: lhs ~ (rhs |> mco)- -- or (rhs |> mco) ~ lhs if swapped- -> EqRel -> SwapFlag- -> TyVar -> TcType -- lhs (or if swapped rhs), pretty lhs- -> TyCon -> [Xi] -> TcType -- rhs (or if swapped lhs) fun and args, pretty rhs- -> MCoercion -- :: kind(rhs) ~N kind(lhs)- -> TcS (StopOrContinue Ct)-canEqTyVarFunEq ev eq_rel swapped tv1 ps_xi1 fun_tc2 fun_args2 ps_xi2 mco- = do { (is_touchable, rhs) <- touchabilityTest (ctEvFlavour ev) tv1 rhs- ; if | case is_touchable of { Untouchable -> False; _ -> True }- , cterHasNoProblem $- checkTyVarEq tv1 rhs `cterRemoveProblem` cteTypeFamily- -> canEqCanLHSFinish ev eq_rel swapped (TyVarLHS tv1) rhs-- | otherwise- -> do { new_ev <- rewriteCastedEquality ev eq_rel swapped- (mkTyVarTy tv1) (mkTyConApp fun_tc2 fun_args2)- mco- ; canEqCanLHSFinish new_ev eq_rel IsSwapped- (TyFamLHS fun_tc2 fun_args2)- (ps_xi1 `mkCastTyMCo` sym_mco) } }- where- sym_mco = mkTcSymMCo mco- rhs = ps_xi2 `mkCastTyMCo` mco---- The RHS here is either not CanEqLHS, or it's one that we--- want to rewrite the LHS to (as per e.g. swapOverTyVars)-canEqCanLHSFinish :: CtEvidence- -> EqRel -> SwapFlag- -> CanEqLHS -- lhs (or, if swapped, rhs)- -> TcType -- rhs (or, if swapped, lhs)- -> TcS (StopOrContinue Ct)-canEqCanLHSFinish ev eq_rel swapped lhs rhs--- RHS is fully rewritten, but with type synonyms--- preserved as much as possible--- guaranteed that tyVarKind lhs == typeKind rhs, for (TyEq:K)--- (TyEq:N) is checked in can_eq_nc', and (TyEq:TV) is handled in canEqCanLHS2-- = do {- -- this performs the swap if necessary- new_ev <- rewriteEqEvidence emptyRewriterSet ev swapped- (mkReflRedn role lhs_ty)- (mkReflRedn role rhs)-- -- by now, (TyEq:K) is already satisfied- ; massert (canEqLHSKind lhs `eqType` tcTypeKind rhs)-- -- by now, (TyEq:N) is already satisfied (if applicable)- ; assertPprM ty_eq_N_OK $- vcat [ text "CanEqCanLHSFinish: (TyEq:N) not satisfied"- , text "rhs:" <+> ppr rhs- ]-- -- guarantees (TyEq:OC), (TyEq:F)- -- Must do the occurs check even on tyvar/tyvar- -- equalities, in case have x ~ (y :: ..x...); this is #12593.- ; let result0 = checkTypeEq lhs rhs `cterRemoveProblem` cteTypeFamily- -- type families are OK here- -- NB: no occCheckExpand here; see Note [Rewriting synonyms] in GHC.Tc.Solver.Rewrite-- -- a ~R# b a is soluble if b later turns out to be Identity- result = case eq_rel of- NomEq -> result0- ReprEq -> cterSetOccursCheckSoluble result0-- reason = NonCanonicalReason result-- ; if cterHasNoProblem result- then do { traceTcS "CEqCan" (ppr lhs $$ ppr rhs)- ; continueWith (CEqCan { cc_ev = new_ev, cc_lhs = lhs- , cc_rhs = rhs, cc_eq_rel = eq_rel }) }-- else do { m_stuff <- breakTyEqCycle_maybe ev result lhs rhs- -- See Note [Type equality cycles];- -- returning Nothing is the vastly common case- ; case m_stuff of- { Nothing ->- do { traceTcS "canEqCanLHSFinish can't make a canonical"- (ppr lhs $$ ppr rhs)- ; continueWith (mkIrredCt reason new_ev) }- ; Just rhs_redn@(Reduction _ new_rhs) ->- do { traceTcS "canEqCanLHSFinish breaking a cycle" $- ppr lhs $$ ppr rhs- ; traceTcS "new RHS:" (ppr new_rhs)-- -- This check is Detail (1) in the Note- ; if cterHasOccursCheck (checkTypeEq lhs new_rhs)-- then do { traceTcS "Note [Type equality cycles] Detail (1)"- (ppr new_rhs)- ; continueWith (mkIrredCt reason new_ev) }-- else do { -- See Detail (6) of Note [Type equality cycles]- new_new_ev <- rewriteEqEvidence emptyRewriterSet- new_ev NotSwapped- (mkReflRedn Nominal lhs_ty)- rhs_redn-- ; continueWith (CEqCan { cc_ev = new_new_ev- , cc_lhs = lhs- , cc_rhs = new_rhs- , cc_eq_rel = eq_rel }) }}}}}- where- role = eqRelRole eq_rel-- lhs_ty = canEqLHSType lhs-- -- This is about (TyEq:N): check that we don't have a newtype- -- whose constructor is in scope at the top-level of the RHS.- ty_eq_N_OK :: TcS Bool- ty_eq_N_OK- | ReprEq <- eq_rel- , Just tc <- tyConAppTyCon_maybe rhs- , Just con <- newTyConDataCon_maybe tc- -- #21010: only a problem if the newtype constructor is in scope- -- yet we didn't rewrite it away.- = do { rdr_env <- getGlobalRdrEnvTcS- ; let con_in_scope = isJust $ lookupGRE_Name rdr_env (dataConName con)- ; return $ not con_in_scope }- | otherwise- = return True---- | Solve a reflexive equality constraint-canEqReflexive :: CtEvidence -- ty ~ ty- -> EqRel- -> TcType -- ty- -> TcS (StopOrContinue Ct) -- always Stop-canEqReflexive ev eq_rel ty- = do { setEvBindIfWanted ev (evCoercion $- mkTcReflCo (eqRelRole eq_rel) ty)- ; stopWith ev "Solved by reflexivity" }--rewriteCastedEquality :: CtEvidence -- :: lhs ~ (rhs |> mco), or (rhs |> mco) ~ lhs- -> EqRel -> SwapFlag- -> TcType -- lhs- -> TcType -- rhs- -> MCoercion -- mco- -> TcS CtEvidence -- :: (lhs |> sym mco) ~ rhs- -- result is independent of SwapFlag-rewriteCastedEquality ev eq_rel swapped lhs rhs mco- = rewriteEqEvidence emptyRewriterSet ev swapped lhs_redn rhs_redn- where- lhs_redn = mkGReflRightMRedn role lhs sym_mco- rhs_redn = mkGReflLeftMRedn role rhs mco-- sym_mco = mkTcSymMCo mco- role = eqRelRole eq_rel--{- Note [Equalities with incompatible kinds]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-What do we do when we have an equality-- (tv :: k1) ~ (rhs :: k2)--where k1 and k2 differ? Easy: we create a coercion that relates k1 and-k2 and use this to cast. To wit, from-- [X] (tv :: k1) ~ (rhs :: k2)--(where [X] is [G] or [W]), we go to-- [X] co :: k2 ~ k1- [X] (tv :: k1) ~ ((rhs |> co) :: k1)--We carry on with the *kind equality*, not the type equality, because-solving the former may unlock the latter. This choice is made in-canEqCanLHSHetero. It is important: otherwise, T13135 loops.--Wrinkles:-- (1) When X is W, the new type-level wanted is effectively rewritten by the- kind-level one. We thus include the kind-level wanted in the RewriterSet- for the type-level one. See Note [Wanteds rewrite Wanteds] in GHC.Tc.Types.Constraint.- This is done in canEqCanLHSHetero.-- (2) If we have [W] w :: alpha ~ (rhs |> co_hole), should we unify alpha? No.- The problem is that the wanted w is effectively rewritten by another wanted,- and unifying alpha effectively promotes this wanted to a given. Doing so- means we lose track of the rewriter set associated with the wanted.-- On the other hand, w is perfectly suitable for rewriting, because of the- way we carefully track rewriter sets.-- We thus allow w to be a CEqCan, but we prevent unification. See- Note [Unification preconditions] in GHC.Tc.Utils.Unify.-- The only tricky part is that we must later indeed unify if/when the kind-level- wanted gets solved. This is done in kickOutAfterFillingCoercionHole,- which kicks out all equalities whose RHS mentions the filled-in coercion hole.- Note that it looks for type family equalities, too, because of the use of- unifyTest in canEqTyVarFunEq.-- (3) Suppose we have [W] (a :: k1) ~ (rhs :: k2). We duly follow the- algorithm detailed here, producing [W] co :: k2 ~ k1, and adding- [W] (a :: k1) ~ ((rhs |> co) :: k1) to the irreducibles. Some time- later, we solve co, and fill in co's coercion hole. This kicks out- the irreducible as described in (2).- But now, during canonicalization, we see the cast- and remove it, in canEqCast. By the time we get into canEqCanLHS, the equality- is heterogeneous again, and the process repeats.-- To avoid this, we don't strip casts off a type if the other type- in the equality is a CanEqLHS (the scenario above can happen with a- type family, too. testcase: typecheck/should_compile/T13822).- And this is an improvement regardless:- because tyvars can, generally, unify with casted types, there's no- reason to go through the work of stripping off the cast when the- cast appears opposite a tyvar. This is implemented in the cast case- of can_eq_nc'.--Historical note:--We used to do this via emitting a Derived kind equality and then parking-the heterogeneous equality as irreducible. But this new approach is much-more direct. And it doesn't produce duplicate Deriveds (as the old one did).--Note [Type synonyms and canonicalization]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-We treat type synonym applications as xi types, that is, they do not-count as type function applications. However, we do need to be a bit-careful with type synonyms: like type functions they may not be-generative or injective. However, unlike type functions, they are-parametric, so there is no problem in expanding them whenever we see-them, since we do not need to know anything about their arguments in-order to expand them; this is what justifies not having to treat them-as specially as type function applications. The thing that causes-some subtleties is that we prefer to leave type synonym applications-*unexpanded* whenever possible, in order to generate better error-messages.--If we encounter an equality constraint with type synonym applications-on both sides, or a type synonym application on one side and some sort-of type application on the other, we simply must expand out the type-synonyms in order to continue decomposing the equality constraint into-primitive equality constraints. For example, suppose we have-- type F a = [Int]--and we encounter the equality-- F a ~ [b]--In order to continue we must expand F a into [Int], giving us the-equality-- [Int] ~ [b]--which we can then decompose into the more primitive equality-constraint-- Int ~ b.--However, if we encounter an equality constraint with a type synonym-application on one side and a variable on the other side, we should-NOT (necessarily) expand the type synonym, since for the purpose of-good error messages we want to leave type synonyms unexpanded as much-as possible. Hence the ps_xi1, ps_xi2 argument passed to canEqCanLHS.--Note [Type equality cycles]-~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider this situation (from indexed-types/should_compile/GivenLoop):-- instance C (Maybe b)- *[G] a ~ Maybe (F a)- [W] C a--or (typecheck/should_compile/T19682b):-- instance C (a -> b)- *[W] alpha ~ (Arg alpha -> Res alpha)- [W] C alpha--or (typecheck/should_compile/T21515):-- type family Code a- *[G] Code a ~ '[ '[ Head (Head (Code a)) ] ]- [W] Code a ~ '[ '[ alpha ] ]--In order to solve the final Wanted, we must use the starred constraint-for rewriting. But note that all starred constraints have occurs-check failures,-and so we can't straightforwardly add these to the inert set and-use them for rewriting. (NB: A rigid type constructor is at the-top of all RHSs, preventing reorienting in canEqTyVarFunEq in the tyvar-cases.)--The key idea is to replace the outermost type family applications in the RHS of the-starred constraints with a fresh variable, which we'll call a cycle-breaker-variable, or cbv. Then, relate the cbv back with the original type family application-via new equality constraints. Our situations thus become:-- instance C (Maybe b)- [G] a ~ Maybe cbv- [G] F a ~ cbv- [W] C a--or-- instance C (a -> b)- [W] alpha ~ (cbv1 -> cbv2)- [W] Arg alpha ~ cbv1- [W] Res alpha ~ cbv2- [W] C alpha--or-- [G] Code a ~ '[ '[ cbv ] ]- [G] Head (Head (Code a)) ~ cbv- [W] Code a ~ '[ '[ alpha ] ]--This transformation (creating the new types and emitting new equality-constraints) is done in breakTyEqCycle_maybe.--The details depend on whether we're working with a Given or a Wanted.--Given--------We emit a new Given, [G] F a ~ cbv, equating the type family application to-our new cbv. Note its orientation: The type family ends up on the left; see-commentary on canEqTyVarFunEq, which decides how to orient such cases. No-special treatment for CycleBreakerTvs is necessary. This scenario is now-easily soluble, by using the first Given to rewrite the Wanted, which can now-be solved.--(The first Given actually also rewrites the second one, giving-[G] F (Maybe cbv) ~ cbv, but this causes no trouble.)--Of course, we don't want our fresh variables leaking into e.g. error messages.-So we fill in the metavariables with their original type family applications-after we're done running the solver (in nestImplicTcS and runTcSWithEvBinds).-This is done by restoreTyVarCycles, which uses the inert_cycle_breakers field in-InertSet, which contains the pairings invented in breakTyEqCycle_maybe.--That is:--We transform- [G] g : lhs ~ ...(F lhs)...-to- [G] (Refl lhs) : F lhs ~ cbv -- CEqCan- [G] g : lhs ~ ...cbv... -- CEqCan--Note that-* `cbv` is a fresh cycle breaker variable.-* `cbv` is a is a meta-tyvar, but it is completely untouchable.-* We track the cycle-breaker variables in inert_cycle_breakers in InertSet-* We eventually fill in the cycle-breakers, with `cbv := F lhs`.- No one else fills in cycle-breakers!-* The evidence for the new `F lhs ~ cbv` constraint is Refl, because we know- this fill-in is ultimately going to happen.-* In inert_cycle_breakers, we remember the (cbv, F lhs) pair; that is, we- remember the /original/ type. The [G] F lhs ~ cbv constraint may be rewritten- by other givens (eg if we have another [G] lhs ~ (b,c)), but at the end we- still fill in with cbv := F lhs-* This fill-in is done when solving is complete, by restoreTyVarCycles- in nestImplicTcS and runTcSWithEvBinds.--Wanted--------The fresh cycle-breaker variables here must actually be normal, touchable-metavariables. That is, they are TauTvs. Nothing at all unusual. Repeating-the example from above, we have-- *[W] alpha ~ (Arg alpha -> Res alpha)--and we turn this into-- *[W] alpha ~ (cbv1 -> cbv2)- [W] Arg alpha ~ cbv1- [W] Res alpha ~ cbv2--where cbv1 and cbv2 are fresh TauTvs. Why TauTvs? See [Why TauTvs] below.--Critically, we emit the two new constraints (the last two above)-directly instead of calling unifyWanted. (Otherwise, we'd end up unifying cbv1-and cbv2 immediately, achieving nothing.)-Next, we unify alpha := cbv1 -> cbv2, having eliminated the occurs check. This-unification -- which must be the next step after breaking the cycles ---happens in the course of normal behavior of top-level-interactions, later in the solver pipeline. We know this unification will-indeed happen because breakTyEqCycle_maybe, which decides whether to apply-this logic, checks to ensure unification will succeed in its final_check.-(In particular, the LHS must be a touchable tyvar, never a type family. We don't-yet have an example of where this logic is needed with a type family, and it's-unclear how to handle this case, so we're skipping for now.) Now, we're-here (including further context from our original example, from the top of the-Note):-- instance C (a -> b)- [W] Arg (cbv1 -> cbv2) ~ cbv1- [W] Res (cbv1 -> cbv2) ~ cbv2- [W] C (cbv1 -> cbv2)--The first two W constraints reduce to reflexivity and are discarded,-and the last is easily soluble.--[Why TauTvs]:-Let's look at another example (typecheck/should_compile/T19682) where we need-to unify the cbvs:-- class (AllEqF xs ys, SameShapeAs xs ys) => AllEq xs ys- instance (AllEqF xs ys, SameShapeAs xs ys) => AllEq xs ys-- type family SameShapeAs xs ys :: Constraint where- SameShapeAs '[] ys = (ys ~ '[])- SameShapeAs (x : xs) ys = (ys ~ (Head ys : Tail ys))-- type family AllEqF xs ys :: Constraint where- AllEqF '[] '[] = ()- AllEqF (x : xs) (y : ys) = (x ~ y, AllEq xs ys)-- [W] alpha ~ (Head alpha : Tail alpha)- [W] AllEqF '[Bool] alpha--Without the logic detailed in this Note, we're stuck here, as AllEqF cannot-reduce and alpha cannot unify. Let's instead apply our cycle-breaker approach,-just as described above. We thus invent cbv1 and cbv2 and unify-alpha := cbv1 -> cbv2, yielding (after zonking)-- [W] Head (cbv1 : cbv2) ~ cbv1- [W] Tail (cbv1 : cbv2) ~ cbv2- [W] AllEqF '[Bool] (cbv1 : cbv2)--The first two W constraints simplify to reflexivity and are discarded.-But the last reduces:-- [W] Bool ~ cbv1- [W] AllEq '[] cbv2--The first of these is solved by unification: cbv1 := Bool. The second-is solved by the instance for AllEq to become-- [W] AllEqF '[] cbv2- [W] SameShapeAs '[] cbv2--While the first of these is stuck, the second makes progress, to lead to-- [W] AllEqF '[] cbv2- [W] cbv2 ~ '[]--This second constraint is solved by unification: cbv2 := '[]. We now-have-- [W] AllEqF '[] '[]--which reduces to-- [W] ()--which is trivially satisfiable. Hooray!--Note that we need to unify the cbvs here; if we did not, there would be-no way to solve those constraints. That's why the cycle-breakers are-ordinary TauTvs.--In all cases---------------We detect this scenario by the following characteristics:- - a constraint with a soluble occurs-check failure- (as indicated by the cteSolubleOccurs bit set in a CheckTyEqResult- from checkTypeEq)- - and a nominal equality- - and either- - a Given flavour (but see also Detail (7) below)- - a Wanted flavour, with a touchable metavariable on the left--We don't use this trick for representational equalities, as there is no-concrete use case where it is helpful (unlike for nominal equalities).-Furthermore, because function applications can be CanEqLHSs, but newtype-applications cannot, the disparities between the cases are enough that it-would be effortful to expand the idea to representational equalities. A quick-attempt, with-- data family N a b-- f :: (Coercible a (N a b), Coercible (N a b) b) => a -> b- f = coerce--failed with "Could not match 'b' with 'b'." Further work is held off-until when we have a concrete incentive to explore this dark corner.--Details:-- (1) We don't look under foralls, at all, when substituting away type family- applications, because doing so can never be fruitful. Recall that we- are in a case like [G] lhs ~ forall b. ... lhs .... Until we have a type- family that can pull the body out from a forall (e.g. type instance F (forall b. ty) = ty),- this will always be- insoluble. Note also that the forall cannot be in an argument to a- type family, or that outer type family application would already have- been substituted away.-- However, we still must check to make sure that breakTyEqCycle_maybe actually- succeeds in getting rid of all occurrences of the offending lhs. If- one is hidden under a forall, this won't be true. A similar problem can- happen if the variable appears only in a kind- (e.g. k ~ ... (a :: k) ...). So we perform an additional check after- performing the substitution. It is tiresome to re-run all of checkTypeEq- here, but reimplementing just the occurs-check is even more tiresome.-- Skipping this check causes typecheck/should_fail/GivenForallLoop and- polykinds/T18451 to loop.-- (2) Our goal here is to avoid loops in rewriting. We can thus skip looking- in coercions, as we don't rewrite in coercions in the algorithm in- GHC.Solver.Rewrite. (This is another reason- we need to re-check that we've gotten rid of all occurrences of the- offending variable.)-- (3) As we're substituting as described in this Note, we can build ill-kinded- types. For example, if we have Proxy (F a) b, where (b :: F a), then- replacing this with Proxy cbv b is ill-kinded. However, we will later- set cbv := F a, and so the zonked type will be well-kinded again.- The temporary ill-kinded type hurts no one, and avoiding this would- be quite painfully difficult.-- Specifically, this detail does not contravene the Purely Kinded Type Invariant- (Note [The Purely Kinded Type Invariant (PKTI)] in GHC.Tc.Gen.HsType).- The PKTI says that we can call typeKind on any type, without failure.- It would be violated if we, say, replaced a kind (a -> b) with a kind c,- because an arrow kind might be consulted in piResultTys. Here, we are- replacing one opaque type like (F a b c) with another, cbv (opaque in- that we never assume anything about its structure, like that it has a- result type or a RuntimeRep argument).-- (4) The evidence for the produced Givens is all just reflexive, because- we will eventually set the cycle-breaker variable to be the type family,- and then, after the zonk, all will be well. See also the notes at the- end of the Given section of this Note.-- (5) The approach here is inefficient because it replaces every (outermost)- type family application with a type variable, regardless of whether that- particular appplication is implicated in the occurs check. An alternative- would be to replce only type-family applications that meantion the offending LHS.- For instance, we could choose to- affect only type family applications that mention the offending LHS:- e.g. in a ~ (F b, G a), we need to replace only G a, not F b. Furthermore,- we could try to detect cases like a ~ (F a, F a) and use the same- tyvar to replace F a. (Cf.- Note [Flattening type-family applications when matching instances]- in GHC.Core.Unify, which- goes to this extra effort.) There may be other opportunities for- improvement. However, this is really a very small corner case.- The investment to craft a clever,- performant solution seems unworthwhile.-- (6) We often get the predicate associated with a constraint from its- evidence with ctPred. We thus must not only make sure the generated- CEqCan's fields have the updated RHS type (that is, the one produced- by replacing type family applications with fresh variables),- but we must also update the evidence itself. This is done by the call to rewriteEqEvidence- in canEqCanLHSFinish.-- (7) We don't wish to apply this magic on the equalities created- by this very same process.- Consider this, from typecheck/should_compile/ContextStack2:-- type instance TF (a, b) = (TF a, TF b)- t :: (a ~ TF (a, Int)) => ...-- [G] a ~ TF (a, Int)-- The RHS reduces, so we get-- [G] a ~ (TF a, TF Int)-- We then break cycles, to get-- [G] g1 :: a ~ (cbv1, cbv2)- [G] g2 :: TF a ~ cbv1- [G] g3 :: TF Int ~ cbv2-- g1 gets added to the inert set, as written. But then g2 becomes- the work item. g1 rewrites g2 to become-- [G] TF (cbv1, cbv2) ~ cbv1-- which then uses the type instance to become-- [G] (TF cbv1, TF cbv2) ~ cbv1-- which looks remarkably like the Given we started with. If left- unchecked, this will end up breaking cycles again, looping ad- infinitum (and resulting in a context-stack reduction error,- not an outright loop). The solution is easy: don't break cycles- on an equality generated by breaking cycles. Instead, we mark this- final Given as a CIrredCan with a NonCanonicalReason with the soluble- occurs-check bit set (only).-- We track these equalities by giving them a special CtOrigin,- CycleBreakerOrigin. This works for both Givens and Wanteds, as- we need the logic in the W case for e.g. typecheck/should_fail/T17139.- Because this logic needs to work for Wanteds, too, we cannot- simply look for a CycleBreakerTv on the left: Wanteds don't use them.-- (8) We really want to do this all only when there is a soluble occurs-check- failure, not when other problems arise (such as an impredicative- equality like alpha ~ forall a. a -> a). That is why breakTyEqCycle_maybe- uses cterHasOnlyProblem when looking at the result of checkTypeEq, which- checks for many of the invariants on a CEqCan.--}--{--************************************************************************-* *- Evidence transformation-* *-************************************************************************--}--data StopOrContinue a- = ContinueWith a -- The constraint was not solved, although it may have- -- been rewritten-- | Stop CtEvidence -- The (rewritten) constraint was solved- SDoc -- Tells how it was solved- -- Any new sub-goals have been put on the work list- deriving (Functor)--instance Outputable a => Outputable (StopOrContinue a) where- ppr (Stop ev s) = text "Stop" <> parens s <+> ppr ev- ppr (ContinueWith w) = text "ContinueWith" <+> ppr w--continueWith :: a -> TcS (StopOrContinue a)-continueWith = return . ContinueWith--stopWith :: CtEvidence -> String -> TcS (StopOrContinue a)-stopWith ev s = return (Stop ev (text s))--andWhenContinue :: TcS (StopOrContinue a)- -> (a -> TcS (StopOrContinue b))- -> TcS (StopOrContinue b)-andWhenContinue tcs1 tcs2- = do { r <- tcs1- ; case r of- Stop ev s -> return (Stop ev s)- ContinueWith ct -> tcs2 ct }-infixr 0 `andWhenContinue` -- allow chaining with ($)--rewriteEvidence :: RewriterSet -- ^ See Note [Wanteds rewrite Wanteds]- -- in GHC.Tc.Types.Constraint- -> CtEvidence -- ^ old evidence- -> Reduction -- ^ new predicate + coercion, of type <type of old evidence> ~ new predicate- -> TcS (StopOrContinue CtEvidence)--- Returns Just new_ev iff either (i) 'co' is reflexivity--- or (ii) 'co' is not reflexivity, and 'new_pred' not cached--- In either case, there is nothing new to do with new_ev-{-- rewriteEvidence old_ev new_pred co-Main purpose: create new evidence for new_pred;- unless new_pred is cached already-* Returns a new_ev : new_pred, with same wanted/given flag as old_ev-* If old_ev was wanted, create a binding for old_ev, in terms of new_ev-* If old_ev was given, AND not cached, create a binding for new_ev, in terms of old_ev-* Returns Nothing if new_ev is already cached-- Old evidence New predicate is Return new evidence- flavour of same flavor- -------------------------------------------------------------------- Wanted Already solved or in inert Nothing- Not Just new_evidence-- Given Already in inert Nothing- Not Just new_evidence--Note [Rewriting with Refl]-~~~~~~~~~~~~~~~~~~~~~~~~~~-If the coercion is just reflexivity then you may re-use the same-variable. But be careful! Although the coercion is Refl, new_pred-may reflect the result of unification alpha := ty, so new_pred might-not _look_ the same as old_pred, and it's vital to proceed from now on-using new_pred.--The rewriter preserves type synonyms, so they should appear in new_pred-as well as in old_pred; that is important for good error messages.--If we are rewriting with Refl, then there are no new rewriters to add to-the rewriter set. We check this with an assertion.- -}---rewriteEvidence rewriters old_ev (Reduction co new_pred)- | isTcReflCo co -- See Note [Rewriting with Refl]- = assert (isEmptyRewriterSet rewriters) $- continueWith (setCtEvPredType old_ev new_pred)--rewriteEvidence rewriters ev@(CtGiven { ctev_evar = old_evar, ctev_loc = loc })- (Reduction co new_pred)- = assert (isEmptyRewriterSet rewriters) $ -- this is a Given, not a wanted- do { new_ev <- newGivenEvVar loc (new_pred, new_tm)- ; continueWith new_ev }- where- -- mkEvCast optimises ReflCo- new_tm = mkEvCast (evId old_evar)- (tcDowngradeRole Representational (ctEvRole ev) co)--rewriteEvidence new_rewriters- ev@(CtWanted { ctev_dest = dest- , ctev_loc = loc- , ctev_rewriters = rewriters })- (Reduction co new_pred)- = do { mb_new_ev <- newWanted loc rewriters' new_pred- ; massert (tcCoercionRole co == ctEvRole ev)- ; setWantedEvTerm dest- (mkEvCast (getEvExpr mb_new_ev)- (tcDowngradeRole Representational (ctEvRole ev) (mkSymCo co)))- ; case mb_new_ev of- Fresh new_ev -> continueWith new_ev- Cached _ -> stopWith ev "Cached wanted" }- where- rewriters' = rewriters S.<> new_rewriters---rewriteEqEvidence :: RewriterSet -- New rewriters- -- See GHC.Tc.Types.Constraint- -- Note [Wanteds rewrite Wanteds]- -> CtEvidence -- Old evidence :: olhs ~ orhs (not swapped)- -- or orhs ~ olhs (swapped)- -> SwapFlag- -> Reduction -- lhs_co :: olhs ~ nlhs- -> Reduction -- rhs_co :: orhs ~ nrhs- -> TcS CtEvidence -- Of type nlhs ~ nrhs--- With reductions (Reduction lhs_co nlhs) (Reduction rhs_co nrhs),--- rewriteEqEvidence yields, for a given equality (Given g olhs orhs):--- If not swapped--- g1 : nlhs ~ nrhs = sym lhs_co ; g ; rhs_co--- If swapped--- g1 : nlhs ~ nrhs = sym lhs_co ; Sym g ; rhs_co------ For a wanted equality (Wanted w), we do the dual thing:--- New w1 : nlhs ~ nrhs--- If not swapped--- w : olhs ~ orhs = lhs_co ; w1 ; sym rhs_co--- If swapped--- w : orhs ~ olhs = rhs_co ; sym w1 ; sym lhs_co------ It's all a form of rewriteEvidence, specialised for equalities-rewriteEqEvidence new_rewriters old_ev swapped (Reduction lhs_co nlhs) (Reduction rhs_co nrhs)- | NotSwapped <- swapped- , isTcReflCo lhs_co -- See Note [Rewriting with Refl]- , isTcReflCo rhs_co- = return (setCtEvPredType old_ev new_pred)-- | CtGiven { ctev_evar = old_evar } <- old_ev- = do { let new_tm = evCoercion ( mkTcSymCo lhs_co- `mkTcTransCo` maybeTcSymCo swapped (mkTcCoVarCo old_evar)- `mkTcTransCo` rhs_co)- ; newGivenEvVar loc' (new_pred, new_tm) }-- | CtWanted { ctev_dest = dest- , ctev_rewriters = rewriters } <- old_ev- , let rewriters' = rewriters S.<> new_rewriters- = do { (new_ev, hole_co) <- newWantedEq loc' rewriters'- (ctEvRole old_ev) nlhs nrhs- ; let co = maybeTcSymCo swapped $- lhs_co- `mkTransCo` hole_co- `mkTransCo` mkTcSymCo rhs_co- ; setWantedEq dest co- ; traceTcS "rewriteEqEvidence" (vcat [ ppr old_ev- , ppr nlhs- , ppr nrhs- , ppr co- , ppr new_rewriters ])- ; return new_ev }--#if __GLASGOW_HASKELL__ <= 810- | otherwise- = panic "rewriteEvidence"-#endif- where- new_pred = mkTcEqPredLikeEv old_ev nlhs nrhs-- -- equality is like a type class. Bumping the depth is necessary because- -- of recursive newtypes, where "reducing" a newtype can actually make- -- it bigger. See Note [Newtypes can blow the stack].- loc = ctEvLoc old_ev- loc' = bumpCtLocDepth loc--{--************************************************************************-* *- Unification-* *-************************************************************************--Note [unifyWanted]-~~~~~~~~~~~~~~~~~~-When decomposing equalities we often create new wanted constraints for-(s ~ t). But what if s=t? Then it'd be faster to return Refl right away.--Rather than making an equality test (which traverses the structure of the-type, perhaps fruitlessly), unifyWanted traverses the common structure, and-bales out when it finds a difference by creating a new Wanted constraint.-But where it succeeds in finding common structure, it just builds a coercion-to reflect it.--}--unifyWanted :: RewriterSet -> CtLoc- -> Role -> TcType -> TcType -> TcS Coercion--- Return coercion witnessing the equality of the two types,--- emitting new work equalities where necessary to achieve that--- Very good short-cut when the two types are equal, or nearly so--- See Note [unifyWanted]--- The returned coercion's role matches the input parameter-unifyWanted rewriters loc Phantom ty1 ty2- = do { kind_co <- unifyWanted rewriters loc Nominal (tcTypeKind ty1) (tcTypeKind ty2)- ; return (mkPhantomCo kind_co ty1 ty2) }--unifyWanted rewriters loc role orig_ty1 orig_ty2- = go orig_ty1 orig_ty2- where- go ty1 ty2 | Just ty1' <- tcView ty1 = go ty1' ty2- go ty1 ty2 | Just ty2' <- tcView ty2 = go ty1 ty2'-- go (FunTy _ w1 s1 t1) (FunTy _ w2 s2 t2)- = do { co_s <- unifyWanted rewriters loc role s1 s2- ; co_t <- unifyWanted rewriters loc role t1 t2- ; co_w <- unifyWanted rewriters loc Nominal w1 w2- ; return (mkFunCo role co_w co_s co_t) }- go (TyConApp tc1 tys1) (TyConApp tc2 tys2)- | tc1 == tc2, tys1 `equalLength` tys2- , isInjectiveTyCon tc1 role -- don't look under newtypes at Rep equality- = do { cos <- zipWith3M (unifyWanted rewriters loc)- (tyConRolesX role tc1) tys1 tys2- ; return (mkTyConAppCo role tc1 cos) }-- go ty1@(TyVarTy tv) ty2- = do { mb_ty <- isFilledMetaTyVar_maybe tv- ; case mb_ty of- Just ty1' -> go ty1' ty2- Nothing -> bale_out ty1 ty2}- go ty1 ty2@(TyVarTy tv)- = do { mb_ty <- isFilledMetaTyVar_maybe tv- ; case mb_ty of- Just ty2' -> go ty1 ty2'- Nothing -> bale_out ty1 ty2 }-- go ty1@(CoercionTy {}) (CoercionTy {})- = return (mkReflCo role ty1) -- we just don't care about coercions!-- go ty1 ty2 = bale_out ty1 ty2-- bale_out ty1 ty2- | ty1 `tcEqType` ty2 = return (mkTcReflCo role ty1)- -- Check for equality; e.g. a ~ a, or (m a) ~ (m a)- | otherwise = emitNewWantedEq loc rewriters role orig_ty1 orig_ty2+{-# LANGUAGE RecursiveDo #-}++module GHC.Tc.Solver.Canonical(+ canonicalize,+ unifyWanted,+ makeSuperClasses,+ StopOrContinue(..), stopWith, continueWith, andWhenContinue,+ solveCallStack -- For GHC.Tc.Solver+ ) where++import GHC.Prelude++import GHC.Tc.Types.Constraint+import GHC.Core.Predicate+import GHC.Tc.Types.Origin+import GHC.Tc.Utils.Unify+import GHC.Tc.Utils.TcType+import GHC.Core.Type+import GHC.Tc.Solver.Rewrite+import GHC.Tc.Solver.Monad+import GHC.Tc.Solver.InertSet+import GHC.Tc.Types.Evidence+import GHC.Tc.Types.EvTerm+import GHC.Core.Class+import GHC.Core.DataCon ( dataConName )+import GHC.Core.TyCon+import GHC.Core.Multiplicity+import GHC.Core.TyCo.Rep -- cleverly decomposes types, good for completeness checking+import GHC.Core.Coercion+import GHC.Core.Coercion.Axiom+import GHC.Core.Reduction+import GHC.Core+import GHC.Types.Id( mkTemplateLocals )+import GHC.Core.FamInstEnv ( FamInstEnvs )+import GHC.Tc.Instance.Family ( tcTopNormaliseNewTypeTF_maybe )+import GHC.Types.Var+import GHC.Types.Var.Env( mkInScopeSet )+import GHC.Types.Var.Set( delVarSetList, anyVarSet )+import GHC.Utils.Outputable+import GHC.Utils.Panic+import GHC.Utils.Panic.Plain+import GHC.Builtin.Types ( anyTypeOfKind )+import GHC.Types.Name.Set+import GHC.Types.Name.Reader+import GHC.Hs.Type( HsIPName(..) )+import GHC.Types.Unique ( hasKey )+import GHC.Builtin.Names ( coercibleTyConKey )++import GHC.Data.Pair+import GHC.Utils.Misc+import GHC.Data.Bag+import GHC.Utils.Monad+import GHC.Utils.Constants( debugIsOn )+import Control.Monad+import Data.Maybe ( isJust, isNothing )+import Data.List ( zip4 )+import GHC.Types.Basic++import qualified Data.Semigroup as S+import Data.Bifunctor ( bimap )++{-+************************************************************************+* *+* The Canonicaliser *+* *+************************************************************************++Note [Canonicalization]+~~~~~~~~~~~~~~~~~~~~~~~++Canonicalization converts a simple constraint to a canonical form. It is+unary (i.e. treats individual constraints one at a time).++Constraints originating from user-written code come into being as+CNonCanonicals. We know nothing about these constraints. So, first:++ Classify CNonCanoncal constraints, depending on whether they+ are equalities, class predicates, or other.++Then proceed depending on the shape of the constraint. Generally speaking,+each constraint gets rewritten and then decomposed into one of several forms+(see type Ct in GHC.Tc.Types).++When an already-canonicalized constraint gets kicked out of the inert set,+it must be recanonicalized. But we know a bit about its shape from the+last time through, so we can skip the classification step.++-}++-- Top-level canonicalization+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++canonicalize :: Ct -> TcS (StopOrContinue Ct)+canonicalize (CNonCanonical { cc_ev = ev })+ = {-# SCC "canNC" #-}+ canNC ev++canonicalize (CQuantCan (QCI { qci_ev = ev, qci_pend_sc = pend_sc }))+ = canForAll ev pend_sc++canonicalize (CIrredCan { cc_ev = ev })+ = canNC ev+ -- Instead of rewriting the evidence before classifying, it's possible we+ -- can make progress without the rewrite. Try this first.+ -- For insolubles (all of which are equalities), do /not/ rewrite the arguments+ -- In #14350 doing so led entire-unnecessary and ridiculously large+ -- type function expansion. Instead, canEqNC just applies+ -- the substitution to the predicate, and may do decomposition;+ -- e.g. a ~ [a], where [G] a ~ [Int], can decompose++canonicalize (CDictCan { cc_ev = ev, cc_class = cls+ , cc_tyargs = xis, cc_pend_sc = pend_sc })+ = {-# SCC "canClass" #-}+ canClass ev cls xis pend_sc++canonicalize (CEqCan { cc_ev = ev+ , cc_lhs = lhs+ , cc_rhs = rhs+ , cc_eq_rel = eq_rel })+ = {-# SCC "canEqLeafTyVarEq" #-}+ canEqNC ev eq_rel (canEqLHSType lhs) rhs++canNC :: CtEvidence -> TcS (StopOrContinue Ct)+canNC ev =+ case classifyPredType pred of+ ClassPred cls tys -> do traceTcS "canEvNC:cls" (ppr cls <+> ppr tys)+ canClassNC ev cls tys+ EqPred eq_rel ty1 ty2 -> do traceTcS "canEvNC:eq" (ppr ty1 $$ ppr ty2)+ canEqNC ev eq_rel ty1 ty2+ IrredPred {} -> do traceTcS "canEvNC:irred" (ppr pred)+ canIrred ev+ ForAllPred tvs th p -> do traceTcS "canEvNC:forall" (ppr pred)+ canForAllNC ev tvs th p++ where+ pred = ctEvPred ev++{-+************************************************************************+* *+* Class Canonicalization+* *+************************************************************************+-}++canClassNC :: CtEvidence -> Class -> [Type] -> TcS (StopOrContinue Ct)+-- "NC" means "non-canonical"; that is, we have got here+-- from a NonCanonical constraint, not from a CDictCan+-- Precondition: EvVar is class evidence+canClassNC ev cls tys+ | isGiven ev -- See Note [Eagerly expand given superclasses]+ = do { sc_cts <- mkStrictSuperClasses ev [] [] cls tys+ ; emitWork sc_cts+ ; canClass ev cls tys False }++ | CtWanted { ctev_rewriters = rewriters } <- ev+ , Just ip_name <- isCallStackPred cls tys+ , isPushCallStackOrigin orig+ -- If we're given a CallStack constraint that arose from a function+ -- call, we need to push the current call-site onto the stack instead+ -- of solving it directly from a given.+ -- See Note [Overview of implicit CallStacks] in GHC.Tc.Types.Evidence+ -- and Note [Solving CallStack constraints] in GHC.Tc.Solver.Types+ = do { -- First we emit a new constraint that will capture the+ -- given CallStack.+ ; let new_loc = setCtLocOrigin loc (IPOccOrigin (HsIPName ip_name))+ -- We change the origin to IPOccOrigin so+ -- this rule does not fire again.+ -- See Note [Overview of implicit CallStacks]+ -- in GHC.Tc.Types.Evidence++ ; new_ev <- newWantedEvVarNC new_loc rewriters pred++ -- Then we solve the wanted by pushing the call-site+ -- onto the newly emitted CallStack+ ; let ev_cs = EvCsPushCall (callStackOriginFS orig)+ (ctLocSpan loc) (ctEvExpr new_ev)+ ; solveCallStack ev ev_cs++ ; canClass new_ev cls tys False -- No superclasses+ }++ | otherwise+ = canClass ev cls tys (has_scs cls)++ where+ has_scs cls = not (null (classSCTheta cls))+ loc = ctEvLoc ev+ orig = ctLocOrigin loc+ pred = ctEvPred ev++solveCallStack :: CtEvidence -> EvCallStack -> TcS ()+-- Also called from GHC.Tc.Solver when defaulting call stacks+solveCallStack ev ev_cs = do+ -- We're given ev_cs :: CallStack, but the evidence term should be a+ -- dictionary, so we have to coerce ev_cs to a dictionary for+ -- `IP ip CallStack`. See Note [Overview of implicit CallStacks]+ cs_tm <- evCallStack ev_cs+ let ev_tm = mkEvCast cs_tm (wrapIP (ctEvPred ev))+ setEvBindIfWanted ev ev_tm++canClass :: CtEvidence+ -> Class -> [Type]+ -> Bool -- True <=> un-explored superclasses+ -> TcS (StopOrContinue Ct)+-- Precondition: EvVar is class evidence++canClass ev cls tys pend_sc+ = -- all classes do *nominal* matching+ assertPpr (ctEvRole ev == Nominal) (ppr ev $$ ppr cls $$ ppr tys) $+ do { (redns@(Reductions _ xis), rewriters) <- rewriteArgsNom ev cls_tc tys+ ; let redn@(Reduction _ xi) = mkClassPredRedn cls redns+ mk_ct new_ev = CDictCan { cc_ev = new_ev+ , cc_tyargs = xis+ , cc_class = cls+ , cc_pend_sc = pend_sc }+ ; mb <- rewriteEvidence rewriters ev redn+ ; traceTcS "canClass" (vcat [ ppr ev+ , ppr xi, ppr mb ])+ ; return (fmap mk_ct mb) }+ where+ cls_tc = classTyCon cls++{- Note [The superclass story]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+We need to add superclass constraints for two reasons:++* For givens [G], they give us a route to proof. E.g.+ f :: Ord a => a -> Bool+ f x = x == x+ We get a Wanted (Eq a), which can only be solved from the superclass+ of the Given (Ord a).++* For wanteds [W], they may give useful+ functional dependencies. E.g.+ class C a b | a -> b where ...+ class C a b => D a b where ...+ Now a [W] constraint (D Int beta) has (C Int beta) as a superclass+ and that might tell us about beta, via C's fundeps. We can get this+ by generating a [W] (C Int beta) constraint. We won't use the evidence,+ but it may lead to unification.++See Note [Why adding superclasses can help].++For these reasons we want to generate superclass constraints for both+Givens and Wanteds. But:++* (Minor) they are often not needed, so generating them aggressively+ is a waste of time.++* (Major) if we want recursive superclasses, there would be an infinite+ number of them. Here is a real-life example (#10318);++ class (Frac (Frac a) ~ Frac a,+ Fractional (Frac a),+ IntegralDomain (Frac a))+ => IntegralDomain a where+ type Frac a :: *++ Notice that IntegralDomain has an associated type Frac, and one+ of IntegralDomain's superclasses is another IntegralDomain constraint.++So here's the plan:++1. Eagerly generate superclasses for given (but not wanted)+ constraints; see Note [Eagerly expand given superclasses].+ This is done using mkStrictSuperClasses in canClassNC, when+ we take a non-canonical Given constraint and cannonicalise it.++ However stop if you encounter the same class twice. That is,+ mkStrictSuperClasses expands eagerly, but has a conservative+ termination condition: see Note [Expanding superclasses] in GHC.Tc.Utils.TcType.++2. Solve the wanteds as usual, but do no further expansion of+ superclasses for canonical CDictCans in solveSimpleGivens or+ solveSimpleWanteds; Note [Danger of adding superclasses during solving]++ However, /do/ continue to eagerly expand superclasses for new /given/+ /non-canonical/ constraints (canClassNC does this). As #12175+ showed, a type-family application can expand to a class constraint,+ and we want to see its superclasses for just the same reason as+ Note [Eagerly expand given superclasses].++3. If we have any remaining unsolved wanteds+ (see Note [When superclasses help] in GHC.Tc.Types.Constraint)+ try harder: take both the Givens and Wanteds, and expand+ superclasses again. See the calls to expandSuperClasses in+ GHC.Tc.Solver.simpl_loop and solveWanteds.++ This may succeed in generating (a finite number of) extra Givens,+ and extra Wanteds. Both may help the proof.++3a An important wrinkle: only expand Givens from the current level.+ Two reasons:+ - We only want to expand it once, and that is best done at+ the level it is bound, rather than repeatedly at the leaves+ of the implication tree+ - We may be inside a type where we can't create term-level+ evidence anyway, so we can't superclass-expand, say,+ (a ~ b) to get (a ~# b). This happened in #15290.++4. Go round to (2) again. This loop (2,3,4) is implemented+ in GHC.Tc.Solver.simpl_loop.++The cc_pend_sc flag in a CDictCan records whether the superclasses of+this constraint have been expanded. Specifically, in Step 3 we only+expand superclasses for constraints with cc_pend_sc set to true (i.e.+isPendingScDict holds).++Why do we do this? Two reasons:++* To avoid repeated work, by repeatedly expanding the superclasses of+ same constraint,++* To terminate the above loop, at least in the -XNoUndecidableSuperClasses+ case. If there are recursive superclasses we could, in principle,+ expand forever, always encountering new constraints.++When we take a CNonCanonical or CIrredCan, but end up classifying it+as a CDictCan, we set the cc_pend_sc flag to False.++Note [Superclass loops]+~~~~~~~~~~~~~~~~~~~~~~~+Suppose we have+ class C a => D a+ class D a => C a++Then, when we expand superclasses, we'll get back to the self-same+predicate, so we have reached a fixpoint in expansion and there is no+point in fruitlessly expanding further. This case just falls out from+our strategy. Consider+ f :: C a => a -> Bool+ f x = x==x+Then canClassNC gets the [G] d1: C a constraint, and eager emits superclasses+G] d2: D a, [G] d3: C a (psc). (The "psc" means it has its sc_pend flag set.)+When processing d3 we find a match with d1 in the inert set, and we always+keep the inert item (d1) if possible: see Note [Replacement vs keeping] in+GHC.Tc.Solver.Interact. So d3 dies a quick, happy death.++Note [Eagerly expand given superclasses]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+In step (1) of Note [The superclass story], why do we eagerly expand+Given superclasses by one layer? (By "one layer" we mean expand transitively+until you meet the same class again -- the conservative criterion embodied+in expandSuperClasses. So a "layer" might be a whole stack of superclasses.)+We do this eagerly for Givens mainly because of some very obscure+cases like this:++ instance Bad a => Eq (T a)++ f :: (Ord (T a)) => blah+ f x = ....needs Eq (T a), Ord (T a)....++Here if we can't satisfy (Eq (T a)) from the givens we'll use the+instance declaration; but then we are stuck with (Bad a). Sigh.+This is really a case of non-confluent proofs, but to stop our users+complaining we expand one layer in advance.++Note [Instance and Given overlap] in GHC.Tc.Solver.Interact.++We also want to do this if we have++ f :: F (T a) => blah++where+ type instance F (T a) = Ord (T a)++So we may need to do a little work on the givens to expose the+class that has the superclasses. That's why the superclass+expansion for Givens happens in canClassNC.++This same scenario happens with quantified constraints, whose superclasses+are also eagerly expanded. Test case: typecheck/should_compile/T16502b+These are handled in canForAllNC, analogously to canClassNC.++Note [Why adding superclasses can help]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Examples of how adding superclasses can help:++ --- Example 1+ class C a b | a -> b+ Suppose we want to solve+ [G] C a b+ [W] C a beta+ Then adding [W] beta~b will let us solve it.++ -- Example 2 (similar but using a type-equality superclass)+ class (F a ~ b) => C a b+ And try to sllve:+ [G] C a b+ [W] C a beta+ Follow the superclass rules to add+ [G] F a ~ b+ [W] F a ~ beta+ Now we get [W] beta ~ b, and can solve that.++ -- Example (tcfail138)+ class L a b | a -> b+ class (G a, L a b) => C a b++ instance C a b' => G (Maybe a)+ instance C a b => C (Maybe a) a+ instance L (Maybe a) a++ When solving the superclasses of the (C (Maybe a) a) instance, we get+ [G] C a b, and hence by superclasses, [G] G a, [G] L a b+ [W] G (Maybe a)+ Use the instance decl to get+ [W] C a beta+ Generate its superclass+ [W] L a beta. Now using fundeps, combine with [G] L a b to get+ [W] beta ~ b+ which is what we want.++Note [Danger of adding superclasses during solving]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Here's a serious, but now out-dated example, from #4497:++ class Num (RealOf t) => Normed t+ type family RealOf x++Assume the generated wanted constraint is:+ [W] RealOf e ~ e+ [W] Normed e++If we were to be adding the superclasses during simplification we'd get:+ [W] RealOf e ~ e+ [W] Normed e+ [W] RealOf e ~ fuv+ [W] Num fuv+==>+ e := fuv, Num fuv, Normed fuv, RealOf fuv ~ fuv++While looks exactly like our original constraint. If we add the+superclass of (Normed fuv) again we'd loop. By adding superclasses+definitely only once, during canonicalisation, this situation can't+happen.++Note [Nested quantified constraint superclasses]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider (typecheck/should_compile/T17202)++ class C1 a+ class (forall c. C1 c) => C2 a+ class (forall b. (b ~ F a) => C2 a) => C3 a++Elsewhere in the code, we get a [G] g1 :: C3 a. We expand its superclass+to get [G] g2 :: (forall b. (b ~ F a) => C2 a). This constraint has a+superclass, as well. But we now must be careful: we cannot just add+(forall c. C1 c) as a Given, because we need to remember g2's context.+That new constraint is Given only when forall b. (b ~ F a) is true.++It's tempting to make the new Given be (forall b. (b ~ F a) => forall c. C1 c),+but that's problematic, because it's nested, and ForAllPred is not capable+of representing a nested quantified constraint. (We could change ForAllPred+to allow this, but the solution in this Note is much more local and simpler.)++So, we swizzle it around to get (forall b c. (b ~ F a) => C1 c).++More generally, if we are expanding the superclasses of+ g0 :: forall tvs. theta => cls tys+and find a superclass constraint+ forall sc_tvs. sc_theta => sc_inner_pred+we must have a selector+ sel_id :: forall cls_tvs. cls cls_tvs -> forall sc_tvs. sc_theta => sc_inner_pred+and thus build+ g_sc :: forall tvs sc_tvs. theta => sc_theta => sc_inner_pred+ g_sc = /\ tvs. /\ sc_tvs. \ theta_ids. \ sc_theta_ids.+ sel_id tys (g0 tvs theta_ids) sc_tvs sc_theta_ids++Actually, we cheat a bit by eta-reducing: note that sc_theta_ids are both the+last bound variables and the last arguments. This avoids the need to produce+the sc_theta_ids at all. So our final construction is++ g_sc = /\ tvs. /\ sc_tvs. \ theta_ids.+ sel_id tys (g0 tvs theta_ids) sc_tvs++ -}++makeSuperClasses :: [Ct] -> TcS [Ct]+-- Returns strict superclasses, transitively, see Note [The superclass story]+-- See Note [The superclass story]+-- The loop-breaking here follows Note [Expanding superclasses] in GHC.Tc.Utils.TcType+-- Specifically, for an incoming (C t) constraint, we return all of (C t)'s+-- superclasses, up to /and including/ the first repetition of C+--+-- Example: class D a => C a+-- class C [a] => D a+-- makeSuperClasses (C x) will return (D x, C [x])+--+-- NB: the incoming constraints have had their cc_pend_sc flag already+-- flipped to False, by isPendingScDict, so we are /obliged/ to at+-- least produce the immediate superclasses+makeSuperClasses cts = concatMapM go cts+ where+ go (CDictCan { cc_ev = ev, cc_class = cls, cc_tyargs = tys })+ = mkStrictSuperClasses ev [] [] cls tys+ go (CQuantCan (QCI { qci_pred = pred, qci_ev = ev }))+ = assertPpr (isClassPred pred) (ppr pred) $ -- The cts should all have+ -- class pred heads+ mkStrictSuperClasses ev tvs theta cls tys+ where+ (tvs, theta, cls, tys) = tcSplitDFunTy (ctEvPred ev)+ go ct = pprPanic "makeSuperClasses" (ppr ct)++mkStrictSuperClasses+ :: CtEvidence+ -> [TyVar] -> ThetaType -- These two args are non-empty only when taking+ -- superclasses of a /quantified/ constraint+ -> Class -> [Type] -> TcS [Ct]+-- Return constraints for the strict superclasses of+-- ev :: forall as. theta => cls tys+mkStrictSuperClasses ev tvs theta cls tys+ = mk_strict_superclasses (unitNameSet (className cls))+ ev tvs theta cls tys++mk_strict_superclasses :: NameSet -> CtEvidence+ -> [TyVar] -> ThetaType+ -> Class -> [Type] -> TcS [Ct]+-- Always return the immediate superclasses of (cls tys);+-- and expand their superclasses, provided none of them are in rec_clss+-- nor are repeated+mk_strict_superclasses rec_clss (CtGiven { ctev_evar = evar, ctev_loc = loc })+ tvs theta cls tys+ = concatMapM do_one_given $+ classSCSelIds cls+ where+ dict_ids = mkTemplateLocals theta+ this_size = pSizeClassPred cls tys++ do_one_given sel_id+ | isUnliftedType sc_pred+ -- NB: class superclasses are never representation-polymorphic,+ -- so isUnliftedType is OK here.+ , not (null tvs && null theta)+ = -- See Note [Equality superclasses in quantified constraints]+ return []+ | otherwise+ = do { given_ev <- newGivenEvVar sc_loc $+ mk_given_desc sel_id sc_pred+ ; mk_superclasses rec_clss given_ev tvs theta sc_pred }+ where+ sc_pred = classMethodInstTy sel_id tys++ -- See Note [Nested quantified constraint superclasses]+ mk_given_desc :: Id -> PredType -> (PredType, EvTerm)+ mk_given_desc sel_id sc_pred+ = (swizzled_pred, swizzled_evterm)+ where+ (sc_tvs, sc_rho) = splitForAllTyCoVars sc_pred+ (sc_theta, sc_inner_pred) = splitFunTys sc_rho++ all_tvs = tvs `chkAppend` sc_tvs+ all_theta = theta `chkAppend` (map scaledThing sc_theta)+ swizzled_pred = mkInfSigmaTy all_tvs all_theta sc_inner_pred++ -- evar :: forall tvs. theta => cls tys+ -- sel_id :: forall cls_tvs. cls cls_tvs+ -- -> forall sc_tvs. sc_theta => sc_inner_pred+ -- swizzled_evterm :: forall tvs sc_tvs. theta => sc_theta => sc_inner_pred+ swizzled_evterm = EvExpr $+ mkLams all_tvs $+ mkLams dict_ids $+ Var sel_id+ `mkTyApps` tys+ `App` (evId evar `mkVarApps` (tvs ++ dict_ids))+ `mkVarApps` sc_tvs++ sc_loc | isCTupleClass cls+ = loc -- For tuple predicates, just take them apart, without+ -- adding their (large) size into the chain. When we+ -- get down to a base predicate, we'll include its size.+ -- #10335++ | isEqPredClass cls || cls `hasKey` coercibleTyConKey+ = loc -- The only superclasses of ~, ~~, and Coercible are primitive+ -- equalities, and they don't use the GivenSCOrigin mechanism+ -- detailed in Note [Solving superclass constraints] in+ -- GHC.Tc.TyCl.Instance. Skip for a tiny performance win.++ | otherwise+ = loc { ctl_origin = mk_sc_origin (ctLocOrigin loc) }++ -- See Note [Solving superclass constraints] in GHC.Tc.TyCl.Instance+ -- for explanation of GivenSCOrigin and Note [Replacement vs keeping] in+ -- GHC.Tc.Solver.Interact for why we need depths+ mk_sc_origin :: CtOrigin -> CtOrigin+ mk_sc_origin (GivenSCOrigin skol_info sc_depth already_blocked)+ = GivenSCOrigin skol_info (sc_depth + 1)+ (already_blocked || newly_blocked skol_info)++ mk_sc_origin (GivenOrigin skol_info)+ = -- These cases do not already have a superclass constraint: depth starts at 1+ GivenSCOrigin skol_info 1 (newly_blocked skol_info)++ mk_sc_origin other_orig = pprPanic "Given constraint without given origin" $+ ppr evar $$ ppr other_orig++ newly_blocked (InstSkol _ head_size) = isJust (this_size `ltPatersonSize` head_size)+ newly_blocked _ = False++mk_strict_superclasses rec_clss ev tvs theta cls tys+ | all noFreeVarsOfType tys+ = return [] -- Wanteds with no variables yield no superclass constraints.+ -- See Note [Improvement from Ground Wanteds]++ | otherwise -- Wanted case, just add Wanted superclasses+ -- that can lead to improvement.+ = assertPpr (null tvs && null theta) (ppr tvs $$ ppr theta) $+ concatMapM do_one (immSuperClasses cls tys)+ where+ loc = ctEvLoc ev `updateCtLocOrigin` WantedSuperclassOrigin (ctEvPred ev)++ do_one sc_pred+ = do { traceTcS "mk_strict_superclasses Wanted" (ppr (mkClassPred cls tys) $$ ppr sc_pred)+ ; sc_ev <- newWantedNC loc (ctEvRewriters ev) sc_pred+ ; mk_superclasses rec_clss sc_ev [] [] sc_pred }++{- Note [Improvement from Ground Wanteds]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Suppose class C b a => D a b+and consider+ [W] D Int Bool+Is there any point in emitting [W] C Bool Int? No! The only point of+emitting superclass constraints for W constraints is to get+improvement, extra unifications that result from functional+dependencies. See Note [Why adding superclasses can help] above.++But no variables means no improvement; case closed.+-}++mk_superclasses :: NameSet -> CtEvidence+ -> [TyVar] -> ThetaType -> PredType -> TcS [Ct]+-- Return this constraint, plus its superclasses, if any+mk_superclasses rec_clss ev tvs theta pred+ | ClassPred cls tys <- classifyPredType pred+ = mk_superclasses_of rec_clss ev tvs theta cls tys++ | otherwise -- Superclass is not a class predicate+ = return [mkNonCanonical ev]++mk_superclasses_of :: NameSet -> CtEvidence+ -> [TyVar] -> ThetaType -> Class -> [Type]+ -> TcS [Ct]+-- Always return this class constraint,+-- and expand its superclasses+mk_superclasses_of rec_clss ev tvs theta cls tys+ | loop_found = do { traceTcS "mk_superclasses_of: loop" (ppr cls <+> ppr tys)+ ; return [this_ct] } -- cc_pend_sc of this_ct = True+ | otherwise = do { traceTcS "mk_superclasses_of" (vcat [ ppr cls <+> ppr tys+ , ppr (isCTupleClass cls)+ , ppr rec_clss+ ])+ ; sc_cts <- mk_strict_superclasses rec_clss' ev tvs theta cls tys+ ; return (this_ct : sc_cts) }+ -- cc_pend_sc of this_ct = False+ where+ cls_nm = className cls+ loop_found = not (isCTupleClass cls) && cls_nm `elemNameSet` rec_clss+ -- Tuples never contribute to recursion, and can be nested+ rec_clss' = rec_clss `extendNameSet` cls_nm++ this_ct | null tvs, null theta+ = CDictCan { cc_ev = ev, cc_class = cls, cc_tyargs = tys+ , cc_pend_sc = loop_found }+ -- NB: If there is a loop, we cut off, so we have not+ -- added the superclasses, hence cc_pend_sc = True+ | otherwise+ = CQuantCan (QCI { qci_tvs = tvs, qci_pred = mkClassPred cls tys+ , qci_ev = ev+ , qci_pend_sc = loop_found })+++{- Note [Equality superclasses in quantified constraints]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider (#15359, #15593, #15625)+ f :: (forall a. theta => a ~ b) => stuff++It's a bit odd to have a local, quantified constraint for `(a~b)`,+but some people want such a thing (see the tickets). And for+Coercible it is definitely useful+ f :: forall m. (forall p q. Coercible p q => Coercible (m p) (m q)))+ => stuff++Moreover it's not hard to arrange; we just need to look up /equality/+constraints in the quantified-constraint environment, which we do in+GHC.Tc.Solver.Interact.doTopReactOther.++There is a wrinkle though, in the case where 'theta' is empty, so+we have+ f :: (forall a. a~b) => stuff++Now, potentially, the superclass machinery kicks in, in+makeSuperClasses, giving us a a second quantified constraint+ (forall a. a ~# b)+BUT this is an unboxed value! And nothing has prepared us for+dictionary "functions" that are unboxed. Actually it does just+about work, but the simplifier ends up with stuff like+ case (/\a. eq_sel d) of df -> ...(df @Int)...+and fails to simplify that any further. And it doesn't satisfy+isPredTy any more.++So for now we simply decline to take superclasses in the quantified+case. Instead we have a special case in GHC.Tc.Solver.Interact.doTopReactOther,+which looks for primitive equalities specially in the quantified+constraints.++See also Note [Evidence for quantified constraints] in GHC.Core.Predicate.+++************************************************************************+* *+* Irreducibles canonicalization+* *+************************************************************************+-}++canIrred :: CtEvidence -> TcS (StopOrContinue Ct)+-- Precondition: ty not a tuple and no other evidence form+canIrred ev+ = do { let pred = ctEvPred ev+ ; traceTcS "can_pred" (text "IrredPred = " <+> ppr pred)+ ; (redn, rewriters) <- rewrite ev pred+ ; rewriteEvidence rewriters ev redn `andWhenContinue` \ new_ev ->++ do { -- Re-classify, in case rewriting has improved its shape+ -- Code is like the canNC, except+ -- that the IrredPred branch stops work+ ; case classifyPredType (ctEvPred new_ev) of+ ClassPred cls tys -> canClassNC new_ev cls tys+ EqPred eq_rel ty1 ty2 -> -- IrredPreds have kind Constraint, so+ -- cannot become EqPreds+ pprPanic "canIrred: EqPred"+ (ppr ev $$ ppr eq_rel $$ ppr ty1 $$ ppr ty2)+ ForAllPred tvs th p -> -- this is highly suspect; Quick Look+ -- should never leave a meta-var filled+ -- in with a polytype. This is #18987.+ do traceTcS "canEvNC:forall" (ppr pred)+ canForAllNC ev tvs th p+ IrredPred {} -> continueWith $+ mkIrredCt IrredShapeReason new_ev } }++{- *********************************************************************+* *+* Quantified predicates+* *+********************************************************************* -}++{- Note [Quantified constraints]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+The -XQuantifiedConstraints extension allows type-class contexts like this:++ data Rose f x = Rose x (f (Rose f x))++ instance (Eq a, forall b. Eq b => Eq (f b))+ => Eq (Rose f a) where+ (Rose x1 rs1) == (Rose x2 rs2) = x1==x2 && rs1 == rs2++Note the (forall b. Eq b => Eq (f b)) in the instance contexts.+This quantified constraint is needed to solve the+ [W] (Eq (f (Rose f x)))+constraint which arises form the (==) definition.++The wiki page is+ https://gitlab.haskell.org/ghc/ghc/wikis/quantified-constraints+which in turn contains a link to the GHC Proposal where the change+is specified, and a Haskell Symposium paper about it.++We implement two main extensions to the design in the paper:++ 1. We allow a variable in the instance head, e.g.+ f :: forall m a. (forall b. m b) => D (m a)+ Notice the 'm' in the head of the quantified constraint, not+ a class.++ 2. We support superclasses to quantified constraints.+ For example (contrived):+ f :: (Ord b, forall b. Ord b => Ord (m b)) => m a -> m a -> Bool+ f x y = x==y+ Here we need (Eq (m a)); but the quantified constraint deals only+ with Ord. But we can make it work by using its superclass.++Here are the moving parts+ * Language extension {-# LANGUAGE QuantifiedConstraints #-}+ and add it to ghc-boot-th:GHC.LanguageExtensions.Type.Extension++ * A new form of evidence, EvDFun, that is used to discharge+ such wanted constraints++ * checkValidType gets some changes to accept forall-constraints+ only in the right places.++ * Predicate.Pred gets a new constructor ForAllPred, and+ and classifyPredType analyses a PredType to decompose+ the new forall-constraints++ * GHC.Tc.Solver.Monad.InertCans gets an extra field, inert_insts,+ which holds all the Given forall-constraints. In effect,+ such Given constraints are like local instance decls.++ * When trying to solve a class constraint, via+ GHC.Tc.Solver.Interact.matchInstEnv, use the InstEnv from inert_insts+ so that we include the local Given forall-constraints+ in the lookup. (See GHC.Tc.Solver.Monad.getInstEnvs.)++ * GHC.Tc.Solver.Canonical.canForAll deals with solving a+ forall-constraint. See+ Note [Solving a Wanted forall-constraint]++ * We augment the kick-out code to kick out an inert+ forall constraint if it can be rewritten by a new+ type equality; see GHC.Tc.Solver.Monad.kick_out_rewritable++Note that a quantified constraint is never /inferred/+(by GHC.Tc.Solver.simplifyInfer). A function can only have a+quantified constraint in its type if it is given an explicit+type signature.++-}++canForAllNC :: CtEvidence -> [TyVar] -> TcThetaType -> TcPredType+ -> TcS (StopOrContinue Ct)+canForAllNC ev tvs theta pred+ | isGiven ev -- See Note [Eagerly expand given superclasses]+ , Just (cls, tys) <- cls_pred_tys_maybe+ = do { sc_cts <- mkStrictSuperClasses ev tvs theta cls tys+ ; emitWork sc_cts+ ; canForAll ev False }++ | otherwise+ = canForAll ev (isJust cls_pred_tys_maybe)++ where+ cls_pred_tys_maybe = getClassPredTys_maybe pred++canForAll :: CtEvidence -> Bool -> TcS (StopOrContinue Ct)+-- We have a constraint (forall as. blah => C tys)+canForAll ev pend_sc+ = do { -- First rewrite it to apply the current substitution+ let pred = ctEvPred ev+ ; (redn, rewriters) <- rewrite ev pred+ ; rewriteEvidence rewriters ev redn `andWhenContinue` \ new_ev ->++ do { -- Now decompose into its pieces and solve it+ -- (It takes a lot less code to rewrite before decomposing.)+ ; case classifyPredType (ctEvPred new_ev) of+ ForAllPred tvs theta pred+ -> solveForAll new_ev tvs theta pred pend_sc+ _ -> pprPanic "canForAll" (ppr new_ev)+ } }++solveForAll :: CtEvidence -> [TyVar] -> TcThetaType -> PredType -> Bool+ -> TcS (StopOrContinue Ct)+solveForAll ev@(CtWanted { ctev_dest = dest, ctev_rewriters = rewriters, ctev_loc = loc })+ tvs theta pred _pend_sc+ = -- See Note [Solving a Wanted forall-constraint]+ setLclEnv (ctLocEnv loc) $+ -- This setLclEnv is important: the emitImplicationTcS uses that+ -- TcLclEnv for the implication, and that in turn sets the location+ -- for the Givens when solving the constraint (#21006)+ do { let empty_subst = mkEmptySubst $ mkInScopeSet $+ tyCoVarsOfTypes (pred:theta) `delVarSetList` tvs+ is_qc = IsQC (ctLocOrigin loc)++ -- rec {..}: see Note [Keeping SkolemInfo inside a SkolemTv]+ -- in GHC.Tc.Utils.TcType+ -- Very like the code in tcSkolDFunType+ ; rec { skol_info <- mkSkolemInfo skol_info_anon+ ; (subst, skol_tvs) <- tcInstSkolTyVarsX skol_info empty_subst tvs+ ; let inst_pred = substTy subst pred+ inst_theta = substTheta subst theta+ skol_info_anon = InstSkol is_qc (get_size inst_pred) }++ ; given_ev_vars <- mapM newEvVar inst_theta+ ; (lvl, (w_id, wanteds))+ <- pushLevelNoWorkList (ppr skol_info) $+ do { let loc' = setCtLocOrigin loc (ScOrigin is_qc NakedSc)+ -- Set the thing to prove to have a ScOrigin, so we are+ -- careful about its termination checks.+ -- See (QC-INV) in Note [Solving a Wanted forall-constraint]+ ; wanted_ev <- newWantedEvVarNC loc' rewriters inst_pred+ ; return ( ctEvEvId wanted_ev+ , unitBag (mkNonCanonical wanted_ev)) }++ ; ev_binds <- emitImplicationTcS lvl (getSkolemInfo skol_info) skol_tvs+ given_ev_vars wanteds++ ; setWantedEvTerm dest $+ EvFun { et_tvs = skol_tvs, et_given = given_ev_vars+ , et_binds = ev_binds, et_body = w_id }++ ; stopWith ev "Wanted forall-constraint" }+ where+ -- Getting the size of the head is a bit horrible+ -- because of the special treament for class predicates+ get_size pred = case classifyPredType pred of+ ClassPred cls tys -> pSizeClassPred cls tys+ _ -> pSizeType pred++ -- See Note [Solving a Given forall-constraint]+solveForAll ev@(CtGiven {}) tvs _theta pred pend_sc+ = do { addInertForAll qci+ ; stopWith ev "Given forall-constraint" }+ where+ qci = QCI { qci_ev = ev, qci_tvs = tvs+ , qci_pred = pred, qci_pend_sc = pend_sc }++{- Note [Solving a Wanted forall-constraint]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Solving a wanted forall (quantified) constraint+ [W] df :: forall ab. (Eq a, Ord b) => C x a b+is delightfully easy. Just build an implication constraint+ forall ab. (g1::Eq a, g2::Ord b) => [W] d :: C x a+and discharge df thus:+ df = /\ab. \g1 g2. let <binds> in d+where <binds> is filled in by solving the implication constraint.+All the machinery is to hand; there is little to do.++The tricky point is about termination: see #19690. We want to maintain+the invariant (QC-INV):++ (QC-INV) Every quantified constraint returns a non-bottom dictionary++just as every top-level instance declaration guarantees to return a non-bottom+dictionary. But as #19690 shows, it is possible to get a bottom dicionary+by superclass selection if we aren't careful. The situation is very similar+to that described in Note [Recursive superclasses] in GHC.Tc.TyCl.Instance;+and we use the same solution:++* Give the Givens a CtOrigin of (GivenOrigin (InstSkol IsQC head_size))+* Give the Wanted a CtOrigin of (ScOrigin IsQC NakedSc)++Both of these things are done in solveForAll. Now the mechanism described+in Note [Solving superclass constraints] in GHC.Tc.TyCl.Instance takes over.++Note [Solving a Given forall-constraint]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+For a Given constraint+ [G] df :: forall ab. (Eq a, Ord b) => C x a b+we just add it to TcS's local InstEnv of known instances,+via addInertForall. Then, if we look up (C x Int Bool), say,+we'll find a match in the InstEnv.++************************************************************************+* *+* Equalities+* *+************************************************************************++Note [Canonicalising equalities]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+In order to canonicalise an equality, we look at the structure of the+two types at hand, looking for similarities. A difficulty is that the+types may look dissimilar before rewriting but similar after rewriting.+However, we don't just want to jump in and rewrite right away, because+this might be wasted effort. So, after looking for similarities and failing,+we rewrite and then try again. Of course, we don't want to loop, so we+track whether or not we've already rewritten.++It is conceivable to do a better job at tracking whether or not a type+is rewritten, but this is left as future work. (Mar '15)++Note [Decomposing FunTy]+~~~~~~~~~~~~~~~~~~~~~~~~+can_eq_nc' may attempt to decompose a FunTy that is un-zonked. This+means that we may very well have a FunTy containing a type of some+unknown kind. For instance, we may have,++ FunTy (a :: k) Int++Where k is a unification variable. So the calls to splitRuntimeRep_maybe may+fail (returning Nothing). In that case we'll fall through, zonk, and try again.+Zonking should fill the variable k, meaning that decomposition will succeed the+second time around.++Also note that we require the FunTyFlag to match. This will stop+us decomposing+ (Int -> Bool) ~ (Show a => blah)+It's as if we treat (->) and (=>) as different type constructors, which+indeed they are!+-}++canEqNC :: CtEvidence -> EqRel -> Type -> Type -> TcS (StopOrContinue Ct)+canEqNC ev eq_rel ty1 ty2+ = do { result <- zonk_eq_types ty1 ty2+ ; case result of+ Right ty -> canEqReflexive ev eq_rel ty+ Left (Pair ty1' ty2') -> can_eq_nc False ev' eq_rel ty1' ty1' ty2' ty2'+ where+ ev' | debugIsOn = setCtEvPredType ev $+ mkPrimEqPredRole (eqRelRole eq_rel) ty1' ty2'+ | otherwise = ev+ -- ev': satisfy the precondition of can_eq_nc+ }++can_eq_nc+ :: Bool -- True => both types are rewritten+ -> CtEvidence+ -> EqRel+ -> Type -> Type -- LHS, after and before type-synonym expansion, resp+ -> Type -> Type -- RHS, after and before type-synonym expansion, resp+ -> TcS (StopOrContinue Ct)+-- Precondition: in DEBUG mode, the `ctev_pred` of `ev` is (ps_ty1 ~# ps_ty2),+-- without zonking+-- This precondition is needed (only in DEBUG) to satisfy the assertions+-- in mkSelCo, called in canDecomposableTyConAppOK and canDecomposableFunTy++can_eq_nc rewritten ev eq_rel ty1 ps_ty1 ty2 ps_ty2+ = do { traceTcS "can_eq_nc" $+ vcat [ ppr rewritten, ppr ev, ppr eq_rel, ppr ty1, ppr ps_ty1, ppr ty2, ppr ps_ty2 ]+ ; rdr_env <- getGlobalRdrEnvTcS+ ; fam_insts <- getFamInstEnvs+ ; can_eq_nc' rewritten rdr_env fam_insts ev eq_rel ty1 ps_ty1 ty2 ps_ty2 }++can_eq_nc'+ :: Bool -- True => both input types are rewritten+ -> GlobalRdrEnv -- needed to see which newtypes are in scope+ -> FamInstEnvs -- needed to unwrap data instances+ -> CtEvidence+ -> EqRel+ -> Type -> Type -- LHS, after and before type-synonym expansion, resp+ -> Type -> Type -- RHS, after and before type-synonym expansion, resp+ -> TcS (StopOrContinue Ct)++-- See Note [Comparing nullary type synonyms] in GHC.Core.Type.+can_eq_nc' _flat _rdr_env _envs ev eq_rel ty1@(TyConApp tc1 []) _ps_ty1 (TyConApp tc2 []) _ps_ty2+ | tc1 == tc2+ = canEqReflexive ev eq_rel ty1++-- Expand synonyms first; see Note [Type synonyms and canonicalization]+can_eq_nc' rewritten rdr_env envs ev eq_rel ty1 ps_ty1 ty2 ps_ty2+ | Just ty1' <- coreView ty1 = can_eq_nc' rewritten rdr_env envs ev eq_rel ty1' ps_ty1 ty2 ps_ty2+ | Just ty2' <- coreView ty2 = can_eq_nc' rewritten rdr_env envs ev eq_rel ty1 ps_ty1 ty2' ps_ty2++-- need to check for reflexivity in the ReprEq case.+-- See Note [Eager reflexivity check]+-- Check only when rewritten because the zonk_eq_types check in canEqNC takes+-- care of the non-rewritten case.+can_eq_nc' True _rdr_env _envs ev ReprEq ty1 _ ty2 _+ | ty1 `tcEqType` ty2+ = canEqReflexive ev ReprEq ty1++-- When working with ReprEq, unwrap newtypes.+-- See Note [Unwrap newtypes first]+-- This must be above the TyVarTy case, in order to guarantee (TyEq:N)+can_eq_nc' _rewritten rdr_env envs ev eq_rel ty1 ps_ty1 ty2 ps_ty2+ | ReprEq <- eq_rel+ , Just stuff1 <- tcTopNormaliseNewTypeTF_maybe envs rdr_env ty1+ = can_eq_newtype_nc ev NotSwapped ty1 stuff1 ty2 ps_ty2++ | ReprEq <- eq_rel+ , Just stuff2 <- tcTopNormaliseNewTypeTF_maybe envs rdr_env ty2+ = can_eq_newtype_nc ev IsSwapped ty2 stuff2 ty1 ps_ty1++-- Then, get rid of casts+can_eq_nc' rewritten _rdr_env _envs ev eq_rel (CastTy ty1 co1) _ ty2 ps_ty2+ | isNothing (canEqLHS_maybe ty2) -- See (3) in Note [Equalities with incompatible kinds]+ = canEqCast rewritten ev eq_rel NotSwapped ty1 co1 ty2 ps_ty2+can_eq_nc' rewritten _rdr_env _envs ev eq_rel ty1 ps_ty1 (CastTy ty2 co2) _+ | isNothing (canEqLHS_maybe ty1) -- See (3) in Note [Equalities with incompatible kinds]+ = canEqCast rewritten ev eq_rel IsSwapped ty2 co2 ty1 ps_ty1++----------------------+-- Otherwise try to decompose+----------------------++-- Literals+can_eq_nc' _rewritten _rdr_env _envs ev eq_rel ty1@(LitTy l1) _ (LitTy l2) _+ | l1 == l2+ = do { setEvBindIfWanted ev (evCoercion $ mkReflCo (eqRelRole eq_rel) ty1)+ ; stopWith ev "Equal LitTy" }++-- Decompose FunTy: (s -> t) and (c => t)+-- NB: don't decompose (Int -> blah) ~ (Show a => blah)+can_eq_nc' _rewritten _rdr_env _envs ev eq_rel+ (FunTy { ft_mult = am1, ft_af = af1, ft_arg = ty1a, ft_res = ty1b }) _ps_ty1+ (FunTy { ft_mult = am2, ft_af = af2, ft_arg = ty2a, ft_res = ty2b }) _ps_ty2+ | af1 == af2 -- See Note [Decomposing FunTy]+ = canDecomposableFunTy ev eq_rel af1 (am1,ty1a,ty1b) (am2,ty2a,ty2b)++-- Decompose type constructor applications+-- NB: we have expanded type synonyms already+can_eq_nc' _rewritten _rdr_env _envs ev eq_rel ty1 _ ty2 _+ | Just (tc1, tys1) <- tcSplitTyConApp_maybe ty1+ , Just (tc2, tys2) <- tcSplitTyConApp_maybe ty2+ -- we want to catch e.g. Maybe Int ~ (Int -> Int) here for better+ -- error messages rather than decomposing into AppTys;+ -- hence no direct match on TyConApp+ , not (isTypeFamilyTyCon tc1)+ , not (isTypeFamilyTyCon tc2)+ = canTyConApp ev eq_rel tc1 tys1 tc2 tys2++can_eq_nc' _rewritten _rdr_env _envs ev eq_rel+ s1@(ForAllTy (Bndr _ vis1) _) _+ s2@(ForAllTy (Bndr _ vis2) _) _+ | vis1 `eqForAllVis` vis2 -- Note [ForAllTy and type equality]+ = can_eq_nc_forall ev eq_rel s1 s2++-- See Note [Canonicalising type applications] about why we require rewritten types+-- Use tcSplitAppTy, not matching on AppTy, to catch oversaturated type families+-- NB: Only decompose AppTy for nominal equality.+-- See Note [Decomposing AppTy equalities]+can_eq_nc' True _rdr_env _envs ev NomEq ty1 _ ty2 _+ | Just (t1, s1) <- tcSplitAppTy_maybe ty1+ , Just (t2, s2) <- tcSplitAppTy_maybe ty2+ = can_eq_app ev t1 s1 t2 s2++-------------------+-- Can't decompose.+-------------------++-- No similarity in type structure detected. Rewrite and try again.+can_eq_nc' False rdr_env envs ev eq_rel _ ps_ty1 _ ps_ty2+ = -- Rewrite the two types and try again+ do { (redn1@(Reduction _ xi1), rewriters1) <- rewrite ev ps_ty1+ ; (redn2@(Reduction _ xi2), rewriters2) <- rewrite ev ps_ty2+ ; new_ev <- rewriteEqEvidence (rewriters1 S.<> rewriters2) ev NotSwapped redn1 redn2+ ; can_eq_nc' True rdr_env envs new_ev eq_rel xi1 xi1 xi2 xi2 }++----------------------------+-- Look for a canonical LHS. See Note [Canonical LHS].+-- Only rewritten types end up below here.+----------------------------++-- NB: pattern match on True: we want only rewritten types sent to canEqLHS+-- This means we've rewritten any variables and reduced any type family redexes+-- See also Note [No top-level newtypes on RHS of representational equalities]+can_eq_nc' True _rdr_env _envs ev eq_rel ty1 ps_ty1 ty2 ps_ty2+ | Just can_eq_lhs1 <- canEqLHS_maybe ty1+ = canEqCanLHS ev eq_rel NotSwapped can_eq_lhs1 ps_ty1 ty2 ps_ty2++ | Just can_eq_lhs2 <- canEqLHS_maybe ty2+ = canEqCanLHS ev eq_rel IsSwapped can_eq_lhs2 ps_ty2 ty1 ps_ty1++ -- If the type is TyConApp tc1 args1, then args1 really can't be less+ -- than tyConArity tc1. It could be *more* than tyConArity, but then we+ -- should have handled the case as an AppTy. That case only fires if+ -- _both_ sides of the equality are AppTy-like... but if one side is+ -- AppTy-like and the other isn't (and it also isn't a variable or+ -- saturated type family application, both of which are handled by+ -- can_eq_nc'), we're in a failure mode and can just fall through.++----------------------------+-- Fall-through. Give up.+----------------------------++-- We've rewritten and the types don't match. Give up.+can_eq_nc' True _rdr_env _envs ev eq_rel _ ps_ty1 _ ps_ty2+ = do { traceTcS "can_eq_nc' catch-all case" (ppr ps_ty1 $$ ppr ps_ty2)+ ; case eq_rel of -- See Note [Unsolved equalities]+ ReprEq -> continueWith (mkIrredCt ReprEqReason ev)+ NomEq -> continueWith (mkIrredCt ShapeMismatchReason ev) }+ -- No need to call canEqFailure/canEqHardFailure because they+ -- rewrite, and the types involved here are already rewritten+++{- Note [Unsolved equalities]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+If we have an unsolved equality like+ (a b ~R# Int)+that is not necessarily insoluble! Maybe 'a' will turn out to be a newtype.+So we want to make it a potentially-soluble Irred not an insoluble one.+Missing this point is what caused #15431+-}++---------------------------------+can_eq_nc_forall :: CtEvidence -> EqRel+ -> Type -> Type -- LHS and RHS+ -> TcS (StopOrContinue Ct)+-- (forall as. phi1) ~ (forall bs. phi2)+-- Check for length match of as, bs+-- Then build an implication constraint: forall as. phi1 ~ phi2[as/bs]+-- But remember also to unify the kinds of as and bs+-- (this is the 'go' loop), and actually substitute phi2[as |> cos / bs]+-- Remember also that we might have forall z (a:z). blah+-- so we must proceed one binder at a time (#13879)++can_eq_nc_forall ev eq_rel s1 s2+ | CtWanted { ctev_loc = loc, ctev_dest = orig_dest, ctev_rewriters = rewriters } <- ev+ = do { let free_tvs = tyCoVarsOfTypes [s1,s2]+ (bndrs1, phi1) = tcSplitForAllTyVarBinders s1+ (bndrs2, phi2) = tcSplitForAllTyVarBinders s2+ ; if not (equalLength bndrs1 bndrs2)+ then do { traceTcS "Forall failure" $+ vcat [ ppr s1, ppr s2, ppr bndrs1, ppr bndrs2+ , ppr (binderFlags bndrs1)+ , ppr (binderFlags bndrs2) ]+ ; canEqHardFailure ev s1 s2 }+ else+ do { traceTcS "Creating implication for polytype equality" $ ppr ev+ ; let empty_subst1 = mkEmptySubst $ mkInScopeSet free_tvs+ ; skol_info <- mkSkolemInfo (UnifyForAllSkol phi1)+ ; (subst1, skol_tvs) <- tcInstSkolTyVarsX skol_info empty_subst1 $+ binderVars bndrs1++ ; let phi1' = substTy subst1 phi1++ -- Unify the kinds, extend the substitution+ go :: [TcTyVar] -> Subst -> [TyVarBinder]+ -> TcS (TcCoercion, Cts)+ go (skol_tv:skol_tvs) subst (bndr2:bndrs2)+ = do { let tv2 = binderVar bndr2+ ; (kind_co, wanteds1) <- unify loc rewriters Nominal (tyVarKind skol_tv)+ (substTy subst (tyVarKind tv2))+ ; let subst' = extendTvSubstAndInScope subst tv2+ (mkCastTy (mkTyVarTy skol_tv) kind_co)+ -- skol_tv is already in the in-scope set, but the+ -- free vars of kind_co are not; hence "...AndInScope"+ ; (co, wanteds2) <- go skol_tvs subst' bndrs2+ ; return ( mkForAllCo skol_tv kind_co co+ , wanteds1 `unionBags` wanteds2 ) }++ -- Done: unify phi1 ~ phi2+ go [] subst bndrs2+ = assert (null bndrs2) $+ unify loc rewriters (eqRelRole eq_rel) phi1' (substTyUnchecked subst phi2)++ go _ _ _ = panic "cna_eq_nc_forall" -- case (s:ss) []++ empty_subst2 = mkEmptySubst (getSubstInScope subst1)++ ; (lvl, (all_co, wanteds)) <- pushLevelNoWorkList (ppr skol_info) $+ go skol_tvs empty_subst2 bndrs2+ ; emitTvImplicationTcS lvl (getSkolemInfo skol_info) skol_tvs wanteds++ ; setWantedEq orig_dest all_co+ ; stopWith ev "Deferred polytype equality" } }++ | otherwise+ = do { traceTcS "Omitting decomposition of given polytype equality" $+ pprEq s1 s2 -- See Note [Do not decompose Given polytype equalities]+ ; stopWith ev "Discard given polytype equality" }++ where+ unify :: CtLoc -> RewriterSet -> Role -> TcType -> TcType -> TcS (TcCoercion, Cts)+ -- This version returns the wanted constraint rather+ -- than putting it in the work list+ unify loc rewriters role ty1 ty2+ | ty1 `tcEqType` ty2+ = return (mkReflCo role ty1, emptyBag)+ | otherwise+ = do { (wanted, co) <- newWantedEq loc rewriters role ty1 ty2+ ; return (co, unitBag (mkNonCanonical wanted)) }++---------------------------------+-- | Compare types for equality, while zonking as necessary. Gives up+-- as soon as it finds that two types are not equal.+-- This is quite handy when some unification has made two+-- types in an inert Wanted to be equal. We can discover the equality without+-- rewriting, which is sometimes very expensive (in the case of type functions).+-- In particular, this function makes a ~20% improvement in test case+-- perf/compiler/T5030.+--+-- Returns either the (partially zonked) types in the case of+-- inequality, or the one type in the case of equality. canEqReflexive is+-- a good next step in the 'Right' case. Returning 'Left' is always safe.+--+-- NB: This does *not* look through type synonyms. In fact, it treats type+-- synonyms as rigid constructors. In the future, it might be convenient+-- to look at only those arguments of type synonyms that actually appear+-- in the synonym RHS. But we're not there yet.+zonk_eq_types :: TcType -> TcType -> TcS (Either (Pair TcType) TcType)+zonk_eq_types = go+ where+ go (TyVarTy tv1) (TyVarTy tv2) = tyvar_tyvar tv1 tv2+ go (TyVarTy tv1) ty2 = tyvar NotSwapped tv1 ty2+ go ty1 (TyVarTy tv2) = tyvar IsSwapped tv2 ty1++ -- We handle FunTys explicitly here despite the fact that they could also be+ -- treated as an application. Why? Well, for one it's cheaper to just look+ -- at two types (the argument and result types) than four (the argument,+ -- result, and their RuntimeReps). Also, we haven't completely zonked yet,+ -- so we may run into an unzonked type variable while trying to compute the+ -- RuntimeReps of the argument and result types. This can be observed in+ -- testcase tc269.+ go (FunTy af1 w1 arg1 res1) (FunTy af2 w2 arg2 res2)+ | af1 == af2+ , eqType w1 w2+ = do { res_a <- go arg1 arg2+ ; res_b <- go res1 res2+ ; return $ combine_rev (FunTy af1 w1) res_b res_a }++ go ty1@(FunTy {}) ty2 = bale_out ty1 ty2+ go ty1 ty2@(FunTy {}) = bale_out ty1 ty2++ go ty1 ty2+ | Just (tc1, tys1) <- splitTyConAppNoView_maybe ty1+ , Just (tc2, tys2) <- splitTyConAppNoView_maybe ty2+ = if tc1 == tc2 && tys1 `equalLength` tys2+ -- Crucial to check for equal-length args, because+ -- we cannot assume that the two args to 'go' have+ -- the same kind. E.g go (Proxy * (Maybe Int))+ -- (Proxy (*->*) Maybe)+ -- We'll call (go (Maybe Int) Maybe)+ -- See #13083+ then tycon tc1 tys1 tys2+ else bale_out ty1 ty2++ go ty1 ty2+ | Just (ty1a, ty1b) <- tcSplitAppTyNoView_maybe ty1+ , Just (ty2a, ty2b) <- tcSplitAppTyNoView_maybe ty2+ = do { res_a <- go ty1a ty2a+ ; res_b <- go ty1b ty2b+ ; return $ combine_rev mkAppTy res_b res_a }++ go ty1@(LitTy lit1) (LitTy lit2)+ | lit1 == lit2+ = return (Right ty1)++ go ty1 ty2 = bale_out ty1 ty2+ -- We don't handle more complex forms here++ bale_out ty1 ty2 = return $ Left (Pair ty1 ty2)++ tyvar :: SwapFlag -> TcTyVar -> TcType+ -> TcS (Either (Pair TcType) TcType)+ -- Try to do as little as possible, as anything we do here is redundant+ -- with rewriting. In particular, no need to zonk kinds. That's why+ -- we don't use the already-defined zonking functions+ tyvar swapped tv ty+ = case tcTyVarDetails tv of+ MetaTv { mtv_ref = ref }+ -> do { cts <- readTcRef ref+ ; case cts of+ Flexi -> give_up+ Indirect ty' -> do { trace_indirect tv ty'+ ; unSwap swapped go ty' ty } }+ _ -> give_up+ where+ give_up = return $ Left $ unSwap swapped Pair (mkTyVarTy tv) ty++ tyvar_tyvar tv1 tv2+ | tv1 == tv2 = return (Right (mkTyVarTy tv1))+ | otherwise = do { (ty1', progress1) <- quick_zonk tv1+ ; (ty2', progress2) <- quick_zonk tv2+ ; if progress1 || progress2+ then go ty1' ty2'+ else return $ Left (Pair (TyVarTy tv1) (TyVarTy tv2)) }++ trace_indirect tv ty+ = traceTcS "Following filled tyvar (zonk_eq_types)"+ (ppr tv <+> equals <+> ppr ty)++ quick_zonk tv = case tcTyVarDetails tv of+ MetaTv { mtv_ref = ref }+ -> do { cts <- readTcRef ref+ ; case cts of+ Flexi -> return (TyVarTy tv, False)+ Indirect ty' -> do { trace_indirect tv ty'+ ; return (ty', True) } }+ _ -> return (TyVarTy tv, False)++ -- This happens for type families, too. But recall that failure+ -- here just means to try harder, so it's OK if the type function+ -- isn't injective.+ tycon :: TyCon -> [TcType] -> [TcType]+ -> TcS (Either (Pair TcType) TcType)+ tycon tc tys1 tys2+ = do { results <- zipWithM go tys1 tys2+ ; return $ case combine_results results of+ Left tys -> Left (mkTyConApp tc <$> tys)+ Right tys -> Right (mkTyConApp tc tys) }++ combine_results :: [Either (Pair TcType) TcType]+ -> Either (Pair [TcType]) [TcType]+ combine_results = bimap (fmap reverse) reverse .+ foldl' (combine_rev (:)) (Right [])++ -- combine (in reverse) a new result onto an already-combined result+ combine_rev :: (a -> b -> c)+ -> Either (Pair b) b+ -> Either (Pair a) a+ -> Either (Pair c) c+ combine_rev f (Left list) (Left elt) = Left (f <$> elt <*> list)+ combine_rev f (Left list) (Right ty) = Left (f <$> pure ty <*> list)+ combine_rev f (Right tys) (Left elt) = Left (f <$> elt <*> pure tys)+ combine_rev f (Right tys) (Right ty) = Right (f ty tys)++{- Note [Unwrap newtypes first]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+See also Note [Decomposing newtype equalities]++Consider+ newtype N m a = MkN (m a)+N will get a conservative, Nominal role for its second parameter 'a',+because it appears as an argument to the unknown 'm'. Now consider+ [W] N Maybe a ~R# N Maybe b++If we /decompose/, we'll get+ [W] a ~N# b++But if instead we /unwrap/ we'll get+ [W] Maybe a ~R# Maybe b+which in turn gives us+ [W] a ~R# b+which is easier to satisfy.++Conclusion: we must unwrap newtypes before decomposing them. This happens+in `can_eq_newtype_nc`++We did flirt with making the /rewriter/ expand newtypes, rather than+doing it in `can_eq_newtype_nc`. But with recursive newtypes we want+to be super-careful about expanding!++ newtype A = MkA [A] -- Recursive!++ f :: A -> [A]+ f = coerce++We have [W] A ~R# [A]. If we rewrite [A], it'll expand to+ [[[[[...]]]]]+and blow the reduction stack. See Note [Newtypes can blow the stack]+in GHC.Tc.Solver.Rewrite. But if we expand only the /top level/ of+both sides, we get+ [W] [A] ~R# [A]+which we can, just, solve by reflexivity.++So we simply unwrap, on-demand, at top level, in `can_eq_newtype_nc`.++This is all very delicate. There is a real risk of a loop in the type checker+with recursive newtypes -- but I think we're doomed to do *something*+delicate, as we're really trying to solve for equirecursive type+equality. Bottom line for users: recursive newtypes do not play well with type+inference for representational equality. See also Section 5.3.1 and 5.3.4 of+"Safe Zero-cost Coercions for Haskell" (JFP 2016).++See also Note [Decomposing newtype equalities].++--- Historical side note ---++We flirted with doing /both/ unwrap-at-top-level /and/ rewrite-deeply;+see #22519. But that didn't work: see discussion in #22924. Specifically+we got a loop with a minor variation:+ f2 :: a -> [A]+ f2 = coerce++Note [Eager reflexivity check]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Suppose we have++ newtype X = MkX (Int -> X)++and++ [W] X ~R X++Naively, we would start unwrapping X and end up in a loop. Instead,+we do this eager reflexivity check. This is necessary only for representational+equality because the rewriter technology deals with the similar case+(recursive type families) for nominal equality.++Note that this check does not catch all cases, but it will catch the cases+we're most worried about, types like X above that are actually inhabited.++Here's another place where this reflexivity check is key:+Consider trying to prove (f a) ~R (f a). The AppTys in there can't+be decomposed, because representational equality isn't congruent with respect+to AppTy. So, when canonicalising the equality above, we get stuck and+would normally produce a CIrredCan. However, we really do want to+be able to solve (f a) ~R (f a). So, in the representational case only,+we do a reflexivity check.++(This would be sound in the nominal case, but unnecessary, and I [Richard+E.] am worried that it would slow down the common case.)++ Note [Newtypes can blow the stack]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Suppose we have++ newtype X = MkX (Int -> X)+ newtype Y = MkY (Int -> Y)++and now wish to prove++ [W] X ~R Y++This Wanted will loop, expanding out the newtypes ever deeper looking+for a solid match or a solid discrepancy. Indeed, there is something+appropriate to this looping, because X and Y *do* have the same representation,+in the limit -- they're both (Fix ((->) Int)). However, no finitely-sized+coercion will ever witness it. This loop won't actually cause GHC to hang,+though, because we check our depth in `can_eq_newtype_nc`.+-}++------------------------+-- | We're able to unwrap a newtype. Update the bits accordingly.+can_eq_newtype_nc :: CtEvidence -- ^ :: ty1 ~ ty2+ -> SwapFlag+ -> TcType -- ^ ty1+ -> ((Bag GlobalRdrElt, TcCoercion), TcType) -- ^ :: ty1 ~ ty1'+ -> TcType -- ^ ty2+ -> TcType -- ^ ty2, with type synonyms+ -> TcS (StopOrContinue Ct)+can_eq_newtype_nc ev swapped ty1 ((gres, co1), ty1') ty2 ps_ty2+ = do { traceTcS "can_eq_newtype_nc" $+ vcat [ ppr ev, ppr swapped, ppr co1, ppr gres, ppr ty1', ppr ty2 ]++ -- Check for blowing our stack, and increase the depth+ -- See Note [Newtypes can blow the stack]+ ; let loc = ctEvLoc ev+ ev' = ev `setCtEvLoc` bumpCtLocDepth loc+ ; checkReductionDepth loc ty1++ -- Next, we record uses of newtype constructors, since coercing+ -- through newtypes is tantamount to using their constructors.+ ; recordUsedGREs gres++ ; let redn1 = mkReduction co1 ty1'++ ; new_ev <- rewriteEqEvidence emptyRewriterSet ev' swapped+ redn1+ (mkReflRedn Representational ps_ty2)+ ; can_eq_nc False new_ev ReprEq ty1' ty1' ty2 ps_ty2 }++---------+-- ^ Decompose a type application.+-- All input types must be rewritten. See Note [Canonicalising type applications]+-- Nominal equality only!+can_eq_app :: CtEvidence -- :: s1 t1 ~N s2 t2+ -> Xi -> Xi -- s1 t1+ -> Xi -> Xi -- s2 t2+ -> TcS (StopOrContinue Ct)++-- AppTys only decompose for nominal equality, so this case just leads+-- to an irreducible constraint; see typecheck/should_compile/T10494+-- See Note [Decomposing AppTy equalities]+can_eq_app ev s1 t1 s2 t2+ | CtWanted { ctev_dest = dest, ctev_rewriters = rewriters } <- ev+ = do { co_s <- unifyWanted rewriters loc Nominal s1 s2+ ; let arg_loc+ | isNextArgVisible s1 = loc+ | otherwise = updateCtLocOrigin loc toInvisibleOrigin+ ; co_t <- unifyWanted rewriters arg_loc Nominal t1 t2+ ; let co = mkAppCo co_s co_t+ ; setWantedEq dest co+ ; stopWith ev "Decomposed [W] AppTy" }++ -- If there is a ForAll/(->) mismatch, the use of the Left coercion+ -- below is ill-typed, potentially leading to a panic in splitTyConApp+ -- Test case: typecheck/should_run/Typeable1+ -- We could also include this mismatch check above (for W and D), but it's slow+ -- and we'll get a better error message not doing it+ | s1k `mismatches` s2k+ = canEqHardFailure ev (s1 `mkAppTy` t1) (s2 `mkAppTy` t2)++ | CtGiven { ctev_evar = evar } <- ev+ = do { let co = mkCoVarCo evar+ co_s = mkLRCo CLeft co+ co_t = mkLRCo CRight co+ ; evar_s <- newGivenEvVar loc ( mkTcEqPredLikeEv ev s1 s2+ , evCoercion co_s )+ ; evar_t <- newGivenEvVar loc ( mkTcEqPredLikeEv ev t1 t2+ , evCoercion co_t )+ ; emitWorkNC [evar_t]+ ; canEqNC evar_s NomEq s1 s2 }++ where+ loc = ctEvLoc ev++ s1k = typeKind s1+ s2k = typeKind s2++ k1 `mismatches` k2+ = isForAllTy k1 && not (isForAllTy k2)+ || not (isForAllTy k1) && isForAllTy k2++-----------------------+-- | Break apart an equality over a casted type+-- looking like (ty1 |> co1) ~ ty2 (modulo a swap-flag)+canEqCast :: Bool -- are both types rewritten?+ -> CtEvidence+ -> EqRel+ -> SwapFlag+ -> TcType -> Coercion -- LHS (res. RHS), ty1 |> co1+ -> TcType -> TcType -- RHS (res. LHS), ty2 both normal and pretty+ -> TcS (StopOrContinue Ct)+canEqCast rewritten ev eq_rel swapped ty1 co1 ty2 ps_ty2+ = do { traceTcS "Decomposing cast" (vcat [ ppr ev+ , ppr ty1 <+> text "|>" <+> ppr co1+ , ppr ps_ty2 ])+ ; new_ev <- rewriteEqEvidence emptyRewriterSet ev swapped+ (mkGReflLeftRedn role ty1 co1)+ (mkReflRedn role ps_ty2)+ ; can_eq_nc rewritten new_ev eq_rel ty1 ty1 ty2 ps_ty2 }+ where+ role = eqRelRole eq_rel++------------------------+canTyConApp :: CtEvidence -> EqRel+ -> TyCon -> [TcType]+ -> TyCon -> [TcType]+ -> TcS (StopOrContinue Ct)+-- See Note [Decomposing TyConApp equalities]+-- See Note [Decomposing Dependent TyCons and Processing Wanted Equalities]+-- Neither tc1 nor tc2 is a saturated funTyCon, nor a type family+-- But they can be data families.+canTyConApp ev eq_rel tc1 tys1 tc2 tys2+ | tc1 == tc2+ , tys1 `equalLength` tys2+ = do { inerts <- getTcSInerts+ ; if can_decompose inerts+ then canDecomposableTyConAppOK ev eq_rel tc1 tys1 tys2+ else canEqFailure ev eq_rel ty1 ty2 }++ -- See Note [Skolem abstract data] in GHC.Core.Tycon+ | tyConSkolem tc1 || tyConSkolem tc2+ = do { traceTcS "canTyConApp: skolem abstract" (ppr tc1 $$ ppr tc2)+ ; continueWith (mkIrredCt AbstractTyConReason ev) }++ -- Fail straight away for better error messages+ -- See Note [Use canEqFailure in canDecomposableTyConApp]+ | eq_rel == ReprEq && not (isGenerativeTyCon tc1 Representational &&+ isGenerativeTyCon tc2 Representational)+ = canEqFailure ev eq_rel ty1 ty2++ | otherwise+ = canEqHardFailure ev ty1 ty2+ where+ -- Reconstruct the types for error messages. This would do+ -- the wrong thing (from a pretty printing point of view)+ -- for functions, because we've lost the FunTyFlag; but+ -- in fact we never call canTyConApp on a saturated FunTyCon+ ty1 = mkTyConApp tc1 tys1+ ty2 = mkTyConApp tc2 tys2++ -- See Note [Decomposing TyConApp equalities]+ -- and Note [Decomposing newtype equalities]+ can_decompose inerts+ = isInjectiveTyCon tc1 (eqRelRole eq_rel)+ || (assert (eq_rel == ReprEq) $+ -- assert: isInjectiveTyCon is always True for Nominal except+ -- for type synonyms/families, neither of which happen here+ -- Moreover isInjectiveTyCon is True for Representational+ -- for algebraic data types. So we are down to newtypes+ -- and data families.+ ctEvFlavour ev == Wanted && noGivenNewtypeReprEqs tc1 inerts)+ -- See Note [Decomposing newtype equalities] (EX2)++{-+Note [Use canEqFailure in canDecomposableTyConApp]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+We must use canEqFailure, not canEqHardFailure here, because there is+the possibility of success if working with a representational equality.+Here is one case:++ type family TF a where TF Char = Bool+ data family DF a+ newtype instance DF Bool = MkDF Int++Suppose we are canonicalising (Int ~R DF (TF a)), where we don't yet+know `a`. This is *not* a hard failure, because we might soon learn+that `a` is, in fact, Char, and then the equality succeeds.++Here is another case:++ [G] Age ~R Int++where Age's constructor is not in scope. We don't want to report+an "inaccessible code" error in the context of this Given!++For example, see typecheck/should_compile/T10493, repeated here:++ import Data.Ord (Down) -- no constructor++ foo :: Coercible (Down Int) Int => Down Int -> Int+ foo = coerce++That should compile, but only because we use canEqFailure and not+canEqHardFailure.++Note [Fast path when decomposing TyConApps]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+If we see (T s1 t1 ~ T s2 t2), then we can just decompose to+ (s1 ~ s2, t1 ~ t2)+and push those back into the work list. But if+ s1 = K k1 s2 = K k2+then we will just decompose s1~s2, and it might be better to+do so on the spot. An important special case is where s1=s2,+and we get just Refl.++So canDecomposableTyConAppOK uses unifyWanted etc to short-cut that work.+See also Note [Decomposing Dependent TyCons and Processing Wanted Equalities]++Note [Decomposing TyConApp equalities]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Suppose we have+ [G/W] T ty1 ~r T ty2+Can we decompose it, and replace it by+ [G/W] ty1 ~r' ty2+and if so what role is r'? (In this Note, all the "~" are primitive+equalities "~#", but I have dropped the noisy "#" symbols.) Lots of+background in the paper "Safe zero-cost coercions for Haskell".++This Note covers the topic for+ * Datatypes+ * Newtypes+ * Data families+For the rest:+ * Type synonyms: are always expanded+ * Type families: see Note [Decomposing type family applications]+ * AppTy: see Note [Decomposing AppTy equalities].++---- Roles of the decomposed constraints ----+For a start, the role r' will always be defined like this:+ * If r=N then r' = N+ * If r=R then r' = role of T's first argument++For example:+ data TR a = MkTR a -- Role of T's first arg is Representational+ data TN a = MkTN (F a) -- Role of T's first arg is Nominal++The function tyConRolesX :: Role -> TyCon -> [Role] gets the argument+role r' for a TyCon T at role r. E.g.+ tyConRolesX Nominal TR = [Nominal]+ tyConRolesX Representational TR = [Representational]++---- Soundness and completeness ----+For Givens, for /soundness/ of decomposition we need, forall ty1,ty2:+ T ty1 ~r T ty2 ===> ty1 ~r' ty2+Here "===>" means "implies". That is, given evidence for (co1 : T ty1 ~r T co2)+we can produce evidence for (co2 : ty1 ~r' ty2). But in the solver we+/replace/ co1 with co2 in the inert set, and we don't want to lose any proofs+thereby. So for /completeness/ of decomposition we also need the reverse:+ ty1 ~r' ty2 ===> T ty1 ~r T ty2++For Wanteds, for /soundness/ of decomposition we need:+ ty1 ~r' ty2 ===> T ty1 ~r T ty2+because if we do decompose we'll get evidence (co2 : ty1 ~r' ty2) and+from that we want to derive evidence (T co2 : T ty1 ~r T ty2).+For /completeness/ of decomposition we need the reverse implication too,+else we may decompose to a new proof obligation that is stronger than+the one we started with. See Note [Decomposing newtype equalities].++---- Injectivity ----+When do these bi-implications hold? In one direction it is easy.+We /always/ have+ ty1 ~r' ty2 ===> T ty1 ~r T ty2+This is the CO_TYCONAPP rule of the paper (Fig 5); see also the+TyConAppCo case of GHC.Core.Lint.lintCoercion.++In the other direction, we have+ T ty1 ~r T ty2 ==> ty1 ~r' ty2 if T is /injective at role r/+This is the very /definition/ of injectivity: injectivity means result+is the same => arguments are the same, modulo the role shift.+See comments on GHC.Core.TyCon.isInjectiveTyCon. This is also+the CO_NTH rule in Fig 5 of the paper, except in the paper only+newtypes are non-injective at representation role, so the rule says "H+is not a newtype".++Injectivity is a bit subtle:+ Nominal Representational+ Datatype YES YES+ Newtype YES NO{1}+ Data family YES NO{2}++{1} Consider newtype N a = MkN (F a) -- Arg has Nominal role+ Is it true that (N t1) ~R (N t2) ==> t1 ~N t2 ?+ No, absolutely not. E.g.+ type instance F Int = Int; type instance F Bool = Char+ Then (N Int) ~R (N Bool), by unwrapping, but we don't want Int~Char!++ See Note [Decomposing newtype equalities]++{2} We must treat data families precisely like newtypes, because of the+ possibility of newtype instances. See also+ Note [Decomposing newtype equalities]. See #10534 and+ test case typecheck/should_fail/T10534.++---- Takeaway summary -----+For sound and complete decomposition, we simply need injectivity;+that is for isInjectiveTyCon to be true:++* At Nominal role, isInjectiveTyCon is True for all the TyCons we are+ considering in this Note: datatypes, newtypes, and data families.++* For Givens, injectivity is necessary for soundness; completeness has no+ side conditions.++* For Wanteds, soundness has no side conditions; but injectivity is needed+ for completeness. See Note [Decomposing newtype equalities]++This is implemented in `can_decompose` in `canTyConApp`; it looks at+injectivity, just as specified above.+++Note [Decomposing type family applications]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Supose we have+ [G/W] (F ty1) ~r (F ty2)+This is handled by the TyFamLHS/TyFamLHS case of canEqCanLHS2.++We never decompose to+ [G/W] ty1 ~r' ty2++Instead++* For Givens we do nothing. Injective type families have no corresponding+ evidence of their injectivity, so we cannot decompose an+ injective-type-family Given.++* For Wanteds, for the Nominal role only, we emit new Wanteds rather like+ functional dependencies, for each injective argument position.++ E.g type family F a b -- injective in first arg, but not second+ [W] (F s1 t1) ~N (F s2 t2)+ Emit new Wanteds+ [W] s1 ~N s2+ But retain the existing, unsolved constraint.++Note [Decomposing newtype equalities]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+This Note also applies to data families, which we treat like+newtype in case of 'newtype instance'.++As Note [Decomposing TyConApp equalities] describes, if N is injective+at role r, we can do this decomposition?+ [G/W] (N ty1) ~r (N ty2) to [G/W] ty1 ~r' ty2++For a Given with r=R, the answer is a solid NO: newtypes are not injective at+representational role, and we must not decompose, or we lose soundness.+Example is wrinkle {1} in Note [Decomposing TyConApp equalities].++For a Wanted with r=R, since newtypes are not injective at representational+role, decomposition is sound, but we may lose completeness. Nevertheless,+if the newtype is abstract (so can't be unwrapped) we can only solve+the equality by (a) using a Given or (b) decomposition. If (a) is impossible+(e.g. no Givens) then (b) is safe albeit potentially incomplete.++There are two ways in which decomposing (N ty1) ~r (N ty2) could be incomplete:++* Incompleteness example (EX1): unwrap first+ newtype Nt a = MkNt (Id a)+ type family Id a where Id a = a++ [W] Nt Int ~R Nt Age++ Because of its use of a type family, Nt's parameter will get inferred to+ have a nominal role. Thus, decomposing the wanted will yield [W] Int ~N Age,+ which is unsatisfiable. Unwrapping, though, leads to a solution.++ Conclusion: always unwrap newtypes before attempting to decompose+ them. This is done in can_eq_nc'. Of course, we can't unwrap if the data+ constructor isn't in scope. See Note [Unwrap newtypes first].++* Incompleteness example (EX2): available Givens+ newtype Nt a = Mk Bool -- NB: a is not used in the RHS,+ type role Nt representational -- but the user gives it an R role anyway++ [G] Nt t1 ~R Nt t2+ [W] Nt alpha ~R Nt beta++ We *don't* want to decompose to [W] alpha ~R beta, because it's possible+ that alpha and beta aren't representationally equal. And if we figure+ out (elsewhere) that alpha:=t1 and beta:=t2, we can solve the Wanted+ from the Given. This is somewhat similar to the question of overlapping+ Givens for class constraints: see Note [Instance and Given overlap] in+ GHC.Tc.Solver.Interact.++ Conclusion: don't decompose [W] N s ~R N t, if there are any Given+ equalities that could later solve it.++ But what precisely does it mean to say "any Given equalities that could+ later solve it"?++ In #22924 we had+ [G] f a ~R# a [W] Const (f a) a ~R# Const a a+ where Const is an abstract newtype. If we decomposed the newtype, we+ could solve. Not-decomposing on the grounds that (f a ~R# a) might turn+ into (Const (f a) a ~R# Const a a) seems a bit silly.++ In #22331 we had+ [G] N a ~R# N b [W] N b ~R# N a+ (where N is abstract so we can't unwrap). Here we really /don't/ want to+ decompose, because the /only/ way to solve the Wanted is from that Given+ (with a Sym).++ In #22519 we had+ [G] a <= b [W] IO Age ~R# IO Int++ (where IO is abstract so we can't unwrap, and newtype Age = Int; and (<=)+ is a type-level comparison on Nats). Here we /must/ decompose, despite the+ existence of an Irred Given, or we will simply be stuck. (Side note: We+ flirted with deep-rewriting of newtypes (see discussion on #22519 and+ !9623) but that turned out not to solve #22924, and also makes type+ inference loop more often on recursive newtypes.)++ The currently-implemented compromise is this:++ we decompose [W] N s ~R# N t unless there is a [G] N s' ~ N t'++ that is, a Given Irred equality with both sides headed with N.+ See the call to noGivenNewtypeReprEqs in canTyConApp.++ This is not perfect. In principle a Given like [G] (a b) ~ (c d), or+ even just [G] c, could later turn into N s ~ N t. But since the free+ vars of a Given are skolems, or at least untouchable unification+ variables, this is extremely unlikely to happen.++ Another worry: there could, just, be a CDictCan with some+ un-expanded equality superclasses; but only in some very obscure+ recursive-superclass situations.++ Yet another approach (!) is desribed in+ Note [Decomposing newtypes a bit more aggressively].++Remember: decomposing Wanteds is always /sound/. This Note is+only about /completeness/.++Note [Decomposing newtypes a bit more aggressively]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+IMPORTANT: the ideas in this Note are *not* implemented. Instead, the+current approach is detailed in Note [Decomposing newtype equalities]+and Note [Unwrap newtypes first].+For more details about the ideas in this Note see+ * GHC propoosal: https://github.com/ghc-proposals/ghc-proposals/pull/549+ * issue #22441+ * discussion on !9282.++Consider [G] c, [W] (IO Int) ~R (IO Age)+where IO is abstract, and+ newtype Age = MkAge Int -- Not abstract+With the above rules, if there any Given Irreds,+the Wanted is insoluble because we can't decompose it. But in fact,+if we look at the defn of IO, roughly,+ newtype IO a = State# -> (State#, a)+we can see that decomposing [W] (IO Int) ~R (IO Age) to+ [W] Int ~R Age+definitely does not lose completeness. Why not? Because the role of+IO's argment is representational. Hence:++ DecomposeNewtypeIdea:+ decompose [W] (N s1 .. sn) ~R (N t1 .. tn)+ if the roles of all N's arguments are representational++If N's arguments really /are/ representational this will not lose+completeness. Here "really are representational" means "if you expand+all newtypes in N's RHS, we'd infer a representational role for each+of N's type variables in that expansion". See Note [Role inference]+in GHC.Tc.TyCl.Utils.++But the user might /override/ a phantom role with an explicit role+annotation, and then we could (obscurely) get incompleteness.+Consider++ module A( silly, T ) where+ newtype T a = MkT Int+ type role T representational -- Override phantom role++ silly :: Coercion (T Int) (T Bool)+ silly = Coercion -- Typechecks by unwrapping the newtype++ data Coercion a b where -- Actually defined in Data.Type.Coercion+ Coercion :: Coercible a b => Coercion a b++ module B where+ import A+ f :: T Int -> T Bool+ f = case silly of Coercion -> coerce++Here the `coerce` gives [W] (T Int) ~R (T Bool) which, if we decompose,+we'll get stuck with (Int ~R Bool). Instead we want to use the+[G] (T Int) ~R (T Bool), which will be in the Irreds.++Summary: we could adopt (DecomposeNewtypeIdea), at the cost of a very+obscure incompleteness (above). But no one is reporting a problem from+the lack of decompostion, so we'll just leave it for now. This long+Note is just to record the thinking for our future selves.++Note [Decomposing AppTy equalities]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+For AppTy all the same questions arise as in+Note [Decomposing TyConApp equalities]. We have++ s1 ~r s2, t1 ~N t2 ==> s1 t1 ~r s2 t2 (rule CO_APP)+ s1 t1 ~N s2 t2 ==> s1 ~N s2, t1 ~N t2 (CO_LEFT, CO_RIGHT)++In the first of these, why do we need Nominal equality in (t1 ~N t2)?+See {2} below.++For sound and complete solving, we need both directions to decompose. So:+* At nominal role, all is well: we have both directions.+* At representational role, decomposition of Givens is unsound (see {1} below),+ and decomposition of Wanteds is incomplete.++Here is an example of the incompleteness for Wanteds:++ [G] g1 :: a ~R b+ [W] w1 :: Maybe b ~R alpha a+ [W] w2 :: alpha ~N Maybe++Suppose we see w1 before w2. If we decompose, using AppCo to prove w1, we get++ w1 := AppCo w3 w4+ [W] w3 :: Maybe ~R alpha+ [W] w4 :: b ~N a++Note that w4 is *nominal*. A nominal role here is necessary because AppCo+requires a nominal role on its second argument. (See {2} for an example of+why.) Now we are stuck, because w4 is insoluble. On the other hand, if we+see w2 first, setting alpha := Maybe, all is well, as we can decompose+Maybe b ~R Maybe a into b ~R a.++Another example:+ newtype Phant x = MkPhant Int+ [W] w1 :: Phant Int ~R alpha Bool+ [W] w2 :: alpha ~ Phant++If we see w1 first, decomposing would be disastrous, as we would then try+to solve Int ~ Bool. Instead, spotting w2 allows us to simplify w1 to become+ [W] w1' :: Phant Int ~R Phant Bool++which can then (assuming MkPhant is in scope) be simplified to Int ~R Int,+and all will be well. See also Note [Unwrap newtypes first].++Bottom line:+* Always decompose AppTy at nominal role: can_eq_app+* Never decompose AppTy at representational role (neither Given nor Wanted):+ the lack of an equation in can_eq_nc'++Extra points+{1} Decomposing a Given AppTy over a representational role is simply+ unsound. For example, if we have co1 :: Phant Int ~R a Bool (for+ the newtype Phant, above), then we surely don't want any relationship+ between Int and Bool, lest we also have co2 :: Phant ~ a around.++{2} The role on the AppCo coercion is a conservative choice, because we don't+ know the role signature of the function. For example, let's assume we could+ have a representational role on the second argument of AppCo. Then, consider++ data G a where -- G will have a nominal role, as G is a GADT+ MkG :: G Int+ newtype Age = MkAge Int++ co1 :: G ~R a -- by assumption+ co2 :: Age ~R Int -- by newtype axiom+ co3 = AppCo co1 co2 :: G Age ~R a Int -- by our broken AppCo++ and now co3 can be used to cast MkG to have type G Age, in violation of+ the way GADTs are supposed to work (which is to use nominal equality).+-}++canDecomposableTyConAppOK :: CtEvidence -> EqRel+ -> TyCon -> [TcType] -> [TcType]+ -> TcS (StopOrContinue Ct)+-- Precondition: tys1 and tys2 are the same finite length, hence "OK"+canDecomposableTyConAppOK ev eq_rel tc tys1 tys2+ = assert (tys1 `equalLength` tys2) $+ do { traceTcS "canDecomposableTyConAppOK"+ (ppr ev $$ ppr eq_rel $$ ppr tc $$ ppr tys1 $$ ppr tys2)+ ; case ev of+ CtWanted { ctev_dest = dest, ctev_rewriters = rewriters }+ -- new_locs and tc_roles are both infinite, so+ -- we are guaranteed that cos has the same lengthm+ -- as tys1 and tys2+ -- See Note [Fast path when decomposing TyConApps]+ -- Caution: unifyWanteds is order sensitive+ -- See Note [Decomposing Dependent TyCons and Processing Wanted Equalities]+ -> do { cos <- unifyWanteds rewriters new_locs tc_roles tys1 tys2+ ; setWantedEq dest (mkTyConAppCo role tc cos) }++ CtGiven { ctev_evar = evar }+ -> do { let ev_co = mkCoVarCo evar+ ; given_evs <- newGivenEvVars loc $+ [ ( mkPrimEqPredRole r ty1 ty2+ , evCoercion $ mkSelCo (SelTyCon i r) ev_co )+ | (r, ty1, ty2, i) <- zip4 tc_roles tys1 tys2 [0..]+ , r /= Phantom+ , not (isCoercionTy ty1) && not (isCoercionTy ty2) ]+ ; emitWorkNC given_evs }++ ; stopWith ev "Decomposed TyConApp" }++ where+ loc = ctEvLoc ev+ role = eqRelRole eq_rel++ -- Infinite, to allow for over-saturated TyConApps+ tc_roles = tyConRoleListX role tc++ -- Add nuances to the location during decomposition:+ -- * if the argument is a kind argument, remember this, so that error+ -- messages say "kind", not "type". This is determined based on whether+ -- the corresponding tyConBinder is named (that is, dependent)+ -- * if the argument is invisible, note this as well, again by+ -- looking at the corresponding binder+ -- For oversaturated tycons, we need the (repeat loc) tail, which doesn't+ -- do either of these changes. (Forgetting to do so led to #16188)+ --+ -- NB: infinite in length+ new_locs = [ new_loc+ | bndr <- tyConBinders tc+ , let new_loc0 | isNamedTyConBinder bndr = toKindLoc loc+ | otherwise = loc+ new_loc | isInvisibleTyConBinder bndr+ = updateCtLocOrigin new_loc0 toInvisibleOrigin+ | otherwise+ = new_loc0 ]+ ++ repeat loc++canDecomposableFunTy :: CtEvidence -> EqRel -> FunTyFlag+ -> (Type,Type,Type) -- (multiplicity,arg,res)+ -> (Type,Type,Type) -- (multiplicity,arg,res)+ -> TcS (StopOrContinue Ct)+canDecomposableFunTy ev eq_rel af f1@(m1,a1,r1) f2@(m2,a2,r2)+ = do { traceTcS "canDecomposableFunTy"+ (ppr ev $$ ppr eq_rel $$ ppr f1 $$ ppr f2)+ ; case ev of+ CtWanted { ctev_dest = dest, ctev_rewriters = rewriters }+ -> do { mult <- unifyWanted rewriters mult_loc (funRole role SelMult) m1 m2+ ; arg <- unifyWanted rewriters loc (funRole role SelArg) a1 a2+ ; res <- unifyWanted rewriters loc (funRole role SelRes) r1 r2+ ; setWantedEq dest (mkNakedFunCo1 role af mult arg res) }++ CtGiven { ctev_evar = evar }+ -> do { let ev_co = mkCoVarCo evar+ ; given_evs <- newGivenEvVars loc $+ [ ( mkPrimEqPredRole role' ty1 ty2+ , evCoercion $ mkSelCo (SelFun fs) ev_co )+ | (fs, ty1, ty2) <- [(SelMult, m1, m2)+ ,(SelArg, a1, a2)+ ,(SelRes, r1, r2)]+ , let role' = funRole role fs ]+ ; emitWorkNC given_evs }++ ; stopWith ev "Decomposed TyConApp" }++ where+ loc = ctEvLoc ev+ role = eqRelRole eq_rel+ mult_loc = updateCtLocOrigin loc toInvisibleOrigin++-- | Call when canonicalizing an equality fails, but if the equality is+-- representational, there is some hope for the future.+-- Examples in Note [Use canEqFailure in canDecomposableTyConApp]+canEqFailure :: CtEvidence -> EqRel+ -> TcType -> TcType -> TcS (StopOrContinue Ct)+canEqFailure ev NomEq ty1 ty2+ = canEqHardFailure ev ty1 ty2+canEqFailure ev ReprEq ty1 ty2+ = do { (redn1, rewriters1) <- rewrite ev ty1+ ; (redn2, rewriters2) <- rewrite ev ty2+ -- We must rewrite the types before putting them in the+ -- inert set, so that we are sure to kick them out when+ -- new equalities become available+ ; traceTcS "canEqFailure with ReprEq" $+ vcat [ ppr ev, ppr redn1, ppr redn2 ]+ ; new_ev <- rewriteEqEvidence (rewriters1 S.<> rewriters2) ev NotSwapped redn1 redn2+ ; continueWith (mkIrredCt ReprEqReason new_ev) }++-- | Call when canonicalizing an equality fails with utterly no hope.+canEqHardFailure :: CtEvidence+ -> TcType -> TcType -> TcS (StopOrContinue Ct)+-- See Note [Make sure that insolubles are fully rewritten]+canEqHardFailure ev ty1 ty2+ = do { traceTcS "canEqHardFailure" (ppr ty1 $$ ppr ty2)+ ; (redn1, rewriters1) <- rewriteForErrors ev ty1+ ; (redn2, rewriters2) <- rewriteForErrors ev ty2+ ; new_ev <- rewriteEqEvidence (rewriters1 S.<> rewriters2) ev NotSwapped redn1 redn2+ ; continueWith (mkIrredCt ShapeMismatchReason new_ev) }++{-+Note [Canonicalising type applications]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Given (s1 t1) ~ ty2, how should we proceed?+The simple thing is to see if ty2 is of form (s2 t2), and+decompose.++However, over-eager decomposition gives bad error messages+for things like+ a b ~ Maybe c+ e f ~ p -> q+Suppose (in the first example) we already know a~Array. Then if we+decompose the application eagerly, yielding+ a ~ Maybe+ b ~ c+we get an error "Can't match Array ~ Maybe",+but we'd prefer to get "Can't match Array b ~ Maybe c".++So instead can_eq_wanted_app rewrites the LHS and RHS, in the hope of+replacing (a b) by (Array b), before using try_decompose_app to+decompose it.++Note [Make sure that insolubles are fully rewritten]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+When an equality fails, we still want to rewrite the equality+all the way down, so that it accurately reflects+ (a) the mutable reference substitution in force at start of solving+ (b) any ty-binds in force at this point in solving+See Note [Rewrite insolubles] in GHC.Tc.Solver.InertSet.+And if we don't do this there is a bad danger that+GHC.Tc.Solver.applyTyVarDefaulting will find a variable+that has in fact been substituted.++Note [Do not decompose Given polytype equalities]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider [G] (forall a. t1 ~ forall a. t2). Can we decompose this?+No -- what would the evidence look like? So instead we simply discard+this given evidence.++Note [No top-level newtypes on RHS of representational equalities]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Suppose we're in this situation:++ work item: [W] c1 : a ~R b+ inert: [G] c2 : b ~R Id a++where+ newtype Id a = Id a++We want to make sure canEqCanLHS sees [W] a ~R a, after b is rewritten+and the Id newtype is unwrapped. This is assured by requiring only rewritten+types in canEqCanLHS *and* having the newtype-unwrapping check above+the tyvar check in can_eq_nc.++Note that this only applies to saturated applications of newtype TyCons, as+we can't rewrite an unsaturated application. See for example T22310, where+we ended up with:++ newtype Compose f g a = ...++ [W] t[tau] ~# Compose Foo Bar++Note [Put touchable variables on the left]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Ticket #10009, a very nasty example:++ f :: (UnF (F b) ~ b) => F b -> ()++ g :: forall a. (UnF (F a) ~ a) => a -> ()+ g _ = f (undefined :: F a)++For g we get [G] g1 : UnF (F a) ~ a+ [W] w1 : UnF (F beta) ~ beta+ [W] w2 : F a ~ F beta++g1 is canonical (CEqCan). It is oriented as above because a is not touchable.+See canEqTyVarFunEq.++w1 is similarly canonical, though the occurs-check in canEqTyVarFunEq is key+here.++w2 is canonical. But which way should it be oriented? As written, we'll be+stuck. When w2 is added to the inert set, nothing gets kicked out: g1 is+a Given (and Wanteds don't rewrite Givens), and w2 doesn't mention the LHS+of w2. We'll thus lose.++But if w2 is swapped around, to++ [W] w3 : F beta ~ F a++then we'll kick w1 out of the inert+set (it mentions the LHS of w3). We then rewrite w1 to++ [W] w4 : UnF (F a) ~ beta++and then, using g1, to++ [W] w5 : a ~ beta++at which point we can unify and go on to glory. (This rewriting actually+happens all at once, in the call to rewrite during canonicalisation.)++But what about the new LHS makes it better? It mentions a variable (beta)+that can appear in a Wanted -- a touchable metavariable never appears+in a Given. On the other hand, the original LHS mentioned only variables+that appear in Givens. We thus choose to put variables that can appear+in Wanteds on the left.++Ticket #12526 is another good example of this in action.++-}++---------------------+canEqCanLHS :: CtEvidence -- ev :: lhs ~ rhs+ -> EqRel -> SwapFlag+ -> CanEqLHS -- lhs (or, if swapped, rhs)+ -> TcType -- lhs: pretty lhs, already rewritten+ -> TcType -> TcType -- rhs: already rewritten+ -> TcS (StopOrContinue Ct)+canEqCanLHS ev eq_rel swapped lhs1 ps_xi1 xi2 ps_xi2+ | k1 `tcEqType` k2+ = canEqCanLHSHomo ev eq_rel swapped lhs1 ps_xi1 xi2 ps_xi2++ | otherwise+ = canEqCanLHSHetero ev eq_rel swapped lhs1 k1 xi2 k2++ where+ k1 = canEqLHSKind lhs1+ k2 = typeKind xi2+++{-+Note [Kind Equality Orientation]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+While in theory [W] x ~ y and [W] y ~ x ought to give us the same behaviour, in practice it does not.+See Note [Fundeps with instances, and equality orientation] where this is discussed at length.+As a rule of thumb: we keep the newest unification variables on the left of the equality.+See also Note [Improvement orientation] in GHC.Tc.Solver.Interact.++In particular, `canEqCanLHSHetero` produces the following constraint equalities++[X] (xi1 :: ki1) ~ (xi2 :: ki2)+ --> [X] kco :: ki1 ~ ki2+ [X] co : xi1 :: ki1 ~ (xi2 |> sym kco) :: ki1++Note that the types in the LHS of the new constraints are the ones that were on the LHS of+the original constraint.++--- Historical note ---+We prevously used to flip the kco to avoid using a sym in the cast++[X] (xi1 :: ki1) ~ (xi2 :: ki2)+ --> [X] kco :: ki2 ~ ki1+ [X] co : xi1 :: ki1 ~ (xi2 |> kco) :: ki1++But this sent solver in an infinite loop (see #19415).+-- End of historical note --+-}++canEqCanLHSHetero :: CtEvidence -- :: (xi1 :: ki1) ~ (xi2 :: ki2)+ -> EqRel -> SwapFlag+ -> CanEqLHS -- xi1+ -> TcKind -- ki1+ -> TcType -- xi2+ -> TcKind -- ki2+ -> TcS (StopOrContinue Ct)+canEqCanLHSHetero ev eq_rel swapped lhs1 ki1 xi2 ki2+ -- See Note [Equalities with incompatible kinds]+ -- See Note [Kind Equality Orientation]+ -- NB: preserve left-to-right orientation!!+ -- See Note [Fundeps with instances, and equality orientation]+ -- wrinkle (W2) in GHC.Tc.Solver.Interact+ = do { (kind_ev, kind_co) <- mk_kind_eq -- :: ki1 ~N ki2++ ; let -- kind_co :: (ki1 :: *) ~N (ki2 :: *) (whether swapped or not)+ lhs_redn = mkReflRedn role xi1+ rhs_redn = mkGReflRightRedn role xi2 (mkSymCo kind_co)++ -- See Note [Equalities with incompatible kinds], Wrinkle (1)+ -- This will be ignored in rewriteEqEvidence if the work item is a Given+ rewriters = rewriterSetFromCo kind_co++ ; traceTcS "Hetero equality gives rise to kind equality"+ (ppr kind_co <+> dcolon <+> sep [ ppr ki1, text "~#", ppr ki2 ])+ ; type_ev <- rewriteEqEvidence rewriters ev swapped lhs_redn rhs_redn++ ; emitWorkNC [type_ev] -- delay the type equality until after we've finished+ -- the kind equality, which may unlock things+ -- See Note [Equalities with incompatible kinds]++ ; canEqNC kind_ev NomEq ki1 ki2 }+ where+ mk_kind_eq :: TcS (CtEvidence, CoercionN)+ mk_kind_eq = case ev of+ CtGiven { ctev_evar = evar }+ -> do { let kind_co = maybe_sym $ mkKindCo (mkCoVarCo evar) -- :: k1 ~ k2+ ; kind_ev <- newGivenEvVar kind_loc (kind_pty, evCoercion kind_co)+ ; return (kind_ev, ctEvCoercion kind_ev) }++ CtWanted { ctev_rewriters = rewriters }+ -> newWantedEq kind_loc rewriters Nominal ki1 ki2++ xi1 = canEqLHSType lhs1+ loc = ctev_loc ev+ role = eqRelRole eq_rel+ kind_loc = mkKindLoc xi1 xi2 loc+ kind_pty = mkHeteroPrimEqPred liftedTypeKind liftedTypeKind ki1 ki2++ maybe_sym = case swapped of+ IsSwapped -> mkSymCo -- if the input is swapped, then we+ -- will have k2 ~ k1, so flip it to k1 ~ k2+ NotSwapped -> id++-- guaranteed that typeKind lhs == typeKind rhs+canEqCanLHSHomo :: CtEvidence+ -> EqRel -> SwapFlag+ -> CanEqLHS -- lhs (or, if swapped, rhs)+ -> TcType -- pretty lhs+ -> TcType -> TcType -- rhs, pretty rhs+ -> TcS (StopOrContinue Ct)+canEqCanLHSHomo ev eq_rel swapped lhs1 ps_xi1 xi2 ps_xi2+ | (xi2', mco) <- split_cast_ty xi2+ , Just lhs2 <- canEqLHS_maybe xi2'+ = canEqCanLHS2 ev eq_rel swapped lhs1 ps_xi1 lhs2 (ps_xi2 `mkCastTyMCo` mkSymMCo mco) mco++ | otherwise+ = canEqCanLHSFinish ev eq_rel swapped lhs1 ps_xi2++ where+ split_cast_ty (CastTy ty co) = (ty, MCo co)+ split_cast_ty other = (other, MRefl)++-- This function deals with the case that both LHS and RHS are potential+-- CanEqLHSs.+canEqCanLHS2 :: CtEvidence -- lhs ~ (rhs |> mco)+ -- or, if swapped: (rhs |> mco) ~ lhs+ -> EqRel -> SwapFlag+ -> CanEqLHS -- lhs (or, if swapped, rhs)+ -> TcType -- pretty lhs+ -> CanEqLHS -- rhs+ -> TcType -- pretty rhs+ -> MCoercion -- :: kind(rhs) ~N kind(lhs)+ -> TcS (StopOrContinue Ct)+canEqCanLHS2 ev eq_rel swapped lhs1 ps_xi1 lhs2 ps_xi2 mco+ | lhs1 `eqCanEqLHS` lhs2+ -- It must be the case that mco is reflexive+ = canEqReflexive ev eq_rel (canEqLHSType lhs1)++ | TyVarLHS tv1 <- lhs1+ , TyVarLHS tv2 <- lhs2+ , swapOverTyVars (isGiven ev) tv1 tv2+ = do { traceTcS "canEqLHS2 swapOver" (ppr tv1 $$ ppr tv2 $$ ppr swapped)+ ; new_ev <- do_swap+ ; canEqCanLHSFinish new_ev eq_rel IsSwapped (TyVarLHS tv2)+ (ps_xi1 `mkCastTyMCo` sym_mco) }++ | TyVarLHS tv1 <- lhs1+ , TyFamLHS fun_tc2 fun_args2 <- lhs2+ = canEqTyVarFunEq ev eq_rel swapped tv1 ps_xi1 fun_tc2 fun_args2 ps_xi2 mco++ | TyFamLHS fun_tc1 fun_args1 <- lhs1+ , TyVarLHS tv2 <- lhs2+ = do { new_ev <- do_swap+ ; canEqTyVarFunEq new_ev eq_rel IsSwapped tv2 ps_xi2+ fun_tc1 fun_args1 ps_xi1 sym_mco }++ | TyFamLHS fun_tc1 fun_args1 <- lhs1+ , TyFamLHS fun_tc2 fun_args2 <- lhs2+ -- See Note [Decomposing type family applications]+ = do { traceTcS "canEqCanLHS2 two type families" (ppr lhs1 $$ ppr lhs2)++ -- emit wanted equalities for injective type families+ ; let inj_eqns :: [TypeEqn] -- TypeEqn = Pair Type+ inj_eqns+ | ReprEq <- eq_rel = [] -- injectivity applies only for nom. eqs.+ | fun_tc1 /= fun_tc2 = [] -- if the families don't match, stop.++ | Injective inj <- tyConInjectivityInfo fun_tc1+ = [ Pair arg1 arg2+ | (arg1, arg2, True) <- zip3 fun_args1 fun_args2 inj ]++ -- built-in synonym families don't have an entry point+ -- for this use case. So, we just use sfInteractInert+ -- and pass two equal RHSs. We *could* add another entry+ -- point, but then there would be a burden to make+ -- sure the new entry point and existing ones were+ -- internally consistent. This is slightly distasteful,+ -- but it works well in practice and localises the+ -- problem.+ | Just ops <- isBuiltInSynFamTyCon_maybe fun_tc1+ = let ki1 = canEqLHSKind lhs1+ ki2 | MRefl <- mco+ = ki1 -- just a small optimisation+ | otherwise+ = canEqLHSKind lhs2++ fake_rhs1 = anyTypeOfKind ki1+ fake_rhs2 = anyTypeOfKind ki2+ in+ sfInteractInert ops fun_args1 fake_rhs1 fun_args2 fake_rhs2++ | otherwise -- ordinary, non-injective type family+ = []++ ; case ev of+ CtWanted { ctev_rewriters = rewriters } ->+ mapM_ (\ (Pair t1 t2) -> unifyWanted rewriters (ctEvLoc ev) Nominal t1 t2) inj_eqns+ CtGiven {} -> return ()+ -- See Note [No Given/Given fundeps] in GHC.Tc.Solver.Interact++ ; tclvl <- getTcLevel+ ; let tvs1 = tyCoVarsOfTypes fun_args1+ tvs2 = tyCoVarsOfTypes fun_args2++ swap_for_rewriting = anyVarSet (isTouchableMetaTyVar tclvl) tvs2 &&+ -- swap 'em: Note [Put touchable variables on the left]+ not (anyVarSet (isTouchableMetaTyVar tclvl) tvs1)+ -- this check is just to avoid unfruitful swapping++ -- If we have F a ~ F (F a), we want to swap.+ swap_for_occurs+ | cterHasNoProblem $ checkTyFamEq fun_tc2 fun_args2+ (mkTyConApp fun_tc1 fun_args1)+ , cterHasOccursCheck $ checkTyFamEq fun_tc1 fun_args1+ (mkTyConApp fun_tc2 fun_args2)+ = True++ | otherwise+ = False++ ; if swap_for_rewriting || swap_for_occurs+ then do { new_ev <- do_swap+ ; canEqCanLHSFinish new_ev eq_rel IsSwapped lhs2 (ps_xi1 `mkCastTyMCo` sym_mco) }+ else finish_without_swapping }++ -- that's all the special cases. Now we just figure out which non-special case+ -- to continue to.+ | otherwise+ = finish_without_swapping++ where+ sym_mco = mkSymMCo mco++ do_swap = rewriteCastedEquality ev eq_rel swapped (canEqLHSType lhs1) (canEqLHSType lhs2) mco+ finish_without_swapping = canEqCanLHSFinish ev eq_rel swapped lhs1 (ps_xi2 `mkCastTyMCo` mco)+++-- This function handles the case where one side is a tyvar and the other is+-- a type family application. Which to put on the left?+-- If the tyvar is a touchable meta-tyvar, put it on the left, as this may+-- be our only shot to unify.+-- Otherwise, put the function on the left, because it's generally better to+-- rewrite away function calls. This makes types smaller. And it seems necessary:+-- [W] F alpha ~ alpha+-- [W] F alpha ~ beta+-- [W] G alpha beta ~ Int ( where we have type instance G a a = a )+-- If we end up with a stuck alpha ~ F alpha, we won't be able to solve this.+-- Test case: indexed-types/should_compile/CEqCanOccursCheck+canEqTyVarFunEq :: CtEvidence -- :: lhs ~ (rhs |> mco)+ -- or (rhs |> mco) ~ lhs if swapped+ -> EqRel -> SwapFlag+ -> TyVar -> TcType -- lhs (or if swapped rhs), pretty lhs+ -> TyCon -> [Xi] -> TcType -- rhs (or if swapped lhs) fun and args, pretty rhs+ -> MCoercion -- :: kind(rhs) ~N kind(lhs)+ -> TcS (StopOrContinue Ct)+canEqTyVarFunEq ev eq_rel swapped tv1 ps_xi1 fun_tc2 fun_args2 ps_xi2 mco+ = do { is_touchable <- touchabilityTest (ctEvFlavour ev) tv1 rhs+ ; if | case is_touchable of { Untouchable -> False; _ -> True }+ , cterHasNoProblem $+ checkTyVarEq tv1 rhs `cterRemoveProblem` cteTypeFamily+ -> canEqCanLHSFinish ev eq_rel swapped (TyVarLHS tv1) rhs++ | otherwise+ -> do { new_ev <- rewriteCastedEquality ev eq_rel swapped+ (mkTyVarTy tv1) (mkTyConApp fun_tc2 fun_args2)+ mco+ ; canEqCanLHSFinish new_ev eq_rel IsSwapped+ (TyFamLHS fun_tc2 fun_args2)+ (ps_xi1 `mkCastTyMCo` sym_mco) } }+ where+ sym_mco = mkSymMCo mco+ rhs = ps_xi2 `mkCastTyMCo` mco++-- The RHS here is either not CanEqLHS, or it's one that we+-- want to rewrite the LHS to (as per e.g. swapOverTyVars)+canEqCanLHSFinish :: CtEvidence+ -> EqRel -> SwapFlag+ -> CanEqLHS -- lhs (or, if swapped, rhs)+ -> TcType -- rhs (or, if swapped, lhs)+ -> TcS (StopOrContinue Ct)+canEqCanLHSFinish ev eq_rel swapped lhs rhs+-- RHS is fully rewritten, but with type synonyms+-- preserved as much as possible+-- guaranteed that tyVarKind lhs == typeKind rhs, for (TyEq:K)+-- (TyEq:N) is checked in can_eq_nc', and (TyEq:TV) is handled in canEqCanLHS2++ = do {+ -- this performs the swap if necessary+ new_ev <- rewriteEqEvidence emptyRewriterSet ev swapped+ (mkReflRedn role lhs_ty)+ (mkReflRedn role rhs)++ -- by now, (TyEq:K) is already satisfied+ ; massert (canEqLHSKind lhs `eqType` typeKind rhs)++ -- by now, (TyEq:N) is already satisfied (if applicable)+ ; assertPprM ty_eq_N_OK $+ vcat [ text "CanEqCanLHSFinish: (TyEq:N) not satisfied"+ , text "rhs:" <+> ppr rhs+ ]++ -- guarantees (TyEq:OC), (TyEq:F)+ -- Must do the occurs check even on tyvar/tyvar+ -- equalities, in case have x ~ (y :: ..x...); this is #12593.+ ; let result0 = checkTypeEq lhs rhs `cterRemoveProblem` cteTypeFamily+ -- type families are OK here+ -- NB: no occCheckExpand here; see Note [Rewriting synonyms] in GHC.Tc.Solver.Rewrite++ -- a ~R# b a is soluble if b later turns out to be Identity+ result = case eq_rel of+ NomEq -> result0+ ReprEq -> cterSetOccursCheckSoluble result0++ reason = NonCanonicalReason result++ ; if cterHasNoProblem result+ then do { traceTcS "CEqCan" (ppr lhs $$ ppr rhs)+ ; continueWith (CEqCan { cc_ev = new_ev, cc_lhs = lhs+ , cc_rhs = rhs, cc_eq_rel = eq_rel }) }++ else do { m_stuff <- breakTyEqCycle_maybe ev result lhs rhs+ -- See Note [Type equality cycles];+ -- returning Nothing is the vastly common case+ ; case m_stuff of+ { Nothing ->+ do { traceTcS "canEqCanLHSFinish can't make a canonical"+ (ppr lhs $$ ppr rhs)+ ; continueWith (mkIrredCt reason new_ev) }+ ; Just rhs_redn@(Reduction _ new_rhs) ->+ do { traceTcS "canEqCanLHSFinish breaking a cycle" $+ ppr lhs $$ ppr rhs+ ; traceTcS "new RHS:" (ppr new_rhs)++ -- This check is Detail (1) in the Note+ ; if cterHasOccursCheck (checkTypeEq lhs new_rhs)++ then do { traceTcS "Note [Type equality cycles] Detail (1)"+ (ppr new_rhs)+ ; continueWith (mkIrredCt reason new_ev) }++ else do { -- See Detail (6) of Note [Type equality cycles]+ new_new_ev <- rewriteEqEvidence emptyRewriterSet+ new_ev NotSwapped+ (mkReflRedn Nominal lhs_ty)+ rhs_redn++ ; continueWith (CEqCan { cc_ev = new_new_ev+ , cc_lhs = lhs+ , cc_rhs = new_rhs+ , cc_eq_rel = eq_rel }) }}}}}+ where+ role = eqRelRole eq_rel++ lhs_ty = canEqLHSType lhs++ -- This is about (TyEq:N): check that we don't have a saturated application+ -- of a newtype TyCon at the top level of the RHS, if the constructor+ -- of the newtype is in scope.+ ty_eq_N_OK :: TcS Bool+ ty_eq_N_OK+ | ReprEq <- eq_rel+ , Just (tc, tc_args) <- splitTyConApp_maybe rhs+ , Just con <- newTyConDataCon_maybe tc+ -- #22310: only a problem if the newtype TyCon is saturated.+ , tc_args `lengthAtLeast` tyConArity tc+ -- #21010: only a problem if the newtype constructor is in scope.+ = do { rdr_env <- getGlobalRdrEnvTcS+ ; let con_in_scope = isJust $ lookupGRE_Name rdr_env (dataConName con)+ ; return $ not con_in_scope }+ | otherwise+ = return True++-- | Solve a reflexive equality constraint+canEqReflexive :: CtEvidence -- ty ~ ty+ -> EqRel+ -> TcType -- ty+ -> TcS (StopOrContinue Ct) -- always Stop+canEqReflexive ev eq_rel ty+ = do { setEvBindIfWanted ev (evCoercion $+ mkReflCo (eqRelRole eq_rel) ty)+ ; stopWith ev "Solved by reflexivity" }++rewriteCastedEquality :: CtEvidence -- :: lhs ~ (rhs |> mco), or (rhs |> mco) ~ lhs+ -> EqRel -> SwapFlag+ -> TcType -- lhs+ -> TcType -- rhs+ -> MCoercion -- mco+ -> TcS CtEvidence -- :: (lhs |> sym mco) ~ rhs+ -- result is independent of SwapFlag+rewriteCastedEquality ev eq_rel swapped lhs rhs mco+ = rewriteEqEvidence emptyRewriterSet ev swapped lhs_redn rhs_redn+ where+ lhs_redn = mkGReflRightMRedn role lhs sym_mco+ rhs_redn = mkGReflLeftMRedn role rhs mco++ sym_mco = mkSymMCo mco+ role = eqRelRole eq_rel++{- Note [Equalities with incompatible kinds]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+What do we do when we have an equality++ (tv :: k1) ~ (rhs :: k2)++where k1 and k2 differ? Easy: we create a coercion that relates k1 and+k2 and use this to cast. To wit, from++ [X] (tv :: k1) ~ (rhs :: k2)++(where [X] is [G] or [W]), we go to++ [X] co :: k1 ~ k2+ [X] (tv :: k1) ~ ((rhs |> sym co) :: k1)++We carry on with the *kind equality*, not the type equality, because+solving the former may unlock the latter. This choice is made in+canEqCanLHSHetero. It is important: otherwise, T13135 loops.++Wrinkles:++ (1) When X is W, the new type-level wanted is effectively rewritten by the+ kind-level one. We thus include the kind-level wanted in the RewriterSet+ for the type-level one. See Note [Wanteds rewrite Wanteds] in GHC.Tc.Types.Constraint.+ This is done in canEqCanLHSHetero.++ (2) If we have [W] w :: alpha ~ (rhs |> sym co_hole), should we unify alpha? No.+ The problem is that the wanted w is effectively rewritten by another wanted,+ and unifying alpha effectively promotes this wanted to a given. Doing so+ means we lose track of the rewriter set associated with the wanted.++ On the other hand, w is perfectly suitable for rewriting, because of the+ way we carefully track rewriter sets.++ We thus allow w to be a CEqCan, but we prevent unification. See+ Note [Unification preconditions] in GHC.Tc.Utils.Unify.++ The only tricky part is that we must later indeed unify if/when the kind-level+ wanted gets solved. This is done in kickOutAfterFillingCoercionHole,+ which kicks out all equalities whose RHS mentions the filled-in coercion hole.+ Note that it looks for type family equalities, too, because of the use of+ unifyTest in canEqTyVarFunEq.++ (3) Suppose we have [W] (a :: k1) ~ (rhs :: k2). We duly follow the+ algorithm detailed here, producing [W] co :: k1 ~ k2, and adding+ [W] (a :: k1) ~ ((rhs |> sym co) :: k1) to the irreducibles. Some time+ later, we solve co, and fill in co's coercion hole. This kicks out+ the irreducible as described in (2).+ But now, during canonicalization, we see the cast+ and remove it, in canEqCast. By the time we get into canEqCanLHS, the equality+ is heterogeneous again, and the process repeats.++ To avoid this, we don't strip casts off a type if the other type+ in the equality is a CanEqLHS (the scenario above can happen with a+ type family, too. testcase: typecheck/should_compile/T13822).+ And this is an improvement regardless:+ because tyvars can, generally, unify with casted types, there's no+ reason to go through the work of stripping off the cast when the+ cast appears opposite a tyvar. This is implemented in the cast case+ of can_eq_nc'.++Historical note:++We used to do this via emitting a Derived kind equality and then parking+the heterogeneous equality as irreducible. But this new approach is much+more direct. And it doesn't produce duplicate Deriveds (as the old one did).++Note [Type synonyms and canonicalization]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+We treat type synonym applications as xi types, that is, they do not+count as type function applications. However, we do need to be a bit+careful with type synonyms: like type functions they may not be+generative or injective. However, unlike type functions, they are+parametric, so there is no problem in expanding them whenever we see+them, since we do not need to know anything about their arguments in+order to expand them; this is what justifies not having to treat them+as specially as type function applications. The thing that causes+some subtleties is that we prefer to leave type synonym applications+*unexpanded* whenever possible, in order to generate better error+messages.++If we encounter an equality constraint with type synonym applications+on both sides, or a type synonym application on one side and some sort+of type application on the other, we simply must expand out the type+synonyms in order to continue decomposing the equality constraint into+primitive equality constraints. For example, suppose we have++ type F a = [Int]++and we encounter the equality++ F a ~ [b]++In order to continue we must expand F a into [Int], giving us the+equality++ [Int] ~ [b]++which we can then decompose into the more primitive equality+constraint++ Int ~ b.++However, if we encounter an equality constraint with a type synonym+application on one side and a variable on the other side, we should+NOT (necessarily) expand the type synonym, since for the purpose of+good error messages we want to leave type synonyms unexpanded as much+as possible. Hence the ps_xi1, ps_xi2 argument passed to canEqCanLHS.++Note [Type equality cycles]+~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider this situation (from indexed-types/should_compile/GivenLoop):++ instance C (Maybe b)+ *[G] a ~ Maybe (F a)+ [W] C a++or (typecheck/should_compile/T19682b):++ instance C (a -> b)+ *[W] alpha ~ (Arg alpha -> Res alpha)+ [W] C alpha++or (typecheck/should_compile/T21515):++ type family Code a+ *[G] Code a ~ '[ '[ Head (Head (Code a)) ] ]+ [W] Code a ~ '[ '[ alpha ] ]++In order to solve the final Wanted, we must use the starred constraint+for rewriting. But note that all starred constraints have occurs-check failures,+and so we can't straightforwardly add these to the inert set and+use them for rewriting. (NB: A rigid type constructor is at the+top of all RHSs, preventing reorienting in canEqTyVarFunEq in the tyvar+cases.)++The key idea is to replace the outermost type family applications in the RHS of the+starred constraints with a fresh variable, which we'll call a cycle-breaker+variable, or cbv. Then, relate the cbv back with the original type family application+via new equality constraints. Our situations thus become:++ instance C (Maybe b)+ [G] a ~ Maybe cbv+ [G] F a ~ cbv+ [W] C a++or++ instance C (a -> b)+ [W] alpha ~ (cbv1 -> cbv2)+ [W] Arg alpha ~ cbv1+ [W] Res alpha ~ cbv2+ [W] C alpha++or++ [G] Code a ~ '[ '[ cbv ] ]+ [G] Head (Head (Code a)) ~ cbv+ [W] Code a ~ '[ '[ alpha ] ]++This transformation (creating the new types and emitting new equality+constraints) is done in breakTyEqCycle_maybe.++The details depend on whether we're working with a Given or a Wanted.++Given+-----++We emit a new Given, [G] F a ~ cbv, equating the type family application to+our new cbv. Note its orientation: The type family ends up on the left; see+commentary on canEqTyVarFunEq, which decides how to orient such cases. No+special treatment for CycleBreakerTvs is necessary. This scenario is now+easily soluble, by using the first Given to rewrite the Wanted, which can now+be solved.++(The first Given actually also rewrites the second one, giving+[G] F (Maybe cbv) ~ cbv, but this causes no trouble.)++Of course, we don't want our fresh variables leaking into e.g. error messages.+So we fill in the metavariables with their original type family applications+after we're done running the solver (in nestImplicTcS and runTcSWithEvBinds).+This is done by restoreTyVarCycles, which uses the inert_cycle_breakers field in+InertSet, which contains the pairings invented in breakTyEqCycle_maybe.++That is:++We transform+ [G] g : lhs ~ ...(F lhs)...+to+ [G] (Refl lhs) : F lhs ~ cbv -- CEqCan+ [G] g : lhs ~ ...cbv... -- CEqCan++Note that+* `cbv` is a fresh cycle breaker variable.+* `cbv` is a is a meta-tyvar, but it is completely untouchable.+* We track the cycle-breaker variables in inert_cycle_breakers in InertSet+* We eventually fill in the cycle-breakers, with `cbv := F lhs`.+ No one else fills in cycle-breakers!+* The evidence for the new `F lhs ~ cbv` constraint is Refl, because we know+ this fill-in is ultimately going to happen.+* In inert_cycle_breakers, we remember the (cbv, F lhs) pair; that is, we+ remember the /original/ type. The [G] F lhs ~ cbv constraint may be rewritten+ by other givens (eg if we have another [G] lhs ~ (b,c)), but at the end we+ still fill in with cbv := F lhs+* This fill-in is done when solving is complete, by restoreTyVarCycles+ in nestImplicTcS and runTcSWithEvBinds.++Wanted+------+The fresh cycle-breaker variables here must actually be normal, touchable+metavariables. That is, they are TauTvs. Nothing at all unusual. Repeating+the example from above, we have++ *[W] alpha ~ (Arg alpha -> Res alpha)++and we turn this into++ *[W] alpha ~ (cbv1 -> cbv2)+ [W] Arg alpha ~ cbv1+ [W] Res alpha ~ cbv2++where cbv1 and cbv2 are fresh TauTvs. Why TauTvs? See [Why TauTvs] below.++Critically, we emit the two new constraints (the last two above)+directly instead of calling unifyWanted. (Otherwise, we'd end up unifying cbv1+and cbv2 immediately, achieving nothing.)+Next, we unify alpha := cbv1 -> cbv2, having eliminated the occurs check. This+unification -- which must be the next step after breaking the cycles --+happens in the course of normal behavior of top-level+interactions, later in the solver pipeline. We know this unification will+indeed happen because breakTyEqCycle_maybe, which decides whether to apply+this logic, checks to ensure unification will succeed in its final_check.+(In particular, the LHS must be a touchable tyvar, never a type family. We don't+yet have an example of where this logic is needed with a type family, and it's+unclear how to handle this case, so we're skipping for now.) Now, we're+here (including further context from our original example, from the top of the+Note):++ instance C (a -> b)+ [W] Arg (cbv1 -> cbv2) ~ cbv1+ [W] Res (cbv1 -> cbv2) ~ cbv2+ [W] C (cbv1 -> cbv2)++The first two W constraints reduce to reflexivity and are discarded,+and the last is easily soluble.++[Why TauTvs]:+Let's look at another example (typecheck/should_compile/T19682) where we need+to unify the cbvs:++ class (AllEqF xs ys, SameShapeAs xs ys) => AllEq xs ys+ instance (AllEqF xs ys, SameShapeAs xs ys) => AllEq xs ys++ type family SameShapeAs xs ys :: Constraint where+ SameShapeAs '[] ys = (ys ~ '[])+ SameShapeAs (x : xs) ys = (ys ~ (Head ys : Tail ys))++ type family AllEqF xs ys :: Constraint where+ AllEqF '[] '[] = ()+ AllEqF (x : xs) (y : ys) = (x ~ y, AllEq xs ys)++ [W] alpha ~ (Head alpha : Tail alpha)+ [W] AllEqF '[Bool] alpha++Without the logic detailed in this Note, we're stuck here, as AllEqF cannot+reduce and alpha cannot unify. Let's instead apply our cycle-breaker approach,+just as described above. We thus invent cbv1 and cbv2 and unify+alpha := cbv1 -> cbv2, yielding (after zonking)++ [W] Head (cbv1 : cbv2) ~ cbv1+ [W] Tail (cbv1 : cbv2) ~ cbv2+ [W] AllEqF '[Bool] (cbv1 : cbv2)++The first two W constraints simplify to reflexivity and are discarded.+But the last reduces:++ [W] Bool ~ cbv1+ [W] AllEq '[] cbv2++The first of these is solved by unification: cbv1 := Bool. The second+is solved by the instance for AllEq to become++ [W] AllEqF '[] cbv2+ [W] SameShapeAs '[] cbv2++While the first of these is stuck, the second makes progress, to lead to++ [W] AllEqF '[] cbv2+ [W] cbv2 ~ '[]++This second constraint is solved by unification: cbv2 := '[]. We now+have++ [W] AllEqF '[] '[]++which reduces to++ [W] ()++which is trivially satisfiable. Hooray!++Note that we need to unify the cbvs here; if we did not, there would be+no way to solve those constraints. That's why the cycle-breakers are+ordinary TauTvs.++In all cases+------------++We detect this scenario by the following characteristics:+ - a constraint with a soluble occurs-check failure+ (as indicated by the cteSolubleOccurs bit set in a CheckTyEqResult+ from checkTypeEq)+ - and a nominal equality+ - and either+ - a Given flavour (but see also Detail (7) below)+ - a Wanted flavour, with a touchable metavariable on the left++We don't use this trick for representational equalities, as there is no+concrete use case where it is helpful (unlike for nominal equalities).+Furthermore, because function applications can be CanEqLHSs, but newtype+applications cannot, the disparities between the cases are enough that it+would be effortful to expand the idea to representational equalities. A quick+attempt, with++ data family N a b++ f :: (Coercible a (N a b), Coercible (N a b) b) => a -> b+ f = coerce++failed with "Could not match 'b' with 'b'." Further work is held off+until when we have a concrete incentive to explore this dark corner.++Details:++ (1) We don't look under foralls, at all, when substituting away type family+ applications, because doing so can never be fruitful. Recall that we+ are in a case like [G] lhs ~ forall b. ... lhs .... Until we have a type+ family that can pull the body out from a forall (e.g. type instance F (forall b. ty) = ty),+ this will always be+ insoluble. Note also that the forall cannot be in an argument to a+ type family, or that outer type family application would already have+ been substituted away.++ However, we still must check to make sure that breakTyEqCycle_maybe actually+ succeeds in getting rid of all occurrences of the offending lhs. If+ one is hidden under a forall, this won't be true. A similar problem can+ happen if the variable appears only in a kind+ (e.g. k ~ ... (a :: k) ...). So we perform an additional check after+ performing the substitution. It is tiresome to re-run all of checkTypeEq+ here, but reimplementing just the occurs-check is even more tiresome.++ Skipping this check causes typecheck/should_fail/GivenForallLoop and+ polykinds/T18451 to loop.++ (2) Our goal here is to avoid loops in rewriting. We can thus skip looking+ in coercions, as we don't rewrite in coercions in the algorithm in+ GHC.Solver.Rewrite. (This is another reason+ we need to re-check that we've gotten rid of all occurrences of the+ offending variable.)++ (3) As we're substituting as described in this Note, we can build ill-kinded+ types. For example, if we have Proxy (F a) b, where (b :: F a), then+ replacing this with Proxy cbv b is ill-kinded. However, we will later+ set cbv := F a, and so the zonked type will be well-kinded again.+ The temporary ill-kinded type hurts no one, and avoiding this would+ be quite painfully difficult.++ Specifically, this detail does not contravene the Purely Kinded Type Invariant+ (Note [The Purely Kinded Type Invariant (PKTI)] in GHC.Tc.Gen.HsType).+ The PKTI says that we can call typeKind on any type, without failure.+ It would be violated if we, say, replaced a kind (a -> b) with a kind c,+ because an arrow kind might be consulted in piResultTys. Here, we are+ replacing one opaque type like (F a b c) with another, cbv (opaque in+ that we never assume anything about its structure, like that it has a+ result type or a RuntimeRep argument).++ (4) The evidence for the produced Givens is all just reflexive, because+ we will eventually set the cycle-breaker variable to be the type family,+ and then, after the zonk, all will be well. See also the notes at the+ end of the Given section of this Note.++ (5) The approach here is inefficient because it replaces every (outermost)+ type family application with a type variable, regardless of whether that+ particular appplication is implicated in the occurs check. An alternative+ would be to replce only type-family applications that mention the offending LHS.+ For instance, we could choose to+ affect only type family applications that mention the offending LHS:+ e.g. in a ~ (F b, G a), we need to replace only G a, not F b. Furthermore,+ we could try to detect cases like a ~ (F a, F a) and use the same+ tyvar to replace F a. (Cf.+ Note [Flattening type-family applications when matching instances]+ in GHC.Core.Unify, which+ goes to this extra effort.) There may be other opportunities for+ improvement. However, this is really a very small corner case.+ The investment to craft a clever,+ performant solution seems unworthwhile.++ (6) We often get the predicate associated with a constraint from its+ evidence with ctPred. We thus must not only make sure the generated+ CEqCan's fields have the updated RHS type (that is, the one produced+ by replacing type family applications with fresh variables),+ but we must also update the evidence itself. This is done by the call to rewriteEqEvidence+ in canEqCanLHSFinish.++ (7) We don't wish to apply this magic on the equalities created+ by this very same process.+ Consider this, from typecheck/should_compile/ContextStack2:++ type instance TF (a, b) = (TF a, TF b)+ t :: (a ~ TF (a, Int)) => ...++ [G] a ~ TF (a, Int)++ The RHS reduces, so we get++ [G] a ~ (TF a, TF Int)++ We then break cycles, to get++ [G] g1 :: a ~ (cbv1, cbv2)+ [G] g2 :: TF a ~ cbv1+ [G] g3 :: TF Int ~ cbv2++ g1 gets added to the inert set, as written. But then g2 becomes+ the work item. g1 rewrites g2 to become++ [G] TF (cbv1, cbv2) ~ cbv1++ which then uses the type instance to become++ [G] (TF cbv1, TF cbv2) ~ cbv1++ which looks remarkably like the Given we started with. If left+ unchecked, this will end up breaking cycles again, looping ad+ infinitum (and resulting in a context-stack reduction error,+ not an outright loop). The solution is easy: don't break cycles+ on an equality generated by breaking cycles. Instead, we mark this+ final Given as a CIrredCan with a NonCanonicalReason with the soluble+ occurs-check bit set (only).++ We track these equalities by giving them a special CtOrigin,+ CycleBreakerOrigin. This works for both Givens and Wanteds, as+ we need the logic in the W case for e.g. typecheck/should_fail/T17139.+ Because this logic needs to work for Wanteds, too, we cannot+ simply look for a CycleBreakerTv on the left: Wanteds don't use them.++ (8) We really want to do this all only when there is a soluble occurs-check+ failure, not when other problems arise (such as an impredicative+ equality like alpha ~ forall a. a -> a). That is why breakTyEqCycle_maybe+ uses cterHasOnlyProblem when looking at the result of checkTypeEq, which+ checks for many of the invariants on a CEqCan.+-}++{-+************************************************************************+* *+ Evidence transformation+* *+************************************************************************+-}++data StopOrContinue a+ = ContinueWith a -- The constraint was not solved, although it may have+ -- been rewritten++ | Stop CtEvidence -- The (rewritten) constraint was solved+ SDoc -- Tells how it was solved+ -- Any new sub-goals have been put on the work list+ deriving (Functor)++instance Outputable a => Outputable (StopOrContinue a) where+ ppr (Stop ev s) = text "Stop" <> parens s <+> ppr ev+ ppr (ContinueWith w) = text "ContinueWith" <+> ppr w++continueWith :: a -> TcS (StopOrContinue a)+continueWith = return . ContinueWith++stopWith :: CtEvidence -> String -> TcS (StopOrContinue a)+stopWith ev s = return (Stop ev (text s))++andWhenContinue :: TcS (StopOrContinue a)+ -> (a -> TcS (StopOrContinue b))+ -> TcS (StopOrContinue b)+andWhenContinue tcs1 tcs2+ = do { r <- tcs1+ ; case r of+ Stop ev s -> return (Stop ev s)+ ContinueWith ct -> tcs2 ct }+infixr 0 `andWhenContinue` -- allow chaining with ($)++rewriteEvidence :: RewriterSet -- ^ See Note [Wanteds rewrite Wanteds]+ -- in GHC.Tc.Types.Constraint+ -> CtEvidence -- ^ old evidence+ -> Reduction -- ^ new predicate + coercion, of type <type of old evidence> ~ new predicate+ -> TcS (StopOrContinue CtEvidence)+-- Returns Just new_ev iff either (i) 'co' is reflexivity+-- or (ii) 'co' is not reflexivity, and 'new_pred' not cached+-- In either case, there is nothing new to do with new_ev+{-+ rewriteEvidence old_ev new_pred co+Main purpose: create new evidence for new_pred;+ unless new_pred is cached already+* Returns a new_ev : new_pred, with same wanted/given flag as old_ev+* If old_ev was wanted, create a binding for old_ev, in terms of new_ev+* If old_ev was given, AND not cached, create a binding for new_ev, in terms of old_ev+* Returns Nothing if new_ev is already cached++ Old evidence New predicate is Return new evidence+ flavour of same flavor+ -------------------------------------------------------------------+ Wanted Already solved or in inert Nothing+ Not Just new_evidence++ Given Already in inert Nothing+ Not Just new_evidence++Note [Rewriting with Refl]+~~~~~~~~~~~~~~~~~~~~~~~~~~+If the coercion is just reflexivity then you may re-use the same+variable. But be careful! Although the coercion is Refl, new_pred+may reflect the result of unification alpha := ty, so new_pred might+not _look_ the same as old_pred, and it's vital to proceed from now on+using new_pred.++The rewriter preserves type synonyms, so they should appear in new_pred+as well as in old_pred; that is important for good error messages.++If we are rewriting with Refl, then there are no new rewriters to add to+the rewriter set. We check this with an assertion.+ -}+++rewriteEvidence rewriters old_ev (Reduction co new_pred)+ | isReflCo co -- See Note [Rewriting with Refl]+ = assert (isEmptyRewriterSet rewriters) $+ continueWith (setCtEvPredType old_ev new_pred)++rewriteEvidence rewriters ev@(CtGiven { ctev_evar = old_evar, ctev_loc = loc })+ (Reduction co new_pred)+ = assert (isEmptyRewriterSet rewriters) $ -- this is a Given, not a wanted+ do { new_ev <- newGivenEvVar loc (new_pred, new_tm)+ ; continueWith new_ev }+ where+ -- mkEvCast optimises ReflCo+ new_tm = mkEvCast (evId old_evar)+ (downgradeRole Representational (ctEvRole ev) co)++rewriteEvidence new_rewriters+ ev@(CtWanted { ctev_dest = dest+ , ctev_loc = loc+ , ctev_rewriters = rewriters })+ (Reduction co new_pred)+ = do { mb_new_ev <- newWanted loc rewriters' new_pred+ ; massert (coercionRole co == ctEvRole ev)+ ; setWantedEvTerm dest+ (mkEvCast (getEvExpr mb_new_ev)+ (downgradeRole Representational (ctEvRole ev) (mkSymCo co)))+ ; case mb_new_ev of+ Fresh new_ev -> continueWith new_ev+ Cached _ -> stopWith ev "Cached wanted" }+ where+ rewriters' = rewriters S.<> new_rewriters+++rewriteEqEvidence :: RewriterSet -- New rewriters+ -- See GHC.Tc.Types.Constraint+ -- Note [Wanteds rewrite Wanteds]+ -> CtEvidence -- Old evidence :: olhs ~ orhs (not swapped)+ -- or orhs ~ olhs (swapped)+ -> SwapFlag+ -> Reduction -- lhs_co :: olhs ~ nlhs+ -> Reduction -- rhs_co :: orhs ~ nrhs+ -> TcS CtEvidence -- Of type nlhs ~ nrhs+-- With reductions (Reduction lhs_co nlhs) (Reduction rhs_co nrhs),+-- rewriteEqEvidence yields, for a given equality (Given g olhs orhs):+-- If not swapped+-- g1 : nlhs ~ nrhs = sym lhs_co ; g ; rhs_co+-- If swapped+-- g1 : nlhs ~ nrhs = sym lhs_co ; Sym g ; rhs_co+--+-- For a wanted equality (Wanted w), we do the dual thing:+-- New w1 : nlhs ~ nrhs+-- If not swapped+-- w : olhs ~ orhs = lhs_co ; w1 ; sym rhs_co+-- If swapped+-- w : orhs ~ olhs = rhs_co ; sym w1 ; sym lhs_co+--+-- It's all a form of rewriteEvidence, specialised for equalities+rewriteEqEvidence new_rewriters old_ev swapped (Reduction lhs_co nlhs) (Reduction rhs_co nrhs)+ | NotSwapped <- swapped+ , isReflCo lhs_co -- See Note [Rewriting with Refl]+ , isReflCo rhs_co+ = return (setCtEvPredType old_ev new_pred)++ | CtGiven { ctev_evar = old_evar } <- old_ev+ = do { let new_tm = evCoercion ( mkSymCo lhs_co+ `mkTransCo` maybeSymCo swapped (mkCoVarCo old_evar)+ `mkTransCo` rhs_co)+ ; newGivenEvVar loc (new_pred, new_tm) }++ | CtWanted { ctev_dest = dest+ , ctev_rewriters = rewriters } <- old_ev+ , let rewriters' = rewriters S.<> new_rewriters+ = do { (new_ev, hole_co) <- newWantedEq loc rewriters'+ (ctEvRole old_ev) nlhs nrhs+ ; let co = maybeSymCo swapped $+ lhs_co+ `mkTransCo` hole_co+ `mkTransCo` mkSymCo rhs_co+ ; setWantedEq dest co+ ; traceTcS "rewriteEqEvidence" (vcat [ ppr old_ev+ , ppr nlhs+ , ppr nrhs+ , ppr co+ , ppr new_rewriters ])+ ; return new_ev }++#if __GLASGOW_HASKELL__ <= 810+ | otherwise+ = panic "rewriteEvidence"+#endif+ where+ new_pred = mkTcEqPredLikeEv old_ev nlhs nrhs+ loc = ctEvLoc old_ev++{-+************************************************************************+* *+ Unification+* *+************************************************************************++Note [unifyWanted]+~~~~~~~~~~~~~~~~~~+When decomposing equalities we often create new wanted constraints for+(s ~ t). But what if s=t? Then it'd be faster to return Refl right away.++Rather than making an equality test (which traverses the structure of the+type, perhaps fruitlessly), unifyWanted traverses the common structure, and+bales out when it finds a difference by creating a new Wanted constraint.+But where it succeeds in finding common structure, it just builds a coercion+to reflect it.+-}++unifyWanted :: RewriterSet -> CtLoc+ -> Role -> TcType -> TcType -> TcS Coercion+-- Return coercion witnessing the equality of the two types,+-- emitting new work equalities where necessary to achieve that+-- Very good short-cut when the two types are equal, or nearly so+-- See Note [unifyWanted]+-- The returned coercion's role matches the input parameter+unifyWanted rewriters loc Phantom ty1 ty2+ = do { kind_co <- unifyWanted rewriters loc Nominal (typeKind ty1) (typeKind ty2)+ ; return (mkPhantomCo kind_co ty1 ty2) }++unifyWanted rewriters loc role orig_ty1 orig_ty2+ = go orig_ty1 orig_ty2+ where+ go ty1 ty2 | Just ty1' <- coreView ty1 = go ty1' ty2+ go ty1 ty2 | Just ty2' <- coreView ty2 = go ty1 ty2'++ go (FunTy af1 w1 s1 t1) (FunTy af2 w2 s2 t2)+ | af1 == af2 -- Important! See #21530+ = do { co_s <- unifyWanted rewriters loc role s1 s2+ ; co_t <- unifyWanted rewriters loc role t1 t2+ ; co_w <- unifyWanted rewriters loc Nominal w1 w2+ ; return (mkNakedFunCo1 role af1 co_w co_s co_t) }++ go (TyConApp tc1 tys1) (TyConApp tc2 tys2)+ | tc1 == tc2, tys1 `equalLength` tys2+ , isInjectiveTyCon tc1 role -- don't look under newtypes at Rep equality+ = do { cos <- zipWith3M (unifyWanted rewriters loc)+ (tyConRoleListX role tc1) tys1 tys2+ ; return (mkTyConAppCo role tc1 cos) }++ go ty1@(TyVarTy tv) ty2+ = do { mb_ty <- isFilledMetaTyVar_maybe tv+ ; case mb_ty of+ Just ty1' -> go ty1' ty2+ Nothing -> bale_out ty1 ty2}+ go ty1 ty2@(TyVarTy tv)+ = do { mb_ty <- isFilledMetaTyVar_maybe tv+ ; case mb_ty of+ Just ty2' -> go ty1 ty2'+ Nothing -> bale_out ty1 ty2 }++ go ty1@(CoercionTy {}) (CoercionTy {})+ = return (mkReflCo role ty1) -- we just don't care about coercions!++ go ty1 ty2 = bale_out ty1 ty2++ bale_out ty1 ty2+ | ty1 `tcEqType` ty2 = return (mkReflCo role ty1)+ -- Check for equality; e.g. a ~ a, or (m a) ~ (m a)+ | otherwise = emitNewWantedEq loc rewriters role orig_ty1 orig_ty2+++{-+Note [Decomposing Dependent TyCons and Processing Wanted Equalities]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+When we decompose a dependent tycon we obtain a list of+mixed wanted type and kind equalities. Ideally we want+all the kind equalities to get solved first so that we avoid+generating duplicate kind equalities++For example, consider decomposing a TyCon equality++ (0) [W] T k_fresh (t1::k_fresh) ~ T k1 (t2::k_fresh)++This gives rise to 2 equalities in the solver worklist++ (1) [W] k_fresh ~ k1+ (2) [W] t1::k_fresh ~ t2::k1++The solver worklist is processed in LIFO order:+see GHC.Tc.Solver.InertSet.selectWorkItem.+i.e. (2) is processed _before_ (1). Now, while solving (2)+we would call `canEqCanLHSHetero` and that would emit a+wanted kind equality++ (3) [W] k_fresh ~ k1++But (3) is exactly the same as (1)!++To avoid such duplicate wanted constraints from being added to the worklist,+we ensure that (2) is processed before (1). Since we are processing+the worklist in a LIFO ordering, we do it by emitting (1) before (2).+This is exactly what we do in `unifyWanteds`.++NB: This ordering is not needed when we decompose FunTyCons as they are not dependently typed+-}++-- NB: Length of [CtLoc] and [Roles] may be infinite+-- but list of RHS [TcType] and LHS [TcType] is finite and both are of equal length+unifyWanteds :: RewriterSet -> [CtLoc] -> [Role]+ -> [TcType] -- List of RHS types+ -> [TcType] -- List of LHS types+ -> TcS [Coercion]+unifyWanteds rewriters ctlocs roles rhss lhss = unify_wanteds rewriters $ zip4 ctlocs roles rhss lhss+ where+ -- Order is important here+ -- See Note [Decomposing Dependent TyCons and Processing Wanted Equalities]+ unify_wanteds _ [] = return []+ unify_wanteds rewriters ((new_loc, tc_role, ty1, ty2) : rest)+ = do { cos <- unify_wanteds rewriters rest+ ; co <- unifyWanted rewriters new_loc tc_role ty1 ty2+ ; return (co:cos) }
compiler/GHC/Tc/Solver/Interact.hs view
@@ -1,5 +1,3 @@--{-# OPTIONS_GHC -Wno-incomplete-record-updates #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} module GHC.Tc.Solver.Interact (@@ -8,26 +6,17 @@ ) where import GHC.Prelude-import GHC.Types.Basic ( SwapFlag(..),- infinity, IntWithInf, intGtLimit )+import GHC.Types.Basic ( SwapFlag(..), IntWithInf, intGtLimit ) import GHC.Tc.Solver.Canonical import GHC.Types.Var.Set-import GHC.Core.Type as Type-import GHC.Core.InstEnv ( DFunInstType ) import GHC.Types.Var import GHC.Tc.Errors.Types import GHC.Tc.Utils.TcType import GHC.Builtin.Names ( coercibleTyConKey, heqTyConKey, eqTyConKey, ipClassKey )-import GHC.Core.Coercion.Axiom ( CoAxBranch (..), CoAxiom (..), TypeEqn, fromBranches, sfInteractInert, sfInteractTop )-import GHC.Core.Class-import GHC.Core.TyCon-import GHC.Core.Reduction import GHC.Tc.Instance.FunDeps import GHC.Tc.Instance.Family import GHC.Tc.Instance.Class ( InstanceWhat(..), safeOverlap )-import GHC.Core.FamInstEnv-import GHC.Core.Unify ( tcUnifyTyWithTFs, ruleMatchTyKiX ) import GHC.Tc.Types.Evidence import GHC.Utils.Outputable@@ -35,31 +24,46 @@ import GHC.Tc.Types import GHC.Tc.Types.Constraint-import GHC.Core.Predicate import GHC.Tc.Types.Origin import GHC.Tc.Utils.TcMType( promoteMetaTyVarTo ) import GHC.Tc.Solver.Types import GHC.Tc.Solver.InertSet import GHC.Tc.Solver.Monad-import GHC.Data.Bag-import GHC.Utils.Monad ( concatMapM, foldlM ) import GHC.Core-import Data.List( deleteFirstsBy )-import Data.Function ( on )+import GHC.Core.Type as Type+import GHC.Core.InstEnv ( DFunInstType )+import GHC.Core.Class+import GHC.Core.TyCon+import GHC.Core.Predicate+import GHC.Core.Coercion+import GHC.Core.FamInstEnv+import GHC.Core.Unify ( tcUnifyTyWithTFs, ruleMatchTyKiX )+import GHC.Core.Coercion.Axiom ( CoAxBranch (..), CoAxiom (..), TypeEqn, fromBranches+ , sfInteractInert, sfInteractTop )+ import GHC.Types.SrcLoc import GHC.Types.Var.Env+import GHC.Types.Unique( hasKey ) -import qualified Data.Semigroup as S-import Control.Monad+import GHC.Data.Bag import GHC.Data.Pair (Pair(..))-import GHC.Types.Unique( hasKey )-import GHC.Driver.Session++import GHC.Utils.Monad ( concatMapM, foldlM ) import GHC.Utils.Misc++import GHC.Driver.Session+ import qualified GHC.LanguageExtensions as LangExt +import Data.List( deleteFirstsBy )+import Data.Maybe ( listToMaybe, mapMaybe )+import Data.Function ( on )+import qualified Data.Semigroup as S+ import Control.Monad.Trans.Class import Control.Monad.Trans.Maybe+import Control.Monad {- **********************************************************************@@ -78,7 +82,7 @@ - inert reactions - spontaneous reactions - top-level interactions- Each stage returns a StopOrContinue and may have sideffected+ Each stage returns a StopOrContinue and may have sideeffected the inerts or worklist. The threading of the stages is as follows:@@ -445,7 +449,7 @@ solveOneFromTheOther :: Ct -- Inert (Dict or Irred) -> Ct -- WorkItem (same predicate as inert)- -> TcS InteractResult+ -> InteractResult -- Precondition: -- * inert and work item represent evidence for the /same/ predicate -- * Both are CDictCan or CIrredCan@@ -457,28 +461,28 @@ solveOneFromTheOther ct_i ct_w | CtWanted { ctev_loc = loc_w } <- ev_w , prohibitedSuperClassSolve loc_i loc_w+ -- See Note [Solving superclass constraints] in GHC.Tc.TyCl.Instance = -- Inert must be Given- do { traceTcS "prohibitedClassSolve1" (ppr ev_i $$ ppr ev_w)- ; return KeepWork }+ KeepWork | CtWanted {} <- ev_w = -- Inert is Given or Wanted case ev_i of- CtGiven {} -> return KeepInert+ CtGiven {} -> KeepInert -- work is Wanted; inert is Given: easy choice. CtWanted {} -- Both are Wanted -- If only one has no pending superclasses, use it -- Otherwise we can get infinite superclass expansion (#22516) -- in silly cases like class C T b => C a b where ...- | not is_psc_i, is_psc_w -> return KeepInert- | is_psc_i, not is_psc_w -> return KeepWork+ | not is_psc_i, is_psc_w -> KeepInert+ | is_psc_i, not is_psc_w -> KeepWork -- If only one is a WantedSuperclassOrigin (arising from expanding -- a Wanted class constraint), keep the other: wanted superclasses -- may be unexpected by users- | not is_wsc_orig_i, is_wsc_orig_w -> return KeepInert- | is_wsc_orig_i, not is_wsc_orig_w -> return KeepWork+ | not is_wsc_orig_i, is_wsc_orig_w -> KeepInert+ | is_wsc_orig_i, not is_wsc_orig_w -> KeepWork -- otherwise, just choose the lower span -- reason: if we have something like (abs 1) (where the@@ -486,46 +490,46 @@ -- get an error about abs than about 1. -- This test might become more elaborate if we see an -- opportunity to improve the error messages- | ((<) `on` ctLocSpan) loc_i loc_w -> return KeepInert- | otherwise -> return KeepWork+ | ((<) `on` ctLocSpan) loc_i loc_w -> KeepInert+ | otherwise -> KeepWork -- From here on the work-item is Given | CtWanted { ctev_loc = loc_i } <- ev_i , prohibitedSuperClassSolve loc_w loc_i- = do { traceTcS "prohibitedClassSolve2" (ppr ev_i $$ ppr ev_w)- ; return KeepInert } -- Just discard the un-usable Given- -- This never actually happens because- -- Givens get processed first+ = KeepInert -- Just discard the un-usable Given+ -- This never actually happens because+ -- Givens get processed first | CtWanted {} <- ev_i- = return KeepWork+ = KeepWork -- From here on both are Given -- See Note [Replacement vs keeping] | lvl_i == lvl_w- = return same_level_strategy+ = same_level_strategy | otherwise -- Both are Given, levels differ- = return different_level_strategy+ = different_level_strategy where ev_i = ctEvidence ct_i ev_w = ctEvidence ct_w pred = ctEvPred ev_i - loc_i = ctEvLoc ev_i- loc_w = ctEvLoc ev_w- lvl_i = ctLocLevel loc_i- lvl_w = ctLocLevel loc_w+ loc_i = ctEvLoc ev_i+ loc_w = ctEvLoc ev_w+ orig_i = ctLocOrigin loc_i+ orig_w = ctLocOrigin loc_w+ lvl_i = ctLocLevel loc_i+ lvl_w = ctLocLevel loc_w is_psc_w = isPendingScDict ct_w is_psc_i = isPendingScDict ct_i - is_wsc_orig_i = is_wanted_superclass_loc loc_i- is_wsc_orig_w = is_wanted_superclass_loc loc_w- is_wanted_superclass_loc = isWantedSuperclassOrigin . ctLocOrigin+ is_wsc_orig_i = isWantedSuperclassOrigin orig_i+ is_wsc_orig_w = isWantedSuperclassOrigin orig_w different_level_strategy -- Both Given | isIPLikePred pred = if lvl_w > lvl_i then KeepWork else KeepInert@@ -534,28 +538,21 @@ -- For the isIPLikePred case see Note [Shadowing of Implicit Parameters] same_level_strategy -- Both Given- = case (ctLocOrigin loc_i, ctLocOrigin loc_w) of- -- case 2(a) from Note [Replacement vs keeping]- (InstSCOrigin _depth_i size_i, InstSCOrigin _depth_w size_w)- | size_w < size_i -> KeepWork- | otherwise -> KeepInert+ = case (orig_i, orig_w) of - -- case 2(c) from Note [Replacement vs keeping]- (InstSCOrigin depth_i _, OtherSCOrigin depth_w _) -> choose_shallower depth_i depth_w- (OtherSCOrigin depth_i _, InstSCOrigin depth_w _) -> choose_shallower depth_i depth_w- (OtherSCOrigin depth_i _, OtherSCOrigin depth_w _) -> choose_shallower depth_i depth_w+ (GivenSCOrigin _ depth_i blocked_i, GivenSCOrigin _ depth_w blocked_w)+ | blocked_i, not blocked_w -> KeepWork -- Case 2(a) from+ | not blocked_i, blocked_w -> KeepInert -- Note [Replacement vs keeping] - -- case 2(b) from Note [Replacement vs keeping]- (InstSCOrigin {}, _) -> KeepWork- (OtherSCOrigin {}, _) -> KeepWork+ -- Both blocked or both not blocked - -- case 2(d) from Note [Replacement vs keeping]- _ -> KeepInert+ | depth_w < depth_i -> KeepWork -- Case 2(c) from+ | otherwise -> KeepInert -- Note [Replacement vs keeping] - choose_shallower depth_i depth_w | depth_w < depth_i = KeepWork- | otherwise = KeepInert- -- favor KeepInert in the equals case, according to 2(d) from the Note+ (GivenSCOrigin {}, _) -> KeepWork -- Case 2(b) from Note [Replacement vs keeping] + _ -> KeepInert -- Case 2(d) from Note [Replacement vs keeping]+ {- Note [Replacement vs keeping] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -580,7 +577,7 @@ 2) Constraints coming from the same level (i.e. same implication) - (a) If both are InstSCOrigin, choose the one with the smallest TypeSize,+ (a) If both are GivenSCOrigin, choose the one that is unblocked if possible according to Note [Solving superclass constraints] in GHC.Tc.TyCl.Instance. (b) Prefer constraints that are not superclass selections. Example:@@ -596,12 +593,13 @@ Getting this wrong was #20602. See also Note [Tracking redundant constraints] in GHC.Tc.Solver. - (c) If both are superclass selections (but not both InstSCOrigin), choose the one- with the shallower superclass-selection depth, in the hope of identifying- more correct redundant constraints. This is really a generalization of- point (b), because the superclass depth of a non-superclass- constraint is 0.+ (c) If both are GivenSCOrigin, chooose the one with the shallower+ superclass-selection depth, in the hope of identifying more correct+ redundant constraints. This is really a generalization of point (b),+ because the superclass depth of a non-superclass constraint is 0. + (If the levels differ, we definitely won't have both with GivenSCOrigin.)+ (d) Finally, when there is still a choice, use KeepInert rather than KeepWork, for two reasons: - to avoid unnecessary munging of the inert set.@@ -663,11 +661,12 @@ , ((ct_i, swap) : _rest) <- bagToList matching_irreds -- See Note [Multiple matching irreds] , let ev_i = ctEvidence ct_i- = do { what_next <- solveOneFromTheOther ct_i ct_w- ; traceTcS "iteractIrred" (ppr ct_w $$ ppr what_next $$ ppr ct_i)- ; case what_next of+ = do { traceTcS "iteractIrred" $+ vcat [ text "wanted:" <+> (ppr ct_w $$ ppr (ctOrigin ct_w))+ , text "inert: " <+> (ppr ct_i $$ ppr (ctOrigin ct_i)) ]+ ; case solveOneFromTheOther ct_i ct_w of KeepInert -> do { setEvBindIfWanted ev_w (swap_me swap ev_i)- ; return (Stop ev_w (text "Irred equal" <+> parens (ppr what_next))) }+ ; return (Stop ev_w (text "Irred equal:KeepInert" <+> ppr ct_w)) } KeepWork -> do { setEvBindIfWanted ev_i (swap_me swap ev_w) ; updInertIrreds (\_ -> others) ; continueWith ct_w } }@@ -680,7 +679,7 @@ swap_me swap ev = case swap of NotSwapped -> ctEvTerm ev- IsSwapped -> evCoercion (mkTcSymCo (evTermCoercion (ctEvTerm ev)))+ IsSwapped -> evCoercion (mkSymCo (evTermCoercion (ctEvTerm ev))) interactIrred _ wi = pprPanic "interactIrred" (ppr wi) @@ -921,7 +920,7 @@ cannot be solved from instances. * The (EvBindMap, DictMap CtEvidence) is an accumulating purely-functional- state that allows try_solve_from_instance to augmennt the evidence+ state that allows try_solve_from_instance to augment the evidence bindings and inert_solved_dicts as it goes. If it succeeds, we commit all these bindings and solved dicts to the@@ -994,7 +993,7 @@ [W] iss ~ is2 : beta Again we can't prove that equality; and worse we'll rewrite iss to- (is2:beta) in deeply nested contraints inside this implication,+ (is2:beta) in deeply nested constraints inside this implication, where beta is untouchable (under other equality constraints), leading to other insoluble constraints. @@ -1005,7 +1004,9 @@ interactDict :: InertCans -> Ct -> TcS (StopOrContinue Ct) interactDict inerts ct_w@(CDictCan { cc_ev = ev_w, cc_class = cls, cc_tyargs = tys }) | Just ct_i <- lookupInertDict inerts (ctEvLoc ev_w) cls tys- , let ev_i = ctEvidence ct_i+ , let ev_i = ctEvidence ct_i+ loc_i = ctEvLoc ev_i+ loc_w = ctEvLoc ev_w = -- There is a matching dictionary in the inert set do { -- First to try to solve it /completely/ from top level instances -- See Note [Shortcut solving]@@ -1013,16 +1014,24 @@ ; short_cut_worked <- shortCutSolver dflags ev_w ev_i ; if short_cut_worked then stopWith ev_w "interactDict/solved from instance"- else - do { -- Ths short-cut solver didn't fire, so we- -- solve ev_w from the matching inert ev_i we found- what_next <- solveOneFromTheOther ct_i ct_w- ; traceTcS "lookupInertDict" (ppr what_next)- ; case what_next of- KeepInert -> do { setEvBindIfWanted ev_w (ctEvTerm ev_i)- ; return $ Stop ev_w (text "Dict equal" <+> parens (ppr what_next)) }- KeepWork -> do { setEvBindIfWanted ev_i (ctEvTerm ev_w)+ -- Next see if we are in "loopy-superclass" land. If so,+ -- we don't want to replace the (Given) inert with the+ -- (Wanted) work-item, or vice versa; we want to hang on+ -- to both, and try to solve the work-item via an instance.+ -- See Note [Solving superclass constraints] in GHC.Tc.TyCl.Instance+ else if prohibitedSuperClassSolve loc_i loc_w+ then continueWith ct_w+ else+ do { -- The short-cut solver didn't fire, and loopy superclasses+ -- are dealt with, so we can either solve+ -- the inert from the work-item or vice-versa.+ ; case solveOneFromTheOther ct_i ct_w of+ KeepInert -> do { traceTcS "lookupInertDict:KeepInert" (ppr ct_w)+ ; setEvBindIfWanted ev_w (ctEvTerm ev_i)+ ; return $ Stop ev_w (text "Dict equal" <+> ppr ct_w) }+ KeepWork -> do { traceTcS "lookupInertDict:KeepWork" (ppr ct_w)+ ; setEvBindIfWanted ev_i (ctEvTerm ev_w) ; updInertDicts $ \ ds -> delDict ds cls tys ; continueWith ct_w } } } @@ -1393,7 +1402,7 @@ injectivity. 1. When we have a [W] F tys1 ~ F tys2. This is handled in canEqCanLHS2, and-described in Note [Decomposing equality] in GHC.Tc.Solver.Canonical.+described in Note [Decomposing type family applications] in GHC.Tc.Solver.Canonical. 2. When we have [W] F tys1 ~ T and [W] F tys2 ~ T. Note that neither of these constraints rewrites the other, as they have different LHSs. This is done@@ -1469,7 +1478,7 @@ That first argument is invisible in the source program (aside from visible type application), so we'd much prefer to get the error from- the second. We track visiblity in the uo_visible field of a TypeEqOrigin.+ the second. We track visibility in the uo_visible field of a TypeEqOrigin. We use this to prioritise visible errors (see GHC.Tc.Errors.tryReporters, the partition on isVisibleOrigin). @@ -1531,10 +1540,10 @@ , cc_eq_rel = eq_rel }) | Just (ev_i, swapped) <- inertsCanDischarge inerts workItem = do { setEvBindIfWanted ev $- evCoercion (maybeTcSymCo swapped $- tcDowngradeRole (eqRelRole eq_rel)- (ctEvRole ev_i)- (ctEvCoercion ev_i))+ evCoercion (maybeSymCo swapped $+ downgradeRole (eqRelRole eq_rel)+ (ctEvRole ev_i)+ (ctEvCoercion ev_i)) ; stopWith ev "Solved from inert" } @@ -1563,7 +1572,7 @@ -> TcType -- RHS -> TcS (StopOrContinue Ct) tryToSolveByUnification work_item ev tv rhs- = do { (is_touchable, rhs) <- touchabilityTest (ctEvFlavour ev) tv rhs+ = do { is_touchable <- touchabilityTest (ctEvFlavour ev) tv rhs ; traceTcS "tryToSolveByUnification" (vcat [ ppr tv <+> char '~' <+> ppr rhs , ppr is_touchable ]) @@ -1596,10 +1605,10 @@ ; traceTcS "Sneaky unification:" $ vcat [text "Unifies:" <+> ppr tv <+> text ":=" <+> ppr xi, text "Coercion:" <+> pprEq tv_ty xi,- text "Left Kind is:" <+> ppr (tcTypeKind tv_ty),- text "Right Kind is:" <+> ppr (tcTypeKind xi) ]+ text "Left Kind is:" <+> ppr (typeKind tv_ty),+ text "Right Kind is:" <+> ppr (typeKind xi) ] ; unifyTyVar tv xi- ; setEvBindIfWanted wd (evCoercion (mkTcNomReflCo xi))+ ; setEvBindIfWanted wd (evCoercion (mkNomReflCo xi)) ; n_kicked <- kickOutAfterUnification tv ; return (Stop wd (text "Solved by unification" <+> pprKicked n_kicked)) } @@ -1734,8 +1743,12 @@ It's exactly the same with implicit parameters, except that the "aggressive" approach would be much easier to implement. -Note [Fundeps with instances]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Note [Fundeps with instances, and equality orientation]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+This Note describes a delicate interaction that constrains the orientation of+equalities. This one is about fundeps, but the /exact/ same thing arises for+type-family injectivity constraints: see Note [Improvement orientation].+ doTopFundepImprovement compares the constraint with all the instance declarations, to see if we can produce any equalities. E.g class C2 a b | a -> b@@ -1749,53 +1762,123 @@ work_item: dwrk :: C (T @ka (a::ka)) (T @kb0 (b0::kb0)) Char where kb0, b0 are unification vars- ==> {fundeps against instance; k0, y0 fresh unification vars}- [W] T kb0 (b0::kb0) ~ T k0 (y0::k0)- Add dwrk to inert set- ==> {solve that equality kb0 := k0, b0 := y0++ ==> {doTopFundepImprovement: compare work_item with instance,+ generate /fresh/ unification variables kfresh0, yfresh0,+ emit a new Wanted, and add dwrk to inert set}++ Suppose we emit this new Wanted from the fundep:+ [W] T kb0 (b0::kb0) ~ T kfresh0 (yfresh0::kfresh0)++ ==> {solve that equality kb0 := kfresh0, b0 := yfresh0} Now kick out dwrk, since it mentions kb0 But now we are back to the start! Loop! -NB1: this example relies on an instance that does not satisfy-the coverage condition (although it may satisfy the weak coverage-condition), which is known to lead to termination trouble+NB1: This example relies on an instance that does not satisfy the+ coverage condition (although it may satisfy the weak coverage+ condition), and hence whose fundeps generate fresh unification+ variables. Not satisfying the coverage condition is known to+ lead to termination trouble, but in this case it's plain silly. -NB2: if the unification was the other way round, k0:=kb0, all would be-well. It's a very delicate problem.+NB2: In this example, the third parameter to C ensures that the+ instance doesn't actually match the Wanted, so we can't use it to+ solve the Wanted -The ticket #19415 discusses various solutions, but the one we adopted-is very simple:+We solve the problem by (#21703): -* There is a flag in CDictCan (cc_fundeps :: Bool)+ carefully orienting the new Wanted so that all the+ freshly-generated unification variables are on the LHS. -* cc_fundeps = True means- a) The class has fundeps- b) We have not had a successful hit against instances yet+ Thus we emit+ [W] T kfresh0 (yfresh0::kfresh0) ~ T kb0 (b0::kb0)+ and /NOT/+ [W] T kb0 (b0::kb0) ~ T kfresh0 (yfresh0::kfresh0) -* In doTopFundepImprovement, if we emit some constraints we flip the flag- to False, so that we won't try again with the same CDictCan. In our- example, dwrk will have its flag set to False.+Now we'll unify kfresh0:=kb0, yfresh0:=b0, and all is well. The general idea+is that we want to preferentially eliminate those freshly-generated+unification variables, rather than unifying older variables, which causes+kick-out etc. -* Not that if we have no "hits" we must /not/ flip the flag. We might have- dwrk :: C alpha beta Char- which does not yet trigger fundeps from the instance, but later we- get alpha := T ka a. We could be cleverer, and spot that the constraint- is such that we will /never/ get any hits (no unifiers) but we don't do- that yet.+Keeping younger variables on the left also gives very minor improvement in+the compiler performance by having less kick-outs and allocations (-0.1% on+average). Indeed Historical Note [Eliminate younger unification variables]+in GHC.Tc.Utils.Unify describes an earlier attempt to do so systematically,+apparently now in abeyance. -Easy! What could go wrong?-* Maybe the class has multiple fundeps, and we get hit with one but not- the other. Per-fundep flags?-* Maybe we get a hit against one instance with one fundep but, after- the work-item is instantiated a bit more, we get a second hit- against a second instance. (This is a pretty strange and- undesirable thing anyway, and can only happen with overlapping- instances; one example is in Note [Weird fundeps].)+But this is is a delicate solution. We must take care to /preserve/+orientation during solving. Wrinkles: -But both of these seem extremely exotic, and ignoring them threatens-completeness (fixable with some type signature), but not termination-(not fixable). So for now we are just doing the simplest thing.+(W1) We start with+ [W] T kfresh0 (yfresh0::kfresh0) ~ T kb0 (b0::kb0)+ Decompose to+ [W] kfresh0 ~ kb0+ [W] (yfresh0::kfresh0) ~ (b0::kb0)+ Preserve orientiation when decomposing!! +(W2) Suppose we happen to tackle the second Wanted from (W1)+ first. Then in canEqCanLHSHetero we emit a /kind/ equality, as+ well as a now-homogeneous type equality+ [W] kco : kfresh0 ~ kb0+ [W] (yfresh0::kfresh0) ~ (b0::kb0) |> (sym kco)+ Preserve orientation in canEqCanLHSHetero!! (Failing to+ preserve orientation here was the immediate cause of #21703.)++(W3) There is a potential interaction with the swapping done by+ GHC.Tc.Utils.Unify.swapOverTyVars. We think it's fine, but it's+ a slight worry. See especially Note [TyVar/TyVar orientation] in+ that module.++The trouble is that "preserving orientation" is a rather global invariant,+and sometimes we definitely do want to swap (e.g. Int ~ alpha), so we don't+even have a precise statement of what the invariant is. The advantage+of the preserve-orientation plan is that it is extremely cheap to implement,+and apparently works beautifully.++--- Alternative plan (1) ---+Rather than have an ill-defined invariant, another possiblity is to+elminate those fresh unification variables at birth, when generating+the new fundep-inspired equalities.++The key idea is to call `instFlexiX` in `emitFunDepWanteds` on only those+type variables that are guaranteed to give us some progress. This means we+have to locally (without calling emitWanteds) identify the type variables+that do not give us any progress. In the above example, we _know_ that+emitting the two wanteds `kco` and `co` is fruitless.++ Q: How do we identify such no-ops?++ 1. Generate a matching substitution from LHS to RHS+ ɸ = [kb0 :-> k0, b0 :-> y0]+ 2. Call `instFlexiX` on only those type variables that do not appear in the domain of ɸ+ ɸ' = instFlexiX ɸ (tvs - domain ɸ)+ 3. Apply ɸ' on LHS and then call emitWanteds+ unifyWanteds ... (subst ɸ' LHS) RHS++Why will this work? The matching substitution ɸ will be a best effort+substitution that gives us all the easy solutions. It can be generated with+modified version of `Core/Unify.unify_tys` where we run it in a matching mode+and never generate `SurelyApart` and always return a `MaybeApart Subst`+instead.++The same alternative plan would work for type-family injectivity constraints:+see Note [Improvement orientation].+--- End of Alternative plan (1) ---++--- Alternative plan (2) ---+We could have a new flavour of TcTyVar (like `TauTv`, `TyVarTv` etc; see GHC.Tc.Utils.TcType.MetaInfo)+for the fresh unification variables introduced by functional dependencies. Say `FunDepTv`. Then in+GHC.Tc.Utils.Unify.swapOverTyVars we could arrange to keep a `FunDepTv` on the left if possible.+Looks possible, but it's one more complication.+--- End of Alternative plan (2) ---+++--- Historical note: Failed Alternative Plan (3) ---+Previously we used a flag `cc_fundeps` in `CDictCan`. It would flip to False+once we used a fun dep to hint the solver to break and to stop emitting more+wanteds. This solution was not complete, and caused a failures while trying+to solve for transitive functional dependencies (test case: T21703)+-- End of Historical note: Failed Alternative Plan (3) --+ Note [Weird fundeps] ~~~~~~~~~~~~~~~~~~~~ Consider class Het a b | a -> b where@@ -1818,25 +1901,17 @@ #7875 is a case in point. -} -doTopFundepImprovement :: Ct -> TcS (StopOrContinue Ct)--- Try to functional-dependency improvement betweeen the constraint+doTopFundepImprovement :: Ct -> TcS ()+-- Try to functional-dependency improvement between the constraint -- and the top-level instance declarations--- See Note [Fundeps with instances]+-- See Note [Fundeps with instances, and equality orientation] -- See also Note [Weird fundeps] doTopFundepImprovement work_item@(CDictCan { cc_ev = ev, cc_class = cls- , cc_tyargs = xis- , cc_fundeps = has_fds })- | has_fds+ , cc_tyargs = xis }) = do { traceTcS "try_fundeps" (ppr work_item) ; instEnvs <- getInstEnvs ; let fundep_eqns = improveFromInstEnv instEnvs mk_ct_loc cls xis- ; case fundep_eqns of- [] -> continueWith work_item -- No improvement- _ -> do { emitFunDepWanteds (ctEvRewriters ev) fundep_eqns- ; continueWith (work_item { cc_fundeps = False }) } }- | otherwise- = continueWith work_item-+ ; emitFunDepWanteds (ctEvRewriters ev) fundep_eqns } where dict_pred = mkClassPred cls xis dict_loc = ctEvLoc ev@@ -1853,8 +1928,11 @@ doTopFundepImprovement work_item = pprPanic "doTopFundepImprovement" (ppr work_item) emitFunDepWanteds :: RewriterSet -- from the work item- -> [FunDepEqn (CtLoc, RewriterSet)] -> TcS ()+ -> [FunDepEqn (CtLoc, RewriterSet)] -> TcS ()++emitFunDepWanteds _ [] = return () -- common case noop -- See Note [FunDep and implicit parameter reactions]+ emitFunDepWanteds work_rewriters fd_eqns = mapM_ do_one_FDEqn fd_eqns where@@ -1867,15 +1945,24 @@ | otherwise = do { traceTcS "emitFunDepWanteds 2" (ppr (ctl_depth loc) $$ ppr tvs $$ ppr eqs)- ; subst <- instFlexi tvs -- Takes account of kind substitution+ ; subst <- instFlexiX emptySubst tvs -- Takes account of kind substitution ; mapM_ (do_one_eq loc all_rewriters subst) (reverse eqs) } -- See Note [Reverse order of fundep equations] where all_rewriters = work_rewriters S.<> rewriters do_one_eq loc rewriters subst (Pair ty1 ty2)- = unifyWanted rewriters loc Nominal- (Type.substTyUnchecked subst ty1) (Type.substTyUnchecked subst ty2)+ = unifyWanted rewriters loc Nominal (substTyUnchecked subst' ty1) ty2+ -- ty2 does not mention fd_qtvs, so no need to subst it.+ -- See GHC.Tc.Instance.Fundeps Note [Improving against instances]+ -- Wrinkle (1)+ where+ subst' = extendSubstInScopeSet subst (tyCoVarsOfType ty1)+ -- The free vars of ty1 aren't just fd_qtvs: ty1 is the result+ -- of matching with the [W] constraint. So we add its free+ -- vars to InScopeSet, to satisfy substTy's invariants, even+ -- though ty1 will never (currently) be a poytype, so this+ -- InScopeSet will never be looked at. {- **********************************************************************@@ -1923,7 +2010,7 @@ | otherwise = do { res <- matchLocalInst pred loc ; case res of- OneInst {} -> chooseInstance ev res+ OneInst {} -> chooseInstance work_item res _ -> continueWith work_item } where@@ -1940,44 +2027,20 @@ doTopReactEqPred :: Ct -> EqRel -> TcType -> TcType -> TcS (StopOrContinue Ct) doTopReactEqPred work_item eq_rel t1 t2 -- See Note [Looking up primitive equalities in quantified constraints]- = do { ev_binds_var <- getTcEvBindsVar- ; ics <- getInertCans- ; if isWanted ev -- Never look up Givens in quantified constraints- && not (null (inert_insts ics)) -- Shortcut common case- && not (isCoEvBindsVar ev_binds_var) -- See Note [Instances in no-evidence implications]- then try_for_qci- else continueWith work_item}+ | Just (cls, tys) <- boxEqPred eq_rel t1 t2+ = do { res <- matchLocalInst (mkClassPred cls tys) loc+ ; case res of+ OneInst { cir_mk_ev = mk_ev }+ -> chooseInstance work_item+ (res { cir_mk_ev = mk_eq_ev cls tys mk_ev })+ _ -> continueWith work_item }++ | otherwise+ = continueWith work_item where ev = ctEvidence work_item loc = ctEvLoc ev - role = eqRelRole eq_rel-- try_for_qci -- First try looking for (lhs ~ rhs)- | Just (cls, tys) <- boxEqPred eq_rel t1 t2- = do { res <- matchLocalInst (mkClassPred cls tys) loc- ; traceTcS "final_qci_check:1" (ppr (mkClassPred cls tys))- ; case res of- OneInst { cir_mk_ev = mk_ev }- -> chooseInstance ev (res { cir_mk_ev = mk_eq_ev cls tys mk_ev })- _ -> try_swapping }- | otherwise- = continueWith work_item-- try_swapping -- Now try looking for (rhs ~ lhs) (see #23333)- | Just (cls, tys) <- boxEqPred eq_rel t2 t1- = do { res <- matchLocalInst (mkClassPred cls tys) loc- ; traceTcS "final_qci_check:2" (ppr (mkClassPred cls tys))- ; case res of- OneInst { cir_mk_ev = mk_ev }- -> do { ev' <- rewriteEqEvidence emptyRewriterSet ev IsSwapped- (mkReflRedn role t2) (mkReflRedn role t1)- ; chooseInstance ev' (res { cir_mk_ev = mk_eq_ev cls tys mk_ev }) }- _ -> do { traceTcS "final_qci_check:3" (ppr work_item)- ; continueWith work_item }}- | otherwise- = continueWith work_item- mk_eq_ev cls tys mk_ev evs = case (mk_ev evs) of EvExpr e -> EvExpr (Var sc_id `mkTyApps` tys `App` e)@@ -2097,13 +2160,15 @@ = return [] where+ in_scope = mkInScopeSet (tyCoVarsOfType rhs_ty)+ buildImprovementData :: [a] -- axioms for a TF (FamInst or CoAxBranch) -> (a -> [TyVar]) -- get bound tyvars of an axiom -> (a -> [Type]) -- get LHS of an axiom -> (a -> Type) -- get RHS of an axiom -> (a -> Maybe CoAxBranch) -- Just => apartness check required- -> [( [Type], TCvSubst, [TyVar], Maybe CoAxBranch )]+ -> [( [Type], Subst, [TyVar], Maybe CoAxBranch )] -- Result: -- ( [arguments of a matching axiom] -- , RHS-unifying substitution@@ -2115,14 +2180,15 @@ , let ax_args = axiomLHS axiom ax_rhs = axiomRHS axiom ax_tvs = axiomTVs axiom- , Just subst <- [tcUnifyTyWithTFs False ax_rhs rhs_ty]+ in_scope1 = in_scope `extendInScopeSetList` ax_tvs+ , Just subst <- [tcUnifyTyWithTFs False in_scope1 ax_rhs rhs_ty] , let notInSubst tv = not (tv `elemVarEnv` getTvSubstEnv subst) unsubstTvs = filter (notInSubst <&&> isTyVar) ax_tvs ] -- The order of unsubstTvs is important; it must be -- in telescope order e.g. (k:*) (a:k) injImproveEqns :: [Bool]- -> ([Type], TCvSubst, [TyCoVar], Maybe CoAxBranch)+ -> ([Type], Subst, [TyCoVar], Maybe CoAxBranch) -> TcS [TypeEqn] injImproveEqns inj_args (ax_args, subst, unsubstTvs, cabr) = do { subst <- instFlexiX subst unsubstTvs@@ -2131,7 +2197,7 @@ -- be sure to apply the current substitution to a's kind. -- Hence instFlexiX. #13135 was an example. - ; return [ Pair (substTyUnchecked subst ax_arg) arg+ ; return [ Pair (substTy subst ax_arg) arg -- NB: the ax_arg part is on the left -- see Note [Improvement orientation] | case cabr of@@ -2148,6 +2214,9 @@ Note [Improvement orientation] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+See also Note [Fundeps with instances, and equality orientation], which describes+the Exact Same Prolem, with the same solution, but for functional dependencies.+ A very delicate point is the orientation of equalities arising from injectivity improvement (#12522). Suppose we have type family F x = t | t -> x@@ -2210,35 +2279,60 @@ OneInst { cir_what = what } -> do { insertSafeOverlapFailureTcS what work_item ; addSolvedDict what ev cls xis- ; chooseInstance ev lkup_res }+ ; chooseInstance work_item lkup_res } _ -> -- NoInstance or NotSure -- We didn't solve it; so try functional dependencies with- -- the instance environment, and return- doTopFundepImprovement work_item }+ -- the instance environment+ do { doTopFundepImprovement work_item+ ; tryLastResortProhibitedSuperclass inerts work_item } } where dict_loc = ctEvLoc ev doTopReactDict _ w = pprPanic "doTopReactDict" (ppr w) +-- | As a last resort, we TEMPORARILY allow a prohibited superclass solve,+-- emitting a loud warning when doing so: we might be creating non-terminating+-- evidence (as we are in T22912 for example).+--+-- See Note [Migrating away from loopy superclass solving] in GHC.Tc.TyCl.Instance.+tryLastResortProhibitedSuperclass :: InertSet -> Ct -> TcS (StopOrContinue Ct)+tryLastResortProhibitedSuperclass inerts+ work_item@(CDictCan { cc_ev = ev_w, cc_class = cls, cc_tyargs = xis })+ | let loc_w = ctEvLoc ev_w+ orig_w = ctLocOrigin loc_w+ , ScOrigin _ NakedSc <- orig_w -- work_item is definitely Wanted+ , Just ct_i <- lookupInertDict (inert_cans inerts) loc_w cls xis+ , let ev_i = ctEvidence ct_i+ , isGiven ev_i+ = do { setEvBindIfWanted ev_w (ctEvTerm ev_i)+ ; ctLocWarnTcS loc_w $+ TcRnLoopySuperclassSolve loc_w (ctPred work_item)+ ; return $ Stop ev_w (text "Loopy superclass") }+tryLastResortProhibitedSuperclass _ work_item+ = continueWith work_item -chooseInstance :: CtEvidence -> ClsInstResult -> TcS (StopOrContinue Ct)+chooseInstance :: Ct -> ClsInstResult -> TcS (StopOrContinue Ct) chooseInstance work_item (OneInst { cir_new_theta = theta , cir_what = what , cir_mk_ev = mk_ev })- = do { traceTcS "doTopReact/found instance for" $ ppr work_item+ = do { traceTcS "doTopReact/found instance for" $ ppr ev ; deeper_loc <- checkInstanceOK loc what pred ; checkReductionDepth deeper_loc pred- ; assertPprM (getTcEvBindsVar >>= return . not . isCoEvBindsVar)- (ppr work_item)- ; evc_vars <- mapM (newWanted deeper_loc (ctEvRewriters work_item)) theta- ; setEvBindIfWanted work_item (mk_ev (map getEvExpr evc_vars))- ; emitWorkNC (freshGoals evc_vars)- ; stopWith work_item "Dict/Top (solved wanted)" }+ ; evb <- getTcEvBindsVar+ ; if isCoEvBindsVar evb+ then continueWith work_item+ -- See Note [Instances in no-evidence implications]+ else+ do { evc_vars <- mapM (newWanted deeper_loc (ctRewriters work_item)) theta+ ; setEvBindIfWanted ev (mk_ev (map getEvExpr evc_vars))+ ; emitWorkNC (freshGoals evc_vars)+ ; stopWith ev "Dict/Top (solved wanted)" }} where- pred = ctEvPred work_item- loc = ctEvLoc work_item+ ev = ctEvidence work_item+ pred = ctEvPred ev+ loc = ctEvLoc ev chooseInstance work_item lookup_res = pprPanic "chooseInstance" (ppr work_item $$ ppr lookup_res)@@ -2256,13 +2350,35 @@ origin = ctLocOrigin loc zap_origin loc -- After applying an instance we can set ScOrigin to- -- infinity, so that prohibitedSuperClassSolve never fires- | ScOrigin {} <- origin- = setCtLocOrigin loc (ScOrigin infinity)+ -- NotNakedSc, so that prohibitedSuperClassSolve never fires+ -- See Note [Solving superclass constraints] in+ -- GHC.Tc.TyCl.Instance, (sc1).+ | ScOrigin what _ <- origin+ = setCtLocOrigin loc (ScOrigin what NotNakedSc) | otherwise = loc +{- Note [Instances in no-evidence implications]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+In #15290 we had+ [G] forall p q. Coercible p q => Coercible (m p) (m q))+ [W] forall <no-ev> a. m (Int, IntStateT m a)+ ~R#+ m (Int, StateT Int m a) +The Given is an ordinary quantified constraint; the Wanted is an implication+equality that arises from+ [W] (forall a. t1) ~R# (forall a. t2)++But because the (t1 ~R# t2) is solved "inside a type" (under that forall a)+we can't generate any term evidence. So we can't actually use that+lovely quantified constraint. Alas!++This test arranges to ignore the instance-based solution under these+(rare) circumstances. It's sad, but I really don't see what else we can do.+-}++ matchClassInst :: DynFlags -> InertSet -> Class -> [Type] -> CtLoc -> TcS ClsInstResult@@ -2273,11 +2389,9 @@ -- See Note [Instance and Given overlap] | not (xopt LangExt.IncoherentInstances dflags) , not (naturallyCoherentClass clas)- , let matchable_givens = matchableGivens loc pred inerts- , not (isEmptyBag matchable_givens)+ , not (noMatchableGivenDicts inerts loc clas tys) = do { traceTcS "Delaying instance application" $- vcat [ text "Work item=" <+> pprClassPred clas tys- , text "Potential matching givens:" <+> ppr matchable_givens ]+ vcat [ text "Work item=" <+> pprClassPred clas tys ] ; return NotSure } | otherwise@@ -2303,7 +2417,7 @@ -- | If a class is "naturally coherent", then we needn't worry at all, in any -- way, about overlapping/incoherent instances. Just solve the thing! -- See Note [Naturally coherent classes]--- See also Note [The equality class story] in "GHC.Builtin.Types.Prim".+-- See also Note [The equality types story] in GHC.Builtin.Types.Prim. naturallyCoherentClass :: Class -> Bool naturallyCoherentClass cls = isCTupleClass cls@@ -2429,7 +2543,7 @@ instance (c1, c2) => (% c1, c2 %) Example in #14218 -Exammples: T5853, T10432, T5315, T9222, T2627b, T3028b+Examples: T5853, T10432, T5315, T9222, T2627b, T3028b PS: the term "naturally coherent" doesn't really seem helpful. Perhaps "invertible" or something? I left it for now though.@@ -2454,7 +2568,7 @@ that unify with it here. It is not like an incoherent user-written instance which might have utterly different behaviour. -Consdider f :: Eq a => blah. If we have [W] Eq a, we certainly+Consider f :: Eq a => blah. If we have [W] Eq a, we certainly get it from the Eq a context, without worrying that there are lots of top-level instances that unify with [W] Eq a! We'll use those instances to build evidence to pass to f. That's just the@@ -2467,110 +2581,236 @@ matchLocalInst pred loc = do { inerts@(IS { inert_cans = ics }) <- getTcSInerts ; case match_local_inst inerts (inert_insts ics) of- ([], Nothing) -> do { traceTcS "No local instance for" (ppr pred)- ; return NoInstance }-- -- See Note [Use only the best local instance] about- -- superclass depths- (matches, unifs)- | [(dfun_ev, inst_tys)] <- best_matches- , maybe True (> min_sc_depth) unifs- -> do { let dfun_id = ctEvEvId dfun_ev- ; (tys, theta) <- instDFunType dfun_id inst_tys- ; let result = OneInst { cir_new_theta = theta- , cir_mk_ev = evDFunApp dfun_id tys- , cir_what = LocalInstance }- ; traceTcS "Best local inst found:" (ppr result)- ; traceTcS "All local insts:" (ppr matches)- ; return result }-- | otherwise- -> do { traceTcS "Multiple local instances for" (ppr pred)- ; return NotSure }+ { ([], []) -> do { traceTcS "No local instance for" (ppr pred)+ ; return NoInstance }+ ; (matches, unifs) ->+ do { matches <- mapM mk_instDFun matches+ ; unifs <- mapM mk_instDFun unifs+ -- See Note [Use only the best matching quantified constraint]+ ; case dominatingMatch matches of+ { Just (dfun_id, tys, theta)+ | all ((theta `impliedBySCs`) . thdOf3) unifs+ ->+ do { let result = OneInst { cir_new_theta = theta+ , cir_mk_ev = evDFunApp dfun_id tys+ , cir_what = LocalInstance }+ ; traceTcS "Best local instance found:" $+ vcat [ text "pred:" <+> ppr pred+ , text "result:" <+> ppr result+ , text "matches:" <+> ppr matches+ , text "unifs:" <+> ppr unifs ]+ ; return result } - where- extract_depth = sc_depth . ctEvLoc . fst- min_sc_depth = minimum (map extract_depth matches)- best_matches = filter ((== min_sc_depth) . extract_depth) matches }+ ; mb_best ->+ do { traceTcS "Multiple local instances; not committing to any"+ $ vcat [ text "pred:" <+> ppr pred+ , text "matches:" <+> ppr matches+ , text "unifs:" <+> ppr unifs+ , text "best_match:" <+> ppr mb_best ]+ ; return NotSure }}}}} where pred_tv_set = tyCoVarsOfType pred - sc_depth :: CtLoc -> Int- sc_depth ctloc = case ctLocOrigin ctloc of- InstSCOrigin depth _ -> depth- OtherSCOrigin depth _ -> depth- _ -> 0+ mk_instDFun :: (CtEvidence, [DFunInstType]) -> TcS InstDFun+ mk_instDFun (ev, tys) =+ let dfun_id = ctEvEvId ev+ in do { (tys, theta) <- instDFunType (ctEvEvId ev) tys+ ; return (dfun_id, tys, theta) } - -- See Note [Use only the best local instance] about superclass depths+ -- Compute matching and unifying local instances match_local_inst :: InertSet -> [QCInst] -> ( [(CtEvidence, [DFunInstType])]- , Maybe Int ) -- Nothing ==> no unifying local insts- -- Just n ==> unifying local insts, with- -- minimum superclass depth- -- of n+ , [(CtEvidence, [DFunInstType])] ) match_local_inst _inerts []- = ([], Nothing)- match_local_inst inerts (qci@(QCI { qci_tvs = qtvs, qci_pred = qpred- , qci_ev = qev })- : qcis)+ = ([], [])+ match_local_inst inerts (qci@(QCI { qci_tvs = qtvs+ , qci_pred = qpred+ , qci_ev = qev })+ :qcis) | let in_scope = mkInScopeSet (qtv_set `unionVarSet` pred_tv_set) , Just tv_subst <- ruleMatchTyKiX qtv_set (mkRnEnv2 in_scope) emptyTvSubstEnv qpred pred , let match = (qev, map (lookupVarEnv tv_subst) qtvs)- = (match:matches, unif)+ = (match:matches, unifs) | otherwise = assertPpr (disjointVarSet qtv_set (tyCoVarsOfType pred)) (ppr qci $$ ppr pred) -- ASSERT: unification relies on the -- quantified variables being fresh- (matches, unif `combine` this_unif)+ (matches, this_unif `combine` unifs) where qloc = ctEvLoc qev qtv_set = mkVarSet qtvs+ (matches, unifs) = match_local_inst inerts qcis this_unif- | mightEqualLater inerts qpred qloc pred loc = Just (sc_depth qloc)- | otherwise = Nothing- (matches, unif) = match_local_inst inerts qcis+ | Just subst <- mightEqualLater inerts qpred qloc pred loc+ = Just (qev, map (lookupTyVar subst) qtvs)+ | otherwise+ = Nothing - combine Nothing Nothing = Nothing- combine (Just n) Nothing = Just n- combine Nothing (Just n) = Just n- combine (Just n1) (Just n2) = Just (n1 `min` n2)+ combine Nothing us = us+ combine (Just u) us = u : us -{- Note [Use only the best local instance]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+-- | Instance dictionary function and type.+type InstDFun = (DFunId, [TcType], TcThetaType)++-- | Try to find a local quantified instance that dominates all others,+-- i.e. which has a weaker instance context than all the others.+--+-- See Note [Use only the best matching quantified constraint].+dominatingMatch :: [InstDFun] -> Maybe InstDFun+dominatingMatch matches =+ listToMaybe $ mapMaybe (uncurry go) (holes matches)+ -- listToMaybe: arbitrarily pick any one context that is weaker than+ -- all others, e.g. so that we can handle [Eq a, Num a] vs [Num a, Eq a]+ -- (see test case T22223).++ where+ go :: InstDFun -> [InstDFun] -> Maybe InstDFun+ go this [] = Just this+ go this@(_,_,this_theta) ((_,_,other_theta):others)+ | this_theta `impliedBySCs` other_theta+ = go this others+ | otherwise+ = Nothing++-- | Whether a collection of constraints is implied by another collection,+-- according to a simple superclass check.+--+-- See Note [When does a quantified instance dominate another?].+impliedBySCs :: TcThetaType -> TcThetaType -> Bool+impliedBySCs c1 c2 = all in_c2 c1+ where+ in_c2 :: TcPredType -> Bool+ in_c2 pred = any (pred `tcEqType`) c2_expanded++ c2_expanded :: [TcPredType] -- Includes all superclasses+ c2_expanded = [ q | p <- c2, q <- p : transSuperClasses p ]+++{- Note [When does a quantified instance dominate another?]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+When matching local quantified instances, it's useful to be able to pick+the one with the weakest precondition, e.g. if one has both++ [G] d1: forall a b. ( Eq a, Num b, C a b ) => D a b+ [G] d2: forall a . C a Int => D a Int+ [W] {w}: D a Int++Then it makes sense to use d2 to solve w, as doing so we end up with a strictly+weaker proof obligation of `C a Int`, compared to `(Eq a, Num Int, C a Int)`+were we to use d1.++In theory, to compute whether one context implies another, we would need to+recursively invoke the constraint solver. This is expensive, so we instead do+a simple check using superclasses, implemented in impliedBySCs.++Examples:++ - [Eq a] is implied by [Ord a]+ - [Ord a] is not implied by [Eq a],+ - any context is implied by itself,+ - the empty context is implied by any context.++Note [Use only the best matching quantified constraint]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Consider (#20582) the ambiguity check for (forall a. Ord (m a), forall a. Semigroup a => Eq (m a)) => m Int Because of eager expansion of given superclasses, we get- [G] forall a. Ord (m a)- [G] forall a. Eq (m a)- [G] forall a. Semigroup a => Eq (m a)+ [G] d1: forall a. Ord (m a)+ [G] d2: forall a. Eq (m a)+ [G] d3: forall a. Semigroup a => Eq (m a) - [W] forall a. Ord (m a)- [W] forall a. Semigroup a => Eq (m a)+ [W] {w1}: forall a. Ord (m a)+ [W] {w2}: forall a. Semigroup a => Eq (m a) The first wanted is solved straightforwardly. But the second wanted-matches *two* local instances. Our general rule around multiple local+matches *two* local instances: d2 and d3. Our general rule around multiple local instances is that we refuse to commit to any of them. However, that means that our type fails the ambiguity check. That's bad: the type is perfectly fine. (This actually came up in the wild, in the streamly library.) -The solution is to prefer local instances with fewer superclass selections.-So, matchLocalInst is careful to whittle down the matches only to the-ones with the shallowest superclass depth, and also to worry about unifying-local instances that are at that depth (or less).+The solution is to prefer local instances which are easier to prove, meaning+that they have a weaker precondition. In this case, the empty context+of d2 is a weaker constraint than the "Semigroup a" context of d3, so we prefer+using it when proving w2. This allows us to pass the ambiguity check here. -By preferring these shallower local instances, we can use the last given-listed above and pass the ambiguity check.+Our criterion for solving a Wanted by matching local quantified instances is+thus as follows: -The instance-depth mechanism uses the same superclass depth-information as described in Note [Replacement vs keeping], 2a.+ - There is a matching local quantified instance that dominates all others+ matches, in the sense of [When does a quantified instance dominate another?].+ Any such match do, we pick it arbitrarily (the T22223 example below says why).+ - This local quantified instance also dominates all the unifiers, as we+ wouldn't want to commit to a single match when we might have multiple,+ genuinely different matches after further unification takes place. -Test case: typecheck/should_compile/T20582.+Some other examples: --} + #15244:++ f :: (C g, D g) => ....+ class S g => C g where ...+ class S g => D g where ...+ class (forall a. Eq a => Eq (g a)) => S g where ...++ Here, in f's RHS, there are two identical quantified constraints+ available, one via the superclasses of C and one via the superclasses+ of D. Given that each implies the other, we pick one arbitrarily.+++ #22216:++ class Eq a+ class Eq a => Ord a+ class (forall b. Eq b => Eq (f b)) => Eq1 f+ class (Eq1 f, forall b. Ord b => Ord (f b)) => Ord1 f++ Suppose we have++ [G] d1: Ord1 f+ [G] d2: Eq a+ [W] {w}: Eq (f a)++ Superclass expansion of d1 gives us:++ [G] d3 : Eq1 f+ [G] d4 : forall b. Ord b => Ord (f b)++ expanding d4 and d5 gives us, respectively:++ [G] d5 : forall b. Eq b => Eq (f b)+ [G] d6 : forall b. Ord b => Eq (f b)++ Now we have two matching local instances that we could use when solving the+ Wanted. However, it's obviously silly to use d6, given that d5 provides us with+ as much information, with a strictly weaker precondition. So we pick d5 to solve+ w. If we chose d6, we would get [W] Ord a, which in this case we can't solve.+++ #22223:++ [G] forall a b. (Eq a, Ord b) => C a b+ [G] forall a b. (Ord b, Eq a) => C a b+ [W] C x y++ Here we should be free to pick either quantified constraint, as they are+ equivalent up to re-ordering of the constraints in the context.+ See also Note [Do not add duplicate quantified instances]+ in GHC.Tc.Solver.Monad.++Test cases:+ typecheck/should_compile/T20582+ quantified-constraints/T15244+ quantified-constraints/T22216{a,b,c,d,e}+ quantified-constraints/T22223++Historical note: a previous solution was to instead pick the local instance+with the least superclass depth (see Note [Replacement vs keeping]),+but that doesn't work for the example from #22216.+-}
compiler/GHC/Tc/Solver/Monad.hs view
@@ -8,14 +8,14 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} -{-# OPTIONS_GHC -Wno-incomplete-record-updates -Wno-orphans #-}+{-# OPTIONS_GHC -Wno-orphans #-} -- | Monadic definitions for the constraint solver module GHC.Tc.Solver.Monad ( -- The TcS monad TcS, runTcS, runTcSEarlyAbort, runTcSWithEvBinds, runTcSInerts,- failTcS, warnTcS, addErrTcS, wrapTcS,+ failTcS, warnTcS, addErrTcS, wrapTcS, ctLocWarnTcS, runTcSEqualities, nestTcS, nestImplicTcS, setEvBindsTcS, emitImplicationTcS, emitTvImplicationTcS,@@ -25,7 +25,7 @@ updWorkListTcS, pushLevelNoWorkList, - runTcPluginTcS, addUsedGRE, addUsedGREs, keepAlive,+ runTcPluginTcS, recordUsedGREs, matchGlobalInst, TcM.ClsInstResult(..), QCInst(..),@@ -89,7 +89,7 @@ instDFunType, -- Instantiation -- MetaTyVars- newFlexiTcSTy, instFlexi, instFlexiX,+ newFlexiTcSTy, instFlexiX, cloneMetaTyVar, tcInstSkolTyVarsX, @@ -131,27 +131,29 @@ import qualified GHC.Tc.Utils.Env as TcM ( checkWellStaged, tcGetDefaultTys, tcLookupClass, tcLookupId, topIdLvl , tcInitTidyEnv )++import GHC.Driver.Session+ import GHC.Tc.Instance.Class( InstanceWhat(..), safeOverlap, instanceReturnsDictCon ) import GHC.Tc.Utils.TcType-import GHC.Driver.Session+import GHC.Tc.Solver.Types+import GHC.Tc.Solver.InertSet+import GHC.Tc.Types.Evidence+import GHC.Tc.Errors.Types+ import GHC.Core.Type import qualified GHC.Core.TyCo.Rep as Rep -- this needs to be used only very locally import GHC.Core.Coercion import GHC.Core.Reduction--import GHC.Tc.Solver.Types-import GHC.Tc.Solver.InertSet--import GHC.Tc.Types.Evidence import GHC.Core.Class import GHC.Core.TyCon-import GHC.Tc.Errors.Types-import GHC.Types.Error ( mkPlainError, noHints ) +import GHC.Types.Error ( mkPlainError, noHints ) import GHC.Types.Name import GHC.Types.TyThing+import GHC.Types.Name.Reader+ import GHC.Unit.Module ( HasModule, getModule, extractModule )-import GHC.Types.Name.Reader ( GlobalRdrEnv, GlobalRdrElt ) import qualified GHC.Rename.Env as TcM import GHC.Types.Var import GHC.Types.Var.Env@@ -173,7 +175,8 @@ import GHC.Utils.Monad import Data.IORef import GHC.Exts (oneShot)-import Data.List ( mapAccumL, partition, find )+import Data.List ( mapAccumL, partition )+import Data.Foldable #if defined(DEBUG) import GHC.Data.Graph.Directed@@ -220,21 +223,17 @@ {- Note [Do not add duplicate quantified instances] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Consider this (#15244):-- f :: (C g, D g) => ....- class S g => C g where ...- class S g => D g where ...- class (forall a. Eq a => Eq (g a)) => S g where ...+As an optimisation, we avoid adding duplicate quantified instances to the+inert set; we use a simple duplicate check using tcEqType for simplicity,+even though it doesn't account for superficial differences, e.g. it will count+the following two constraints as different (#22223): -Then in f's RHS there are two identical quantified constraints-available, one via the superclasses of C and one via the superclasses-of D. The two are identical, and it seems wrong to reject the program-because of that. But without doing duplicate-elimination we will have-two matching QCInsts when we try to solve constraints arising from f's-RHS.+ - forall a b. C a b+ - forall b a. C a b -The simplest thing is simply to eliminate duplicates, which we do here.+The main logic that allows us to pick local instances, even in the presence of+duplicates, is explained in Note [Use only the best matching quantified constraint]+in GHC.Tc.Solver.Interact. -} {- *********************************************************************@@ -674,8 +673,18 @@ lookupInInerts loc pty | ClassPred cls tys <- classifyPredType pty = do { inerts <- getTcSInerts- ; return (lookupSolvedDict inerts loc cls tys `mplus`- fmap ctEvidence (lookupInertDict (inert_cans inerts) loc cls tys)) }+ ; let mb_solved = lookupSolvedDict inerts loc cls tys+ mb_inert = fmap ctEvidence (lookupInertDict (inert_cans inerts) loc cls tys)+ ; return $ do -- Maybe monad+ found_ev <- mb_solved `mplus` mb_inert++ -- We're about to "solve" the wanted we're looking up, so we+ -- must make sure doing so wouldn't run afoul of+ -- Note [Solving superclass constraints] in GHC.Tc.TyCl.Instance.+ -- Forgetting this led to #20666.+ guard $ not (prohibitedSuperClassSolve (ctEvLoc found_ev) loc)++ return found_ev } | otherwise -- NB: No caching for equalities, IPs, holes, or errors = return Nothing @@ -785,8 +794,12 @@ } ----------------newtype TcS a = TcS { unTcS :: TcSEnv -> TcM a } deriving (Functor)+newtype TcS a = TcS { unTcS :: TcSEnv -> TcM a }+ deriving (Functor) +instance MonadFix TcS where+ mfix k = TcS $ \env -> mfix (\x -> unTcS (k x) env)+ -- | Smart constructor for 'TcS', as describe in Note [The one-shot state -- monad trick] in "GHC.Utils.Monad". mkTcS :: (TcSEnv -> TcM a) -> TcS a@@ -844,6 +857,10 @@ addErrTcS = wrapTcS . TcM.addErr panicTcS doc = pprPanic "GHC.Tc.Solver.Canonical" doc +-- | Emit a warning within the 'TcS' monad at the location given by the 'CtLoc'.+ctLocWarnTcS :: CtLoc -> TcRnMessage -> TcS ()+ctLocWarnTcS loc msg = wrapTcS $ TcM.setCtLocM loc $ TcM.addDiagnostic msg+ traceTcS :: String -> SDoc -> TcS () traceTcS herald doc = wrapTcS (TcM.traceTc herald doc) {-# INLINE traceTcS #-} -- see Note [INLINE conditional tracing utilities]@@ -902,7 +919,7 @@ ; ev_binds <- TcM.getTcEvBindsMap ev_binds_var ; return (res, ev_binds) } --- | This variant of 'runTcS' will immediatley fail upon encountering an+-- | This variant of 'runTcS' will immediately fail upon encountering an -- insoluble ct. See Note [Speeding up valid hole-fits]. Its one usage -- site does not need the ev_binds, so we do not return them. runTcSEarlyAbort :: TcS a -> TcM a@@ -1250,37 +1267,35 @@ ppr (TouchableOuterLevel tvs lvl) = text "TouchableOuterLevel" <> parens (ppr lvl <+> ppr tvs) ppr Untouchable = text "Untouchable" -touchabilityTest :: CtFlavour -> TcTyVar -> TcType -> TcS (TouchabilityTestResult, TcType)--- ^ This is the key test for untouchability:+touchabilityTest :: CtFlavour -> TcTyVar -> TcType -> TcS TouchabilityTestResult+-- This is the key test for untouchability: -- See Note [Unification preconditions] in GHC.Tc.Utils.Unify -- and Note [Solve by unification] in GHC.Tc.Solver.Interact------ Returns a new rhs type, as this function can turn make some metavariables concrete. touchabilityTest flav tv1 rhs | flav /= Given -- See Note [Do not unify Givens] , MetaTv { mtv_tclvl = tv_lvl, mtv_info = info } <- tcTyVarDetails tv1- = do { continue_solving <- wrapTcS $ startSolvingByUnification info rhs- ; case continue_solving of- { Nothing -> return (Untouchable, rhs)- ; Just rhs ->- do { let (free_metas, free_skols) = partition isPromotableMetaTyVar $- nonDetEltsUniqSet $- tyCoVarsOfType rhs- ; ambient_lvl <- getTcLevel+ = do { can_continue_solving <- wrapTcS $ startSolvingByUnification info rhs+ ; if not can_continue_solving+ then return Untouchable+ else+ do { ambient_lvl <- getTcLevel ; given_eq_lvl <- getInnermostGivenEqLevel ; if | tv_lvl `sameDepthAs` ambient_lvl- -> return (TouchableSameLevel, rhs)+ -> return TouchableSameLevel | tv_lvl `deeperThanOrSame` given_eq_lvl -- No intervening given equalities , all (does_not_escape tv_lvl) free_skols -- No skolem escapes- -> return (TouchableOuterLevel free_metas tv_lvl, rhs)+ -> return (TouchableOuterLevel free_metas tv_lvl) | otherwise- -> return (Untouchable, rhs) } } }+ -> return Untouchable } } | otherwise- = return (Untouchable, rhs)+ = return Untouchable where+ (free_metas, free_skols) = partition isPromotableMetaTyVar $+ nonDetEltsUniqSet $+ tyCoVarsOfType rhs does_not_escape tv_lvl fv | isTyVar fv = tv_lvl `deeperThanOrSame` tcTyVarLevel fv@@ -1365,17 +1380,22 @@ tcLookupId :: Name -> TcS Id tcLookupId n = wrapTcS $ TcM.tcLookupId n --- Setting names as used (used in the deriving of Coercible evidence)--- Too hackish to expose it to TcS? In that case somehow extract the used--- constructors from the result of solveInteract-addUsedGREs :: [GlobalRdrElt] -> TcS ()-addUsedGREs gres = wrapTcS $ TcM.addUsedGREs gres--addUsedGRE :: Bool -> GlobalRdrElt -> TcS ()-addUsedGRE warn_if_deprec gre = wrapTcS $ TcM.addUsedGRE warn_if_deprec gre+-- Any use of this function is a bit suspect, because it violates the+-- pure veneer of TcS. But it's just about warnings around unused imports+-- and local constructors (GHC will issue fewer warnings than it otherwise+-- might), so it's not worth losing sleep over.+recordUsedGREs :: Bag GlobalRdrElt -> TcS ()+recordUsedGREs gres+ = do { wrapTcS $ TcM.addUsedGREs gre_list+ -- If a newtype constructor was imported, don't warn about not+ -- importing it...+ ; wrapTcS $ traverse_ (TcM.keepAlive . greMangledName) gre_list }+ -- ...and similarly, if a newtype constructor was defined in the same+ -- module, don't warn about it being unused.+ -- See Note [Tracking unused binding and imports] in GHC.Tc.Utils. -keepAlive :: Name -> TcS ()-keepAlive = wrapTcS . TcM.keepAlive+ where+ gre_list = bagToList gres -- Various smaller utilities [TODO, maybe will be absorbed in the instance matcher] -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -1616,22 +1636,21 @@ cloneMetaTyVar :: TcTyVar -> TcS TcTyVar cloneMetaTyVar tv = wrapTcS (TcM.cloneMetaTyVar tv) -instFlexi :: [TKVar] -> TcS TCvSubst-instFlexi = instFlexiX emptyTCvSubst--instFlexiX :: TCvSubst -> [TKVar] -> TcS TCvSubst+instFlexiX :: Subst -> [TKVar] -> TcS Subst instFlexiX subst tvs = wrapTcS (foldlM instFlexiHelper subst tvs) -instFlexiHelper :: TCvSubst -> TKVar -> TcM TCvSubst+instFlexiHelper :: Subst -> TKVar -> TcM Subst+-- Makes fresh tyvar, extends the substitution, and the in-scope set instFlexiHelper subst tv = do { uniq <- TcM.newUnique ; details <- TcM.newMetaDetails TauTv- ; let name = setNameUnique (tyVarName tv) uniq- kind = substTyUnchecked subst (tyVarKind tv)- ty' = mkTyVarTy (mkTcTyVar name kind details)- ; TcM.traceTc "instFlexi" (ppr ty')- ; return (extendTvSubst subst tv ty') }+ ; let name = setNameUnique (tyVarName tv) uniq+ kind = substTyUnchecked subst (tyVarKind tv)+ tv' = mkTcTyVar name kind details+ subst' = extendTvSubstWithClone subst tv tv'+ ; TcM.traceTc "instFlexi" (ppr tv')+ ; return (extendTvSubst subst' tv (mkTyVarTy tv')) } matchGlobalInst :: DynFlags -> Bool -- True <=> caller is the short-cut solver@@ -1640,7 +1659,7 @@ matchGlobalInst dflags short_cut cls tys = wrapTcS (TcM.matchGlobalInst dflags short_cut cls tys) -tcInstSkolTyVarsX :: SkolemInfo -> TCvSubst -> [TyVar] -> TcS (TCvSubst, [TcTyVar])+tcInstSkolTyVarsX :: SkolemInfo -> Subst -> [TyVar] -> TcS (Subst, [TcTyVar]) tcInstSkolTyVarsX skol_info subst tvs = wrapTcS $ TcM.tcInstSkolTyVarsX skol_info subst tvs -- Creating and setting evidence variables and CtFlavors@@ -1691,7 +1710,7 @@ = -- See Note [Yukky eq_sel for a HoleDest] do { let co_var = coHoleCoVar hole ; setEvBind (mkWantedEvBind co_var tm)- ; fillCoercionHole hole (mkTcCoVarCo co_var) }+ ; fillCoercionHole hole (mkCoVarCo co_var) } setWantedEvTerm (EvVarDest ev_id) tm = setEvBind (mkWantedEvBind ev_id tm)@@ -1704,8 +1723,8 @@ Consider [G] forall a. blah => a ~ T [W] S ~# T -Then doTopReactEqPred carefully looks up the (boxed) constraint (S ~ T)-in the quantified constraints, and wraps the (boxed) evidence it+Then doTopReactEqPred carefully looks up the (boxed) constraint (S ~+T) in the quantified constraints, and wraps the (boxed) evidence it gets back in an eq_sel to extract the unboxed (S ~# T). We can't put that term into a coercion, so we add a value binding h = eq_sel (...)@@ -1875,7 +1894,7 @@ ; env0 <- TcM.tcInitTidyEnv ; let tidy_env = tidyFreeTyCoVars env0 (tyCoVarsOfTypeList ty) tidy_ty = tidyType tidy_env ty- msg = TcRnUnknownMessage $ mkPlainError noHints $+ msg = mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ text "Reduction stack overflow; size =" <+> ppr depth , hang (text "When simplifying the following type:") 2 (ppr tidy_ty)@@ -1921,21 +1940,23 @@ -- See Detail (8) of the Note. = do { should_break <- final_check- ; mapM go should_break }+ ; if should_break then do { redn <- go rhs+ ; return (Just redn) }+ else return Nothing } where flavour = ctEvFlavour ev eq_rel = ctEvEqRel ev final_check = case flavour of- Given -> return $ Just rhs+ Given -> return True Wanted -- Wanteds work only with a touchable tyvar on the left -- See "Wanted" section of the Note. | TyVarLHS lhs_tv <- lhs ->- do { (result, rhs) <- touchabilityTest Wanted lhs_tv rhs+ do { result <- touchabilityTest Wanted lhs_tv rhs ; return $ case result of- Untouchable -> Nothing- _ -> Just rhs }- | otherwise -> return Nothing+ Untouchable -> False+ _ -> True }+ | otherwise -> return False -- This could be considerably more efficient. See Detail (5) of Note. go :: TcType -> TcS ReductionN@@ -1945,7 +1966,7 @@ -- causing trouble? See Detail (5) of Note. = do { let (fun_args, extra_args) = splitAt (tyConArity tc) tys fun_app = mkTyConApp tc fun_args- fun_app_kind = tcTypeKind fun_app+ fun_app_kind = typeKind fun_app ; fun_redn <- emit_work fun_app_kind fun_app ; arg_redns <- unzipRedns <$> mapM go extra_args ; return $ mkAppRedns fun_redn arg_redns }@@ -2015,5 +2036,5 @@ rewriterView :: TcType -> Maybe TcType rewriterView ty@(Rep.TyConApp tc _) | isForgetfulSynTyCon tc || (isTypeSynonymTyCon tc && not (isFamFreeTyCon tc))- = tcView ty+ = coreView ty rewriterView _other = Nothing
compiler/GHC/Tc/Solver/Rewrite.hs view
@@ -2,10 +2,8 @@ {-# LANGUAGE DeriveFunctor #-} -{-# OPTIONS_GHC -Wno-incomplete-record-updates #-}- module GHC.Tc.Solver.Rewrite(- rewrite, rewriteArgsNom,+ rewrite, rewriteForErrors, rewriteArgsNom, rewriteType ) where @@ -40,8 +38,10 @@ import GHC.Exts (oneShot) import Control.Monad import Control.Applicative (liftA3)-import GHC.Builtin.Types.Prim (tYPETyCon)+import GHC.Builtin.Types (tYPETyCon) import Data.List ( find )+import GHC.Data.List.Infinite (Infinite)+import qualified GHC.Data.List.Infinite as Inf {- ************************************************************************@@ -223,6 +223,22 @@ ; traceTcS "rewrite }" (ppr $ reductionReducedType redn) ; return result } +-- | See Note [Rewriting]+-- `rewriteForErrors` is a variant of 'rewrite' that rewrites+-- w.r.t. nominal equality only, as this is better than full rewriting+-- for error messages. (This was important when we flirted with rewriting+-- newtypes but perhaps less so now.)+rewriteForErrors :: CtEvidence -> TcType+ -> TcS (Reduction, RewriterSet)+rewriteForErrors ev ty+ = do { traceTcS "rewriteForErrors {" (ppr ty)+ ; result@(redn, rewriters) <-+ runRewrite (ctEvLoc ev) (ctEvFlavour ev) NomEq (rewrite_one ty)+ ; traceTcS "rewriteForErrors }" (ppr $ reductionReducedType redn)+ ; return $ case ctEvEqRel ev of+ NomEq -> result+ ReprEq -> (mkSubRedn redn, rewriters) }+ -- See Note [Rewriting] rewriteArgsNom :: CtEvidence -> TyCon -> [TcType] -> TcS (Reductions, RewriterSet)@@ -275,8 +291,8 @@ Key invariants: (F0) co :: zonk(ty') ~ xi where zonk(ty') ~ zonk(ty)- (F1) tcTypeKind(xi) succeeds and returns a fully zonked kind- (F2) tcTypeKind(xi) `eqType` zonk(tcTypeKind(ty))+ (F1) typeKind(xi) succeeds and returns a fully zonked kind+ (F2) typeKind(xi) `eqType` zonk(typeKind(ty)) Note that it is rewrite's job to try to reduce *every type function it sees*. @@ -297,14 +313,14 @@ when (co :: ty ~ xi) is important even before we zonk the whole program. For example, see the RTRNotFollowed case in rewriteTyVar. -Why have these invariants on rewriting? Because we sometimes use tcTypeKind+Why have these invariants on rewriting? Because we sometimes use typeKind during canonicalisation, and we want this kind to be zonked (e.g., see GHC.Tc.Solver.Canonical.canEqCanLHS). Rewriting is always homogeneous. That is, the kind of the result of rewriting is always the same as the kind of the input, modulo zonking. More formally: - (F2) zonk(tcTypeKind(ty)) `eqType` tcTypeKind(xi)+ (F2) zonk(typeKind(ty)) `eqType` typeKind(xi) This invariant means that the kind of a rewritten type might not itself be rewritten. @@ -368,7 +384,7 @@ {-# INLINE rewrite_args_tc #-} rewrite_args_tc :: TyCon -- T- -> Maybe [Role] -- Nothing: ambient role is Nominal; all args are Nominal+ -> Maybe (Infinite Role) -- Nothing: ambient role is Nominal; all args are Nominal -- Otherwise: no assumptions; use roles provided -> [Type] -> RewriteM ArgsReductions -- See the commentary on rewrite_args@@ -389,16 +405,16 @@ -- NB: Those bangs there drop allocations in T9872{a,c,d} by 8%. {-# INLINE rewrite_args #-}-rewrite_args :: [TyCoBinder] -> Bool -- Binders, and True iff any of them are+rewrite_args :: [PiTyBinder] -> Bool -- Binders, and True iff any of them are -- named. -> Kind -> TcTyCoVarSet -- function kind; kind's free vars- -> Maybe [Role] -> [Type] -- these are in 1-to-1 correspondence+ -> Maybe (Infinite Role) -> [Type] -- these are in 1-to-1 correspondence -- Nothing: use all Nominal -> RewriteM ArgsReductions -- This function returns ArgsReductions (Reductions cos xis) res_co -- coercions: co_i :: ty_i ~ xi_i, at roles given -- types: xi_i--- coercion: res_co :: tcTypeKind(fun tys) ~N tcTypeKind(fun xis)+-- coercion: res_co :: typeKind(fun tys) ~N typeKind(fun xis) -- That is, the result coercion relates the kind of some function (whose kind is -- passed as the first parameter) instantiated at tys to the kind of that -- function instantiated at the xis. This is useful in keeping rewriting@@ -413,7 +429,7 @@ = case (orig_m_roles, any_named_bndrs) of (Nothing, False) -> rewrite_args_fast orig_tys _ -> rewrite_args_slow orig_binders orig_inner_ki orig_fvs orig_roles orig_tys- where orig_roles = fromMaybe (repeat Nominal) orig_m_roles+ where orig_roles = fromMaybe (Inf.repeat Nominal) orig_m_roles {-# INLINE rewrite_args_fast #-} -- | fast path rewrite_args, in which none of the binders are named and@@ -437,11 +453,11 @@ {-# INLINE rewrite_args_slow #-} -- | Slow path, compared to rewrite_args_fast, because this one must track -- a lifting context.-rewrite_args_slow :: [TyCoBinder] -> Kind -> TcTyCoVarSet- -> [Role] -> [Type]+rewrite_args_slow :: [PiTyBinder] -> Kind -> TcTyCoVarSet+ -> Infinite Role -> [Type] -> RewriteM ArgsReductions rewrite_args_slow binders inner_ki fvs roles tys- = do { rewritten_args <- zipWithM rw roles tys+ = do { rewritten_args <- zipWithM rw (Inf.toList roles) tys ; return (simplifyArgsWorker binders inner_ki fvs roles rewritten_args) } where {-# INLINE rw #-}@@ -487,10 +503,8 @@ | isTypeFamilyTyCon tc = rewrite_fam_app tc tys - -- For * a normal data type application- -- * data family application- -- we just recursively rewrite the arguments.- | otherwise+ | otherwise -- We just recursively rewrite the arguments.+ -- See Note [Do not rewrite newtypes] = rewrite_ty_con_app tc tys rewrite_one (FunTy { ft_af = vis, ft_mult = mult, ft_arg = ty1, ft_res = ty2 })@@ -587,9 +601,9 @@ = do { het_redn <- case tcSplitTyConApp_maybe fun_xi of Just (tc, xis) -> do { let tc_roles = tyConRolesRepresentational tc- arg_roles = dropList xis tc_roles+ arg_roles = Inf.dropList xis tc_roles ; ArgsReductions (Reductions arg_cos arg_xis) kind_co- <- rewrite_vector (tcTypeKind fun_xi) arg_roles arg_tys+ <- rewrite_vector (typeKind fun_xi) arg_roles arg_tys -- We start with a reduction of the form -- fun_co :: ty ~ T xi_1 ... xi_n@@ -606,9 +620,9 @@ app_co = case eq_rel of NomEq -> mkAppCos fun_co arg_cos ReprEq -> mkAppCos fun_co (map mkNomReflCo arg_tys)- `mkTcTransCo`- mkTcTyConAppCo Representational tc- (zipWith mkReflCo tc_roles xis ++ arg_cos)+ `mkTransCo`+ mkTyConAppCo Representational tc+ (zipWith mkReflCo (Inf.toList tc_roles) xis ++ arg_cos) ; return $ mkHetReduction@@ -616,7 +630,7 @@ kind_co } Nothing -> do { ArgsReductions redns kind_co- <- rewrite_vector (tcTypeKind fun_xi) (repeat Nominal) arg_tys+ <- rewrite_vector (typeKind fun_xi) (Inf.repeat Nominal) arg_tys ; return $ mkHetReduction (mkAppRedns fun_redn redns) kind_co } ; role <- getRole@@ -636,7 +650,7 @@ -- Rewrite a vector (list of arguments). rewrite_vector :: Kind -- of the function being applied to these arguments- -> [Role] -- If we're rewriting w.r.t. ReprEq, what roles do the+ -> Infinite Role -- If we're rewriting w.r.t. ReprEq, what roles do the -- args have? -> [Type] -- the args to rewrite -> RewriteM ArgsReductions@@ -650,7 +664,13 @@ fvs = tyCoVarsOfType ki {-# INLINE rewrite_vector #-} -{-++{- Note [Do not rewrite newtypes]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+We flirted with unwrapping newtypes in the rewriter -- see GHC.Tc.Solver.Canonical+Note [Unwrap newtypes first]. But that turned out to be a bad idea because+of recursive newtypes, as that Note says. So be careful if you re-add it!+ Note [Rewriting synonyms] ~~~~~~~~~~~~~~~~~~~~~~~~~~ Not expanding synonyms aggressively improves error messages, and@@ -1053,7 +1073,7 @@ -- | Like 'splitPiTys'' but comes with a 'Bool' which is 'True' iff there is at -- least one named binder.-split_pi_tys' :: Type -> ([TyCoBinder], Type, Bool)+split_pi_tys' :: Type -> ([PiTyBinder], Type, Bool) split_pi_tys' ty = split ty ty where -- put common cases first@@ -1064,20 +1084,20 @@ split _ (FunTy { ft_af = af, ft_mult = w, ft_arg = arg, ft_res = res }) = let -- See #19102 !(bs, ty, named) = split res res- in (Anon af (mkScaled w arg) : bs, ty, named)+ in (Anon (mkScaled w arg) af : bs, ty, named) split orig_ty ty | Just ty' <- coreView ty = split orig_ty ty' split orig_ty _ = ([], orig_ty, False) {-# INLINE split_pi_tys' #-} --- | Like 'tyConBindersTyCoBinders' but you also get a 'Bool' which is true iff+-- | Like 'tyConBindersPiTyBinders' but you also get a 'Bool' which is true iff -- there is at least one named binder.-ty_con_binders_ty_binders' :: [TyConBinder] -> ([TyCoBinder], Bool)+ty_con_binders_ty_binders' :: [TyConBinder] -> ([PiTyBinder], Bool) ty_con_binders_ty_binders' = foldr go ([], False) where go (Bndr tv (NamedTCB vis)) (bndrs, _) = (Named (Bndr tv vis) : bndrs, True) go (Bndr tv (AnonTCB af)) (bndrs, n)- = (Anon af (tymult (tyVarKind tv)) : bndrs, n)+ = (Anon (tymult (tyVarKind tv)) af : bndrs, n) {-# INLINE go #-} {-# INLINE ty_con_binders_ty_binders' #-}
compiler/GHC/Tc/TyCl.hs view
@@ -34,7 +34,8 @@ import GHC.Hs -import GHC.Tc.Errors.Types ( TcRnMessage(..), FixedRuntimeRepProvenance(..) )+import GHC.Tc.Errors.Types ( TcRnMessage(..), FixedRuntimeRepProvenance(..)+ , mkTcRnUnknownMessage, IllegalNewtypeReason (..) ) import GHC.Tc.TyCl.Build import GHC.Tc.Solver( pushLevelAndSolveEqualities, pushLevelAndSolveEqualitiesX , reportUnsolvedEqualities )@@ -98,11 +99,14 @@ import GHC.Utils.Constants (debugIsOn) import GHC.Utils.Misc +import Language.Haskell.Syntax.Basic (FieldLabelString(..))+ import Control.Monad+import Data.Foldable ( toList, traverse_ ) import Data.Functor.Identity import Data.List ( partition) import Data.List.NonEmpty ( NonEmpty(..) )-import qualified Data.Set as Set+import qualified Data.List.NonEmpty as NE import Data.Tuple( swap ) {-@@ -385,7 +389,7 @@ This treatment of type synonyms only applies to Haskell 98-style synonyms. General type functions can be recursive, and hence, appear in `alg_decls'. -The kind of an open type family is solely determinded by its kind signature;+The kind of an open type family is solely determined by its kind signature; hence, only kind signatures participate in the construction of the initial kind environment (as constructed by `inferInitialKind'). In fact, we ignore instances of families altogether in the following. However, we need to include@@ -406,21 +410,27 @@ 2. When checking a type/class declaration (in module GHC.Tc.TyCl), we come upon knowledge of the eventual tycon in bits and pieces, and we use a TcTyCon to record what we know before we are ready to build the- final TyCon.+ final TyCon. Here is the plan: - We first build a MonoTcTyCon, then generalise to a PolyTcTyCon- See Note [TcTyCon, MonoTcTyCon, and PolyTcTyCon] in GHC.Tc.Utils.TcType- Specifically:+ Step 1 (inferInitialKinds, inference only, skipped for checking):+ Make a MonoTcTyCon whose binders are TcTyVars,+ which may contain free unification variables - S1) In kcTyClGroup, we use checkInitialKinds to get the- utterly-final Kind of all TyCons in the group that- (a) have a kind signature or- (b) have a CUSK.- This produces a PolyTcTyCon, that is, a TcTyCon in which the binders- and result kind are full of TyVars (not TcTyVars). No unification- variables here; everything is in its final form.+ Step 2 (generaliseTcTyCon)+ Generalise that MonoTcTyCon to make a PolyTcTyCon+ Its binders are skolem TcTyVars, with accurate SkolemInfo - S2) In kcTyClGroup, we use inferInitialKinds to look over the+ Step 3 (tcTyClDecl)+ Typecheck the type and class decls to produce a final TyCon+ Its binders are final TyVars, not TcTyVars++ Note that a MonoTcTyCon can contain unification variables,+ but a PolyTcTyCon does not: only skolem TcTyVars. See+ Note [TcTyCon, MonoTcTyCon, and PolyTcTyCon] in GHC.Tc.Utils.TcType++ More details about /kind inference/:++ S1) In kcTyClGroup, we use inferInitialKinds to look over the declaration of any TyCon that lacks a kind signature or CUSK, to determine its "shape"; for example, the number of parameters, and any kind signatures.@@ -429,27 +439,30 @@ "mono" because it has not been been generalised, and its binders and result kind may have free unification variables. - S3) Still in kcTyClGroup, we use kcLTyClDecl to kind-check the+ S2) Still in kcTyClGroup, we use kcLTyClDecl to kind-check the body (class methods, data constructors, etc.) of each of these MonoTcTyCons, which has the effect of filling in the metavariables in the tycon's initial kind. - S4) Still in kcTyClGroup, we use generaliseTyClDecl to generalize- each MonoTcTyCon to get a PolyTcTyCon, with final TyVars in it,+ S3) Still in kcTyClGroup, we use generaliseTyClDecl to generalize+ each MonoTcTyCon to get a PolyTcTyCon, with skolem TcTyVars in it, and a final, fixed kind. - S5) Finally, back in TcTyClDecls, we extend the environment with+ S4) Finally, back in TcTyClDecls, we extend the environment with the PolyTcTyCons, and typecheck each declaration (regardless of kind signatures etc) to get final TyCon. - These TcTyCons are stored in the local environment in GHC.Tc.TyCl,- until the real full TyCons can be created during desugaring. A- desugared program should never have a TcTyCon.+ More details about /kind checking/ -3. A MonoTcTyCon can contain unification variables, but a PolyTcTyCon- does not: only skolem TcTyVars.+ S5) In kcTyClGroup, we use checkInitialKinds to get the+ utterly-final Kind of all TyCons in the group that+ (a) have a separate kind signature or+ (b) have a CUSK.+ This produces a PolyTcTyCon, that is, a TcTyCon in which the binders+ and result kind are full of TyVars (not TcTyVars). No unification+ variables here; everything is in its final form. -4. tyConScopedTyVars. A challenging piece in all of this is that we+3. tyConScopedTyVars. A challenging piece in all of this is that we end up taking three separate passes over every declaration: - one in inferInitialKind (this pass look only at the head, not the body) - one in kcTyClDecls (to kind-check the body)@@ -624,7 +637,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In 'kcTyClGroup', there is a missed opportunity to make kind inference work in a few more cases. The idea is analogous-to Note [Single function non-recursive binding special-case]:+to Note [Special case for non-recursive function bindings]: * If we have an SCC with a single decl, which is non-recursive, instead of creating a unification variable representing the@@ -642,7 +655,7 @@ Note [Don't process associated types in getInitialKind] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Previously, we processed associated types in the thing_inside in getInitialKind,-but this was wrong -- we want to do ATs sepearately.+but this was wrong -- we want to do ATs separately. The consequence for not doing it this way is #15142: class ListTuple (tuple :: Type) (as :: [(k, Type)]) where@@ -968,7 +981,7 @@ around even between minor released. By forbidding visible type application, we ensure users aren't caught unawares. -Go read Note [VarBndrs, TyCoVarBinders, TyConBinders, and visibility] in GHC.Core.TyCo.Rep.+Go read Note [VarBndrs, ForAllTyBinders, TyConBinders, and visibility] in GHC.Core.TyCo.Rep. The question for this Note is this: given a TyClDecl, how are its quantified type variables classified?@@ -982,7 +995,7 @@ 1. Inferred variables 2. Specified variables; in the left-to-right order in which the user wrote them, modified by scopedSort (see below)- to put them in depdendency order.+ to put them in dependency order. 3. Required variables before a top-level :: 4. All variables after a top-level :: @@ -1051,7 +1064,7 @@ ~~~~~~~~~~~~~~~~~~~ * For associated types we considered putting the class variables before the local variables, in a nod to the treatment for class- methods. But it got too compilicated; see #15592, comment:21ff.+ methods. But it got too complicated; see #15592, comment:21ff. * We rigidly require the ordering above, even though we could be much more permissive. Relevant musings are at@@ -1282,9 +1295,15 @@ , tcdDataDefn = HsDataDefn { dd_cons = cons } }) = unitNameEnv name (APromotionErr TyConPE) `plusNameEnv`- mkNameEnv [ (con, APromotionErr RecDataConPE)- | L _ con' <- cons+ mkNameEnv [ (con, APromotionErr conPE)+ | L _ con' <- toList cons , L _ con <- getConNames con' ]+ where+ -- In a "type data" declaration, the constructors are at the type level.+ -- See Note [Type data declarations] in GHC.Rename.Module.+ conPE+ | isTypeDataDefnCons cons = TyConPE+ | otherwise = RecDataConPE mk_prom_err_env decl = unitNameEnv (tcdName decl) (APromotionErr TyConPE)@@ -1352,14 +1371,13 @@ getInitialKind strategy (DataDecl { tcdLName = L _ name , tcdTyVars = ktvs- , tcdDataDefn = HsDataDefn { dd_kindSig = m_sig- , dd_ND = new_or_data } })- = do { let flav = newOrDataToFlavour new_or_data+ , tcdDataDefn = HsDataDefn { dd_kindSig = m_sig, dd_cons = cons } })+ = do { let flav = newOrDataToFlavour (dataDefnConsNewOrData cons) ctxt = DataKindCtxt name ; tc <- kcDeclHeader strategy name flav ktvs $ case m_sig of Just ksig -> TheKind <$> tcLHsKindSig ctxt ksig- Nothing -> return $ dataDeclDefaultResultKind strategy new_or_data+ Nothing -> return $ dataDeclDefaultResultKind strategy (dataDefnConsNewOrData cons) ; return [tc] } getInitialKind InitialKindInfer (FamDecl { tcdFam = decl })@@ -1567,8 +1585,7 @@ -- - In this function, those TcTyVars are unified with other kind variables during -- kind inference (see [How TcTyCons work]) -kcTyClDecl (DataDecl { tcdLName = (L _ _name), tcdDataDefn = defn }) tycon- | HsDataDefn { dd_ctxt = ctxt, dd_cons = cons, dd_ND = new_or_data } <- defn+kcTyClDecl (DataDecl { tcdLName = (L _ _name), tcdDataDefn = HsDataDefn { dd_ctxt = ctxt, dd_cons = cons } }) tycon = tcExtendNameTyVarEnv (tcTyConScopedTyVars tycon) $ -- NB: binding these tyvars isn't necessary for GADTs, but it does no -- harm. For GADTs, each data con brings its own tyvars into scope,@@ -1576,7 +1593,7 @@ -- (conceivably) shadowed. do { traceTc "kcTyClDecl" (ppr tycon $$ ppr (tyConTyVars tycon) $$ ppr (tyConResKind tycon)) ; _ <- tcHsContext ctxt- ; kcConDecls new_or_data (tyConResKind tycon) cons+ ; kcConDecls (dataDefnConsNewOrData cons) (tyConResKind tycon) cons } kcTyClDecl (SynDecl { tcdLName = L _ _name, tcdRhs = rhs }) tycon@@ -1631,14 +1648,14 @@ RecConGADT (L _ flds) _ -> kcConArgTys new_or_data res_kind $ map (hsLinear . cd_fld_type . unLoc) flds -kcConDecls :: NewOrData+kcConDecls :: Foldable f+ => NewOrData -> TcKind -- The result kind signature -- Used only in H98 case- -> [LConDecl GhcRn] -- The data constructors+ -> f (LConDecl GhcRn) -- The data constructors -> TcM () -- See Note [kcConDecls: kind-checking data type decls]-kcConDecls new_or_data tc_res_kind cons- = mapM_ (wrapLocMA_ (kcConDecl new_or_data tc_res_kind)) cons+kcConDecls new_or_data tc_res_kind = traverse_ (wrapLocMA_ (kcConDecl new_or_data tc_res_kind)) -- Kind check a data constructor. In additional to the data constructor, -- we also need to know about whether or not its corresponding type was@@ -1655,7 +1672,7 @@ kcConDecl new_or_data tc_res_kind (ConDeclH98 { con_name = name, con_ex_tvs = ex_tvs , con_mb_cxt = ex_ctxt, con_args = args })- = addErrCtxt (dataConCtxt [name]) $+ = addErrCtxt (dataConCtxt (NE.singleton name)) $ discardResult $ bindExplicitTKBndrs_Tv ex_tvs $ do { _ <- tcHsContext ex_ctxt@@ -1785,7 +1802,10 @@ APromotionErr is only used for DataCons, and only used during type checking in tcTyClGroup. +The same restriction applies constructors in to "type data" declarations.+See Note [Type data declarations] in GHC.Rename.Module. + ************************************************************************ * * \subsection{Type checking}@@ -1817,7 +1837,7 @@ "knot". So we aren't allowed to look at the TyCon T itself; we are only allowed to put it (lazily) in the returned structures. But when kind-checking the RHS of T's decl, we *do* need to know T's kind (so-that we can correctly elaboarate (T k f a). How can we get T's kind+that we can correctly elaborate (T k f a). How can we get T's kind without looking at T? Delicate answer: during tcTyClDecl, we extend *Global* env with T -> ATyCon (the (not yet built) final TyCon for T)@@ -1892,8 +1912,7 @@ is produced by processing the return kind in etaExpandAlgTyCon, called in tcDataDefn. - See also Note [TyConBinders for the result kind signatures of a data type]- in GHC.Tc.Gen.HsType.+ See also Note [splitTyConKind] in GHC.Tc.Gen.HsType. DT4 Datatype return kind restriction: A data type return kind must end in a type that, after type-synonym expansion, yields `TYPE LiftedRep`. By@@ -2312,7 +2331,7 @@ data family F a :: UnliftedType data instance F Int = TInt - The ommission of a kind signature for `F` should not mean a result kind+ The omission of a kind signature for `F` should not mean a result kind of `Type` (and thus a kind error) here. * STEP 2: No change to kcTyClDecl.@@ -2360,7 +2379,7 @@ , HsDataDefn { dd_derivs = derivs } <- dataDefn = [ DerivInfo { di_rep_tc = tycon , di_scoped_tvs =- if isFunTyCon tycon || isPrimTyCon tycon+ if isPrimTyCon tycon then [] -- no tyConTyVars else mkTyVarNamePairs (tyConTyVars tycon) , di_clauses = derivs@@ -2414,15 +2433,15 @@ -> TcM Class tcClassDecl1 roles_info class_name hs_ctxt meths fundeps sigs ats at_defs = fixM $ \ clas -> -- We need the knot because 'clas' is passed into tcClassATs- bindTyClTyVars class_name $ \ binders res_kind ->+ bindTyClTyVars class_name $ \ tc_bndrs res_kind -> do { checkClassKindSig res_kind- ; traceTc "tcClassDecl 1" (ppr class_name $$ ppr binders)+ ; traceTc "tcClassDecl 1" (ppr class_name $$ ppr tc_bndrs) ; let tycon_name = class_name -- We use the same name roles = roles_info tycon_name -- for TyCon and Class ; (ctxt, fds, sig_stuff, at_stuff)- <- pushLevelAndSolveEqualities skol_info binders $- -- The (binderVars binders) is needed bring into scope the+ <- pushLevelAndSolveEqualities skol_info tc_bndrs $+ -- The (binderVars tc_bndrs) is needed bring into scope the -- skolems bound by the class decl header (#17841) do { ctxt <- tcHsContext hs_ctxt ; fds <- mapM (addLocMA tc_fundep) fundeps@@ -2447,9 +2466,10 @@ -- any unfilled coercion variables unless there is such an error -- The zonk also squeeze out the TcTyCons, and converts -- Skolems to tyvars.- ; ze <- mkEmptyZonkEnv NoFlexi- ; ctxt <- zonkTcTypesToTypesX ze ctxt- ; sig_stuff <- mapM (zonkTcMethInfoToMethInfoX ze) sig_stuff+ ; ze <- mkEmptyZonkEnv NoFlexi+ ; (ze, bndrs) <- zonkTyVarBindersX ze tc_bndrs -- From TcTyVars to TyVars+ ; ctxt <- zonkTcTypesToTypesX ze ctxt+ ; sig_stuff <- mapM (zonkTcMethInfoToMethInfoX ze) sig_stuff -- ToDo: do we need to zonk at_stuff? -- TODO: Allow us to distinguish between abstract class,@@ -2471,8 +2491,8 @@ | otherwise = Just (ctxt, at_stuff, sig_stuff, mindef) - ; clas <- buildClass class_name binders roles fds body- ; traceTc "tcClassDecl" (ppr fundeps $$ ppr binders $$+ ; clas <- buildClass class_name bndrs roles fds body+ ; traceTc "tcClassDecl" (ppr fundeps $$ ppr bndrs $$ ppr fds) ; return clas } where@@ -2540,7 +2560,7 @@ = return Nothing -- No default declaration tcDefaultAssocDecl _ (d1:_:_)- = failWithTc (TcRnUnknownMessage $ mkPlainError noHints $+ = failWithTc (mkTcRnUnknownMessage $ mkPlainError noHints $ text "More than one default declaration for" <+> ppr (tyFamInstDeclName (unLoc d1))) @@ -2586,7 +2606,7 @@ ]) ; let subst = case traverse getTyVar_maybe pats of Just cpt_tvs -> zipTvSubst cpt_tvs (mkTyVarTys fam_tvs)- Nothing -> emptyTCvSubst+ Nothing -> emptySubst -- The Nothing case can only be reached in invalid -- associated type family defaults. In such cases, we -- simply create an empty substitution and let GHC fall@@ -2701,7 +2721,7 @@ , fdResultSig = L _ sig , fdInjectivityAnn = inj }) | DataFamily <- fam_info- = bindTyClTyVarsAndZonk tc_name $ \ binders res_kind -> do+ = bindTyClTyVarsAndZonk tc_name $ \ tc_bndrs res_kind -> do { traceTc "tcFamDecl1 data family:" (ppr tc_name) ; checkFamFlag tc_name @@ -2718,8 +2738,8 @@ -- See also Note [Datatype return kinds] ; checkDataKindSig DataFamilySort res_kind ; tc_rep_name <- newTyConRepName tc_name- ; let inj = Injective $ replicate (length binders) True- tycon = mkFamilyTyCon tc_name binders+ ; let inj = Injective $ replicate (length tc_bndrs) True+ tycon = mkFamilyTyCon tc_name tc_bndrs res_kind (resultVariableName sig) (DataFamilyTyCon tc_rep_name)@@ -2727,12 +2747,12 @@ ; return tycon } | OpenTypeFamily <- fam_info- = bindTyClTyVarsAndZonk tc_name $ \ binders res_kind -> do+ = bindTyClTyVarsAndZonk tc_name $ \ tc_bndrs res_kind -> do { traceTc "tcFamDecl1 open type family:" (ppr tc_name) ; checkFamFlag tc_name- ; inj' <- tcInjectivity binders inj+ ; inj' <- tcInjectivity tc_bndrs inj ; checkResultSigFlag tc_name sig -- check after injectivity for better errors- ; let tycon = mkFamilyTyCon tc_name binders res_kind+ ; let tycon = mkFamilyTyCon tc_name tc_bndrs res_kind (resultVariableName sig) OpenSynFamilyTyCon parent inj' ; return tycon }@@ -2743,10 +2763,10 @@ do { traceTc "tcFamDecl1 Closed type family:" (ppr tc_name) -- the variables in the header scope only over the injectivity -- declaration but this is not involved here- ; (inj', binders, res_kind)- <- bindTyClTyVarsAndZonk tc_name $ \ binders res_kind ->- do { inj' <- tcInjectivity binders inj- ; return (inj', binders, res_kind) }+ ; (inj', tc_bndrs, res_kind)+ <- bindTyClTyVarsAndZonk tc_name $ \ tc_bndrs res_kind ->+ do { inj' <- tcInjectivity tc_bndrs inj+ ; return (inj', tc_bndrs, res_kind) } ; checkFamFlag tc_name -- make sure we have -XTypeFamilies ; checkResultSigFlag tc_name sig@@ -2755,14 +2775,14 @@ -- but eqns might be empty in the Just case as well ; case mb_eqns of Nothing ->- return $ mkFamilyTyCon tc_name binders res_kind+ return $ mkFamilyTyCon tc_name tc_bndrs res_kind (resultVariableName sig) AbstractClosedSynFamilyTyCon parent inj' Just eqns -> do { -- Process the equations, creating CoAxBranches- ; let tc_fam_tc = mkTcTyCon tc_name binders res_kind+ ; let tc_fam_tc = mkTcTyCon tc_name tc_bndrs res_kind noTcTyConScopedTyVars False {- this doesn't matter here -} ClosedTypeFamilyFlavour@@ -2781,7 +2801,7 @@ | null eqns = Nothing -- mkBranchedCoAxiom fails on empty list | otherwise = Just (mkBranchedCoAxiom co_ax_name fam_tc branches) - fam_tc = mkFamilyTyCon tc_name binders res_kind (resultVariableName sig)+ fam_tc = mkFamilyTyCon tc_name tc_bndrs res_kind (resultVariableName sig) (ClosedSynFamilyTyCon mb_co_ax) parent inj' -- We check for instance validity later, when doing validity@@ -2827,7 +2847,7 @@ do { let tvs = binderVars tcbs ; dflags <- getDynFlags ; checkTc (xopt LangExt.TypeFamilyDependencies dflags)- (TcRnUnknownMessage $ mkPlainError noHints $+ (mkTcRnUnknownMessage $ mkPlainError noHints $ text "Illegal injectivity annotation" $$ text "Use TypeFamilyDependencies to allow this") ; inj_tvs <- mapM (tcLookupTyVar . unLoc) lInjNames@@ -2842,10 +2862,10 @@ tcTySynRhs :: RolesInfo -> Name -> LHsType GhcRn -> TcM TyCon tcTySynRhs roles_info tc_name hs_ty- = bindTyClTyVars tc_name $ \ binders res_kind ->+ = bindTyClTyVars tc_name $ \ tc_bndrs res_kind -> do { env <- getLclEnv ; traceTc "tc-syn" (ppr tc_name $$ ppr (tcl_env env))- ; rhs_ty <- pushLevelAndSolveEqualities skol_info binders $+ ; rhs_ty <- pushLevelAndSolveEqualities skol_info tc_bndrs $ tcCheckLHsType hs_ty (TheKind res_kind) -- See Note [Error on unconstrained meta-variables] in GHC.Tc.Utils.TcMType@@ -2859,11 +2879,11 @@ , ppr rhs_ty ] ) } ; doNotQuantifyTyVars dvs mk_doc - ; ze <- mkEmptyZonkEnv NoFlexi- ; (ze, binders) <- zonkTyVarBindersX ze binders- ; rhs_ty <- zonkTcTypeToTypeX ze rhs_ty+ ; ze <- mkEmptyZonkEnv NoFlexi+ ; (ze, bndrs) <- zonkTyVarBindersX ze tc_bndrs+ ; rhs_ty <- zonkTcTypeToTypeX ze rhs_ty ; let roles = roles_info tc_name- ; return (buildSynTyCon tc_name binders res_kind roles rhs_ty) }+ ; return (buildSynTyCon tc_name bndrs res_kind roles rhs_ty) } where skol_info = TyConSkol TypeSynonymFlavour tc_name @@ -2871,7 +2891,7 @@ -> HsDataDefn GhcRn -> TcM (TyCon, [DerivInfo]) -- NB: not used for newtype/data instances (whether associated or not) tcDataDefn err_ctxt roles_info tc_name- (HsDataDefn { dd_ND = new_or_data, dd_cType = cType+ (HsDataDefn { dd_cType = cType , dd_ctxt = ctxt , dd_kindSig = mb_ksig -- Already in tc's kind -- via inferInitialKinds@@ -2883,12 +2903,12 @@ -- - for H98 constructors only, the ConDecl -- But it does no harm to bring them into scope -- over GADT ConDecls as well; and it's awkward not to- do { gadt_syntax <- dataDeclChecks tc_name new_or_data ctxt cons+ do { gadt_syntax <- dataDeclChecks tc_name ctxt cons ; tcg_env <- getGblEnv ; let hsc_src = tcg_src tcg_env ; unless (mk_permissive_kind hsc_src cons) $- checkDataKindSig (DataDeclSort new_or_data) res_kind+ checkDataKindSig (DataDeclSort (dataDefnConsNewOrData cons)) res_kind ; stupid_tc_theta <- pushLevelAndSolveEqualities skol_info tc_bndrs $ tcHsContext ctxt@@ -2916,8 +2936,7 @@ ; res_kind <- zonkTcTypeToTypeX ze res_kind ; tycon <- fixM $ \ rec_tycon -> do- { data_cons <- tcConDecls new_or_data DDataType rec_tycon- tc_bndrs res_kind cons+ { data_cons <- tcConDecls DDataType rec_tycon tc_bndrs res_kind cons ; tc_rhs <- mk_tc_rhs hsc_src rec_tycon data_cons ; tc_rep_nm <- newTyConRepName tc_name @@ -2941,7 +2960,7 @@ ; return (tycon, [deriv_info]) } where skol_info = TyConSkol flav tc_name- flav = newOrDataToFlavour new_or_data+ flav = newOrDataToFlavour (dataDefnConsNewOrData cons) -- Abstract data types in hsig files can have arbitrary kinds, -- because they may be implemented by type synonyms@@ -2951,23 +2970,22 @@ -- so one could not have, say, a data family instance in an hsig file that -- has kind `Bool`. Therefore, this check need only occur in the code that -- typechecks data type declarations.- mk_permissive_kind HsigFile [] = True+ mk_permissive_kind HsigFile (DataTypeCons _ []) = True mk_permissive_kind _ _ = False -- In an hs-boot or a signature file, -- a 'data' declaration with no constructors -- indicates a nominally distinct abstract data type.- mk_tc_rhs (isHsBootOrSig -> True) _ []+ mk_tc_rhs (isHsBootOrSig -> True) _ (DataTypeCons _ []) = return AbstractTyCon - mk_tc_rhs _ tycon data_cons- = case new_or_data of- DataType -> return $+ mk_tc_rhs _ tycon data_cons = case data_cons of+ DataTypeCons is_type_data data_cons -> return $ mkLevPolyDataTyConRhs (isFixedRuntimeRepKind (tyConResKind tycon))+ is_type_data data_cons- NewType -> assert (not (null data_cons)) $- mkNewTyConRhs tc_name tycon (head data_cons)+ NewTypeCon data_con -> mkNewTyConRhs tc_name tycon data_con ------------------------- kcTyFamInstEqn :: TcTyCon -> LTyFamInstEqn GhcRn -> TcM ()@@ -3083,7 +3101,7 @@ So, we use bindOuterFamEqnTKBndrs (which does not create an implication for the telescope), and generalise over /all/ the variables in the LHS,-without treating the explicitly-quantifed ones specially. Wrinkles:+without treating the explicitly-quantified ones specially. Wrinkles: - When generalising, include the explicit user-specified forall'd variables, so that we get an error from Validity.checkFamPatBinders@@ -3310,26 +3328,20 @@ ************************************************************************ -} -dataDeclChecks :: Name -> NewOrData- -> Maybe (LHsContext GhcRn) -> [LConDecl GhcRn]+dataDeclChecks :: Name+ -> Maybe (LHsContext GhcRn) -> DataDefnCons (LConDecl GhcRn) -> TcM Bool-dataDeclChecks tc_name new_or_data mctxt cons+dataDeclChecks tc_name mctxt cons = do { let stupid_theta = fromMaybeContext mctxt -- Check that we don't use GADT syntax in H98 world ; gadtSyntax_ok <- xoptM LangExt.GADTSyntax- ; let gadt_syntax = consUseGadtSyntax cons+ ; let gadt_syntax = anyLConIsGadt cons ; checkTc (gadtSyntax_ok || not gadt_syntax) (badGadtDecl tc_name) -- Check that the stupid theta is empty for a GADT-style declaration. -- See Note [The stupid context] in GHC.Core.DataCon. ; checkTc (null stupid_theta || not gadt_syntax) (badStupidTheta tc_name) - -- Check that a newtype has exactly one constructor- -- Do this before checking for empty data decls, so that- -- we don't suggest -XEmptyDataDecls for newtypes- ; checkTc (new_or_data == DataType || isSingleton cons)- (newtypeConError tc_name (length cons))- -- Check that there's at least one condecl, -- or else we're reading an hs-boot file, or -XEmptyDataDecls ; empty_data_decls <- xoptM LangExt.EmptyDataDecls@@ -3340,12 +3352,6 @@ ------------------------------------consUseGadtSyntax :: [LConDecl GhcRn] -> Bool-consUseGadtSyntax (L _ (ConDeclGADT {}) : _) = True-consUseGadtSyntax _ = False- -- All constructors have same shape------------------------------------- data DataDeclInfo = DDataType -- data T a b = T1 a | T2 b | DDataInstance -- data instance D [a] = D1 a | D2@@ -3358,19 +3364,30 @@ mkTyVarTys (binderVars tc_bndrs) DDataInstance header_ty -> header_ty -tcConDecls :: NewOrData- -> DataDeclInfo+-- We use `concatMapDataDefnConsTcM` here, since the following is illegal:+-- @newtype T a where T1, T2 :: a -> T a@+-- It would be represented as a single 'ConDeclGadt' with multiple names, which is valid for 'data', but not 'newtype'.+-- So when 'tcConDecl' expands the 'ConDecl' per each name it has, if we are type-checking a 'newtype' declaration, we+-- must fail if it returns more than one.+tcConDecls :: DataDeclInfo -> KnotTied TyCon -- Representation TyCon -> [TcTyConBinder] -- Binders of representation TyCon -> TcKind -- Result kind- -> [LConDecl GhcRn] -> TcM [DataCon]-tcConDecls new_or_data dd_info rep_tycon tmpl_bndrs res_kind- = concatMapM $ addLocMA $- tcConDecl new_or_data dd_info rep_tycon tmpl_bndrs res_kind- (mkTyConTagMap rep_tycon)+ -> DataDefnCons (LConDecl GhcRn) -> TcM (DataDefnCons DataCon)+tcConDecls dd_info rep_tycon tmpl_bndrs res_kind+ = concatMapDataDefnConsTcM (tyConName rep_tycon) $ \ new_or_data ->+ addLocMA $ tcConDecl new_or_data dd_info rep_tycon tmpl_bndrs res_kind (mkTyConTagMap rep_tycon) -- mkTyConTagMap: it's important that we pay for tag allocation here, -- once per TyCon. See Note [Constructor tag allocation], fixes #14657 +-- 'concatMap' for 'DataDefnCons', but fail if the given function returns multiple values and the argument is a 'NewTypeCon'.+concatMapDataDefnConsTcM :: Name -> (NewOrData -> a -> TcM (NonEmpty b)) -> DataDefnCons a -> TcM (DataDefnCons b)+concatMapDataDefnConsTcM name f = \ case+ NewTypeCon a -> f NewType a >>= \ case+ b:|[] -> pure (NewTypeCon b)+ bs -> failWithTc $ newtypeConError name (length bs)+ DataTypeCons is_type_data as -> DataTypeCons is_type_data <$> concatMapM (fmap toList . f DataType) as+ tcConDecl :: NewOrData -> DataDeclInfo -> KnotTied TyCon -- Representation tycon. Knot-tied!@@ -3378,14 +3395,14 @@ -> TcKind -- Result kind -> NameEnv ConTag -> ConDecl GhcRn- -> TcM [DataCon]+ -> TcM (NonEmpty DataCon) tcConDecl new_or_data dd_info rep_tycon tc_bndrs res_kind tag_map (ConDeclH98 { con_name = lname@(L _ name) , con_ex_tvs = explicit_tkv_nms , con_mb_cxt = hs_ctxt , con_args = hs_args })- = addErrCtxt (dataConCtxt [lname]) $+ = addErrCtxt (dataConCtxt (NE.singleton lname)) $ do { -- NB: the tyvars from the declaration header are in scope -- Get hold of the existential type variables@@ -3414,8 +3431,8 @@ ; let tc_tvs = binderVars tc_bndrs fake_ty = mkSpecForAllTys tc_tvs $ mkInvisForAllTys exp_tvbndrs $- mkPhiTy ctxt $- mkVisFunTys arg_tys $+ tcMkPhiTy ctxt $+ tcMkScaledFunTys arg_tys $ unitTy -- That type is a lie, of course. (It shouldn't end in ()!) -- And we could construct a proper result type from the info@@ -3473,7 +3490,7 @@ -- constructor type signature into the data constructor; -- that way checkValidDataCon can complain if it's wrong. - ; return [dc] }+ ; return (NE.singleton dc) } tcConDecl new_or_data dd_info rep_tycon tc_bndrs _res_kind tag_map -- NB: don't use res_kind here, as it's ill-scoped. Instead,@@ -3484,7 +3501,7 @@ , con_res_ty = hs_res_ty }) = addErrCtxt (dataConCtxt names) $ do { traceTc "tcConDecl 1 gadt" (ppr names)- ; let (L _ name : _) = names+ ; let L _ name :| _ = names ; skol_info <- mkSkolemInfo (DataConSkol name) ; (tclvl, wanted, (outer_bndrs, (ctxt, arg_tys, res_ty, field_lbls, stricts))) <- pushLevelAndSolveEqualitiesX "tcConDecl:GADT" $@@ -3520,8 +3537,8 @@ ; tkvs <- kindGeneralizeAll skol_info (mkInvisForAllTys outer_tv_bndrs $- mkPhiTy ctxt $- mkVisFunTys arg_tys $+ tcMkPhiTy ctxt $+ tcMkScaledFunTys arg_tys $ res_ty) ; traceTc "tcConDecl:GADT" (ppr names $$ ppr res_ty $$ ppr tkvs) ; reportUnsolvedEqualities skol_info tkvs tclvl wanted@@ -3799,7 +3816,7 @@ [InvisTVBinder], -- The constructor's rejigged, user-written -- type variables [EqSpec], -- Equality predicates- TCvSubst) -- Substitution to apply to argument types+ Subst) -- Substitution to apply to argument types -- We don't check that the TyCon given in the ResTy is -- the same as the parent tycon, because checkValidDataCon will do it -- NB: All arguments may potentially be knot-tied@@ -3830,10 +3847,12 @@ -- since the dcUserTyVarBinders invariant guarantees that the -- substitution has *all* the tyvars in its domain. -- See Note [DataCon user type variable binders] in GHC.Core.DataCon.- subst_user_tvs = mapVarBndrs (getTyVar "rejigConRes" . substTyVar arg_subst)+ subst_user_tvs = mapVarBndrs (substTyVarToTyVar arg_subst) substed_tvbndrs = subst_user_tvs dc_tvbndrs - substed_eqs = map (substEqSpec arg_subst) raw_eqs+ substed_eqs = [ mkEqSpec (substTyVarToTyVar arg_subst tv)+ (substTy arg_subst ty)+ | (tv,ty) <- raw_eqs ] in (univ_tvs, substed_ex_tvs, substed_tvbndrs, substed_eqs, arg_subst) @@ -3848,7 +3867,7 @@ -- albeit bogus, relying on checkValidDataCon to check the -- bad-result-type error before seeing that the other fields look odd -- See Note [rejigConRes]- = (tc_tvs, dc_tvs `minusList` tc_tvs, dc_tvbndrs, [], emptyTCvSubst)+ = (tc_tvs, dc_tvs `minusList` tc_tvs, dc_tvbndrs, [], emptySubst) where dc_tvs = binderVars dc_tvbndrs tc_tvs = binderVars tc_tvbndrs@@ -3996,28 +4015,28 @@ -- See Note [mkGADTVars]. mkGADTVars :: [TyVar] -- ^ The tycon vars -> [TyVar] -- ^ The datacon vars- -> TCvSubst -- ^ The matching between the template result type+ -> Subst -- ^ The matching between the template result type -- and the actual result type -> ( [TyVar]- , [EqSpec]- , TCvSubst ) -- ^ The univ. variables, the GADT equalities,+ , [(TyVar,Type)] -- The un-substituted eq-spec+ , Subst ) -- ^ The univ. variables, the GADT equalities, -- and a subst to apply to the GADT equalities -- and existentials. mkGADTVars tmpl_tvs dc_tvs subst = choose [] [] empty_subst empty_subst tmpl_tvs where in_scope = mkInScopeSet (mkVarSet tmpl_tvs `unionVarSet` mkVarSet dc_tvs)- `unionInScope` getTCvInScope subst- empty_subst = mkEmptyTCvSubst in_scope+ `unionInScope` getSubstInScope subst+ empty_subst = mkEmptySubst in_scope - choose :: [TyVar] -- accumulator of univ tvs, reversed- -> [EqSpec] -- accumulator of GADT equalities, reversed- -> TCvSubst -- template substitution- -> TCvSubst -- res. substitution+ choose :: [TyVar] -- accumulator of univ tvs, reversed+ -> [(TyVar,Type)] -- accumulator of GADT equalities, reversed+ -> Subst -- template substitution+ -> Subst -- res. substitution -> [TyVar] -- template tvs (the univ tvs passed in) -> ( [TyVar] -- the univ_tvs- , [EqSpec] -- GADT equalities- , TCvSubst ) -- a substitution to fix kinds in ex_tvs+ , [(TyVar,Type)] -- GADT equalities+ , Subst ) -- a substitution to fix kinds in ex_tvs choose univs eqs _t_sub r_sub [] = (reverse univs, reverse eqs, r_sub)@@ -4028,6 +4047,8 @@ | not (r_tv `elem` univs) , tyVarKind r_tv `eqType` (substTy t_sub (tyVarKind t_tv)) -> -- simple, well-kinded variable substitution.+ -- the name of the universal comes from the result of the ctor+ -- see (R2) of Note [DataCon user type variable binders] in GHC.Core.DataCon choose (r_tv:univs) eqs (extendTvSubst t_sub t_tv r_ty') (extendTvSubst r_sub r_tv r_ty')@@ -4037,13 +4058,19 @@ r_ty' = mkTyVarTy r_tv1 -- Not a simple substitution: make an equality predicate- _ -> choose (t_tv':univs) (mkEqSpec t_tv' r_ty : eqs)+ -- the name of the universal comes from the datatype header+ -- see (R2) of Note [DataCon user type variable binders] in GHC.Core.DataCon+ _ -> choose (t_tv':univs) eqs' (extendTvSubst t_sub t_tv (mkTyVarTy t_tv')) -- We've updated the kind of t_tv, -- so add it to t_sub (#14162) r_sub t_tvs where- t_tv' = updateTyVarKind (substTy t_sub) t_tv+ tv_kind = tyVarKind t_tv+ tv_kind' = substTy t_sub tv_kind+ t_tv' = setTyVarKind t_tv tv_kind'+ eqs' | isConstraintLikeKind (typeKind tv_kind') = eqs+ | otherwise = (t_tv', r_ty) : eqs | otherwise = pprPanic "mkGADTVars" (ppr tmpl_tvs $$ ppr subst)@@ -4236,7 +4263,7 @@ ; ClosedSynFamilyTyCon Nothing -> return () ; AbstractClosedSynFamilyTyCon -> do { hsBoot <- tcIsHsBootOrSig- ; checkTc hsBoot $ TcRnUnknownMessage $ mkPlainError noHints $+ ; checkTc hsBoot $ mkTcRnUnknownMessage $ mkPlainError noHints $ text "You may define an abstract closed type family" $$ text "only in a .hs-boot file" } ; DataFamilyTyCon {} -> return ()@@ -4271,7 +4298,7 @@ -- The order of these equivalence classes might conceivably (non-deterministically) -- depend on the result of this comparison, but that just affects the order in which -- fields are checked for compatibility. It will not affect the compiled binary.- cmp_fld (f1,_) (f2,_) = flLabel f1 `uniqCompareFS` flLabel f2+ cmp_fld (f1,_) (f2,_) = field_label (flLabel f1) `uniqCompareFS` field_label (flLabel f2) get_fields con = dataConFieldLabels con `zip` repeat con -- dataConFieldLabels may return the empty list, which is fine @@ -4314,7 +4341,7 @@ checkPartialRecordField all_cons fld = setSrcSpan loc $ warnIf (not is_exhaustive && not (startsWithUnderscore occ_name))- (TcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag Opt_WarnPartialFields) noHints $+ (mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag Opt_WarnPartialFields) noHints $ sep [text "Use of partial record field selector" <> colon, nest 2 $ quotes (ppr occ_name)]) where@@ -4326,9 +4353,7 @@ is_exhaustive = all (dataConCannotMatch inst_tys) cons_without_field con1 = assert (not (null cons_with_field)) $ head cons_with_field- (univ_tvs, _, eq_spec, _, _, _) = dataConFullSig con1- eq_subst = mkTvSubstPrs (map eqSpecPair eq_spec)- inst_tys = substTyVars eq_subst univ_tvs+ inst_tys = dataConResRepTyArgs con1 checkFieldCompat :: FieldLabelString -> DataCon -> DataCon -> Type -> Type -> Type -> Type -> TcM ()@@ -4343,7 +4368,7 @@ checkValidDataCon :: DynFlags -> Bool -> TyCon -> DataCon -> TcM () checkValidDataCon dflags existential_ok tc con = setSrcSpan con_loc $- addErrCtxt (dataConCtxt [L (noAnnSrcSpan con_loc) con_name]) $+ addErrCtxt (dataConCtxt (NE.singleton (L (noAnnSrcSpan con_loc) con_name))) $ do { let tc_tvs = tyConTyVars tc res_ty_tmpl = mkFamilyTyConApp tc (mkTyVarTys tc_tvs) arg_tys = dataConOrigArgTys con@@ -4351,15 +4376,15 @@ ; traceTc "checkValidDataCon" (vcat [ ppr con, ppr tc, ppr tc_tvs- , ppr res_ty_tmpl <+> dcolon <+> ppr (tcTypeKind res_ty_tmpl)- , ppr orig_res_ty <+> dcolon <+> ppr (tcTypeKind orig_res_ty)])+ , ppr res_ty_tmpl <+> dcolon <+> ppr (typeKind res_ty_tmpl)+ , ppr orig_res_ty <+> dcolon <+> ppr (typeKind orig_res_ty)]) -- Check that the return type of the data constructor -- matches the type constructor; eg reject this: -- data T a where { MkT :: Bogus a } -- It's important to do this first:- -- see Note [rejigCon+ -- see Note [rejigConRes] -- and c.f. Note [Check role annotations in a second pass] -- Check that the return type of the data constructor is an instance@@ -4414,21 +4439,28 @@ ; checkTc (existential_ok || isVanillaDataCon con) (badExistential con) - -- Check that UNPACK pragmas and bangs work out- -- E.g. reject data T = MkT {-# UNPACK #-} Int -- No "!"- -- data T = MkT {-# UNPACK #-} !a -- Can't unpack+ -- Check that the only constraints in signatures of constructors+ -- in a "type data" declaration are equality constraints.+ -- See Note [Type data declarations] in GHC.Rename.Module,+ -- restriction (R4).+ ; when (isTypeDataCon con) $+ checkTc (all isEqPred (dataConOtherTheta con))+ (TcRnConstraintInKind (dataConRepType con))+ ; hsc_env <- getTopEnv ; let check_bang :: Type -> HsSrcBang -> HsImplBang -> Int -> TcM () check_bang orig_arg_ty bang rep_bang n | HsSrcBang _ _ SrcLazy <- bang , not (bang_opt_strict_data bang_opts)- = addErrTc $ TcRnUnknownMessage $ mkPlainError noHints $+ = addErrTc $ mkTcRnUnknownMessage $ mkPlainError noHints $ (bad_bang n (text "Lazy annotation (~) without StrictData")) + -- Warn about UNPACK without "!"+ -- e.g. data T = MkT {-# UNPACK #-} Int | HsSrcBang _ want_unpack strict_mark <- bang , isSrcUnpacked want_unpack, not (is_strict strict_mark) , not (isUnliftedType orig_arg_ty)- = addDiagnosticTc $ TcRnUnknownMessage $+ = addDiagnosticTc $ mkTcRnUnknownMessage $ mkPlainDiagnostic WarningWithoutFlag noHints (bad_bang n (text "UNPACK pragma lacks '!'")) -- Warn about a redundant ! on an unlifted type@@ -4437,13 +4469,20 @@ , isUnliftedType orig_arg_ty = addDiagnosticTc $ TcRnBangOnUnliftedType orig_arg_ty + -- Warn about a ~ on an unlifted type (#21951)+ -- e.g. data T = MkT ~Int#+ | HsSrcBang _ _ SrcLazy <- bang+ , isUnliftedType orig_arg_ty+ = addDiagnosticTc $ TcRnLazyBangOnUnliftedType orig_arg_ty++ -- Warn about unusable UNPACK pragmas+ -- e.g. data T a = MkT {-# UNPACK #-} !a -- Can't unpack | HsSrcBang _ want_unpack _ <- bang- , isSrcUnpacked want_unpack- , case rep_bang of { HsUnpack {} -> False; _ -> True }- -- If not optimising, we don't unpack (rep_bang is never- -- HsUnpack), so don't complain! This happens, e.g., in Haddock.- -- See dataConSrcToImplBang.- , not (bang_opt_unbox_disable bang_opts)++ -- See Note [Detecting useless UNPACK pragmas] in GHC.Core.DataCon.+ , isSrcUnpacked want_unpack -- this means the user wrote {-# UNPACK #-}+ , case rep_bang of { HsUnpack {} -> False; HsStrict True -> False; _ -> True }+ -- When typechecking an indefinite package in Backpack, we -- may attempt to UNPACK an abstract type. The test here will -- conclude that this is unusable, but it might become usable@@ -4451,7 +4490,7 @@ -- warn in this case (it gives users the wrong idea about whether -- or not UNPACK on abstract types is supported; it is!) , isHomeUnitDefinite (hsc_home_unit hsc_env)- = addDiagnosticTc $ TcRnUnknownMessage $+ = addDiagnosticTc $ mkTcRnUnknownMessage $ mkPlainDiagnostic WarningWithoutFlag noHints (bad_bang n (text "Ignoring unusable UNPACK pragma")) | otherwise@@ -4465,17 +4504,8 @@ -- checked here because we sometimes build invalid DataCons before -- erroring above here ; when debugIsOn $- do { let (univs, exs, eq_spec, _, _, _) = dataConFullSig con- user_tvs = dataConUserTyVars con- user_tvbs_invariant- = Set.fromList (filterEqSpec eq_spec univs ++ exs)- == Set.fromList user_tvs- ; massertPpr user_tvbs_invariant- $ vcat ([ ppr con- , ppr univs- , ppr exs- , ppr eq_spec- , ppr user_tvs ]) }+ massertPpr (checkDataConTyVars con) $+ ppr con $$ ppr (dataConFullSig con) $$ ppr (dataConUserTyVars con) ; traceTc "Done validity of data con" $ vcat [ ppr con@@ -4506,40 +4536,30 @@ ------------------------------- checkNewDataCon :: DataCon -> TcM () -- Further checks for the data constructor of a newtype+-- You might wonder if we need to check for an unlifted newtype+-- without -XUnliftedNewTypes, such as+-- newtype C = MkC Int#+-- But they are caught earlier, by GHC.Tc.Gen.HsType.checkDataKindSig checkNewDataCon con- = do { checkTc (isSingleton arg_tys) (newtypeFieldErr con (length arg_tys))- -- One argument-- ; unlifted_newtypes <- xoptM LangExt.UnliftedNewtypes- ; let allowedArgType =- unlifted_newtypes || typeLevity_maybe (scaledThing arg_ty1) == Just Lifted- ; checkTc allowedArgType $ TcRnUnknownMessage $ mkPlainError noHints $ vcat- [ text "A newtype cannot have an unlifted argument type"- , text "Perhaps you intended to use UnliftedNewtypes"- ]- ; show_linear_types <- xopt LangExt.LinearTypes <$> getDynFlags+ = do { show_linear_types <- xopt LangExt.LinearTypes <$> getDynFlags - ; let check_con what msg =- checkTc what $ TcRnUnknownMessage $ mkPlainError noHints $- (msg $$ ppr con <+> dcolon <+> ppr (dataConDisplayType show_linear_types con))+ ; checkTc (isSingleton arg_tys) $+ TcRnIllegalNewtype con show_linear_types (DoesNotHaveSingleField $ length arg_tys) ; checkTc (ok_mult (scaledMult arg_ty1)) $- TcRnUnknownMessage $ mkPlainError noHints $ text "A newtype constructor must be linear"+ TcRnIllegalNewtype con show_linear_types IsNonLinear - ; check_con (null eq_spec) $- text "A newtype constructor must have a return type of form T a1 ... an"- -- Return type is (T a b c)+ ; checkTc (null eq_spec) $+ TcRnIllegalNewtype con show_linear_types IsGADT - ; check_con (null theta) $- text "A newtype constructor cannot have a context in its type"+ ; checkTc (null theta) $+ TcRnIllegalNewtype con show_linear_types HasConstructorContext - ; check_con (null ex_tvs) $- text "A newtype constructor cannot have existential type variables"- -- No existentials+ ; checkTc (null ex_tvs) $+ TcRnIllegalNewtype con show_linear_types HasExistentialTyVar - ; checkTc (all ok_bang (dataConSrcBangs con))- (newtypeStrictError con)- -- No strictness annotations+ ; checkTc (all ok_bang (dataConSrcBangs con)) $+ TcRnIllegalNewtype con show_linear_types HasStrictnessAnnotation } where (_univ_tvs, ex_tvs, eq_spec, theta, arg_tys, _res_ty)@@ -4551,9 +4571,8 @@ ok_bang (HsSrcBang _ _ SrcLazy) = False ok_bang _ = True - ok_mult One = True- ok_mult _ = False-+ ok_mult OneTy = True+ ok_mult _ = False ------------------------------- checkValidClass :: Class -> TcM ()@@ -4580,7 +4599,7 @@ ; unless undecidable_super_classes $ case checkClassCycles cls of Just err -> setSrcSpan (getSrcSpan cls) $- addErrTc (TcRnUnknownMessage $ mkPlainError noHints err)+ addErrTc (mkTcRnUnknownMessage $ mkPlainError noHints err) Nothing -> return () -- Check the class operations.@@ -4615,7 +4634,7 @@ -- Test case: rep-poly/RepPolyClassMethod. ; unless constrained_class_methods $- mapM_ check_constraint (tail (cls_pred:op_theta))+ mapM_ check_constraint op_theta ; check_dm ctxt sel_id cls_pred tau2 dm }@@ -4723,7 +4742,7 @@ -- default foo2 :: a -> b unless (isJust $ tcMatchTys [dm_phi_ty, vanilla_phi_ty] [vanilla_phi_ty, dm_phi_ty]) $ addErrTc $- TcRnUnknownMessage $ mkPlainError noHints $+ mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "The default type signature for" <+> ppr sel_id <> colon) 2 (ppr dm_ty)@@ -4743,14 +4762,14 @@ ; checkTc idx_tys err_msg } where err_msg :: TcRnMessage- err_msg = TcRnUnknownMessage $ mkPlainError noHints $+ err_msg = mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Illegal family declaration for" <+> quotes (ppr tc_name)) 2 (text "Enable TypeFamilies to allow indexed type families") checkResultSigFlag :: Name -> FamilyResultSig GhcRn -> TcM () checkResultSigFlag tc_name (TyVarSig _ tvb) = do { ty_fam_deps <- xoptM LangExt.TypeFamilyDependencies- ; checkTc ty_fam_deps $ TcRnUnknownMessage $ mkPlainError noHints $+ ; checkTc ty_fam_deps $ mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Illegal result type variable" <+> ppr tvb <+> text "for" <+> quotes (ppr tc_name)) 2 (text "Enable TypeFamilyDependencies to allow result variable names") } checkResultSigFlag _ _ = return () -- other cases OK@@ -4861,6 +4880,31 @@ split as many foralls as it takes to retrieve the tau types we which to check. See Note [Splitting nested sigma types in class type signatures]. +Extra note: July 22. If we have+ class C a b where+ op :: op_ty+ default op :: def_ty+ op = blah++then we'll generate something like+ $gdm_op :: C a b => def_ty+ $gdm_op = blah++We expect to write an instance that looks (in effect) like this:+ instance G => C t1 t2 where+ op = $gdm_op -- Added when you leave out binding for 'op'++So we need that+ assuming constraints G, and C t1 t2,+ we have (def_ty[t1/a,t2/b] <= op_ty[t1/a,t2/b]++In the validity check, we want to check that there is such a G.+E.g. if we check def_ty <= op_ty, and get an insoluble constraint+(Int~Bool), we know there will never be such a G, and can complain.++This seems to be a more general way of thinking about the problem.+But no one is complaining, so it'll have to wait for another day+ Note [Splitting nested sigma types in class type signatures] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Consider this type synonym and class definition:@@ -5008,7 +5052,7 @@ check_dc_roles datacon = do { traceTc "check_dc_roles" (ppr datacon <+> ppr (tyConRoles tc)) ; mapM_ (check_ty_roles role_env Representational) $- eqSpecPreds eq_spec ++ theta ++ (map scaledThing arg_tys) }+ eqSpecPreds eq_spec ++ theta ++ map scaledThing arg_tys } -- See Note [Role-checking data constructor arguments] in GHC.Tc.TyCl.Utils where (univ_tvs, ex_tvs, eq_spec, theta, arg_tys, _res_ty)@@ -5068,7 +5112,7 @@ check_ty_roles env role ty report_error doc- = addErrTc $ TcRnUnknownMessage $ mkPlainError noHints $+ = addErrTc $ mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [text "Internal error in role inference:", doc, text "Please report this as a GHC bug: https://www.haskell.org/ghc/reportabug"]@@ -5089,8 +5133,7 @@ -- See Note [Inferring visible dependent quantification] -- Only types without a signature (CUSK or SAK) here addVDQNote tycon thing_inside- | assertPpr (isTcTyCon tycon) (ppr tycon) $- assertPpr (not (tcTyConIsPoly tycon)) (ppr tycon $$ ppr tc_kind)+ | assertPpr (isMonoTcTyCon tycon) (ppr tycon $$ ppr tc_kind) has_vdq = addLandmarkErrCtxt vdq_warning thing_inside | otherwise@@ -5152,24 +5195,24 @@ resultTypeMisMatch :: FieldLabelString -> DataCon -> DataCon -> TcRnMessage resultTypeMisMatch field_name con1 con2- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [sep [text "Constructors" <+> ppr con1 <+> text "and" <+> ppr con2, text "have a common field" <+> quotes (ppr field_name) <> comma], nest 2 $ text "but have different result types"] fieldTypeMisMatch :: FieldLabelString -> DataCon -> DataCon -> TcRnMessage fieldTypeMisMatch field_name con1 con2- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ sep [text "Constructors" <+> ppr con1 <+> text "and" <+> ppr con2, text "give different types for field", quotes (ppr field_name)] -dataConCtxt :: [LocatedN Name] -> SDoc-dataConCtxt cons = text "In the definition of data constructor" <> plural cons- <+> ppr_cons cons+dataConCtxt :: NonEmpty (LocatedN Name) -> SDoc+dataConCtxt cons = text "In the definition of data constructor" <> plural (toList cons)+ <+> ppr_cons (toList cons) -dataConResCtxt :: [LocatedN Name] -> SDoc-dataConResCtxt cons = text "In the result type of data constructor" <> plural cons- <+> ppr_cons cons+dataConResCtxt :: NonEmpty (LocatedN Name) -> SDoc+dataConResCtxt cons = text "In the result type of data constructor" <> plural (toList cons)+ <+> ppr_cons (toList cons) ppr_cons :: [LocatedN Name] -> SDoc ppr_cons [con] = quotes (ppr con)@@ -5184,20 +5227,20 @@ | n == 0 = mkErr "No" "no-parameter" | otherwise = mkErr "Too many" "multi-parameter" where- mkErr howMany allowWhat = TcRnUnknownMessage $ mkPlainError noHints $+ mkErr howMany allowWhat = mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [text (howMany ++ " parameters for class") <+> quotes (ppr cls), parens (text ("Enable MultiParamTypeClasses to allow " ++ allowWhat ++ " classes"))] classFunDepsErr :: Class -> TcRnMessage classFunDepsErr cls- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [text "Fundeps in class" <+> quotes (ppr cls), parens (text "Enable FunctionalDependencies to allow fundeps")] badMethPred :: Id -> TcPredType -> TcRnMessage badMethPred sel_id pred- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ hang (text "Constraint" <+> quotes (ppr pred) <+> text "in the type of" <+> quotes (ppr sel_id)) 2 (text "constrains only the class type variables")@@ -5205,14 +5248,14 @@ noClassTyVarErr :: Class -> TyCon -> TcRnMessage noClassTyVarErr clas fam_tc- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ sep [ text "The associated type" <+> quotes (ppr fam_tc <+> hsep (map ppr (tyConTyVars fam_tc))) , text "mentions none of the type or kind variables of the class" <+> quotes (ppr clas <+> hsep (map ppr (classTyVars clas)))] badDataConTyCon :: DataCon -> Type -> TcRnMessage badDataConTyCon data_con res_ty_tmpl- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Data constructor" <+> quotes (ppr data_con) <+> text "returns type" <+> quotes (ppr actual_res_ty)) 2 (text "instead of an instance of its parent type" <+> quotes (ppr res_ty_tmpl))@@ -5221,13 +5264,13 @@ badGadtDecl :: Name -> TcRnMessage badGadtDecl tc_name- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ text "Illegal generalised algebraic data declaration for" <+> quotes (ppr tc_name) , nest 2 (parens $ text "Enable the GADTs extension to allow this") ] badExistential :: DataCon -> TcRnMessage badExistential con- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ sdocOption sdocLinearTypes (\show_linear_types -> hang (text "Data constructor" <+> quotes (ppr con) <+> text "has existential type variables, a context, or a specialised result type")@@ -5236,43 +5279,31 @@ badStupidTheta :: Name -> TcRnMessage badStupidTheta tc_name- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ text "A data type declared in GADT style cannot have a context:" <+> quotes (ppr tc_name) newtypeConError :: Name -> Int -> TcRnMessage newtypeConError tycon n- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ sep [text "A newtype must have exactly one constructor,", nest 2 $ text "but" <+> quotes (ppr tycon) <+> text "has" <+> speakN n ] -newtypeStrictError :: DataCon -> TcRnMessage-newtypeStrictError con- = TcRnUnknownMessage $ mkPlainError noHints $- sep [text "A newtype constructor cannot have a strictness annotation,",- nest 2 $ text "but" <+> quotes (ppr con) <+> text "does"]--newtypeFieldErr :: DataCon -> Int -> TcRnMessage-newtypeFieldErr con_name n_flds- = TcRnUnknownMessage $ mkPlainError noHints $- sep [text "The constructor of a newtype must have exactly one field",- nest 2 $ text "but" <+> quotes (ppr con_name) <+> text "has" <+> speakN n_flds]- badSigTyDecl :: Name -> TcRnMessage badSigTyDecl tc_name- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ text "Illegal kind signature" <+> quotes (ppr tc_name) , nest 2 (parens $ text "Use KindSignatures to allow kind signatures") ] emptyConDeclsErr :: Name -> TcRnMessage emptyConDeclsErr tycon- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ sep [quotes (ppr tycon) <+> text "has no constructors", nest 2 $ text "(EmptyDataDecls permits this)"] wrongKindOfFamily :: TyCon -> TcRnMessage wrongKindOfFamily family- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ text "Wrong category of family instance; declaration was for a" <+> kindOfFamily where@@ -5285,13 +5316,13 @@ -- See Note [Oversaturated type family equations] in "GHC.Tc.Validity". wrongNumberOfParmsErr :: Arity -> TcRnMessage wrongNumberOfParmsErr max_args- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ text "Number of parameters must match family declaration; expected" <+> ppr max_args badRoleAnnot :: Name -> Role -> Role -> TcRnMessage badRoleAnnot var annot inferred- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Role mismatch on variable" <+> ppr var <> colon) 2 (sep [ text "Annotation says", ppr annot , text "but role", ppr inferred@@ -5299,7 +5330,7 @@ wrongNumberOfRoles :: [a] -> LRoleAnnotDecl GhcRn -> TcRnMessage wrongNumberOfRoles tyvars d@(L _ (RoleAnnotDecl _ _ annots))- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Wrong number of roles listed in role annotation;" $$ text "Expected" <+> (ppr $ length tyvars) <> comma <+> text "got" <+> (ppr $ length annots) <> colon)@@ -5310,25 +5341,25 @@ illegalRoleAnnotDecl (L loc (RoleAnnotDecl _ tycon _)) = setErrCtxt [] $ setSrcSpanA loc $- addErrTc $ TcRnUnknownMessage $ mkPlainError noHints $+ addErrTc $ mkTcRnUnknownMessage $ mkPlainError noHints $ (text "Illegal role annotation for" <+> ppr tycon <> char ';' $$ text "they are allowed only for datatypes and classes.") needXRoleAnnotations :: TyCon -> TcRnMessage needXRoleAnnotations tc- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ text "Illegal role annotation for" <+> ppr tc <> char ';' $$ text "did you intend to use RoleAnnotations?" incoherentRoles :: TcRnMessage-incoherentRoles = TcRnUnknownMessage $ mkPlainError noHints $+incoherentRoles = mkTcRnUnknownMessage $ mkPlainError noHints $ (text "Roles other than" <+> quotes (text "nominal") <+> text "for class parameters can lead to incoherence.") $$ (text "Use IncoherentInstances to allow this; bad role found") wrongTyFamName :: Name -> Name -> TcRnMessage wrongTyFamName fam_tc_name eqn_tc_name- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Mismatched type name in type family instance.") 2 (vcat [ text "Expected:" <+> ppr fam_tc_name , text " Actual:" <+> ppr eqn_tc_name ])
compiler/GHC/Tc/TyCl/Build.hs view
@@ -18,26 +18,29 @@ import GHC.Prelude import GHC.Iface.Env-import GHC.Core.FamInstEnv( FamInstEnvs, mkNewTypeCoAxiom ) import GHC.Builtin.Types( isCTupleTyConName, unboxedUnitTy )++import GHC.Tc.Utils.TcType+import GHC.Tc.Utils.Monad+ import GHC.Core.DataCon import GHC.Core.PatSyn+import GHC.Core.Class+import GHC.Core.TyCon+import GHC.Core.Type+import GHC.Core.Multiplicity+import GHC.Core.FamInstEnv( FamInstEnvs, mkNewTypeCoAxiom )++import GHC.Types.SrcLoc( SrcSpan, noSrcSpan ) import GHC.Types.Var import GHC.Types.Var.Set import GHC.Types.Basic import GHC.Types.Name import GHC.Types.Name.Env import GHC.Types.Id.Make-import GHC.Core.Class-import GHC.Core.TyCon-import GHC.Core.Type import GHC.Types.SourceText-import GHC.Tc.Utils.TcType-import GHC.Core.Multiplicity--import GHC.Types.SrcLoc( SrcSpan, noSrcSpan )-import GHC.Tc.Utils.Monad import GHC.Types.Unique.Supply+ import GHC.Utils.Misc import GHC.Utils.Outputable import GHC.Utils.Panic@@ -175,7 +178,7 @@ data_con = mkDataCon src_name declared_infix prom_info src_bangs field_lbls univ_tvs ex_tvs user_tvbs eq_spec ctxt- arg_tys res_ty NoRRI rep_tycon tag+ arg_tys res_ty NoPromInfo rep_tycon tag stupid_ctxt dc_wrk dc_rep dc_wrk = mkDataConWorkId work_name data_con dc_rep = initUs_ us (mkDataConRep dc_bang_opts fam_envs wrap_name data_con)@@ -320,22 +323,20 @@ -- (We used to call them D_C, but now we can have two different -- superclasses both called C!) - ; let use_newtype = isSingleton arg_tys+ ; let use_newtype = isSingleton (sc_theta ++ op_tys) -- Use a newtype if the data constructor -- (a) has exactly one value field -- i.e. exactly one operation or superclass taken together -- (b) that value is of lifted type (which they always are, because -- we box equality superclasses) -- See Note [Class newtypes and equality predicates]-- -- We treat the dictionary superclasses as ordinary arguments.- -- That means that in the case of+ --+ -- In the case of -- class C a => D a- -- we don't get a newtype with no arguments!+ -- we use a newtype, but with one superclass and no arguments args = sc_sel_names ++ op_names op_tys = [ty | (_,ty,_) <- sig_stuff] op_names = [op | (op,_,_) <- sig_stuff]- arg_tys = sc_theta ++ op_tys rec_tycon = classTyCon rec_clas univ_bndrs = tyConInvisTVBinders binders univ_tvs = binderVars univ_bndrs@@ -353,8 +354,8 @@ [{- no existentials -}] univ_bndrs [{- No GADT equalities -}]- [{- No theta -}]- (map unrestricted arg_tys) -- type classes are unrestricted+ sc_theta+ (map unrestricted op_tys) -- type classes are unrestricted (mkTyConApp rec_tycon (mkTyVarTys univ_tvs)) rec_tycon (mkTyConTagMap rec_tycon)
compiler/GHC/Tc/TyCl/Class.hs view
@@ -18,12 +18,10 @@ , tcClassMinimalDef , HsSigFun , mkHsSigFun- , badMethodErr , instDeclCtxt1 , instDeclCtxt2 , instDeclCtxt3 , tcATDefault- , substATBndrs ) where @@ -39,7 +37,7 @@ import GHC.Tc.Utils.Instantiate( tcSuperSkolTyVars ) import GHC.Tc.Gen.HsType import GHC.Tc.Utils.TcMType-import GHC.Core.Type ( extendTvSubstWithClone, piResultTys )+import GHC.Core.Type ( piResultTys ) import GHC.Core.Predicate import GHC.Core.Multiplicity import GHC.Tc.Types.Origin@@ -57,7 +55,7 @@ import GHC.Types.Name.Env import GHC.Types.Name.Set import GHC.Types.Var-import GHC.Types.Var.Env ( lookupVarEnv )+import GHC.Types.Var.Env import GHC.Types.SourceFile (HscSource(..)) import GHC.Utils.Outputable import GHC.Utils.Panic@@ -71,6 +69,7 @@ import Control.Monad import Data.List ( mapAccumL, partition )+import qualified Data.List.NonEmpty as NE {- Dictionary handling@@ -100,7 +99,7 @@ newtype CDict a = CDict (forall b. a -> b -> b) -Now DictTy in Type is just a form of type synomym:+Now DictTy in Type is just a form of type synonym: DictTy c t = TyConTy CDict `AppTy` t Death to "ExpandingDicts".@@ -113,10 +112,6 @@ ************************************************************************ -} -illegalHsigDefaultMethod :: Name -> TcRnMessage-illegalHsigDefaultMethod n = TcRnUnknownMessage $ mkPlainError noHints $- text "Illegal default method(s) in class definition of" <+> ppr n <+> text "in hsig file"- tcClassSigs :: Name -- Name of the class -> [LSig GhcRn] -> LHsBinds GhcRn@@ -131,7 +126,7 @@ ; op_info <- concatMapM (addLocM (tc_sig gen_dm_env)) vanilla_sigs ; let op_names = mkNameSet [ n | (n,_,_) <- op_info ]- ; sequence_ [ failWithTc (badMethodErr clas n)+ ; sequence_ [ failWithTc (TcRnBadMethodErr clas n) | n <- dm_bind_names, not (n `elemNameSet` op_names) ] -- Value binding for non class-method (ie no TypeSig) @@ -142,11 +137,12 @@ -- (Generic signatures without value bindings indicate -- that a default of this form is expected to be -- provided.)- when (not (null def_methods)) $- failWithTc (illegalHsigDefaultMethod clas)+ case bagToList def_methods of+ [] -> return ()+ meth : meths -> failWithTc (TcRnIllegalHsigDefaultMethods clas (meth NE.:| meths)) else -- Error for each generic signature without value binding- sequence_ [ failWithTc (badGenericMethod clas n)+ sequence_ [ failWithTc (TcRnBadGenericMethod clas n) | (n,_) <- gen_dm_prs, not (n `elem` dm_bind_names) ] ; traceTc "tcClassSigs 2" (ppr clas)@@ -237,7 +233,7 @@ tcDefMeth _ _ _ _ _ prag_fn (sel_id, Nothing) = do { -- No default method- mapM_ (addLocMA (badDmPrag sel_id))+ mapM_ (addLocMA (badDmPrag sel_id )) (lookupPragEnv prag_fn (idName sel_id)) ; return emptyBag } @@ -263,9 +259,8 @@ ; spec_prags <- discardConstraints $ tcSpecPrags global_dm_id prags- ; let dia = TcRnUnknownMessage $- mkPlainDiagnostic WarningWithoutFlag noHints $- (text "Ignoring SPECIALISE pragmas on default method" <+> quotes (ppr sel_name))+ ; let dia = TcRnIgnoreSpecialisePragmaOnDefMethod sel_name+ ; diagnosticTc (not (null spec_prags)) dia ; let hs_ty = hs_sig_fn sel_name@@ -295,7 +290,7 @@ ctxt = FunSigCtxt sel_name warn_redundant - ; let local_dm_id = mkLocalId local_dm_name Many local_dm_ty+ ; let local_dm_id = mkLocalId local_dm_name ManyTy local_dm_ty local_dm_sig = CompleteSig { sig_bndr = local_dm_id , sig_ctxt = ctxt , sig_loc = getLocA hs_ty }@@ -341,7 +336,7 @@ -- since you can't write a default implementation. when (tcg_src tcg_env /= HsigFile) $ whenIsJust (isUnsatisfied (mindef `impliesAtom`) defMindef) $- (\bf -> addDiagnosticTc (warningMinimalDefIncomplete bf))+ (\bf -> addDiagnosticTc (TcRnWarningMinimalDefIncomplete bf)) return mindef where -- By default require all methods without a default implementation@@ -404,7 +399,7 @@ findMinimalDef = firstJusts . map toMinimalDef where toMinimalDef :: LSig GhcRn -> Maybe ClassMinimalDef- toMinimalDef (L _ (MinimalSig _ _ (L _ bf))) = Just (fmap unLoc bf)+ toMinimalDef (L _ (MinimalSig _ (L _ bf))) = Just (fmap unLoc bf) toMinimalDef _ = Nothing {-@@ -442,18 +437,6 @@ ************************************************************************ -} -badMethodErr :: Outputable a => a -> Name -> TcRnMessage-badMethodErr clas op- = TcRnUnknownMessage $ mkPlainError noHints $- hsep [text "Class", quotes (ppr clas),- text "does not have a method", quotes (ppr op)]--badGenericMethod :: Outputable a => a -> Name -> TcRnMessage-badGenericMethod clas op- = TcRnUnknownMessage $ mkPlainError noHints $- hsep [text "Class", quotes (ppr clas),- text "has a generic-default signature without a binding", quotes (ppr op)]- {- badGenericInstanceType :: LHsBinds Name -> SDoc badGenericInstanceType binds@@ -473,19 +456,10 @@ where ppr_inst_ty (_,inst) = ppr (simpleInstInfoTy inst) -}+ badDmPrag :: TcId -> Sig GhcRn -> TcM () badDmPrag sel_id prag- = addErrTc (TcRnUnknownMessage $ mkPlainError noHints $- text "The" <+> hsSigDoc prag <+> text "for default method"- <+> quotes (ppr sel_id)- <+> text "lacks an accompanying binding")--warningMinimalDefIncomplete :: ClassMinimalDef -> TcRnMessage-warningMinimalDefIncomplete mindef- = TcRnUnknownMessage $ mkPlainDiagnostic WarningWithoutFlag noHints $- vcat [ text "The MINIMAL pragma does not require:"- , nest 2 (pprBooleanFormulaNice mindef)- , text "but there is no default implementation." ]+ = addErrTc (TcRnDefaultMethodForPragmaLacksBinding sel_id prag) instDeclCtxt1 :: LHsSigType GhcRn -> SDoc instDeclCtxt1 hs_inst_ty@@ -506,7 +480,7 @@ 2 (quotes doc) tcATDefault :: SrcSpan- -> TCvSubst+ -> Subst -> NameSet -> ClassATItem -> TcM [FamInst]@@ -523,7 +497,8 @@ -- instance C [x] -- Then we want to generate the decl: type F [x] b = () | Just (rhs_ty, _loc) <- defs- = do { let (subst', pat_tys') = substATBndrs inst_subst (tyConTyVars fam_tc)+ = do { let (subst', pat_tys') = mapAccumL subst_tv inst_subst+ (tyConTyVars fam_tc) rhs' = substTyUnchecked subst' rhs_ty tcv' = tyCoVarsOfTypesList pat_tys' (tv', cv') = partition isTyVar tcv'@@ -546,73 +521,14 @@ | otherwise -- defs = Nothing = do { warnMissingAT (tyConName fam_tc) ; return [] }---- | Apply a substitution to the type variable binders of an associated type--- family. This is used to compute default instances for associated type--- families (see 'tcATDefault') as well as @newtype@-derived associated type--- family instances (see @gen_Newtype_fam_insts@ in "GHC.Tc.Deriv.Generate").------ As a concrete example, consider the following class and associated type--- family:------ @--- class C k (a :: k) where--- type F k a (b :: k) :: Type--- type F j p q = (Proxy @j p, Proxy @j (q :: j))--- @------ If a user defines this instance:------ @--- instance C (Type -> Type) Maybe where {}--- @------ Then in order to typecheck the default @F@ instance, we must apply the--- substitution @[k :-> (Type -> Type), a :-> Maybe]@ to @F@'s binders, which--- are @[k, a, (b :: k)]@. The result should look like this:------ @--- type F (Type -> Type) Maybe (b :: Type -> Type) =--- (Proxy @(Type -> Type) Maybe, Proxy @(Type -> Type) (b :: Type -> Type))--- @------ Making this work requires some care. There are two cases:------ 1. If we encounter a type variable in the domain of the substitution (e.g.,--- @k@ or @a@), then we apply the substitution directly.------ 2. Otherwise, we substitute into the type variable's kind (e.g., turn--- @b :: k@ to @b :: Type -> Type@). We then return an extended substitution--- where the old @b@ (of kind @k@) maps to the new @b@ (of kind @Type -> Type@).------ This step is important to do in case there are later occurrences of @b@,--- which we must ensure have the correct kind. Otherwise, we might end up--- with @Proxy \@(Type -> Type) (b :: k)@ on the right-hand side of the--- default instance, which would be completely wrong.------ Contrast 'substATBndrs' function with similar substitution functions:------ * 'substTyVars' does not substitute into the kinds of each type variable,--- nor does it extend the substitution. 'substTyVars' is meant for occurrences--- of type variables, whereas 'substATBndr's is meant for binders.------ * 'substTyVarBndrs' does substitute into kinds and extends the substitution,--- but it does not apply the substitution to the variables themselves. As--- such, 'substTyVarBndrs' returns a list of 'TyVar's rather than a list of--- 'Type's.-substATBndrs :: TCvSubst -> [TyVar] -> (TCvSubst, [Type])-substATBndrs = mapAccumL substATBndr where- substATBndr :: TCvSubst -> TyVar -> (TCvSubst, Type)- substATBndr subst tc_tv- -- Case (1) in the Haddocks+ subst_tv subst tc_tv | Just ty <- lookupVarEnv (getTvSubstEnv subst) tc_tv = (subst, ty)- -- Case (2) in the Haddocks | otherwise- = (extendTvSubstWithClone subst tc_tv tc_tv', mkTyVarTy tc_tv')+ = (extendTvSubst subst tc_tv ty', ty') where- tc_tv' = updateTyVarKind (substTy subst) tc_tv+ ty' = mkTyVarTy (updateTyVarKind (substTyUnchecked subst) tc_tv) warnMissingAT :: Name -> TcM () warnMissingAT name@@ -622,10 +538,6 @@ -- hs-boot and signatures never need to provide complete "definitions" -- of any sort, as they aren't really defining anything, but just -- constraining items which are defined elsewhere.- ; let dia = TcRnUnknownMessage $- mkPlainDiagnostic (WarningWithFlag Opt_WarnMissingMethods) noHints $- (text "No explicit" <+> text "associated type"- <+> text "or default declaration for"- <+> quotes (ppr name))+ ; let dia = TcRnNoExplicitAssocTypeOrDefaultDeclaration name ; diagnosticTc (warn && hsc_src == HsSrcFile) dia }
compiler/GHC/Tc/TyCl/Instance.hs view
@@ -8,7 +8,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-} -{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} {-# OPTIONS_GHC -Wno-incomplete-record-updates #-} -- | Typechecking instance declarations@@ -27,8 +26,8 @@ import GHC.Tc.TyCl import GHC.Tc.TyCl.Utils ( addTyConsToGblEnv ) import GHC.Tc.TyCl.Class ( tcClassDecl2, tcATDefault,- HsSigFun, mkHsSigFun, badMethodErr,- findMethodBind, instantiateMethod )+ HsSigFun, mkHsSigFun, findMethodBind,+ instantiateMethod ) import GHC.Tc.Solver( pushLevelAndSolveEqualitiesX, reportUnsolvedEqualities ) import GHC.Tc.Gen.Sig import GHC.Tc.Utils.Monad@@ -79,10 +78,9 @@ import GHC.Types.Name.Set import GHC.Utils.Outputable import GHC.Utils.Panic-import GHC.Utils.Panic.Plain import GHC.Types.SrcLoc import GHC.Utils.Misc-import GHC.Data.BooleanFormula ( isUnsatisfied, pprBooleanFormulaNice )+import GHC.Data.BooleanFormula ( isUnsatisfied ) import qualified GHC.LanguageExtensions as LangExt import Control.Monad@@ -176,7 +174,7 @@ inline df_i in it, and that in turn means that (since it'll be a loop-breaker because df_i isn't), op1_i will ironically never be inlined. But this is OK: the recursion breaking happens by way of- a RULE (the magic ClassOp rule above), and RULES work inside InlineRule+ a RULE (the magic ClassOp rule above), and RULES work inside stable unfoldings. See Note [RULEs enabled in InitialPhase] in GHC.Core.Opt.Simplify.Utils Note [ClassOp/DFun selection]@@ -492,15 +490,15 @@ do { dfun_ty <- tcHsClsInstType (InstDeclCtxt False) hs_ty ; let (tyvars, theta, clas, inst_tys) = tcSplitDFunTy dfun_ty -- NB: tcHsClsInstType does checkValidInstance- ; skol_info <- mkSkolemInfo InstSkol+ ; skol_info <- mkSkolemInfo (mkClsInstSkol clas inst_tys) ; (subst, skol_tvs) <- tcInstSkolTyVars skol_info tyvars ; let tv_skol_prs = [ (tyVarName tv, skol_tv) | (tv, skol_tv) <- tyvars `zip` skol_tvs ] -- Map from the skolemized Names to the original Names. -- See Note [Associated data family instances and di_scoped_tvs]. tv_skol_env = mkVarEnv $ map swap tv_skol_prs- n_inferred = countWhile ((== Inferred) . binderArgFlag) $- fst $ splitForAllTyCoVarBinders dfun_ty+ n_inferred = countWhile ((== Inferred) . binderFlag) $+ fst $ splitForAllForAllTyBinders dfun_ty visible_skol_tvs = drop n_inferred skol_tvs ; traceTc "tcLocalInstDecl 1" (ppr dfun_ty $$ ppr (invisibleTyBndrCount dfun_ty) $$ ppr skol_tvs)@@ -592,7 +590,7 @@ -- (0) Check it's an open type family ; checkTc (isTypeFamilyTyCon fam_tc) (wrongKindOfFamily fam_tc)- ; checkTc (isOpenTypeFamilyTyCon fam_tc) (notOpenFamily fam_tc)+ ; checkTc (isOpenTypeFamilyTyCon fam_tc) (TcRnNotOpenFamily fam_tc) -- (1) do the work of verifying the synonym group -- For some reason we don't have a location for the equation@@ -619,16 +617,16 @@ ; traceTc "tcFamInstDecl" (ppr fam_tc) ; type_families <- xoptM LangExt.TypeFamilies ; is_boot <- tcIsHsBootOrSig -- Are we compiling an hs-boot file?- ; checkTc type_families $ badFamInstDecl fam_tc- ; checkTc (not is_boot) $ badBootFamInstDeclErr+ ; checkTc type_families (TcRnBadFamInstDecl fam_tc)+ ; checkTc (not is_boot) TcRnBadBootFamInstDecl -- Check that it is a family TyCon, and that -- oplevel type instances are not for associated types.- ; checkTc (isFamilyTyCon fam_tc) (notFamily fam_tc)+ ; checkTc (isFamilyTyCon fam_tc) (TcRnIllegalFamilyInstance fam_tc) ; when (isNotAssociated mb_clsinfo && -- Not in a class decl isTyConAssoc fam_tc) -- but an associated type- (addErr $ assocInClassErr fam_tc)+ (addErr $ TcRnMissingClassAssoc fam_tc) } {- Note [Associated type instances]@@ -674,8 +672,7 @@ , feqn_pats = hs_pats , feqn_tycon = lfam_name@(L _ fam_name) , feqn_fixity = fixity- , feqn_rhs = HsDataDefn { dd_ND = new_or_data- , dd_cType = cType+ , feqn_rhs = HsDataDefn { dd_cType = cType , dd_ctxt = hs_ctxt , dd_cons = hs_cons , dd_kindSig = m_ksig@@ -688,10 +685,11 @@ -- Check that the family declaration is for the right kind ; checkTc (isDataFamilyTyCon fam_tc) (wrongKindOfFamily fam_tc)- ; gadt_syntax <- dataDeclChecks fam_name new_or_data hs_ctxt hs_cons+ ; gadt_syntax <- dataDeclChecks fam_name hs_ctxt hs_cons -- Do /not/ check that the number of patterns = tyConArity fam_tc -- See [Arity of data families] in GHC.Core.FamInstEnv ; skol_info <- mkSkolemInfo FamInstSkol+ ; let new_or_data = dataDefnConsNewOrData hs_cons ; (qtvs, pats, tc_res_kind, stupid_theta) <- tcDataFamInstHeader mb_clsinfo skol_info fam_tc outer_bndrs fixity hs_ctxt hs_pats m_ksig new_or_data@@ -767,19 +765,18 @@ do { data_cons <- tcExtendTyVarEnv (binderVars tc_ty_binders) $ -- For H98 decls, the tyvars scope -- over the data constructors- tcConDecls new_or_data (DDataInstance orig_res_ty)- rec_rep_tc tc_ty_binders tc_res_kind- hs_cons+ tcConDecls (DDataInstance orig_res_ty) rec_rep_tc tc_ty_binders tc_res_kind+ hs_cons ; rep_tc_name <- newFamInstTyConName lfam_name pats ; axiom_name <- newFamInstAxiomName lfam_name [pats]- ; tc_rhs <- case new_or_data of- DataType -> return $+ ; tc_rhs <- case data_cons of+ DataTypeCons type_data data_cons -> return $ mkLevPolyDataTyConRhs (isFixedRuntimeRepKind res_kind)+ type_data data_cons- NewType -> assert (not (null data_cons)) $- mkNewTyConRhs rep_tc_name rec_rep_tc (head data_cons)+ NewTypeCon data_con -> mkNewTyConRhs rep_tc_name rec_rep_tc data_con ; let ax_rhs = mkTyConApp rep_tc (mkTyVarTys zonked_post_eta_qtvs) axiom = mkSingleCoAxiom Representational axiom_name@@ -893,7 +890,6 @@ -> NewOrData -> TcM ([TcTyVar], [TcType], TcKind, TcThetaType) -- All skolem TcTyVars, all zonked so it's clear what the free vars are- -- The "header" of a data family instance is the part other than -- the data constructors themselves -- e.g. data instance D [a] :: * -> * where ...@@ -944,7 +940,7 @@ -- clearer to duplicate it. Still, if you fix a bug here, -- check there too! - -- See GHC.Tc.TyCl Note [Generalising in tcFamTyPatsGuts]+ -- See GHC.Tc.TyCl Note [Generalising in tcTyFamInstEqnGuts] ; dvs <- candidateQTyVarsWithBinders outer_tvs lhs_ty ; qtvs <- quantifyTyVars skol_info TryNotToDefaultNonStandardTyVars dvs ; let final_tvs = scopedSort (qtvs ++ outer_tvs)@@ -1099,7 +1095,7 @@ Now the subtleties of Note [Newtype eta and homogeneous axioms] are dealt with by the newtype (via mkNewTyConRhs called in tcDataFamInstDecl) while the axiom connecting F Int ~ R:FIntb is eta-reduced, but the- quantifer 'b' is derived from the original data family F, and so the+ quantifier 'b' is derived from the original data family F, and so the kinds will always match. Note [Kind inference for data family instances]@@ -1132,7 +1128,7 @@ So what kind do 'p' and 'q' have? No clues from the header, but from the data constructor we can clearly see that (r :: Type->Type). Does-that mean that the the /entire data instance/ is instantiated at Type,+that mean that the /entire data instance/ is instantiated at Type, like this? data instance T @Type (p :: Type->Type) (q :: Type) where ...@@ -1232,19 +1228,14 @@ tcInstDecl2 :: InstInfo GhcRn -> TcM (LHsBinds GhcTc) -- Returns a binding for the dfun tcInstDecl2 (InstInfo { iSpec = ispec, iBinds = ibinds })- = recoverM (return emptyLHsBinds) $- setSrcSpan loc $- addErrCtxt (instDeclCtxt2 (idType dfun_id)) $+ = recoverM (return emptyLHsBinds) $+ setSrcSpan loc $+ addErrCtxt (instDeclCtxt2 dfun_ty) $ do { -- Instantiate the instance decl with skolem constants- ; skol_info <- mkSkolemInfo InstSkol- ; (inst_tyvars, dfun_theta, inst_head) <- tcSkolDFunType skol_info dfun_id+ (skol_info, inst_tyvars, dfun_theta, clas, inst_tys) <- tcSkolDFunType dfun_ty ; dfun_ev_vars <- newEvVars dfun_theta- -- We instantiate the dfun_id with superSkolems.- -- See Note [Subtle interaction of recursion and overlap]- -- and Note [Binding when looking up instances] - ; let (clas, inst_tys) = tcSplitDFunHead inst_head- (class_tyvars, sc_theta, _, op_items) = classBigSig clas+ ; let (class_tyvars, sc_theta, _, op_items) = classBigSig clas sc_theta' = substTheta (zipTvSubst class_tyvars inst_tys) sc_theta ; traceTc "tcInstDecl2" (vcat [ppr inst_tyvars, ppr inst_tys, ppr dfun_theta, ppr sc_theta'])@@ -1260,13 +1251,12 @@ ; (tclvl, (sc_meth_ids, sc_meth_binds, sc_meth_implics)) <- pushTcLevelM $ do { (sc_ids, sc_binds, sc_implics)- <- tcSuperClasses dfun_id clas inst_tyvars dfun_ev_vars- inst_tys dfun_ev_binds- sc_theta'+ <- tcSuperClasses skol_info dfun_id clas inst_tyvars+ dfun_ev_vars dfun_ev_binds sc_theta' -- Typecheck the methods ; (meth_ids, meth_binds, meth_implics)- <- tcMethods dfun_id clas inst_tyvars dfun_ev_vars+ <- tcMethods skol_info dfun_id clas inst_tyvars dfun_ev_vars inst_tys dfun_ev_binds spec_inst_info op_items ibinds @@ -1281,13 +1271,13 @@ , ic_given = dfun_ev_vars , ic_wanted = mkImplicWC sc_meth_implics , ic_binds = dfun_ev_binds_var- , ic_info = InstSkol }+ , ic_info = skol_info } -- Create the result bindings ; self_dict <- newDict clas inst_tys ; let class_tc = classTyCon clas loc' = noAnnSrcSpan loc- [dict_constr] = tyConDataCons class_tc+ dict_constr = tyConSingleDataCon class_tc dict_bind = mkVarBind self_dict (L loc' con_app_args) -- We don't produce a binding for the dict_constr; instead we@@ -1339,6 +1329,7 @@ } where dfun_id = instanceDFunId ispec+ dfun_ty = idType dfun_id loc = getSrcSpan dfun_id addDFunPrags :: DFunId -> [Id] -> DFunId@@ -1353,7 +1344,7 @@ -- is messing with. addDFunPrags dfun_id sc_meth_ids | is_newtype- = dfun_id `setIdUnfolding` mkInlineUnfoldingWithArity 0 defaultSimpleOpts con_app+ = dfun_id `setIdUnfolding` mkInlineUnfoldingWithArity defaultSimpleOpts StableSystemSrc 0 con_app `setInlinePragma` alwaysInlinePragma { inl_sat = Just 0 } | otherwise = dfun_id `setIdUnfolding` mkDFunUnfolding dfun_bndrs dict_con dict_args@@ -1371,7 +1362,7 @@ ev_ids = mkTemplateLocalsNum 1 dfun_theta dfun_bndrs = dfun_tvs ++ ev_ids clas_tc = classTyCon clas- [dict_con] = tyConDataCons clas_tc+ dict_con = tyConSingleDataCon clas_tc is_newtype = isNewTyCon clas_tc wrapId :: HsWrapper -> Id -> HsExpr GhcTc@@ -1446,7 +1437,8 @@ ************************************************************************ -} -tcSuperClasses :: DFunId -> Class -> [TcTyVar] -> [EvVar] -> [TcType]+tcSuperClasses :: SkolemInfoAnon -> DFunId -> Class -> [TcTyVar]+ -> [EvVar] -> TcEvBinds -> TcThetaType -> TcM ([EvVar], LHsBinds GhcTc, Bag Implication)@@ -1458,22 +1450,22 @@ -- See Note [Recursive superclasses] for why this is so hard! -- In effect, we build a special-purpose solver for the first step -- of solving each superclass constraint-tcSuperClasses dfun_id cls tyvars dfun_evs inst_tys dfun_ev_binds sc_theta+tcSuperClasses skol_info dfun_id cls tyvars dfun_evs dfun_ev_binds sc_theta = do { (ids, binds, implics) <- mapAndUnzip3M tc_super (zip sc_theta [fIRST_TAG..]) ; return (ids, listToBag binds, listToBag implics) } where loc = getSrcSpan dfun_id- size = sizeTypes inst_tys tc_super (sc_pred, n) = do { (sc_implic, ev_binds_var, sc_ev_tm)- <- checkInstConstraints $ emitWanted (ScOrigin size) sc_pred+ <- checkInstConstraints skol_info $+ emitWanted (ScOrigin IsClsInst NakedSc) sc_pred+ -- ScOrigin IsClsInst True: see Note [Solving superclass constraints] ; sc_top_name <- newName (mkSuperDictAuxOcc n (getOccName cls)) ; sc_ev_id <- newEvVar sc_pred ; addTcEvBind ev_binds_var $ mkWantedEvBind sc_ev_id sc_ev_tm- ; let sc_top_ty = mkInfForAllTys tyvars $- mkPhiTy (map idType dfun_evs) sc_pred- sc_top_id = mkLocalId sc_top_name Many sc_top_ty+ ; let sc_top_ty = tcMkDFunSigmaTy tyvars (map idType dfun_evs) sc_pred+ sc_top_id = mkLocalId sc_top_name ManyTy sc_top_ty export = ABE { abe_wrap = idHsWrapper , abe_poly = sc_top_id , abe_mono = sc_ev_id@@ -1489,10 +1481,10 @@ ; return (sc_top_id, L (noAnnSrcSpan loc) bind, sc_implic) } --------------------checkInstConstraints :: TcM result+checkInstConstraints :: SkolemInfoAnon -> TcM result -> TcM (Implication, EvBindsVar, result) -- See Note [Typechecking plan for instance declarations]-checkInstConstraints thing_inside+checkInstConstraints skol_info thing_inside = do { (tclvl, wanted, result) <- pushLevelAndCaptureConstraints $ thing_inside @@ -1501,7 +1493,7 @@ ; let implic' = implic { ic_tclvl = tclvl , ic_wanted = wanted , ic_binds = ev_binds_var- , ic_info = InstSkol }+ , ic_info = skol_info } ; return (implic', ev_binds_var, result) } @@ -1554,82 +1546,134 @@ To achieve the Superclass Invariant, in a dfun definition we can generate a guaranteed-non-bottom superclass witness from: (sc1) one of the dictionary arguments itself (all non-bottom)- (sc2) an immediate superclass of a smaller dictionary+ (sc2) an immediate superclass of a non-bottom dictionary that is+ /Paterson-smaller/ than the instance head+ See Note [The PatersonSize of a type] in GHC.Tc.Utils.TcType (sc3) a call of a dfun (always returns a dictionary constructor) -The tricky case is (sc2). We proceed by induction on the size of-the (type of) the dictionary, defined by GHC.Tc.Validity.sizeTypes.-Let's suppose we are building a dictionary of size 3, and-suppose the Superclass Invariant holds of smaller dictionaries.-Then if we have a smaller dictionary, its immediate superclasses-will be non-bottom by induction.+The tricky case is (sc2). We proceed by induction on the size of the+(type of) the dictionary, defined by GHC.Tc.Utils.TcType.pSizeType. Let's+suppose we are building a dictionary of size 3 (the "head"), and suppose+the Superclass Invariant holds of smaller dictionaries. Then if we have a+smaller dictionary, its immediate superclasses will be non-bottom by+induction. -What does "we have a smaller dictionary" mean? It might be-one of the arguments of the instance, or one of its superclasses.+Why "Paterson-smaller"? See Note [Paterson conditions] in GHC.Tc.Validity.+We want to be sure that the superclass dictionary is smaller /for any+ground instatiation/ of the instance, so we need to account for type+variables that occur more than once, and for type families (#20666). And+that's exactly what the Paterson conditions check!+ Here is an example, taken from CmmExpr: class Ord r => UserOfRegs r a where ... (i1) instance UserOfRegs r a => UserOfRegs r (Maybe a) where (i2) instance (Ord r, UserOfRegs r CmmReg) => UserOfRegs r CmmExpr where -For (i1) we can get the (Ord r) superclass by selection from (UserOfRegs r a),-since it is smaller than the thing we are building (UserOfRegs r (Maybe a).+For (i1) we can get the (Ord r) superclass by selection from+(UserOfRegs r a), since it (i.e. UserOfRegs r a) is smaller than the+thing we are building, namely (UserOfRegs r (Maybe a)). -But for (i2) that isn't the case, so we must add an explicit, and-perhaps surprising, (Ord r) argument to the instance declaration.+But for (i2) that isn't the case: (UserOfRegs r CmmReg) is not smaller+than the thing we are building (UserOfRegs r CmmExpr), so we can't use+the superclasses of the former. Hence we must instead add an explicit,+and perhaps surprising, (Ord r) argument to the instance declaration. Here's another example from #6161: - class Super a => Duper a where ...- class Duper (Fam a) => Foo a where ...-(i3) instance Foo a => Duper (Fam a) where ...-(i4) instance Foo Float where ...+ class Super a => Duper a where ...+ class Duper (Maybe a) => Foo a where ...+(i3) instance Foo a => Duper (Maybe a) where ...+(i4) instance Foo Float where ... It would be horribly wrong to define- dfDuperFam :: Foo a -> Duper (Fam a) -- from (i3)- dfDuperFam d = MkDuper (sc_sel1 (sc_sel2 d)) ...+ dfDuperMaybe :: Foo a -> Duper (Maybe a) -- from (i3)+ dfDuperMaybe d = MkDuper (sc_sel1 (sc_sel2 d)) ... dfFooFloat :: Foo Float -- from (i4)- dfFooFloat = MkFoo (dfDuperFam dfFooFloat) ...+ dfFooFloat = MkFoo (dfDuperMaybe dfFooFloat) ... -Now the Super superclass of Duper is definitely bottom!+Let's expand the RHS of dfFooFloat:+ dfFooFloat = MkFoo (MkDuper (sc_sel1 (sc_sel2 dfFooFloat)) ...) ...+That superclass argument to MkDuper is bottom! -This won't happen because when processing (i3) we can use the-superclasses of (Foo a), which is smaller, namely Duper (Fam a). But-that is *not* smaller than the target so we can't take *its*-superclasses. As a result the program is rightly rejected, unless you-add (Super (Fam a)) to the context of (i3).+This program gets rejected because:+* When processing (i3) we need to construct a dictionary for Super+ (Maybe a), to put in the superclass field of (Duper (Maybe a)).+* We /can/ use the superclasses of (Foo a), because the latter is+ smaller than the head of the instance, namely Duper (Maybe a).+* So we know (by (sc2)) that this Duper (Maybe a) dictionary is+ non-bottom. But because (Duper (Maybe a)) is not smaller than the+ instance head (Duper (Maybe a)), we can't take *its* superclasses.+As a result the program is rightly rejected, unless you add+(Super (Maybe a)) to the context of (i3). +Wrinkle (W1):+ (sc2) says we only get a non-bottom dict if the dict we are+ selecting from is itself non-bottom. So in a superclass chain,+ all the dictionaries in the chain must be non-bottom.+ class C a => D3 a+ class D2 a [[Maybe b]] => D1 a b+ class D3 a => D2 a b+ class C a => E a b+ instance D1 a b => E a [b]+ The instance needs the wanted superclass (C a). We can get it+ by superclass selection from+ D1 a b --> D2 a [[Maybe b]] --> D3 a --> C a+ But on the way we go through the too-big (D2 a [[Maybe b]]), and+ we don't know that is non-bottom.+ Note [Solving superclass constraints] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-How do we ensure that every superclass witness is generated by-one of (sc1) (sc2) or (sc3) in Note [Recursive superclasses].+How do we ensure that every superclass witness in an instance declaration+is generated by one of (sc1) (sc2) or (sc3) in Note [Recursive superclasses]? Answer: - * Superclass "wanted" constraints have CtOrigin of (ScOrigin size)- where 'size' is the size of the instance declaration. e.g.- class C a => D a where...- instance blah => D [a] where ...- The wanted superclass constraint for C [a] has origin- ScOrigin size, where size = size( D [a] ).+ * The "given" constraints of an instance decl have CtOrigin of+ (GivenOrigin (InstSkol head_size)), where head_size is the+ PatersonSize of the head of the instance declaration. E.g. in+ instance D a => C [a]+ the `[G] D a` constraint has a CtOrigin whose head_size is the+ PatersonSize of (C [a]). + * When we make a superclass selection from a Given (transitively)+ we give it a CtOrigin of (GivenSCOrigin skol_info sc_depth blocked).++ The 'blocked :: Bool' flag says if the superclass can be used to+ solve a superclass Wanted. The new superclass is blocked unless:++ it is the superclass of an unblocked dictionary (wrinkle (W1)),+ that is Paterson-smaller than the instance head.++ This is implemented in GHC.Tc.Solver.Canonical.mk_strict_superclasses+ (in the mk_given_loc helper function).++ * Superclass "Wanted" constraints have CtOrigin of (ScOrigin NakedSc)+ The 'NakedSc' says that this is a naked superclass Wanted; we must+ be careful when solving it.+ * (sc1) When we rewrite such a wanted constraint, it retains its origin. But if we apply an instance declaration, we can set the- origin to (ScOrigin infinity), thus lifting any restrictions by- making prohibitedSuperClassSolve return False.+ origin to (ScOrigin NotNakedSc), thus lifting any restrictions by+ making prohibitedSuperClassSolve return False. This happens+ in GHC.Tc.Solver.Interact.checkInstanceOK. * (sc2) ScOrigin wanted constraints can't be solved from a superclass selection, except at a smaller type. This test is- implemented by GHC.Tc.Solver.Interact.prohibitedSuperClassSolve+ implemented by GHC.Tc.Solver.InertSet.prohibitedSuperClassSolve - * The "given" constraints of an instance decl have CtOrigin- GivenOrigin InstSkol.+Note [Migrating away from loopy superclass solving]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+The logic from Note [Solving superclass constraints] was implemented in GHC 9.6.+However, we want to provide a migration strategy for users, to avoid suddenly+breaking their code going when upgrading to GHC 9.6. To this effect, we temporarily+continue to allow the constraint solver to create these potentially non-terminating+solutions, but emit a loud warning when doing so: see+GHC.Tc.Solver.Interact.tryLastResortProhibitedSuperclass. - * When we make a superclass selection from InstSkol we use- a CtOrigin of (InstSCOrigin size), where 'size' is the size of- the constraint whose superclass we are taking. And similarly- when taking the superclass of an InstSCOrigin. This is implemented- in GHC.Tc.Solver.Canonical.mk_strict_superclasses (in the- mk_given_loc helper function).+Users can silence the warning by manually adding the necessary constraint to the+context. GHC will then keep this user-written Given, dropping the Given arising+from superclass expansion which has greater SC depth, as explained in+Note [Replacement vs keeping] in GHC.Tc.Solver.Interact. Note [Silent superclass arguments] (historical interest only) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -1702,7 +1746,7 @@ - Use tcValBinds to do the checking -} -tcMethods :: DFunId -> Class+tcMethods :: SkolemInfoAnon -> DFunId -> Class -> [TcTyVar] -> [EvVar] -> [TcType] -> TcEvBinds@@ -1712,7 +1756,7 @@ -> TcM ([Id], LHsBinds GhcTc, Bag Implication) -- The returned inst_meth_ids all have types starting -- forall tvs. theta => ...-tcMethods dfun_id clas tyvars dfun_ev_vars inst_tys+tcMethods skol_info dfun_id clas tyvars dfun_ev_vars inst_tys dfun_ev_binds (spec_inst_prags, prag_fn) op_items (InstBindings { ib_binds = binds , ib_tyvars = lexical_tvs@@ -1745,10 +1789,10 @@ tc_item :: ClassOpItem -> TcM (Id, LHsBind GhcTc, Maybe Implication) tc_item (sel_id, dm_info) | Just (user_bind, bndr_loc, prags) <- findMethodBind (idName sel_id) binds prag_fn- = tcMethodBody clas tyvars dfun_ev_vars inst_tys- dfun_ev_binds is_derived hs_sig_fn- spec_inst_prags prags- sel_id user_bind bndr_loc+ = tcMethodBody skol_info clas tyvars dfun_ev_vars inst_tys+ dfun_ev_binds is_derived hs_sig_fn+ spec_inst_prags prags+ sel_id user_bind bndr_loc | otherwise = do { traceTc "tc_def" (ppr sel_id) ; tc_default sel_id dm_info }@@ -1759,7 +1803,7 @@ tc_default sel_id (Just (dm_name, _)) = do { (meth_bind, inline_prags) <- mkDefMethBind inst_loc dfun_id clas sel_id dm_name- ; tcMethodBody clas tyvars dfun_ev_vars inst_tys+ ; tcMethodBody skol_info clas tyvars dfun_ev_vars inst_tys dfun_ev_binds is_derived hs_sig_fn spec_inst_prags inline_prags sel_id meth_bind inst_loc }@@ -1786,7 +1830,7 @@ meth_tau = classMethodInstTy sel_id inst_tys error_string dflags = showSDoc dflags (hcat [ppr inst_loc, vbar, ppr sel_id ])- lam_wrapper = mkWpTyLams tyvars <.> mkWpLams dfun_ev_vars+ lam_wrapper = mkWpTyLams tyvars <.> mkWpEvLams dfun_ev_vars ---------------------- -- Check if one of the minimal complete definitions is satisfied@@ -1800,7 +1844,7 @@ -- Check if any method bindings do not correspond to the class. -- See Note [Mismatched class methods and associated type families]. checkMethBindMembership- = mapM_ (addErrTc . badMethodErr clas) mismatched_meths+ = mapM_ (addErrTc . TcRnBadMethodErr (className clas)) mismatched_meths where bind_nms = map unLoc $ collectMethodBinders binds cls_meth_nms = map (idName . fst) op_items@@ -1877,13 +1921,14 @@ -} -------------------------tcMethodBody :: Class -> [TcTyVar] -> [EvVar] -> [TcType]+tcMethodBody :: SkolemInfoAnon+ -> Class -> [TcTyVar] -> [EvVar] -> [TcType] -> TcEvBinds -> Bool -> HsSigFun -> [LTcSpecPrag] -> [LSig GhcRn] -> Id -> LHsBind GhcRn -> SrcSpan -> TcM (TcId, LHsBind GhcTc, Maybe Implication)-tcMethodBody clas tyvars dfun_ev_vars inst_tys+tcMethodBody skol_info clas tyvars dfun_ev_vars inst_tys dfun_ev_binds is_derived sig_fn spec_inst_prags prags sel_id (L bind_loc meth_bind) bndr_loc@@ -1901,7 +1946,7 @@ -- taking instance signature into account might change the type of -- the local_meth_id ; (meth_implic, ev_binds_var, tc_bind)- <- checkInstConstraints $+ <- checkInstConstraints skol_info $ tcMethodBodyHelp sig_fn sel_id local_meth_id (L bind_loc lm_bind) ; global_meth_id <- addInlinePrags global_meth_id prags@@ -1940,7 +1985,7 @@ = do { (sig_ty, hs_wrap) <- setSrcSpan (getLocA hs_sig_ty) $ do { inst_sigs <- xoptM LangExt.InstanceSigs- ; checkTc inst_sigs (misplacedInstSig sel_name hs_sig_ty)+ ; checkTc inst_sigs (TcRnMisplacedInstSig sel_name hs_sig_ty) ; let ctxt = FunSigCtxt sel_name NoRRC ; sig_ty <- tcHsSigType ctxt hs_sig_ty ; let local_meth_ty = idType local_meth_id@@ -1957,7 +2002,7 @@ ; let ctxt = FunSigCtxt sel_name (lhsSigTypeContextSpan hs_sig_ty) -- WantRCC <=> check for redundant constraints in the -- user-specified instance signature- inner_meth_id = mkLocalId inner_meth_name Many sig_ty+ inner_meth_id = mkLocalId inner_meth_name ManyTy sig_ty inner_meth_sig = CompleteSig { sig_bndr = inner_meth_id , sig_ctxt = ctxt , sig_loc = getLocA hs_sig_ty }@@ -2006,8 +2051,8 @@ ; local_meth_name <- newName sel_occ -- Base the local_meth_name on the selector name, because -- type errors from tcMethodBody come from here- ; let poly_meth_id = mkLocalId poly_meth_name Many poly_meth_ty- local_meth_id = mkLocalId local_meth_name Many local_meth_ty+ ; let poly_meth_id = mkLocalId poly_meth_name ManyTy poly_meth_ty+ local_meth_id = mkLocalId local_meth_name ManyTy local_meth_ty ; return (poly_meth_id, local_meth_id) } where@@ -2028,14 +2073,6 @@ , text " Class sig:" <+> ppr meth_ty ]) ; return (env2, msg) } -misplacedInstSig :: Name -> LHsSigType GhcRn -> TcRnMessage-misplacedInstSig name hs_ty- = TcRnUnknownMessage $ mkPlainError noHints $- vcat [ hang (text "Illegal type signature in instance declaration:")- 2 (hang (pprPrefixName name)- 2 (dcolon <+> ppr hs_ty))- , text "(Use InstanceSigs to allow this)" ]- {- Note [Instance method signatures] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ With -XInstanceSigs we allow the user to supply a signature for the@@ -2050,7 +2087,7 @@ class method (in this case: Age -> Age -> Bool), but it cannot be less polymorphic. Moreover, if a signature is given, the implementation code should match the signature, and type variables bound in the-singature should scope over the method body.+signature should scope over the method body. We achieve this by building a TcSigInfo for the method, whether or not there is an instance method signature, and using that to typecheck@@ -2126,7 +2163,7 @@ fn = noLocA (idName sel_id) visible_inst_tys = [ ty | (tcb, ty) <- tyConBinders (classTyCon clas) `zip` inst_tys- , tyConBinderArgFlag tcb /= Inferred ]+ , tyConBinderForAllTyFlag tcb /= Inferred ] rhs = foldl' mk_vta (nlHsVar dm_name) visible_inst_tys bind = L (noAnnSrcSpan loc) $ mkTopFunBind Generated fn@@ -2142,8 +2179,8 @@ (_, _, _, inst_tys) = tcSplitDFunTy (idType dfun_id) mk_vta :: LHsExpr GhcRn -> Type -> LHsExpr GhcRn- mk_vta fun ty = noLocA (HsAppType noExtField fun (mkEmptyWildCardBndrs $ nlHsParTy- $ noLocA $ XHsType ty))+ mk_vta fun ty = noLocA (HsAppType noExtField fun noHsTok+ (mkEmptyWildCardBndrs $ nlHsParTy $ noLocA $ XHsType ty)) -- NB: use visible type application -- See Note [Default methods in instances] @@ -2158,14 +2195,9 @@ warnUnsatisfiedMinimalDefinition :: ClassMinimalDef -> TcM () warnUnsatisfiedMinimalDefinition mindef = do { warn <- woptM Opt_WarnMissingMethods- ; let msg = TcRnUnknownMessage $- mkPlainDiagnostic (WarningWithFlag Opt_WarnMissingMethods) noHints message+ ; let msg = TcRnUnsatisfiedMinimalDef mindef ; diagnosticTc warn msg }- where- message = vcat [text "No explicit implementation for"- ,nest 2 $ pprBooleanFormulaNice mindef- ] {- Note [Export helper functions]@@ -2347,7 +2379,7 @@ ------------------------------ tcSpecInst :: Id -> Sig GhcRn -> TcM TcSpecPrag-tcSpecInst dfun_id prag@(SpecInstSig _ _ hs_ty)+tcSpecInst dfun_id prag@(SpecInstSig _ hs_ty) = addErrCtxt (spec_ctxt prag) $ do { spec_dfun_ty <- tcHsClsInstType SpecInstCtxt hs_ty ; co_fn <- tcSpecWrapper SpecInstCtxt (idType dfun_id) spec_dfun_ty@@ -2371,38 +2403,11 @@ instDeclCtxt2 :: Type -> SDoc instDeclCtxt2 dfun_ty- = inst_decl_ctxt (ppr (mkClassPred cls tys))+ = inst_decl_ctxt (ppr head_ty) where- (_,_,cls,tys) = tcSplitDFunTy dfun_ty+ (_,_,head_ty) = tcSplitQuantPredTy dfun_ty inst_decl_ctxt :: SDoc -> SDoc inst_decl_ctxt doc = hang (text "In the instance declaration for") 2 (quotes doc) -badBootFamInstDeclErr :: TcRnMessage-badBootFamInstDeclErr- = TcRnUnknownMessage $ mkPlainError noHints $ text "Illegal family instance in hs-boot file"--notFamily :: TyCon -> TcRnMessage-notFamily tycon- = TcRnUnknownMessage $ mkPlainError noHints $- vcat [ text "Illegal family instance for" <+> quotes (ppr tycon)- , nest 2 $ parens (ppr tycon <+> text "is not an indexed type family")]--assocInClassErr :: TyCon -> TcRnMessage-assocInClassErr name- = TcRnUnknownMessage $ mkPlainError noHints $- text "Associated type" <+> quotes (ppr name) <+>- text "must be inside a class instance"--badFamInstDecl :: TyCon -> TcRnMessage-badFamInstDecl tc_name- = TcRnUnknownMessage $ mkPlainError noHints $- vcat [ text "Illegal family instance for" <+>- quotes (ppr tc_name)- , nest 2 (parens $ text "Use TypeFamilies to allow indexed type families") ]--notOpenFamily :: TyCon -> TcRnMessage-notOpenFamily tc- = TcRnUnknownMessage $ mkPlainError noHints $- text "Illegal instance for closed family" <+> quotes (ppr tc)
compiler/GHC/Tc/TyCl/PatSyn.hs view
@@ -2,8 +2,6 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeFamilies #-} -{-# OPTIONS_GHC -Wno-incomplete-record-updates #-}- {- (c) The University of Glasgow 2006 (c) The GRASP/AQUA Project, Glasgow University, 1992-1998@@ -21,17 +19,27 @@ import GHC.Prelude import GHC.Hs+ import GHC.Tc.Gen.Pat-import GHC.Core.Multiplicity-import GHC.Core.Type ( tidyTyCoVarBinders, tidyTypes, tidyType, isManyDataConTy )-import GHC.Core.TyCo.Subst( extendTvSubstWithClone )+import GHC.Tc.Utils.Env+import GHC.Tc.Utils.TcMType+import GHC.Tc.Utils.Zonk import GHC.Tc.Errors.Types import GHC.Tc.Utils.Monad import GHC.Tc.Gen.Sig ( TcPragEnv, emptyPragEnv, completeSigFromId, lookupPragEnv , addInlinePrags, addInlinePragArity )-import GHC.Tc.Utils.Env-import GHC.Tc.Utils.TcMType-import GHC.Tc.Utils.Zonk+import GHC.Tc.Solver+import GHC.Tc.Utils.Unify+import GHC.Tc.Utils.TcType+import GHC.Tc.Types.Evidence+import GHC.Tc.Types.Origin+import GHC.Tc.TyCl.Build++import GHC.Core.Multiplicity+import GHC.Core.Type ( typeKind, tidyForAllTyBinders, tidyTypes, tidyType, isManyTy, mkTYPEapp )+import GHC.Core.TyCo.Subst( extendTvSubstWithClone )+import GHC.Core.Predicate+ import GHC.Builtin.Types.Prim import GHC.Types.Error import GHC.Types.Name@@ -42,21 +50,13 @@ import GHC.Utils.Outputable import GHC.Data.FastString import GHC.Types.Var-import GHC.Types.Var.Env( emptyTidyEnv, mkInScopeSet )+import GHC.Types.Var.Env( emptyTidyEnv, mkInScopeSetList ) import GHC.Types.Id-import GHC.Types.Id.Info( RecSelParent(..), setLevityInfoWithType )+import GHC.Types.Id.Info( RecSelParent(..) ) import GHC.Tc.Gen.Bind import GHC.Types.Basic-import GHC.Tc.Solver-import GHC.Tc.Utils.Unify-import GHC.Core.Predicate import GHC.Builtin.Types-import GHC.Tc.Utils.TcType-import GHC.Tc.Types.Evidence-import GHC.Tc.Types.Origin-import GHC.Tc.TyCl.Build import GHC.Types.Var.Set-import GHC.Types.Id.Make import GHC.Tc.TyCl.Utils import GHC.Core.ConLike import GHC.Types.FieldLabel@@ -127,7 +127,7 @@ ones; because pattern synonyms are top-level things, we will never solve them later if we can't solve them now. And if we were to carry on, tc_patsyn_finish does zonkTcTypeToType, which defaults any-unsolved unificatdion variables to Any, which confuses the error+unsolved unification variables to Any, which confuses the error reporting no end (#15685). So we use simplifyTop to completely solve the constraint, report@@ -215,8 +215,8 @@ -- See Note [Equality evidence in pattern synonyms] mkProvEvidence ev_id | EqPred r ty1 ty2 <- classifyPredType pred- , let k1 = tcTypeKind ty1- k2 = tcTypeKind ty2+ , let k1 = typeKind ty1+ k2 = typeKind ty2 is_homo = k1 `tcEqType` k2 homo_tys = [k1, ty1, ty2] hetero_tys = [k1, k2, ty1, ty2]@@ -242,7 +242,7 @@ -- See Note [Coercions that escape] dependentArgErr (arg, bad_cos) = failWithTc $ -- fail here: otherwise we get downstream errors- TcRnUnknownMessage $ mkPlainError noHints $+ mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ text "Iceland Jack! Iceland Jack! Stop torturing me!" , hang (text "Pattern-bound variable") 2 (ppr arg <+> dcolon <+> ppr (idType arg))@@ -277,7 +277,7 @@ can't go in the inner forall either. (What would the matcher apply the continuation to?) -Solution: do not quantiify over any unification variable whose kind+Solution: do not quantify over any unification variable whose kind mentions the existentials. We can conveniently do that by making the "taus" passed to simplifyInfer look like forall ex_tvs. arg_ty@@ -327,7 +327,7 @@ and is not implicitly instantiated. So in mkProvEvidence we lift (a ~# b) to (a ~ b). Tiresome, and-marginally less efficient, if the builder/martcher are not inlined.+marginally less efficient, if the builder/matcher are not inlined. See also Note [Lift equality constraints when quantifying] in GHC.Tc.Solver @@ -407,7 +407,7 @@ -- The existential 'x' should not appear in the result type -- Can't check this until we know P's arity (decl_arity above) ; let bad_tvs = filter (`elemVarSet` tyCoVarsOfType pat_ty) $ binderVars explicit_ex_bndrs- ; checkTc (null bad_tvs) $ TcRnUnknownMessage $ mkPlainError noHints $+ ; checkTc (null bad_tvs) $ mkTcRnUnknownMessage $ mkPlainError noHints $ hang (sep [ text "The result type of the signature for" <+> quotes (ppr name) <> comma , text "namely" <+> quotes (ppr pat_ty) ]) 2 (text "mentions existential type variable" <> plural bad_tvs@@ -423,7 +423,7 @@ ex_tvs = binderVars ex_bndrs -- Pattern synonyms currently cannot be linear (#18806)- ; checkTc (all (isManyDataConTy . scaledMult) arg_tys) $+ ; checkTc (all (isManyTy . scaledMult) arg_tys) $ TcRnLinearPatSyn sig_body_ty ; skol_info <- mkSkolemInfo (SigSkol (PatSynCtxt name) pat_ty [])@@ -436,7 +436,7 @@ -- expected type. Even though the tyvars in the type are -- already skolems, this step changes their TcLevels, -- avoiding level-check errors when unifying.- ; (skol_subst0, skol_univ_bndrs) <- skolemiseTvBndrsX skol_info emptyTCvSubst univ_bndrs+ ; (skol_subst0, skol_univ_bndrs) <- skolemiseTvBndrsX skol_info emptySubst univ_bndrs ; (skol_subst, skol_ex_bndrs) <- skolemiseTvBndrsX skol_info skol_subst0 ex_bndrs ; let skol_univ_tvs = binderVars skol_univ_bndrs skol_ex_tvs = binderVars skol_ex_bndrs@@ -456,8 +456,8 @@ pushLevelAndCaptureConstraints $ tcExtendNameTyVarEnv univ_tv_prs $ tcCheckPat PatSyn lpat (unrestricted skol_pat_ty) $- do { let in_scope = mkInScopeSet (mkVarSet skol_univ_tvs)- empty_subst = mkEmptyTCvSubst in_scope+ do { let in_scope = mkInScopeSetList skol_univ_tvs+ empty_subst = mkEmptySubst in_scope ; (inst_subst, ex_tvs') <- mapAccumLM newMetaTyVarX empty_subst skol_ex_tvs -- newMetaTyVarX: see the "Existential type variables" -- part of Note [Checking against a pattern signature]@@ -494,7 +494,7 @@ (args', skol_arg_tys) skol_pat_ty rec_fields } where- tc_arg :: TCvSubst -> Name -> Type -> TcM (LHsExpr GhcTc)+ tc_arg :: Subst -> Name -> Type -> TcM (LHsExpr GhcTc) -- Look up the variable actually bound by lpat -- and check that it has the expected type tc_arg subst arg_name arg_ty@@ -515,8 +515,8 @@ -- See Note [Pattern synonyms and higher rank types] ; return (mkLHsWrap wrap $ nlHsVar arg_id) } -skolemiseTvBndrsX :: SkolemInfo -> TCvSubst -> [VarBndr TyVar flag]- -> TcM (TCvSubst, [VarBndr TcTyVar flag])+skolemiseTvBndrsX :: SkolemInfo -> Subst -> [VarBndr TyVar flag]+ -> TcM (Subst, [VarBndr TcTyVar flag]) -- Make new TcTyVars, all skolems with levels, but do not clone -- The level is one level deeper than the current level -- See Note [Skolemising when checking a pattern synonym]@@ -525,8 +525,8 @@ ; let pushed_lvl = pushTcLevel tc_lvl details = SkolemTv skol_info pushed_lvl False - mk_skol_tv_x :: TCvSubst -> VarBndr TyVar flag- -> (TCvSubst, VarBndr TcTyVar flag)+ mk_skol_tv_x :: Subst -> VarBndr TyVar flag+ -> (Subst, VarBndr TcTyVar flag) mk_skol_tv_x subst (Bndr tv flag) = (subst', Bndr new_tv flag) where@@ -681,7 +681,7 @@ wrongNumberOfParmsErr :: Name -> Arity -> Arity -> TcM a wrongNumberOfParmsErr name decl_arity missing- = failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $+ = failWithTc $ mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Pattern synonym" <+> quotes (ppr name) <+> text "has" <+> speakNOf decl_arity (text "argument")) 2 (text "but its type signature has" <+> int missing <+> text "fewer arrows")@@ -719,8 +719,8 @@ ; pat_ty' <- zonkTcTypeToTypeX ze pat_ty ; arg_tys' <- zonkTcTypesToTypesX ze arg_tys - ; let (env1, univ_tvs) = tidyTyCoVarBinders emptyTidyEnv univ_tvs'- (env2, ex_tvs) = tidyTyCoVarBinders env1 ex_tvs'+ ; let (env1, univ_tvs) = tidyForAllTyBinders emptyTidyEnv univ_tvs'+ (env2, ex_tvs) = tidyForAllTyBinders env1 ex_tvs' req_theta = tidyTypes env2 req_theta' prov_theta = tidyTypes env2 prov_theta' arg_tys = tidyTypes env2 arg_tys'@@ -788,25 +788,25 @@ (ex_tvs, ex_tys, prov_theta, prov_dicts) (args, arg_tys) pat_ty = do { let loc' = locA loc- ; rr_name <- newNameAt (mkTyVarOcc "rep") loc'- ; tv_name <- newNameAt (mkTyVarOcc "r") loc'+ ; rr_name <- newNameAt (mkTyVarOccFS (fsLit "rep")) loc'+ ; tv_name <- newNameAt (mkTyVarOccFS (fsLit "r")) loc' ; let rr_tv = mkTyVar rr_name runtimeRepTy rr = mkTyVarTy rr_tv res_tv = mkTyVar tv_name (mkTYPEapp rr) res_ty = mkTyVarTy res_tv is_unlifted = null args && null prov_dicts (cont_args, cont_arg_tys)- | is_unlifted = ([nlHsVar voidPrimId], [unboxedUnitTy])- | otherwise = (args, arg_tys)+ | is_unlifted = ([nlHsDataCon unboxedUnitDataCon], [unboxedUnitTy])+ | otherwise = (args, arg_tys) cont_ty = mkInfSigmaTy ex_tvs prov_theta $ mkVisFunTysMany cont_arg_tys res_ty fail_ty = mkVisFunTyMany unboxedUnitTy res_ty ; matcher_name <- newImplicitBinder ps_name mkMatcherOcc- ; scrutinee <- newSysLocalId (fsLit "scrut") Many pat_ty- ; cont <- newSysLocalId (fsLit "cont") Many cont_ty- ; fail <- newSysLocalId (fsLit "fail") Many fail_ty+ ; scrutinee <- newSysLocalId (fsLit "scrut") ManyTy pat_ty+ ; cont <- newSysLocalId (fsLit "cont") ManyTy cont_ty+ ; fail <- newSysLocalId (fsLit "fail") ManyTy fail_ty ; dflags <- getDynFlags ; let matcher_tau = mkVisFunTysMany [pat_ty, cont_ty, fail_ty] res_ty@@ -818,7 +818,7 @@ inst_wrap = mkWpEvApps prov_dicts <.> mkWpTyApps ex_tys cont' = foldl' nlHsApp (mkLHsWrap inst_wrap (nlHsVar cont)) cont_args - fail' = nlHsApps fail [nlHsVar voidPrimId]+ fail' = nlHsApps fail [nlHsDataCon unboxedUnitDataCon] args = map nlVarPat [scrutinee, cont, fail] lwpat = noLocA $ WildPat pat_ty@@ -830,15 +830,13 @@ L (getLoc lpat) $ HsCase noExtField (nlHsVar scrutinee) $ MG{ mg_alts = L (l2l $ getLoc lpat) cases- , mg_ext = MatchGroupTc [unrestricted pat_ty] res_ty- , mg_origin = Generated+ , mg_ext = MatchGroupTc [unrestricted pat_ty] res_ty Generated } body' = noLocA $ HsLam noExtField $ MG{ mg_alts = noLocA [mkSimpleMatch LambdaExpr args body]- , mg_ext = MatchGroupTc (map unrestricted [pat_ty, cont_ty, fail_ty]) res_ty- , mg_origin = Generated+ , mg_ext = MatchGroupTc (map unrestricted [pat_ty, cont_ty, fail_ty]) res_ty Generated } match = mkMatch (mkPrefixFunRhs (L loc (idName patsyn_id))) [] (mkHsLams (rr_tv:res_tv:univ_tvs)@@ -846,8 +844,7 @@ (EmptyLocalBinds noExtField) mg :: MatchGroup GhcTc (LHsExpr GhcTc) mg = MG{ mg_alts = L (l2l $ getLoc match) [match]- , mg_ext = MatchGroupTc [] res_ty- , mg_origin = Generated+ , mg_ext = MatchGroupTc [] res_ty Generated } matcher_arity = length req_theta + 3 -- See Note [Pragmas for pattern synonyms]@@ -860,8 +857,8 @@ ; let bind = FunBind{ fun_id = L loc matcher_prag_id , fun_matches = mg- , fun_ext = idHsWrapper- , fun_tick = [] }+ , fun_ext = (idHsWrapper, [])+ } matcher_bind = unitBag (noLocA bind) ; traceTc "tcPatSynMatcher" (ppr ps_name $$ ppr (idType matcher_id)) ; traceTc "tcPatSynMatcher" (ppr matcher_bind)@@ -926,7 +923,7 @@ = return emptyBag | Left why <- mb_match_group -- Can't invert the pattern- = setSrcSpan (getLocA lpat) $ failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $+ = setSrcSpan (getLocA lpat) $ failWithTc $ mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ hang (text "Invalid right-hand side of bidirectional pattern synonym" <+> quotes (ppr ps_name) <> colon) 2 why@@ -942,9 +939,7 @@ Just (builder_name, builder_ty, need_dummy_arg) -> -- Normal case do { -- Bidirectional, so patSynBuilder returns Just- let pat_ty = patSynResultType patsyn- builder_id = modifyIdInfo (`setLevityInfoWithType` pat_ty) $- mkExportedVanillaId builder_name builder_ty+ let builder_id = mkExportedVanillaId builder_name builder_ty -- See Note [Exported LocalIds] in GHC.Types.Id (_, req_theta, _, prov_theta, arg_tys, _) = patSynSigBndr patsyn@@ -964,7 +959,7 @@ bind = FunBind { fun_id = L loc (idName builder_id) , fun_matches = match_group' , fun_ext = emptyNameSet- , fun_tick = [] }+ } sig = completeSigFromId (PatSynCtxt ps_name) builder_id @@ -1093,9 +1088,8 @@ = return $ unLoc $ foldl' nlHsApp (noLocA neg) [noLocA (HsOverLit noAnn n)] | otherwise = return $ HsOverLit noAnn n- go1 (SplicePat _ (HsSpliced _ _ (HsSplicedPat pat)))- = go1 pat- go1 (SplicePat _ (HsSpliced{})) = panic "Invalid splice variety"+ go1 (SplicePat (HsUntypedSpliceTop _ pat) _) = go1 pat+ go1 (SplicePat (HsUntypedSpliceNested _) _) = panic "tcPatToExpr: invalid nested splice" go1 (XPat (HsPatExpanded _ pat))= go1 pat -- See Note [Invertible view patterns]@@ -1112,9 +1106,6 @@ go1 p@(WildPat {}) = notInvertible p go1 p@(AsPat {}) = notInvertible p go1 p@(NPlusKPat {}) = notInvertible p- go1 p@(SplicePat _ (HsTypedSplice {})) = notInvertible p- go1 p@(SplicePat _ (HsUntypedSplice {})) = notInvertible p- go1 p@(SplicePat _ (HsQuasiQuote {})) = notInvertible p notInvertible p = Left (not_invertible_msg p) @@ -1280,7 +1271,7 @@ go1 :: Pat GhcTc -> ([TyVar], [EvVar]) go1 (LazyPat _ p) = go p- go1 (AsPat _ _ p) = go p+ go1 (AsPat _ _ _ p) = go p go1 (ParPat _ _ p _) = go p go1 (BangPat _ p) = go p go1 (ListPat _ ps) = mergeMany . map go $ ps
compiler/GHC/Tc/TyCl/Utils.hs view
@@ -64,8 +64,6 @@ import GHC.Unit.Module -import GHC.Rename.Utils (wrapGenSpan)- import GHC.Types.Basic import GHC.Types.Error import GHC.Types.FieldLabel@@ -83,6 +81,8 @@ import GHC.Types.TyThing import qualified GHC.LanguageExtensions as LangExt +import Language.Haskell.Syntax.Basic (FieldLabelString(..))+ import Control.Monad {-@@ -139,14 +139,15 @@ go_co (TyConAppCo _ tc cs) = go_tc tc `plusNameEnv` go_co_s cs go_co (AppCo co co') = go_co co `plusNameEnv` go_co co' go_co (ForAllCo _ co co') = go_co co `plusNameEnv` go_co co'- go_co (FunCo _ co_mult co co') = go_co co_mult `plusNameEnv` go_co co `plusNameEnv` go_co co'+ go_co (FunCo { fco_mult = m, fco_arg = a, fco_res = r })+ = go_co m `plusNameEnv` go_co a `plusNameEnv` go_co r go_co (CoVarCo _) = emptyNameEnv go_co (HoleCo {}) = emptyNameEnv go_co (AxiomInstCo _ _ cs) = go_co_s cs go_co (UnivCo p _ ty ty') = go_prov p `plusNameEnv` go ty `plusNameEnv` go ty' go_co (SymCo co) = go_co co go_co (TransCo co co') = go_co co `plusNameEnv` go_co co'- go_co (NthCo _ _ co) = go_co co+ go_co (SelCo _ co) = go_co co go_co (LRCo _ co) = go_co co go_co (InstCo co co') = go_co co `plusNameEnv` go_co co' go_co (KindCo co) = go_co co@@ -172,7 +173,7 @@ -- TODO: TyConSet is implemented as IntMap over uniques. -- But we could get away with something based on IntSet--- since we only check membershib, but never extract the+-- since we only check membership, but never extract the -- elements. type SynCycleState = TyConSet @@ -208,7 +209,7 @@ checkSynCycles :: Unit -> [TyCon] -> [LTyClDecl GhcRn] -> TcM () checkSynCycles this_uid tcs tyclds = case runSynCycleM (mapM_ (go emptyTyConSet []) tcs) emptyTyConSet of- Left (loc, err) -> setSrcSpan loc $ failWithTc (TcRnUnknownMessage $ mkPlainError noHints err)+ Left (loc, err) -> setSrcSpan loc $ failWithTc (mkTcRnUnknownMessage $ mkPlainError noHints err) Right _ -> return () where -- Try our best to print the LTyClDecl for locally defined things@@ -500,8 +501,8 @@ | otherwise = pprPanic "initialRoleEnv1" (ppr tc) where name = tyConName tc bndrs = tyConBinders tc- argflags = map tyConBinderArgFlag bndrs- num_exps = count isVisibleArgFlag argflags+ argflags = map tyConBinderForAllTyFlag bndrs+ num_exps = count isVisibleForAllTyFlag argflags -- if the number of annotations in the role annotation decl -- is wrong, just ignore it. We check this in the validity check.@@ -513,7 +514,7 @@ default_roles = build_default_roles argflags role_annots build_default_roles (argf : argfs) (m_annot : ras)- | isVisibleArgFlag argf+ | isVisibleForAllTyFlag argf = (m_annot `orElse` default_role) : build_default_roles argfs ras build_default_roles (_argf : argfs) ras = Nominal : build_default_roles argfs ras@@ -855,14 +856,14 @@ tcRecSelBinds :: [(Id, LHsBind GhcRn)] -> TcM TcGblEnv tcRecSelBinds sel_bind_prs- = tcExtendGlobalValEnv [sel_id | (L _ (IdSig _ sel_id)) <- sigs] $+ = tcExtendGlobalValEnv [sel_id | (L _ (XSig (IdSig sel_id))) <- sigs] $ do { (rec_sel_binds, tcg_env) <- discardWarnings $ -- See Note [Impredicative record selectors] setXOptM LangExt.ImpredicativeTypes $ tcValBinds TopLevel binds sigs getGblEnv ; return (tcg_env `addTypecheckedBinds` map snd rec_sel_binds) } where- sigs = [ L (noAnnSrcSpan loc) (IdSig noExtField sel_id)+ sigs = [ L (noAnnSrcSpan loc) (XSig $ IdSig sel_id) | (sel_id, _) <- sel_bind_prs , let loc = getSrcSpan sel_id ] binds = [(NonRecursive, unitBag bind) | (_, bind) <- sel_bind_prs]@@ -902,14 +903,30 @@ con1 = assert (not (null cons_w_field)) $ head cons_w_field -- Selector type; Note [Polymorphic selectors]- field_ty = conLikeFieldType con1 lbl- data_tvbs = filter (\tvb -> binderVar tvb `elemVarSet` data_tv_set) $- conLikeUserTyVarBinders con1- data_tv_set= tyCoVarsOfTypes inst_tys- is_naughty = not (tyCoVarsOfType field_ty `subVarSet` data_tv_set)- || has_sel == NoFieldSelectors+ (univ_tvs, _, _, _, req_theta, _, data_ty) = conLikeFullSig con1++ field_ty = conLikeFieldType con1 lbl+ field_ty_tvs = tyCoVarsOfType field_ty+ data_ty_tvs = tyCoVarsOfType data_ty+ sel_tvs = field_ty_tvs `unionVarSet` data_ty_tvs+ sel_tvbs = filter (\tvb -> binderVar tvb `elemVarSet` sel_tvs) $+ conLikeUserTyVarBinders con1++ -- is_naughty: see Note [Naughty record selectors]+ is_naughty = not ok_scoping || no_selectors+ ok_scoping = case con1 of+ RealDataCon {} -> field_ty_tvs `subVarSet` data_ty_tvs+ PatSynCon {} -> field_ty_tvs `subVarSet` mkVarSet univ_tvs+ -- In the PatSynCon case, the selector type is (data_ty -> field_ty), but+ -- fvs(data_ty) are all universals (see Note [Pattern synonym result type] in+ -- GHC.Core.PatSyn, so no need to check them.++ no_selectors = has_sel == NoFieldSelectors -- No field selectors => all are naughty+ -- thus suppressing making a binding+ -- A slight hack!+ sel_ty | is_naughty = unitTy -- See Note [Naughty record selectors]- | otherwise = mkForAllTys (tyVarSpecToBinders data_tvbs) $+ | otherwise = mkForAllTys (tyVarSpecToBinders sel_tvbs) $ -- Urgh! See Note [The stupid context] in GHC.Core.DataCon mkPhiTy (conLikeStupidTheta con1) $ -- req_theta is empty for normal DataCon@@ -920,7 +937,7 @@ -- fields in all the constructor have multiplicity Many. field_ty - -- Make the binding: sel (C2 { fld = x }) = x+ -- make the binding: sel (C2 { fld = x }) = x -- sel (C7 { fld = x }) = x -- where cons_w_field = [C2,C7] sel_bind = mkTopFunBind Generated sel_lname alts@@ -937,7 +954,7 @@ { hfbAnn = noAnn , hfbLHS = L locc (FieldOcc sel_name- (L locn $ mkVarUnqual lbl))+ (L locn $ mkVarUnqual (field_label lbl))) , hfbRHS = L loc' (VarPat noExtField (L locn field_var)) , hfbPun = False })@@ -949,11 +966,10 @@ -- mentions this particular record selector deflt | all dealt_with all_cons = [] | otherwise = [mkSimpleMatch CaseAlt- [wrapGenSpan (WildPat noExtField)]- (wrapGenSpan- (HsApp noComments- (wrapGenSpan (HsVar noExtField (wrapGenSpan (getName rEC_SEL_ERROR_ID))))- (wrapGenSpan (HsLit noComments msg_lit))))]+ [L loc' (WildPat noExtField)]+ (mkHsApp (L loc' (HsVar noExtField+ (L locn (getName rEC_SEL_ERROR_ID))))+ (L loc' (HsLit noComments msg_lit)))] -- Do not add a default case unless there are unmatched -- constructors. We must take account of GADTs, else we@@ -966,26 +982,13 @@ -- B :: { fld :: Int } -> T Int Char dealt_with :: ConLike -> Bool dealt_with (PatSynCon _) = False -- We can't predict overlap- dealt_with con@(RealDataCon dc) =- con `elem` cons_w_field || dataConCannotMatch inst_tys dc-- (univ_tvs, _, eq_spec, _, req_theta, _, data_ty) = conLikeFullSig con1-- eq_subst = mkTvSubstPrs (map eqSpecPair eq_spec)- -- inst_tys corresponds to one of the following:- --- -- * The arguments to the user-written return type (for GADT constructors).- -- In this scenario, eq_subst provides a mapping from the universally- -- quantified type variables to the argument types. Note that eq_subst- -- does not need to be applied to any other part of the DataCon- -- (see Note [The dcEqSpec domain invariant] in GHC.Core.DataCon).- -- * The universally quantified type variables- -- (for Haskell98-style constructors and pattern synonyms). In these- -- scenarios, eq_subst is an empty substitution.- inst_tys = substTyVars eq_subst univ_tvs+ dealt_with con@(RealDataCon dc)+ = con `elem` cons_w_field || dataConCannotMatch inst_tys dc+ where+ inst_tys = dataConResRepTyArgs dc unit_rhs = mkLHsTupleExpr [] noExtField- msg_lit = HsStringPrim NoSourceText (bytesFS lbl)+ msg_lit = HsStringPrim NoSourceText (bytesFS (field_label lbl)) {- Note [Polymorphic selectors]@@ -1042,16 +1045,41 @@ helpfully, rather than saying unhelpfully that 'x' is not in scope. Hence the sel_naughty flag, to identify record selectors that don't really exist. -In general, a field is "naughty" if its type mentions a type variable that-isn't in the result type of the constructor. Note that this *allows*-GADT record selectors (Note [GADT record selectors]) whose types may look-like sel :: T [a] -> a- For naughty selectors we make a dummy binding sel = () so that the later type-check will add them to the environment, and they'll be exported. The function is never called, because the typechecker spots the sel_naughty field.++To determine naughtiness we distingish two cases:++* For RealDataCons, a field is "naughty" if its type mentions a+ type variable that isn't in the (original, user-written) result type+ of the constructor. Note that this *allows* GADT record selectors+ (Note [GADT record selectors]) whose types may look like sel :: T [a] -> a++* For a PatSynCon, a field is "naughty" if its type mentions a type variable+ that isn't in the universal type variables.++ This is a bit subtle. Consider test patsyn/should_run/records_run:+ pattern ReadP :: forall a. ReadP a => a -> String+ pattern ReadP {fld} <- (read -> readp)+ The selector is defined like this:+ $selReadPfld :: forall a. ReadP a => String -> a+ $selReadPfld @a (d::ReadP a) s = readp @a d s+ Perfectly fine! The (ReadP a) constraint lets us contruct a value of type+ 'a' from a bare String.++ Another curious case (#23038):+ pattern N :: forall a. () => forall. () => a -> Any+ pattern N { fld } <- ( unsafeCoerce -> fld1 ) where N = unsafeCoerce+ The selector looks like this+ $selNfld :: forall a. Any -> a+ $selNfld @a x = unsafeCoerce @Any @a x+ Pretty strange (but used in the `cleff` package).++ TL;DR for pattern synonyms, the selector is OK if the field type mentions only+ the universal type variables of the pattern synonym. Note [NoFieldSelectors and naughty record selectors] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compiler/GHC/Tc/Utils/Backpack.hs view
@@ -27,7 +27,6 @@ import GHC.Types.Fixity.Env import GHC.Types.TypeEnv import GHC.Types.Name.Reader-import GHC.Types.Id import GHC.Types.Name import GHC.Types.Name.Env import GHC.Types.Name.Set@@ -35,6 +34,7 @@ import GHC.Types.SrcLoc import GHC.Types.SourceFile import GHC.Types.Var+import GHC.Types.Id( idType ) import GHC.Types.Unique.DSet import GHC.Types.Name.Shape import GHC.Types.PkgQual@@ -62,8 +62,6 @@ import GHC.Core.InstEnv import GHC.Core.FamInstEnv-import GHC.Core.Type-import GHC.Core.Multiplicity import GHC.IfaceToCore import GHC.Iface.Load@@ -90,9 +88,10 @@ import {-# SOURCE #-} GHC.Tc.Module + fixityMisMatch :: TyThing -> Fixity -> Fixity -> TcRnMessage fixityMisMatch real_thing real_fixity sig_fixity =- TcRnUnknownMessage $ mkPlainError noHints $+ mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ppr real_thing <+> text "has conflicting fixities in the module", text "and its hsig file", text "Main module:" <+> ppr_fix real_fixity,@@ -169,7 +168,7 @@ -- tcg_env (TODO: but maybe this isn't relevant anymore). r <- tcLookupImported_maybe name case r of- Failed err -> addErr (TcRnUnknownMessage $ mkPlainError noHints err)+ Failed err -> addErr (TcRnInterfaceLookupError name err) Succeeded real_thing -> checkHsigDeclM sig_iface sig_thing real_thing -- The hsig did NOT define this function; that means it must@@ -220,32 +219,23 @@ -- (we might conclude the module exports an instance when it doesn't, see -- #9422), but we will never refuse to compile something. check_inst :: ClsInst -> TcM ()-check_inst sig_inst = do+check_inst sig_inst@(ClsInst { is_dfun = dfun_id }) = do -- TODO: This could be very well generalized to support instance -- declarations in boot files. tcg_env <- getGblEnv -- NB: Have to tug on the interface, not necessarily -- tugged... but it didn't work? mapM_ tcLookupImported_maybe (nameSetElemsStable (orphNamesOfClsInst sig_inst))+ -- Based off of 'simplifyDeriv'- let ty = idType (instanceDFunId sig_inst)- -- Based off of tcSplitDFunTy- (tvs, theta, pred) =- case tcSplitForAllInvisTyVars ty of { (tvs, rho) ->- case splitFunTys rho of { (theta, pred) ->- (tvs, theta, pred) }}- origin = InstProvidedOrigin (tcg_semantic_mod tcg_env) sig_inst- skol_info <- mkSkolemInfo InstSkol- (skol_subst, tvs_skols) <- tcInstSkolTyVars skol_info tvs -- Skolemize+ let origin = InstProvidedOrigin (tcg_semantic_mod tcg_env) sig_inst+ (skol_info, tvs_skols, inst_theta, cls, inst_tys) <- tcSkolDFunType (idType dfun_id) (tclvl,cts) <- pushTcLevelM $ do- wanted <- newWanted origin- (Just TypeLevel)- (substTy skol_subst pred)- givens <- forM theta $ \given -> do+ wanted <- newWanted origin (Just TypeLevel) (mkClassPred cls inst_tys)+ givens <- forM inst_theta $ \given -> do loc <- getCtLocM origin (Just TypeLevel)- let given_pred = substTy skol_subst (scaledThing given)- new_ev <- newEvVar given_pred- return CtGiven { ctev_pred = given_pred+ new_ev <- newEvVar given+ return CtGiven { ctev_pred = given -- Doesn't matter, make something up , ctev_evar = new_ev , ctev_loc = loc@@ -253,7 +243,7 @@ return $ wanted : givens unsolved <- simplifyWantedsTcM cts - (implic, _) <- buildImplicationFor tclvl (getSkolemInfo skol_info) tvs_skols [] unsolved+ (implic, _) <- buildImplicationFor tclvl skol_info tvs_skols [] unsolved reportAllUnsolved (mkImplicWC implic) -- | For a module @modname@ of type 'HscSource', determine the list@@ -687,7 +677,7 @@ -- 3(d). Extend the name substitution (performing shaping) mb_r <- extend_ns nsubst as2 case mb_r of- Left err -> failWithTc (TcRnUnknownMessage $ mkPlainError noHints err)+ Left err -> failWithTc (mkTcRnUnknownMessage $ mkPlainError noHints err) Right nsubst' -> return (nsubst',oks',(imod, thinned_iface):ifaces) nsubst0 = mkNameShape (moduleName inner_mod) (mi_exports lcl_iface0) ok_to_use0 = mkOccSet (exportOccs (mi_exports lcl_iface0))@@ -855,7 +845,6 @@ -- we hope that we get lucky / the overlapping instances never -- get used, but it is not a very good situation to be in. --- hsc_env <- getTopEnv let merge_inst (insts, inst_env) inst | memberInstEnv inst_env inst -- test DFun Type equality = (insts, inst_env)@@ -866,18 +855,17 @@ (insts, inst_env) = foldl' merge_inst (tcg_insts tcg_env, tcg_inst_env tcg_env) (instEnvElts $ md_insts details)- -- This is a HACK to prevent calculateAvails from including imp_mod- -- in the listing. We don't want it because a module is NOT+ -- Use mi_deps directly rather than calculateAvails.+ -- because a module is NOT -- supposed to include itself in its dep_orphs/dep_finsts. See #13214- iface' = iface { mi_final_exts = (mi_final_exts iface){ mi_orphan = False, mi_finsts = False } }- home_unit = hsc_home_unit hsc_env- other_home_units = hsc_all_home_unit_ids hsc_env- avails = plusImportAvails (tcg_imports tcg_env) $- calculateAvails home_unit other_home_units iface' False NotBoot ImportedBySystem+ avails = tcg_imports tcg_env+ deps = mi_deps iface+ avails_with_trans = addTransitiveDepInfo avails deps+ return tcg_env { tcg_inst_env = inst_env, tcg_insts = insts,- tcg_imports = avails,+ tcg_imports = avails_with_trans, tcg_merged = if outer_mod == mi_module iface -- Don't add ourselves!@@ -911,6 +899,20 @@ return tcg_env +-- | Add on the necessary transitive information from the merged signature to+-- the 'ImportAvails' of the result of merging. This propagates the orphan instances+-- which were in the transitive closure of the signature through the merge.+addTransitiveDepInfo :: ImportAvails -- ^ From the signature resulting from the merge+ -> Dependencies -- ^ From the original signature+ -> ImportAvails+addTransitiveDepInfo avails deps =+ -- Avails for the merged in signature+ -- Add on transitive information from the signature but nothing else..+ -- because we do not "import" the signature.+ avails { imp_orphs = imp_orphs avails ++ dep_orphs deps+ , imp_finsts = imp_finsts avails ++ dep_finsts deps+ , imp_sig_mods = imp_sig_mods avails ++ dep_sig_mods deps }+ -- | Top-level driver for signature instantiation (run when compiling -- an @hsig@ file.) tcRnInstantiateSignature ::@@ -994,7 +996,7 @@ isig_mod sig_mod NotBoot isig_iface <- case mb_isig_iface of Succeeded (iface, _) -> return iface- Failed err -> failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $+ Failed err -> failWithTc $ mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Could not find hi interface for signature" <+> quotes (ppr isig_mod) <> colon) 4 err @@ -1002,7 +1004,7 @@ -- we need. (Notice we IGNORE the Modules in the AvailInfos.) forM_ (exportOccs (mi_exports isig_iface)) $ \occ -> case lookupGlobalRdrEnv impl_gr occ of- [] -> addErr $ TcRnUnknownMessage $ mkPlainError noHints $+ [] -> addErr $ mkTcRnUnknownMessage $ mkPlainError noHints $ quotes (ppr occ) <+> text "is exported by the hsig file, but not exported by the implementing module" <+> quotes (pprWithUnitState unit_state $ ppr impl_mod)
compiler/GHC/Tc/Utils/Concrete.hs view
@@ -18,16 +18,16 @@ import GHC.Builtin.Types ( liftedTypeKindTyCon, unliftedTypeKindTyCon ) -import GHC.Core.Coercion ( coToMCo, mkCastTyMCo )+import GHC.Core.Coercion ( coToMCo, mkCastTyMCo+ , mkGReflRightMCo, mkNomReflCo ) import GHC.Core.TyCo.Rep ( Type(..), MCoercion(..) ) import GHC.Core.TyCon ( isConcreteTyCon )-import GHC.Core.Type ( isConcrete, typeKind, tyVarKind, tcView+import GHC.Core.Type ( isConcrete, typeKind, tyVarKind, coreView , mkTyVarTy, mkTyConApp, mkFunTy, mkAppTy ) import GHC.Tc.Types ( TcM, ThStage(..), PendingStuff(..) ) import GHC.Tc.Types.Constraint ( NotConcreteError(..), NotConcreteReason(..) )-import GHC.Tc.Types.Evidence ( Role(..), TcCoercionN, TcMCoercionN- , mkTcGReflRightMCo, mkTcNomReflCo )+import GHC.Tc.Types.Evidence ( Role(..), TcCoercionN, TcMCoercionN ) import GHC.Tc.Types.Origin ( CtOrigin(..), FixedRuntimeRepContext, FixedRuntimeRepOrigin(..) ) import GHC.Tc.Utils.Monad ( emitNotConcreteError, setTcLevel, getCtLocM, getStage, traceTc ) import GHC.Tc.Utils.TcType ( TcType, TcKind, TcTypeFRR@@ -455,12 +455,12 @@ -- Otherwise: ensure that the kind 'ki' of 'ty' is concrete. | otherwise -> do { kco <- check_kind frr_orig ki- ; return ( mkTcGReflRightMCo Nominal ty kco+ ; return ( mkGReflRightMCo Nominal ty kco , mkCastTyMCo ty kco ) } } where refl :: (TcCoercionN, TcType)- refl = (mkTcNomReflCo ty, ty)+ refl = (mkNomReflCo ty, ty) ki :: TcKind ki = typeKind ty frr_orig :: FixedRuntimeRepOrigin@@ -634,7 +634,7 @@ where go :: TcType -> WriterT [NotConcreteReason] TcM TcType go ty- | Just ty <- tcView ty+ | Just ty <- coreView ty = go ty | isConcrete ty = pure ty
compiler/GHC/Tc/Utils/Env.hs view
@@ -257,7 +257,7 @@ do { mb_thing <- tcLookupImported_maybe name ; case mb_thing of Succeeded thing -> return thing- Failed msg -> failWithTc (TcRnUnknownMessage $ mkPlainError noHints msg)+ Failed msg -> failWithTc (TcRnInterfaceLookupError name msg) }}} -- Look up only in this module's global env't. Don't look in imports, etc.@@ -328,17 +328,17 @@ = do { instEnv <- tcGetInstEnvs ; case lookupUniqueInstEnv instEnv cls tys of Left err ->- failWithTc $ TcRnUnknownMessage+ failWithTc $ mkTcRnUnknownMessage $ mkPlainError noHints (text "Couldn't match instance:" <+> err) Right (inst, tys) | uniqueTyVars tys -> return inst- | otherwise -> failWithTc (TcRnUnknownMessage $ mkPlainError noHints errNotExact)+ | otherwise -> failWithTc (mkTcRnUnknownMessage $ mkPlainError noHints errNotExact) } where errNotExact = text "Not an exact match (i.e., some variables get instantiated)" uniqueTyVars tys = all isTyVarTy tys- && hasNoDups (map (getTyVar "tcLookupInstance") tys)+ && hasNoDups (map getTyVar tys) tcGetInstEnvs :: TcM InstEnvs -- Gets both the external-package inst-env@@ -553,7 +553,7 @@ names = [(name, ATyVar name tv) | (name, tv) <- binds] isTypeClosedLetBndr :: Id -> Bool--- See Note [Bindings with closed types] in GHC.Tc.Types+-- See Note [Bindings with closed types: ClosedTypeId] in GHC.Tc.Types isTypeClosedLetBndr = noFreeVarsOfType . idType tcExtendRecIds :: [(Name, TcId)] -> TcM a -> TcM a@@ -684,7 +684,7 @@ ; traceTc "check_then_add_usage" (ppr id_mult $$ ppr actual_u) ; wrapper <- case actual_u of Bottom -> return idHsWrapper- Zero -> tcSubMult (UsageEnvironmentOf name) Many id_mult+ Zero -> tcSubMult (UsageEnvironmentOf name) ManyTy id_mult MUsage m -> do { m <- promote_mult m ; tcSubMult (UsageEnvironmentOf name) m id_mult } ; tcEmitBindingUsage (deleteUE uenv name)@@ -853,7 +853,7 @@ It is entirely reasonable to reject this, but to do so we need A to be in the kind environment when kind-checking the signature for B. -Hence tcAddPatSynPlaceholers adds a binding+Hence tcAddPatSynPlaceholders adds a binding A -> APromotionErr PatSynPE to the environment. Then GHC.Tc.Gen.HsType.tcTyVar will find A in the kind environment, and will give a 'wrongThingErr' as a result. But the@@ -899,7 +899,7 @@ | otherwise -- Badly staged = failWithTc $ -- E.g. \x -> $(f x)- TcRnUnknownMessage $ mkPlainError noHints $+ mkTcRnUnknownMessage $ mkPlainError noHints $ text "Stage error:" <+> pp_thing <+> hsep [text "is bound at stage" <+> ppr bind_lvl, text "but used at stage" <+> ppr use_lvl]@@ -907,7 +907,7 @@ stageRestrictionError :: SDoc -> TcM a stageRestrictionError pp_thing = failWithTc $- TcRnUnknownMessage $ mkPlainError noHints $+ mkTcRnUnknownMessage $ mkPlainError noHints $ sep [ text "GHC stage restriction:" , nest 2 (vcat [ pp_thing <+> text "is used in a top-level splice, quasi-quote, or annotation," , text "and must be imported, not defined locally"])]@@ -961,7 +961,7 @@ -- User-supplied defaults Nothing -> do - -- No use-supplied default+ -- No user-supplied default -- Use [Integer, Double], plus modifications { integer_ty <- tcMetaTy integerTyConName ; list_ty <- tcMetaTy listTyConName@@ -1125,7 +1125,7 @@ => IORef (ModuleEnv Int) -> String -> String -> m FastString -- ^ @mkWrapperName ref what nameBase@ ----- See Note [Generating fresh names for ccall wrapper] for @ref@'s purpose.+-- See Note [Generating fresh names for FFI wrappers] for @ref@'s purpose. mkWrapperName wrapperRef what nameBase = do thisMod <- getModule let pkg = unitString (moduleUnit thisMod)@@ -1139,7 +1139,7 @@ {- Note [Generating fresh names for FFI wrappers]-+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We used to use a unique, rather than nextWrapperNum, to distinguish between FFI wrapper functions. However, the wrapper names that we generate are external names. This means that if a call to them ends up@@ -1175,7 +1175,7 @@ -- don't report it again (#11941) | otherwise -> stageRestrictionError (quotes (ppr name)) _ -> failWithTc $- TcRnUnknownMessage $ mkPlainError noHints $+ mkTcRnUnknownMessage $ mkPlainError noHints $ vcat[text "GHC internal error:" <+> quotes (ppr name) <+> text "is not in scope during type checking, but it passed the renamer", text "tcl_env of environment:" <+> ppr (tcl_env lcl_env)]@@ -1187,11 +1187,8 @@ } wrongThingErr :: String -> TcTyThing -> Name -> TcM a--- It's important that this only calls pprTcTyThingCategory, which in--- turn does not look at the details of the TcTyThing.--- See Note [Placeholder PatSyn kinds] in GHC.Tc.Gen.Bind wrongThingErr expected thing name- = let msg = TcRnUnknownMessage $ mkPlainError noHints $+ = let msg = mkTcRnUnknownMessage $ mkPlainError noHints $ (pprTcTyThingCategory thing <+> quotes (ppr name) <+> text "used as a" <+> text expected) in failWithTc msg
compiler/GHC/Tc/Utils/Instantiate.hs view
@@ -1,9 +1,7 @@--{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleContexts, RecursiveDo #-} {-# LANGUAGE DisambiguateRecordFields #-} {-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}-{-# OPTIONS_GHC -Wno-incomplete-record-updates #-} {- (c) The University of Glasgow 2006@@ -179,7 +177,7 @@ topSkolemise skolem_info ty = go init_subst idHsWrapper [] [] ty where- init_subst = mkEmptyTCvSubst (mkInScopeSet (tyCoVarsOfType ty))+ init_subst = mkEmptySubst (mkInScopeSet (tyCoVarsOfType ty)) -- Why recursive? See Note [Skolemisation] go subst wrap tv_prs ev_vars ty@@ -188,7 +186,7 @@ = do { (subst', tvs1) <- tcInstSkolTyVarsX skolem_info subst tvs ; ev_vars1 <- newEvVars (substTheta subst' theta) ; go subst'- (wrap <.> mkWpTyLams tvs1 <.> mkWpLams ev_vars1)+ (wrap <.> mkWpTyLams tvs1 <.> mkWpEvLams ev_vars1) (tv_prs ++ (map tyVarName tvs `zip` tvs1)) (ev_vars ++ ev_vars1) inner_ty }@@ -204,7 +202,7 @@ -- NB: returns a type with no (=>), -- and no invisible forall at the top topInstantiate orig sigma- | (tvs, body1) <- tcSplitSomeForAllTyVars isInvisibleArgFlag sigma+ | (tvs, body1) <- tcSplitSomeForAllTyVars isInvisibleForAllTyFlag sigma , (theta, body2) <- tcSplitPhiTy body1 , not (null tvs && null theta) = do { (_, wrap1, body3) <- instantiateSigma orig tvs theta body2@@ -220,7 +218,7 @@ instantiateSigma :: CtOrigin -> [TyVar] -> TcThetaType -> TcSigmaType -> TcM ([TcTyVar], HsWrapper, TcSigmaType) -- (instantiate orig tvs theta ty)--- instantiates the the type variables tvs, emits the (instantiated)+-- instantiates the type variables tvs, emits the (instantiated) -- constraints theta, and returns the (instantiated) type ty instantiateSigma orig tvs theta body_ty = do { (subst, inst_tvs) <- mapAccumLM newMetaTyVarX empty_subst tvs@@ -241,16 +239,16 @@ where free_tvs = tyCoVarsOfType body_ty `unionVarSet` tyCoVarsOfTypes theta in_scope = mkInScopeSet (free_tvs `delVarSetList` tvs)- empty_subst = mkEmptyTCvSubst in_scope+ empty_subst = mkEmptySubst in_scope -instTyVarsWith :: CtOrigin -> [TyVar] -> [TcType] -> TcM TCvSubst+instTyVarsWith :: CtOrigin -> [TyVar] -> [TcType] -> TcM Subst -- Use this when you want to instantiate (forall a b c. ty) with -- types [ta, tb, tc], but when the kinds of 'a' and 'ta' might -- not yet match (perhaps because there are unsolved constraints; #14154) -- If they don't match, emit a kind-equality to promise that they will--- eventually do so, and thus make a kind-homongeneous substitution.+-- eventually do so, and thus make a kind-homogeneous substitution. instTyVarsWith orig tvs tys- = go emptyTCvSubst tvs tys+ = go emptySubst tvs tys where go subst [] [] = return subst@@ -262,7 +260,7 @@ ; go (extendTvSubstAndInScope subst tv (ty `mkCastTy` co)) tvs tys } where tv_kind = substTy subst (tyVarKind tv)- ty_kind = tcTypeKind ty+ ty_kind = typeKind ty go _ _ _ = pprPanic "instTysWith" (ppr tvs $$ ppr tys) @@ -335,11 +333,11 @@ where dfun_ty = idType dfun_id (dfun_tvs, dfun_theta, _) = tcSplitSigmaTy dfun_ty- empty_subst = mkEmptyTCvSubst (mkInScopeSet (tyCoVarsOfType dfun_ty))+ empty_subst = mkEmptySubst (mkInScopeSet (tyCoVarsOfType dfun_ty)) -- With quantified constraints, the -- type of a dfun may not be closed - go :: TCvSubst -> [TyVar] -> [DFunInstType] -> TcM (TCvSubst, [TcType])+ go :: Subst -> [TyVar] -> [DFunInstType] -> TcM (Subst, [TcType]) go subst [] [] = return (subst, []) go subst (tv:tvs) (Just ty : mb_tys) = do { (subst', tys) <- go (extendTvSubstAndInScope subst tv ty)@@ -369,6 +367,7 @@ -- | Given ty::forall k1 k2. k, instantiate all the invisible forall-binders -- returning ty @kk1 @kk2 :: k[kk1/k1, kk2/k1]+-- Called only to instantiate kinds, in user-written type signatures tcInstInvisibleTyBinders :: TcType -> TcKind -> TcM (TcType, TcKind) tcInstInvisibleTyBinders ty kind = do { (extra_args, kind') <- tcInstInvisibleTyBindersN n_invis kind@@ -377,37 +376,40 @@ n_invis = invisibleTyBndrCount kind tcInstInvisibleTyBindersN :: Int -> TcKind -> TcM ([TcType], TcKind)+-- Called only to instantiate kinds, in user-written type signatures tcInstInvisibleTyBindersN 0 kind = return ([], kind) tcInstInvisibleTyBindersN n ty = go n empty_subst ty where- empty_subst = mkEmptyTCvSubst (mkInScopeSet (tyCoVarsOfType ty))+ empty_subst = mkEmptySubst (mkInScopeSet (tyCoVarsOfType ty)) go n subst kind | n > 0 , Just (bndr, body) <- tcSplitPiTy_maybe kind- , isInvisibleBinder bndr+ , isInvisiblePiTyBinder bndr = do { (subst', arg) <- tcInstInvisibleTyBinder subst bndr ; (args, inner_ty) <- go (n-1) subst' body ; return (arg:args, inner_ty) } | otherwise = return ([], substTy subst kind) --- | Used only in *types*-tcInstInvisibleTyBinder :: TCvSubst -> TyBinder -> TcM (TCvSubst, TcType)+tcInstInvisibleTyBinder :: Subst -> PiTyVarBinder -> TcM (Subst, TcType)+-- Called only to instantiate kinds, in user-written type signatures+ tcInstInvisibleTyBinder subst (Named (Bndr tv _)) = do { (subst', tv') <- newMetaTyVarX subst tv ; return (subst', mkTyVarTy tv') } -tcInstInvisibleTyBinder subst (Anon af ty)+tcInstInvisibleTyBinder subst (Anon ty af) | Just (mk, k1, k2) <- get_eq_tys_maybe (substTy subst (scaledThing ty))- -- Equality is the *only* constraint currently handled in types.+ -- For kinds like (k1 ~ k2) => blah, we want to emit a unification+ -- constraint for (k1 ~# k2) and return the argument (Eq# k1 k2) -- See Note [Constraints in kinds] in GHC.Core.TyCo.Rep- = assert (af == InvisArg) $+ -- Equality is the *only* constraint currently handled in types.+ = assert (isInvisibleFunArg af) $ do { co <- unifyKind Nothing k1 k2- ; arg' <- mk co- ; return (subst, arg') }+ ; return (subst, mk co) } | otherwise -- This should never happen -- See GHC.Core.TyCo.Rep Note [Constraints in kinds]@@ -415,9 +417,9 @@ ------------------------------- get_eq_tys_maybe :: Type- -> Maybe ( Coercion -> TcM Type- -- given a coercion proving t1 ~# t2, produce the- -- right instantiation for the TyBinder at hand+ -> Maybe ( Coercion -> Type+ -- Given a coercion proving t1 ~# t2, produce the+ -- right instantiation for the PiTyVarBinder at hand , Type -- t1 , Type -- t2 )@@ -426,31 +428,28 @@ -- Lifted heterogeneous equality (~~) | Just (tc, [_, _, k1, k2]) <- splitTyConApp_maybe ty , tc `hasKey` heqTyConKey- = Just (\co -> mkHEqBoxTy co k1 k2, k1, k2)+ = Just (mkHEqBoxTy k1 k2, k1, k2) -- Lifted homogeneous equality (~) | Just (tc, [_, k1, k2]) <- splitTyConApp_maybe ty , tc `hasKey` eqTyConKey- = Just (\co -> mkEqBoxTy co k1 k2, k1, k2)+ = Just (mkEqBoxTy k1 k2, k1, k2) | otherwise = Nothing -- | This takes @a ~# b@ and returns @a ~~ b@.-mkHEqBoxTy :: TcCoercion -> Type -> Type -> TcM Type--- monadic just for convenience with mkEqBoxTy-mkHEqBoxTy co ty1 ty2- = return $- mkTyConApp (promoteDataCon heqDataCon) [k1, k2, ty1, ty2, mkCoercionTy co]- where k1 = tcTypeKind ty1- k2 = tcTypeKind ty2+mkHEqBoxTy :: Type -> Type -> TcCoercion -> Type+mkHEqBoxTy ty1 ty2 co+ = mkTyConApp (promoteDataCon heqDataCon) [k1, k2, ty1, ty2, mkCoercionTy co]+ where k1 = typeKind ty1+ k2 = typeKind ty2 -- | This takes @a ~# b@ and returns @a ~ b@.-mkEqBoxTy :: TcCoercion -> Type -> Type -> TcM Type-mkEqBoxTy co ty1 ty2- = return $- mkTyConApp (promoteDataCon eqDataCon) [k, ty1, ty2, mkCoercionTy co]- where k = tcTypeKind ty1+mkEqBoxTy :: Type -> Type -> TcCoercion -> Type+mkEqBoxTy ty1 ty2 co+ = mkTyConApp (promoteDataCon eqDataCon) [k, ty1, ty2, mkCoercionTy co]+ where k = typeKind ty1 {- ********************************************************************* * *@@ -458,7 +457,7 @@ * * ********************************************************************* -} -tcInstType :: ([TyVar] -> TcM (TCvSubst, [TcTyVar]))+tcInstType :: ([TyVar] -> TcM (Subst, [TcTyVar])) -- ^ How to instantiate the type variables -> Id -- ^ Type to instantiate -> TcM ([(Name, TcTyVar)], TcThetaType, TcType) -- ^ Result@@ -470,50 +469,62 @@ | otherwise = do { (subst, tyvars') <- inst_tyvars tyvars ; let tv_prs = map tyVarName tyvars `zip` tyvars'- subst' = extendTCvInScopeSet subst (tyCoVarsOfType rho)+ subst' = extendSubstInScopeSet subst (tyCoVarsOfType rho) ; return (tv_prs, substTheta subst' theta, substTy subst' tau) } where (tyvars, rho) = tcSplitForAllInvisTyVars (idType id) (theta, tau) = tcSplitPhiTy rho -tcInstTypeBndrs :: Id -> TcM ([(Name, InvisTVBinder)], TcThetaType, TcType)+tcInstTypeBndrs :: Type -> TcM ([(Name, InvisTVBinder)], TcThetaType, TcType) -- (type vars, preds (incl equalities), rho) -- Instantiate the binders of a type signature with TyVarTvs-tcInstTypeBndrs id+tcInstTypeBndrs poly_ty | null tyvars -- There may be overloading despite no type variables; -- (?x :: Int) => Int -> Int = return ([], theta, tau) | otherwise- = do { (subst, tyvars') <- mapAccumLM inst_invis_bndr emptyTCvSubst tyvars+ = do { (subst, tyvars') <- mapAccumLM inst_invis_bndr emptySubst tyvars ; let tv_prs = map (tyVarName . binderVar) tyvars `zip` tyvars'- subst' = extendTCvInScopeSet subst (tyCoVarsOfType rho)+ subst' = extendSubstInScopeSet subst (tyCoVarsOfType rho) ; return (tv_prs, substTheta subst' theta, substTy subst' tau) } where- (tyvars, rho) = splitForAllInvisTVBinders (idType id)+ (tyvars, rho) = tcSplitForAllInvisTVBinders poly_ty (theta, tau) = tcSplitPhiTy rho - inst_invis_bndr :: TCvSubst -> InvisTVBinder- -> TcM (TCvSubst, InvisTVBinder)+ inst_invis_bndr :: Subst -> InvisTVBinder+ -> TcM (Subst, InvisTVBinder) inst_invis_bndr subst (Bndr tv spec) = do { (subst', tv') <- newMetaTyVarTyVarX subst tv ; return (subst', Bndr tv' spec) } ---------------------------tcSkolDFunType :: SkolemInfo -> DFunId -> TcM ([TcTyVar], TcThetaType, TcType)+tcSkolDFunType :: Type -> TcM (SkolemInfoAnon, [TcTyVar], TcThetaType, Class, [TcType]) -- Instantiate a type signature with skolem constants. -- This freshens the names, but no need to do so-tcSkolDFunType skol_info dfun- = do { (tv_prs, theta, tau) <- tcInstType (tcInstSuperSkolTyVars skol_info) dfun- ; return (map snd tv_prs, theta, tau) }+tcSkolDFunType dfun_ty+ = do { let (tvs, theta, cls, tys) = tcSplitDFunTy dfun_ty -tcSuperSkolTyVars :: TcLevel -> SkolemInfo -> [TyVar] -> (TCvSubst, [TcTyVar])+ -- rec {..}: see Note [Keeping SkolemInfo inside a SkolemTv]+ -- in GHC.Tc.Utils.TcType+ ; rec { skol_info <- mkSkolemInfo skol_info_anon+ ; (subst, inst_tvs) <- tcInstSuperSkolTyVars skol_info tvs+ -- We instantiate the dfun_tyd with superSkolems.+ -- See Note [Subtle interaction of recursion and overlap]+ -- and Note [Binding when looking up instances]+ ; let inst_tys = substTys subst tys+ skol_info_anon = mkClsInstSkol cls inst_tys }++ ; let inst_theta = substTheta subst theta+ ; return (skol_info_anon, inst_tvs, inst_theta, cls, inst_tys) }++tcSuperSkolTyVars :: TcLevel -> SkolemInfo -> [TyVar] -> (Subst, [TcTyVar]) -- Make skolem constants, but do *not* give them new names, as above -- As always, allocate them one level in -- Moreover, make them "super skolems"; see GHC.Core.InstEnv -- Note [Binding when looking up instances] -- See Note [Kind substitution when instantiating] -- Precondition: tyvars should be ordered by scoping-tcSuperSkolTyVars tc_lvl skol_info = mapAccumL do_one emptyTCvSubst+tcSuperSkolTyVars tc_lvl skol_info = mapAccumL do_one emptySubst where details = SkolemTv skol_info (pushTcLevel tc_lvl) True -- The "super" bit@@ -525,29 +536,31 @@ -- | Given a list of @['TyVar']@, skolemize the type variables, -- returning a substitution mapping the original tyvars to the -- skolems, and the list of newly bound skolems.-tcInstSkolTyVars :: SkolemInfo -> [TyVar] -> TcM (TCvSubst, [TcTyVar])+tcInstSkolTyVars :: SkolemInfo -> [TyVar] -> TcM (Subst, [TcTyVar]) -- See Note [Skolemising type variables]-tcInstSkolTyVars skol_info = tcInstSkolTyVarsX skol_info emptyTCvSubst+tcInstSkolTyVars skol_info = tcInstSkolTyVarsX skol_info emptySubst -tcInstSkolTyVarsX :: SkolemInfo -> TCvSubst -> [TyVar] -> TcM (TCvSubst, [TcTyVar])+tcInstSkolTyVarsX :: SkolemInfo -> Subst -> [TyVar] -> TcM (Subst, [TcTyVar]) -- See Note [Skolemising type variables] tcInstSkolTyVarsX skol_info = tcInstSkolTyVarsPushLevel skol_info False -tcInstSuperSkolTyVars :: SkolemInfo -> [TyVar] -> TcM (TCvSubst, [TcTyVar])+tcInstSuperSkolTyVars :: SkolemInfo -> [TyVar] -> TcM (Subst, [TcTyVar]) -- See Note [Skolemising type variables] -- This version freshens the names and creates "super skolems";--- see comments around superSkolemTv.-tcInstSuperSkolTyVars skol_info = tcInstSuperSkolTyVarsX skol_info emptyTCvSubst+-- see comments around superSkolemTv.+-- Must be lazy in skol_info:+-- see Note [Keeping SkolemInfo inside a SkolemTv] in GHC.Tc.Utils.TcType+tcInstSuperSkolTyVars skol_info = tcInstSuperSkolTyVarsX skol_info emptySubst -tcInstSuperSkolTyVarsX :: SkolemInfo -> TCvSubst -> [TyVar] -> TcM (TCvSubst, [TcTyVar])+tcInstSuperSkolTyVarsX :: SkolemInfo -> Subst -> [TyVar] -> TcM (Subst, [TcTyVar]) -- See Note [Skolemising type variables] -- This version freshens the names and creates "super skolems"; -- see comments around superSkolemTv. tcInstSuperSkolTyVarsX skol_info subst = tcInstSkolTyVarsPushLevel skol_info True subst tcInstSkolTyVarsPushLevel :: SkolemInfo -> Bool -- True <=> make "super skolem"- -> TCvSubst -> [TyVar]- -> TcM (TCvSubst, [TcTyVar])+ -> Subst -> [TyVar]+ -> TcM (Subst, [TcTyVar]) -- Skolemise one level deeper, hence pushTcLevel -- See Note [Skolemising type variables] tcInstSkolTyVarsPushLevel skol_info overlappable subst tvs@@ -557,8 +570,8 @@ ; tcInstSkolTyVarsAt skol_info pushed_lvl overlappable subst tvs } tcInstSkolTyVarsAt :: SkolemInfo -> TcLevel -> Bool- -> TCvSubst -> [TyVar]- -> TcM (TCvSubst, [TcTyVar])+ -> Subst -> [TyVar]+ -> TcM (Subst, [TcTyVar]) tcInstSkolTyVarsAt skol_info lvl overlappable subst tvs = freshenTyCoVarsX new_skol_tv subst tvs where@@ -575,12 +588,12 @@ ; skol_info <- mkSkolemInfo skol_info ; let details = SkolemTv skol_info lvl False mk_skol_tv name kind = return (mkTcTyVar name kind details) -- No freshening- ; (subst, tvs') <- instantiateTyVarsX mk_skol_tv emptyTCvSubst tvs+ ; (subst, tvs') <- instantiateTyVarsX mk_skol_tv emptySubst tvs ; return (tvs', substTy subst body_ty) } instantiateTyVarsX :: (Name -> Kind -> TcM TcTyVar)- -> TCvSubst -> [TyVar]- -> TcM (TCvSubst, [TcTyVar])+ -> Subst -> [TyVar]+ -> TcM (Subst, [TcTyVar]) -- Instantiate each type variable in turn with the specified function instantiateTyVarsX mk_tv subst tvs = case tvs of@@ -592,25 +605,25 @@ ; return (subst', tv':tvs') } -------------------freshenTyVarBndrs :: [TyVar] -> TcM (TCvSubst, [TyVar])+freshenTyVarBndrs :: [TyVar] -> TcM (Subst, [TyVar]) -- ^ Give fresh uniques to a bunch of TyVars, but they stay -- as TyVars, rather than becoming TcTyVars -- Used in 'GHC.Tc.Instance.Family.newFamInst', and 'GHC.Tc.Utils.Instantiate.newClsInst' freshenTyVarBndrs = freshenTyCoVars mkTyVar -freshenCoVarBndrsX :: TCvSubst -> [CoVar] -> TcM (TCvSubst, [CoVar])+freshenCoVarBndrsX :: Subst -> [CoVar] -> TcM (Subst, [CoVar]) -- ^ Give fresh uniques to a bunch of CoVars -- Used in "GHC.Tc.Instance.Family.newFamInst" freshenCoVarBndrsX subst = freshenTyCoVarsX mkCoVar subst ------------------ freshenTyCoVars :: (Name -> Kind -> TyCoVar)- -> [TyVar] -> TcM (TCvSubst, [TyCoVar])-freshenTyCoVars mk_tcv = freshenTyCoVarsX mk_tcv emptyTCvSubst+ -> [TyVar] -> TcM (Subst, [TyCoVar])+freshenTyCoVars mk_tcv = freshenTyCoVarsX mk_tcv emptySubst freshenTyCoVarsX :: (Name -> Kind -> TyCoVar)- -> TCvSubst -> [TyCoVar]- -> TcM (TCvSubst, [TyCoVar])+ -> Subst -> [TyCoVar]+ -> TcM (Subst, [TyCoVar]) -- This a complete freshening operation: -- the skolems have a fresh unique, and a location from the monad -- See Note [Skolemising type variables]@@ -670,7 +683,7 @@ from old variables to the new variables, then instantiate the type variables substituting the original kind. -Exemple: If we want to instantiate+Example: If we want to instantiate [(k1 :: *), (k2 :: *), (a :: k1 -> k2), (b :: k1)] we want [(?k1 :: *), (?k2 :: *), (?a :: ?k1 -> ?k2), (?b :: ?k1)]
compiler/GHC/Tc/Utils/Monad.hs view
@@ -4,7 +4,6 @@ {-# LANGUAGE RecordWildCards #-} {-# OPTIONS_GHC -fno-warn-orphans #-}-{-# OPTIONS_GHC -Wno-incomplete-record-updates #-} {- (c) The University of Glasgow 2006@@ -47,7 +46,7 @@ -- * Debugging traceTc, traceRn, traceOptTcRn, dumpOptTcRn, dumpTcRn,- getPrintUnqualified,+ getNamePprCtx, printForUserTcRn, traceIf, traceOptIf, debugTc,@@ -70,7 +69,7 @@ addErrAt, addErrs, checkErr, addMessages,- discardWarnings,+ discardWarnings, mkDetailedMessage, -- * Usage environment tcCollectingUsage, tcScalingUsage, tcEmitBindingUsage,@@ -848,11 +847,11 @@ dumpTcRn :: Bool -> DumpFlag -> String -> DumpFormat -> SDoc -> TcRn () dumpTcRn useUserStyle flag title fmt doc = do logger <- getLogger- printer <- getPrintUnqualified+ name_ppr_ctx <- getNamePprCtx real_doc <- wrapDocLoc doc let sty = if useUserStyle- then mkUserStyle printer AllTheWay- else mkDumpStyle printer+ then mkUserStyle name_ppr_ctx AllTheWay+ else mkDumpStyle name_ppr_ctx liftIO $ logDumpFile logger sty flag title fmt real_doc -- | Add current location if -dppr-debug@@ -867,18 +866,19 @@ else return doc -getPrintUnqualified :: TcRn PrintUnqualified-getPrintUnqualified- = do { rdr_env <- getGlobalRdrEnv+getNamePprCtx :: TcRn NamePprCtx+getNamePprCtx+ = do { ptc <- initPromotionTickContext <$> getDynFlags+ ; rdr_env <- getGlobalRdrEnv ; hsc_env <- getTopEnv- ; return $ mkPrintUnqualified (hsc_unit_env hsc_env) rdr_env }+ ; return $ mkNamePprCtx ptc (hsc_unit_env hsc_env) rdr_env } -- | Like logInfoTcRn, but for user consumption printForUserTcRn :: SDoc -> TcRn () printForUserTcRn doc = do logger <- getLogger- printer <- getPrintUnqualified- liftIO (printOutputForUser logger printer doc)+ name_ppr_ctx <- getNamePprCtx+ liftIO (printOutputForUser logger name_ppr_ctx doc) {- traceIf works in the TcRnIf monad, where no RdrEnv is@@ -1068,8 +1068,13 @@ addErrAt loc msg = do { ctxt <- getErrCtxt ; tidy_env <- tcInitTidyEnv ; err_info <- mkErrInfo tidy_env ctxt- ; add_long_err_at loc (TcRnMessageDetailed (ErrInfo err_info Outputable.empty) msg) }+ ; let detailed_msg = mkDetailedMessage (ErrInfo err_info Outputable.empty) msg+ ; add_long_err_at loc detailed_msg } +mkDetailedMessage :: ErrInfo -> TcRnMessage -> TcRnMessageDetailed+mkDetailedMessage err_info msg =+ TcRnMessageDetailed err_info msg+ addErrs :: [(SrcSpan,TcRnMessage)] -> TcRn () addErrs msgs = mapM_ add msgs where@@ -1113,9 +1118,9 @@ where mk_long_err_at :: SrcSpan -> TcRnMessageDetailed -> TcRn (MsgEnvelope TcRnMessage) mk_long_err_at loc msg- = do { printer <- getPrintUnqualified ;+ = do { name_ppr_ctx <- getNamePprCtx ; unit_state <- hsc_units <$> getTopEnv ;- return $ mkErrorMsgEnvelope loc printer+ return $ mkErrorMsgEnvelope loc name_ppr_ctx $ TcRnMessageWithInfo unit_state msg } @@ -1123,16 +1128,16 @@ -> TcRnMessage -> TcRn (MsgEnvelope TcRnMessage) mkTcRnMessage loc msg- = do { printer <- getPrintUnqualified ;+ = do { name_ppr_ctx <- getNamePprCtx ; diag_opts <- initDiagOpts <$> getDynFlags ;- return $ mkMsgEnvelope diag_opts loc printer msg }+ return $ mkMsgEnvelope diag_opts loc name_ppr_ctx msg } reportDiagnostics :: [MsgEnvelope TcRnMessage] -> TcM () reportDiagnostics = mapM_ reportDiagnostic reportDiagnostic :: MsgEnvelope TcRnMessage -> TcRn () reportDiagnostic msg- = do { traceTc "Adding diagnostic:" (pprLocMsgEnvelope msg) ;+ = do { traceTc "Adding diagnostic:" (pprLocMsgEnvelopeDefault msg) ; errs_var <- getErrsVar ; msgs <- readTcRef errs_var ; writeTcRef errs_var (msg `addMessage` msgs) }@@ -1191,7 +1196,7 @@ This reliance on delicate inlining and Called Arity is not good. See #18202 for a more general approach. But meanwhile, these-ininings seem unobjectional, and they solve the immediate+inlinings seem unobjectional, and they solve the immediate problem. Note [Error contexts in generated code]@@ -1601,19 +1606,20 @@ = do { ctxt <- getErrCtxt ; extra <- mkErrInfo env0 ctxt ; let err_info = ErrInfo extra Outputable.empty- ; add_diagnostic (TcRnMessageDetailed err_info msg) }+ detailed_msg = mkDetailedMessage err_info msg+ ; add_diagnostic detailed_msg } -- | A variation of 'addDiagnostic' that takes a function to produce a 'TcRnDsMessage' -- given some additional context about the diagnostic. addDetailedDiagnostic :: (ErrInfo -> TcRnMessage) -> TcM () addDetailedDiagnostic mkMsg = do loc <- getSrcSpanM- printer <- getPrintUnqualified+ name_ppr_ctx <- getNamePprCtx !diag_opts <- initDiagOpts <$> getDynFlags env0 <- tcInitTidyEnv ctxt <- getErrCtxt err_info <- mkErrInfo env0 ctxt- reportDiagnostic (mkMsgEnvelope diag_opts loc printer (mkMsg (ErrInfo err_info empty)))+ reportDiagnostic (mkMsgEnvelope diag_opts loc name_ppr_ctx (mkMsg (ErrInfo err_info empty))) addTcRnDiagnostic :: TcRnMessage -> TcM () addTcRnDiagnostic msg = do@@ -1623,14 +1629,14 @@ -- | Display a diagnostic for the current source location, taken from -- the 'TcRn' monad. addDiagnostic :: TcRnMessage -> TcRn ()-addDiagnostic msg = add_diagnostic (TcRnMessageDetailed no_err_info msg)+addDiagnostic msg = add_diagnostic (mkDetailedMessage no_err_info msg) -- | Display a diagnostic for a given source location. addDiagnosticAt :: SrcSpan -> TcRnMessage -> TcRn () addDiagnosticAt loc msg = do unit_state <- hsc_units <$> getTopEnv- let dia = TcRnMessageDetailed no_err_info msg- mkTcRnMessage loc (TcRnMessageWithInfo unit_state dia) >>= reportDiagnostic+ let detailed_msg = mkDetailedMessage no_err_info msg+ mkTcRnMessage loc (TcRnMessageWithInfo unit_state detailed_msg) >>= reportDiagnostic -- | Display a diagnostic, with an optional flag, for the current source -- location.@@ -1652,7 +1658,7 @@ -> TcM () add_err_tcm tidy_env msg loc ctxt = do { err_info <- mkErrInfo tidy_env ctxt ;- add_long_err_at loc (TcRnMessageDetailed (ErrInfo err_info Outputable.empty) msg) }+ add_long_err_at loc (mkDetailedMessage (ErrInfo err_info Outputable.empty) msg) } mkErrInfo :: TidyEnv -> [ErrCtxt] -> TcM SDoc -- Tidy the error info, trimming excessive contexts@@ -1910,12 +1916,12 @@ emitAnonTypeHole extra_constraints tv = do { ct_loc <- getCtLocM (TypeHoleOrigin occ) Nothing ; let hole = Hole { hole_sort = sort- , hole_occ = occ+ , hole_occ = mkRdrUnqual occ , hole_ty = mkTyVarTy tv , hole_loc = ct_loc } ; emitHole hole } where- occ = mkTyVarOcc "_"+ occ = mkTyVarOccFS (fsLit "_") sort | YesExtraConstraint <- extra_constraints = ConstraintHole | otherwise = TypeHole @@ -1924,7 +1930,7 @@ = do { ct_loc <- setSrcSpan (nameSrcSpan name) $ getCtLocM (TypeHoleOrigin occ) Nothing ; let hole = Hole { hole_sort = TypeHole- , hole_occ = occ+ , hole_occ = nameRdrName name , hole_ty = mkTyVarTy tv , hole_loc = ct_loc } ; emitHole hole }@@ -1970,7 +1976,7 @@ It's distressingly delicate though: * If we discard too /many/ constraints we may fail to report the error- that led us to interrupte the constraint gathering process.+ that led us to interrupt the constraint gathering process. One particular example "variable out of scope" Hole constraints. For example (#12529):
compiler/GHC/Tc/Utils/TcMType.hs view
@@ -1,6 +1,4 @@- {-# LANGUAGE MultiWayIf #-}-{-# LANGUAGE RecursiveDo #-} {-# LANGUAGE TupleSections #-} {-# OPTIONS_GHC -Wno-incomplete-record-updates #-}@@ -113,9 +111,9 @@ import qualified GHC.LanguageExtensions as LangExt import GHC.Tc.Types.Origin-import GHC.Tc.Utils.Monad -- TcType, amongst others import GHC.Tc.Types.Constraint import GHC.Tc.Types.Evidence+import GHC.Tc.Utils.Monad -- TcType, amongst others import GHC.Tc.Utils.TcType import GHC.Tc.Errors.Types import GHC.Tc.Errors.Ppr@@ -151,11 +149,11 @@ import GHC.Utils.Panic import GHC.Utils.Panic.Plain import GHC.Utils.Constants (debugIsOn)-import GHC.Utils.Trace import Control.Monad import GHC.Data.Maybe import qualified Data.Semigroup as Semi+import GHC.Types.Name.Reader {- ************************************************************************@@ -194,7 +192,7 @@ newEvVar :: TcPredType -> TcRnIf gbl lcl EvVar -- Creates new *rigid* variables for predicates newEvVar ty = do { name <- newSysName (predTypeOccName ty)- ; return (mkLocalIdOrCoVar name Many ty) }+ ; return (mkLocalIdOrCoVar name ManyTy ty) } -- | Create a new Wanted constraint with the given 'CtLoc'. newWantedWithLoc :: CtLoc -> PredType -> TcM CtEvidence@@ -303,7 +301,7 @@ emitWantedEvVars orig = mapM (emitWantedEvVar orig) -- | Emit a new wanted expression hole-emitNewExprHole :: OccName -- of the hole+emitNewExprHole :: RdrName -- of the hole -> Type -> TcM HoleExprRef emitNewExprHole occ ty = do { u <- newUnique@@ -322,7 +320,7 @@ newDict :: Class -> [TcType] -> TcM DictId newDict cls tys = do { name <- newSysName (mkDictOcc (getOccName cls))- ; return (mkLocalId name Many (mkClassPred cls tys)) }+ ; return (mkLocalId name ManyTy (mkClassPred cls tys)) } predTypeOccName :: PredType -> OccName predTypeOccName ty = case classifyPredType ty of@@ -502,16 +500,7 @@ newInferExpType = new_inferExpType Nothing newInferExpTypeFRR :: FixedRuntimeRepContext -> TcM ExpTypeFRR-newInferExpTypeFRR frr_orig- = do { th_stage <- getStage- ; if- -- See [Wrinkle: Typed Template Haskell]- -- in Note [hasFixedRuntimeRep] in GHC.Tc.Utils.Concrete.- | Brack _ (TcPending {}) <- th_stage- -> new_inferExpType Nothing-- | otherwise- -> new_inferExpType (Just frr_orig) }+newInferExpTypeFRR frr_orig = new_inferExpType (Just frr_orig) new_inferExpType :: Maybe FixedRuntimeRepContext -> TcM ExpType new_inferExpType mb_frr_orig@@ -567,28 +556,20 @@ inferResultToType :: InferResult -> TcM Type inferResultToType (IR { ir_uniq = u, ir_lvl = tc_lvl- , ir_ref = ref- , ir_frr = mb_frr })+ , ir_ref = ref }) = do { mb_inferred_ty <- readTcRef ref ; tau <- case mb_inferred_ty of Just ty -> do { ensureMonoType ty -- See Note [inferResultToType] ; return ty }- Nothing -> do { tau <- new_meta+ Nothing -> do { rr <- newMetaTyVarTyAtLevel tc_lvl runtimeRepTy+ ; tau <- newMetaTyVarTyAtLevel tc_lvl (mkTYPEapp rr)+ -- See Note [TcLevel of ExpType] ; writeMutVar ref (Just tau) ; return tau } ; traceTc "Forcing ExpType to be monomorphic:" (ppr u <+> text ":=" <+> ppr tau) ; return tau }- where- -- See Note [TcLevel of ExpType]- new_meta = case mb_frr of- Nothing -> do { rr <- newMetaTyVarTyAtLevel tc_lvl runtimeRepTy- ; newMetaTyVarTyAtLevel tc_lvl (mkTYPEapp rr) }- Just frr -> mdo { rr <- newConcreteTyVarAtLevel conc_orig tc_lvl runtimeRepTy- ; tau <- newMetaTyVarTyAtLevel tc_lvl (mkTYPEapp rr)- ; let conc_orig = ConcreteFRR $ FixedRuntimeRepOrigin tau frr- ; return tau } {- Note [inferResultToType] ~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -633,7 +614,7 @@ -- Assuming that the argument type is of kind (TYPE r), -- ensure that it is a /monotype/ -- If it is not a monotype we can see right away (since unification--- varibles and type-function applications stand for monotypes), but+-- variables and type-function applications stand for monotypes), but -- we emit a Wanted equality just to delay the error message until later ensureMonoType res_ty | isTauTy res_ty -- isTauTy doesn't need zonking or anything@@ -663,7 +644,7 @@ promoteTcType dest_lvl ty = do { cur_lvl <- getTcLevel ; if (cur_lvl `sameDepthAs` dest_lvl)- then return (mkTcNomReflCo ty, ty)+ then return (mkNomReflCo ty, ty) else promote_it } where promote_it :: TcM (TcCoercion, TcType)@@ -765,7 +746,7 @@ 1. In kind signatures, see GHC.Tc.TyCl Note [Inferring kinds for type declarations]- and Note [Kind checking for GADTs]+ and Note [Using TyVarTvs for kind-checking GADTs] 2. In partial type signatures. See GHC.Tc.Types Note [Quantified variables in partial type signatures]@@ -911,13 +892,6 @@ , mtv_ref = ref , mtv_tclvl = tclvl }) } -newConcreteTvDetailsAtLevel :: ConcreteTvOrigin -> TcLevel -> TcM TcTyVarDetails-newConcreteTvDetailsAtLevel conc_orig tclvl- = do { ref <- newMutVar Flexi- ; return (MetaTv { mtv_info = ConcreteTv conc_orig- , mtv_ref = ref- , mtv_tclvl = tclvl }) }- cloneMetaTyVar :: TcTyVar -> TcM TcTyVar cloneMetaTyVar tv = assert (isTcTyVar tv) $@@ -963,7 +937,7 @@ -------------------- -- Works with both type and kind variables-writeMetaTyVar :: HasDebugCallStack => TcTyVar -> TcType -> TcM ()+writeMetaTyVar :: TcTyVar -> TcType -> TcM () -- Write into a currently-empty MetaTyVar writeMetaTyVar tyvar ty@@ -981,7 +955,7 @@ = massertPpr False (text "Writing to non-meta tyvar" <+> ppr tyvar) ---------------------writeMetaTyVarRef :: HasDebugCallStack => TcTyVar -> TcRef MetaDetails -> TcType -> TcM ()+writeMetaTyVarRef :: TcTyVar -> TcRef MetaDetails -> TcType -> TcM () -- Here the tyvar is for error checking only; -- the ref cell must be for the same tyvar writeMetaTyVarRef tyvar ref ty@@ -998,13 +972,11 @@ -- Zonk kinds to allow the error check to work ; zonked_tv_kind <- zonkTcType tv_kind ; zonked_ty <- zonkTcType ty- ; let zonked_ty_kind = tcTypeKind zonked_ty+ ; let zonked_ty_kind = typeKind zonked_ty zonked_ty_lvl = tcTypeLevel zonked_ty level_check_ok = not (zonked_ty_lvl `strictlyDeeperThan` tv_lvl) level_check_msg = ppr zonked_ty_lvl $$ ppr tv_lvl $$ ppr tyvar $$ ppr ty kind_check_ok = zonked_ty_kind `eqType` zonked_tv_kind- -- Hack alert! eqType, not tcEqType. see:- -- Note [coreView vs tcView] in GHC.Core.Type -- Note [Extra-constraint holes in partial type signatures] in GHC.Tc.Gen.HsType kind_msg = hang (text "Ill-kinded update to meta tyvar")@@ -1097,35 +1069,35 @@ ; let rr = mkTyConApp boxedRepDataConTyCon [lev] ; return (mkTYPEapp rr) } -newMetaTyVars :: [TyVar] -> TcM (TCvSubst, [TcTyVar])+newMetaTyVars :: [TyVar] -> TcM (Subst, [TcTyVar]) -- Instantiate with META type variables -- Note that this works for a sequence of kind, type, and coercion variables -- variables. Eg [ (k:*), (a:k->k) ] -- Gives [ (k7:*), (a8:k7->k7) ]-newMetaTyVars = newMetaTyVarsX emptyTCvSubst- -- emptyTCvSubst has an empty in-scope set, but that's fine here+newMetaTyVars = newMetaTyVarsX emptySubst+ -- emptySubst has an empty in-scope set, but that's fine here -- Since the tyvars are freshly made, they cannot possibly be -- captured by any existing for-alls. -newMetaTyVarsX :: TCvSubst -> [TyVar] -> TcM (TCvSubst, [TcTyVar])+newMetaTyVarsX :: Subst -> [TyVar] -> TcM (Subst, [TcTyVar]) -- Just like newMetaTyVars, but start with an existing substitution. newMetaTyVarsX subst = mapAccumLM newMetaTyVarX subst -newMetaTyVarX :: TCvSubst -> TyVar -> TcM (TCvSubst, TcTyVar)+newMetaTyVarX :: Subst -> TyVar -> TcM (Subst, TcTyVar) -- Make a new unification variable tyvar whose Name and Kind come from -- an existing TyVar. We substitute kind variables in the kind. newMetaTyVarX = new_meta_tv_x TauTv -newMetaTyVarTyVarX :: TCvSubst -> TyVar -> TcM (TCvSubst, TcTyVar)+newMetaTyVarTyVarX :: Subst -> TyVar -> TcM (Subst, TcTyVar) -- Just like newMetaTyVarX, but make a TyVarTv newMetaTyVarTyVarX = new_meta_tv_x TyVarTv -newWildCardX :: TCvSubst -> TyVar -> TcM (TCvSubst, TcTyVar)+newWildCardX :: Subst -> TyVar -> TcM (Subst, TcTyVar) newWildCardX subst tv = do { new_tv <- newAnonMetaTyVar TauTv (substTy subst (tyVarKind tv)) ; return (extendTvSubstWithClone subst tv new_tv, new_tv) } -new_meta_tv_x :: MetaInfo -> TCvSubst -> TyVar -> TcM (TCvSubst, TcTyVar)+new_meta_tv_x :: MetaInfo -> Subst -> TyVar -> TcM (Subst, TcTyVar) new_meta_tv_x info subst tv = do { new_tv <- cloneAnonMetaTyVar info tv substd_kind ; let subst1 = extendTvSubstWithClone subst tv new_tv@@ -1148,13 +1120,6 @@ ; name <- newMetaTyVarName (fsLit "p") ; return (mkTyVarTy (mkTcTyVar name kind details)) } -newConcreteTyVarAtLevel :: ConcreteTvOrigin -> TcLevel -> TcKind -> TcM TcType-newConcreteTyVarAtLevel conc_orig tc_lvl kind- = do { details <- newConcreteTvDetailsAtLevel conc_orig tc_lvl- ; name <- newMetaTyVarName (fsLit "c")- ; return (mkTyVarTy (mkTcTyVar name kind details)) }-- {- ********************************************************************* * * Finding variables to quantify over@@ -1535,24 +1500,24 @@ -> TcM CandidatesQTvs collect_cand_qtvs_co orig_ty bound = go_co where- go_co dv (Refl ty) = collect_cand_qtvs orig_ty True bound dv ty- go_co dv (GRefl _ ty mco) = do dv1 <- collect_cand_qtvs orig_ty True bound dv ty- go_mco dv1 mco- go_co dv (TyConAppCo _ _ cos) = foldlM go_co dv cos- go_co dv (AppCo co1 co2) = foldlM go_co dv [co1, co2]- go_co dv (FunCo _ w co1 co2) = foldlM go_co dv [w, co1, co2]- go_co dv (AxiomInstCo _ _ cos) = foldlM go_co dv cos- go_co dv (AxiomRuleCo _ cos) = foldlM go_co dv cos- go_co dv (UnivCo prov _ t1 t2) = do dv1 <- go_prov dv prov- dv2 <- collect_cand_qtvs orig_ty True bound dv1 t1- collect_cand_qtvs orig_ty True bound dv2 t2- go_co dv (SymCo co) = go_co dv co- go_co dv (TransCo co1 co2) = foldlM go_co dv [co1, co2]- go_co dv (NthCo _ _ co) = go_co dv co- go_co dv (LRCo _ co) = go_co dv co- go_co dv (InstCo co1 co2) = foldlM go_co dv [co1, co2]- go_co dv (KindCo co) = go_co dv co- go_co dv (SubCo co) = go_co dv co+ go_co dv (Refl ty) = collect_cand_qtvs orig_ty True bound dv ty+ go_co dv (GRefl _ ty mco) = do dv1 <- collect_cand_qtvs orig_ty True bound dv ty+ go_mco dv1 mco+ go_co dv (TyConAppCo _ _ cos) = foldlM go_co dv cos+ go_co dv (AppCo co1 co2) = foldlM go_co dv [co1, co2]+ go_co dv (FunCo _ _ _ w co1 co2) = foldlM go_co dv [w, co1, co2]+ go_co dv (AxiomInstCo _ _ cos) = foldlM go_co dv cos+ go_co dv (AxiomRuleCo _ cos) = foldlM go_co dv cos+ go_co dv (UnivCo prov _ t1 t2) = do dv1 <- go_prov dv prov+ dv2 <- collect_cand_qtvs orig_ty True bound dv1 t1+ collect_cand_qtvs orig_ty True bound dv2 t2+ go_co dv (SymCo co) = go_co dv co+ go_co dv (TransCo co1 co2) = foldlM go_co dv [co1, co2]+ go_co dv (SelCo _ co) = go_co dv co+ go_co dv (LRCo _ co) = go_co dv co+ go_co dv (InstCo co1 co2) = foldlM go_co dv [co1, co2]+ go_co dv (KindCo co) = go_co dv co+ go_co dv (SubCo co) = go_co dv co go_co dv (HoleCo hole) = do m_co <- unpackCoercionHole_maybe hole@@ -1768,12 +1733,6 @@ = return Nothing -- this can happen for a covar that's associated with -- a coercion hole. Test case: typecheck/should_compile/T2494 --- Omit: no TyVars now--- | not (isTcTyVar tkv)--- = return (Just tkv) -- For associated types in a class with a standalone--- -- kind signature, we have the class variables in--- -- scope, and they are TyVars not TcTyVars- | otherwise = Just <$> skolemiseQuantifiedTyVar skol_info tkv @@ -1818,13 +1777,19 @@ skolemiseQuantifiedTyVar skol_info tv = case tcTyVarDetails tv of- SkolemTv {} -> do { kind <- zonkTcType (tyVarKind tv)- ; return (setTyVarKind tv kind) }- -- It might be a skolem type variable,- -- for example from a user type signature- MetaTv {} -> skolemiseUnboundMetaTyVar skol_info tv + SkolemTv _ lvl _ -- It might be a skolem type variable,+ -- for example from a user type signature+ -- But it might also be a shared meta-variable across several+ -- type declarations, each with its own skol_info. The first+ -- will skolemise it, but the other uses must update its+ -- skolem info (#22379)+ -> do { kind <- zonkTcType (tyVarKind tv)+ ; let details = SkolemTv skol_info lvl False+ name = tyVarName tv+ ; return (mkTcTyVar name kind details) }+ _other -> pprPanic "skolemiseQuantifiedTyVar" (ppr tv) -- RuntimeUnk -- | Default a type variable using the given defaulting strategy.@@ -1858,17 +1823,6 @@ ; writeMetaTyVar tv manyDataConTy ; return True } - | isConcreteTyVar tv- -- We don't want to quantify; but neither can we default to- -- anything sensible. (If it has kind RuntimeRep or Levity, as is- -- often the case, it'll have been caught earlier by earlier- -- cases. So in this exotic situation we just promote. Not very- -- satisfing, but it's very much a corner case: #23051)- -- We should really implement the plan in #20686.- = do { lvl <- getTcLevel- ; _ <- promoteMetaTyVarTo lvl tv- ; return True }- | DefaultKindVars <- def_strat -- -XNoPolyKinds and this is a kind var: we must default it = default_kind_var tv @@ -1889,7 +1843,7 @@ ; writeMetaTyVar kv liftedTypeKind ; return True } | otherwise- = do { addErr $ TcRnUnknownMessage $ mkPlainError noHints $+ = do { addErr $ mkTcRnUnknownMessage $ mkPlainError noHints $ (vcat [ text "Cannot default kind variable" <+> quotes (ppr kv') , text "of kind:" <+> ppr (tyVarKind kv') , text "Perhaps enable PolyKinds or add a kind signature" ])@@ -2013,7 +1967,7 @@ D. We could error. We choose (D), as described in #17567, and implement this choice in-doNotQuantifyTyVars. Dicsussion of alternativs A-C is below.+doNotQuantifyTyVars. Discussion of alternativs A-C is below. NB: this is all rather similar to, but sadly not the same as Note [Naughty quantification candidates]@@ -2109,7 +2063,7 @@ ; unless (null leftover_metas) $ do { let (tidy_env1, tidied_tvs) = tidyOpenTyCoVars emptyTidyEnv leftover_metas ; (tidy_env2, where_doc) <- where_found tidy_env1- ; let msg = TcRnUnknownMessage $+ ; let msg = mkTcRnUnknownMessage $ mkPlainError noHints $ pprWithExplicitKindsWhen True $ vcat [ text "Uninferrable type variable"@@ -2283,7 +2237,7 @@ * * ********************************************************************* -} -promoteMetaTyVarTo :: HasDebugCallStack => TcLevel -> TcTyVar -> TcM Bool+promoteMetaTyVarTo :: TcLevel -> TcTyVar -> TcM Bool -- When we float a constraint out of an implication we must restore -- invariant (WantedInv) in Note [TcLevel invariants] in GHC.Tc.Utils.TcType -- Return True <=> we did some promotion@@ -2301,7 +2255,7 @@ = return False -- Returns whether or not *any* tyvar is defaulted-promoteTyVarSet :: HasDebugCallStack => TcTyVarSet -> TcM Bool+promoteTyVarSet :: TcTyVarSet -> TcM Bool promoteTyVarSet tvs = do { tclvl <- getTcLevel ; bools <- mapM (promoteMetaTyVarTo tclvl) $@@ -2531,9 +2485,9 @@ -- A non-poly TcTyCon may have unification -- variables that need zonking, but poly ones cannot zonkTcTyCon tc- | tcTyConIsPoly tc = return tc- | otherwise = do { tck' <- zonkTcType (tyConKind tc)+ | isMonoTcTyCon tc = do { tck' <- zonkTcType (tyConKind tc) ; return (setTcTyConKind tc tck') }+ | otherwise = return tc zonkTcTyVar :: TcTyVar -> TcM TcType -- Simply look through all Flexis@@ -2565,7 +2519,7 @@ zonkTcTyVarToTcTyVar :: HasDebugCallStack => TcTyVar -> TcM TcTyVar zonkTcTyVarToTcTyVar tv = do { ty <- zonkTcTyVar tv- ; let tv' = case tcGetTyVar_maybe ty of+ ; let tv' = case getTyVar_maybe ty of Just tv' -> tv' Nothing -> pprPanic "zonkTcTyVarToTcTyVar" (ppr tv $$ ppr ty)@@ -2625,10 +2579,10 @@ = do { skol_info1 <- zonkSkolemInfoAnon skol_info ; let skol_info2 = tidySkolemInfoAnon env skol_info1 ; return (env, GivenOrigin skol_info2) }-zonkTidyOrigin env (OtherSCOrigin sc_depth skol_info)+zonkTidyOrigin env (GivenSCOrigin skol_info sc_depth blocked) = do { skol_info1 <- zonkSkolemInfoAnon skol_info ; let skol_info2 = tidySkolemInfoAnon env skol_info1- ; return (env, OtherSCOrigin sc_depth skol_info2) }+ ; return (env, GivenSCOrigin skol_info2 sc_depth blocked) } zonkTidyOrigin env orig@(TypeEqOrigin { uo_actual = act , uo_expected = exp }) = do { (env1, act') <- zonkTidyTcType env act@@ -2779,7 +2733,7 @@ orig_ty' = tidyType env orig_ty1 ppr_tidied = pprTyVars . map (tidyTyCoVarOcc env)- msg = TcRnUnknownMessage $ mkPlainError noHints $+ msg = mkTcRnUnknownMessage $ mkPlainError noHints $ pprWithExplicitKindsWhen True $ vcat [ sep [ text "Cannot generalise type; skolem" <> plural escapees' , quotes $ ppr_tidied escapees'
compiler/GHC/Tc/Utils/Unify.hs view
@@ -2,9 +2,6 @@ {-# LANGUAGE TupleSections #-} {-# LANGUAGE RecursiveDo #-} -{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}-{-# OPTIONS_GHC -Wno-incomplete-record-updates #-}- {- (c) The University of Glasgow 2006 (c) The GRASP/AQUA Project, Glasgow University, 1992-1998@@ -15,7 +12,7 @@ -- Full-blown subsumption tcWrapResult, tcWrapResultO, tcWrapResultMono, tcTopSkolemise, tcSkolemiseScoped, tcSkolemiseExpType,- tcSubType, tcSubTypeNC, tcSubTypeSigma, tcSubTypePat,+ tcSubType, tcSubTypeSigma, tcSubTypePat, tcSubTypeDS, tcSubTypeAmbiguity, tcSubMult, checkConstraints, checkTvConstraints, buildImplicationFor, buildTvImplication, emitResidualTvConstraint,@@ -42,8 +39,7 @@ import GHC.Prelude import GHC.Hs-import GHC.Core.TyCo.Rep-import GHC.Core.TyCo.Ppr( debugPprType )+ import GHC.Tc.Utils.Concrete ( hasFixedRuntimeRep, makeTypeConcrete, hasFixedRuntimeRep_syntactic ) import GHC.Tc.Utils.Env import GHC.Tc.Utils.Instantiate@@ -52,6 +48,9 @@ import GHC.Tc.Utils.TcType import GHC.Core.Type+import GHC.Core.TyCo.Rep+import GHC.Core.TyCo.Ppr( debugPprType )+import GHC.Core.TyCon import GHC.Core.Coercion import GHC.Core.Multiplicity @@ -60,10 +59,9 @@ import GHC.Tc.Types.Evidence import GHC.Tc.Types.Constraint import GHC.Tc.Types.Origin+import GHC.Types.Name( Name, isSystemName ) -import GHC.Core.TyCon import GHC.Builtin.Types-import GHC.Types.Name( Name, isSystemName ) import GHC.Types.Var as Var import GHC.Types.Var.Set import GHC.Types.Var.Env@@ -132,10 +130,10 @@ go :: TcRhoType -- The type we're processing, perhaps after -- expanding type synonyms -> TcM (HsWrapper, Scaled TcSigmaTypeFRR, TcSigmaType)- go ty | Just ty' <- tcView ty = go ty'+ go ty | Just ty' <- coreView ty = go ty' go (FunTy { ft_af = af, ft_mult = w, ft_arg = arg_ty, ft_res = res_ty })- = assert (af == VisArg) $+ = assert (isVisibleFunArg af) $ do { hasFixedRuntimeRep_syntactic (FRRExpectedFunTy herald 1) arg_ty ; return (idHsWrapper, Scaled w arg_ty, res_ty) } @@ -168,7 +166,7 @@ = do { arg_ty <- newOpenFlexiTyVarTy ; res_ty <- newOpenFlexiTyVarTy ; mult <- newFlexiTyVarTy multiplicityTy- ; let unif_fun_ty = mkVisFunTy mult arg_ty res_ty+ ; let unif_fun_ty = tcMkVisFunTy mult arg_ty res_ty ; co <- unifyType mb_thing fun_ty unif_fun_ty ; hasFixedRuntimeRep_syntactic (FRRExpectedFunTy herald 1) arg_ty ; return (mkWpCastN co, Scaled mult arg_ty, res_ty) }@@ -205,7 +203,7 @@ -- | Like 'matchExpectedFunTys', but used when you have an "actual" type, -- for example in function application. ----- INVARIANT: the returned arguemnt types all have a syntactically fixed RuntimeRep+-- INVARIANT: the returned argument types all have a syntactically fixed RuntimeRep -- in the sense of Note [Fixed RuntimeRep] in GHC.Tc.Utils.Concrete. -- See Note [Return arguments with a fixed RuntimeRep]. matchActualFunTysRho :: ExpectedFunTyOrigin -- ^ See Note [Herald for matchExpectedFunTys]@@ -387,17 +385,17 @@ go acc_arg_tys n ty' ; return (wrap_gen <.> wrap_res, result) } - -- No more args; do this /before/ tcView, so+ -- No more args; do this /before/ coreView, so -- that we do not unnecessarily unwrap synonyms go acc_arg_tys 0 rho_ty = do { result <- thing_inside (reverse acc_arg_tys) (mkCheckExpType rho_ty) ; return (idHsWrapper, result) } go acc_arg_tys n ty- | Just ty' <- tcView ty = go acc_arg_tys n ty'+ | Just ty' <- coreView ty = go acc_arg_tys n ty' - go acc_arg_tys n (FunTy { ft_mult = mult, ft_af = af, ft_arg = arg_ty, ft_res = res_ty })- = assert (af == VisArg) $+ go acc_arg_tys n (FunTy { ft_af = af, ft_mult = mult, ft_arg = arg_ty, ft_res = res_ty })+ = assert (isVisibleFunArg af) $ do { let arg_pos = 1 + length acc_arg_tys -- for error messages only ; (arg_co, arg_ty) <- hasFixedRuntimeRep (FRRExpectedFunTy herald arg_pos) arg_ty ; (wrap_res, result) <- go ((Scaled mult $ mkCheckExpType arg_ty) : acc_arg_tys)@@ -440,7 +438,7 @@ ; result <- thing_inside (reverse acc_arg_tys ++ more_arg_tys) res_ty ; more_arg_tys <- mapM (\(Scaled m t) -> Scaled m <$> readExpType t) more_arg_tys ; res_ty <- readExpType res_ty- ; let unif_fun_ty = mkVisFunTys more_arg_tys res_ty+ ; let unif_fun_ty = mkScaledFunTys more_arg_tys res_ty ; wrap <- tcSubType AppOrigin ctx unif_fun_ty fun_ty -- Not a good origin at all :-( ; return (wrap, result) }@@ -465,7 +463,7 @@ -> TcM (TidyEnv, SDoc) mkFunTysMsg env herald arg_tys res_ty n_val_args_in_call = do { (env', fun_rho) <- zonkTidyTcType env $- mkVisFunTys arg_tys res_ty+ mkScaledFunTys arg_tys res_ty ; let (all_arg_tys, _) = splitFunTys fun_rho n_fun_args = length all_arg_tys@@ -502,15 +500,16 @@ -- Postcondition: (T k1 k2 k3 a b c) is well-kinded matchExpectedTyConApp tc orig_ty- = assert (not $ isFunTyCon tc) $ go orig_ty+ = assertPpr (isAlgTyCon tc) (ppr tc) $+ go orig_ty where go ty- | Just ty' <- tcView ty+ | Just ty' <- coreView ty = go ty' go ty@(TyConApp tycon args) | tc == tycon -- Common case- = return (mkTcNomReflCo ty, args)+ = return (mkNomReflCo ty, args) go (TyVarTy tv) | isMetaTyVar tv@@ -550,10 +549,10 @@ = go orig_ty where go ty- | Just ty' <- tcView ty = go ty'+ | Just ty' <- coreView ty = go ty' | Just (fun_ty, arg_ty) <- tcSplitAppTy_maybe ty- = return (mkTcNomReflCo orig_ty, (fun_ty, arg_ty))+ = return (mkNomReflCo orig_ty, (fun_ty, arg_ty)) go (TyVarTy tv) | isMetaTyVar tv@@ -571,7 +570,7 @@ ; co <- unifyType Nothing (mkAppTy ty1 ty2) orig_ty ; return (co, (ty1, ty2)) } - orig_kind = tcTypeKind orig_ty+ orig_kind = typeKind orig_ty kind1 = mkVisFunTyMany liftedTypeKind orig_kind kind2 = liftedTypeKind -- m :: * -> k -- arg type :: *@@ -651,7 +650,7 @@ Just frr_orig -> hasFixedRuntimeRep frr_orig act_res_ty -- Compose the two coercions.- ; let final_co = prom_co `mkTcTransCo` frr_co+ ; let final_co = prom_co `mkTransCo` frr_co ; writeTcRef ref (Just act_res_ty) @@ -721,7 +720,7 @@ case e of T1 -> e1 :: (forall a. a->a) -> Int G2 -> e2-where T1 is not GADT or existential, but G2 is a GADT. Then supppose the+where T1 is not GADT or existential, but G2 is a GADT. Then suppose the T1 alternative fills the hole with (forall a. a->a) -> Int, which is fine. But now the G2 alternative must not *just* unify with that else we'd risk allowing through (e2 :: (forall a. a->a) -> Int). If we'd checked G2 first@@ -840,7 +839,7 @@ = do { co <- fillInferResult ty_expected inf_res -- In patterns we do not instantatiate - ; return (mkWpCastN (mkTcSymCo co)) }+ ; return (mkWpCastN (mkSymCo co)) } --------------- tcSubType :: CtOrigin -> UserTypeCtxt@@ -854,6 +853,24 @@ ; tcSubTypeNC orig ctxt Nothing ty_actual ty_expected } ---------------+tcSubTypeDS :: HsExpr GhcRn+ -> TcRhoType -- Actual -- a rho-type not a sigma-type+ -> ExpRhoType -- Expected+ -> TcM HsWrapper+-- Similar signature to unifyExpectedType; does deep subsumption+-- Only one call site, in GHC.Tc.Gen.App.tcApp+tcSubTypeDS rn_expr act_rho res_ty+ = case res_ty of+ Check exp_rho -> tc_sub_type_deep (unifyType m_thing) orig+ GenSigCtxt act_rho exp_rho++ Infer inf_res -> do { co <- fillInferResult act_rho inf_res+ ; return (mkWpCastN co) }+ where+ orig = exprCtOrigin rn_expr+ m_thing = Just (HsExprRnThing rn_expr)++--------------- tcSubTypeNC :: CtOrigin -- ^ Used when instantiating -> UserTypeCtxt -- ^ Used when skolemising -> Maybe TypedThing -- ^ The expression that has type 'actual' (if known)@@ -1138,7 +1155,7 @@ https://github.com/ghc-proposals/ghc-proposals/pull/511. makes a best-efforts attempt implement deep subsumption as it was-prior to the the Simplify Subsumption proposal:+prior to the Simplify Subsumption proposal: https://github.com/ghc-proposals/ghc-proposals/pull/287 @@ -1154,7 +1171,7 @@ signatures (e.g. f :: ty; f = e), we must deeply skolemise the type; see the call to tcDeeplySkolemise in tcSkolemiseScoped. -4. In GHC.Tc.Gen.App.tcApp we call tcSubTypeNC to match the result+4. In GHC.Tc.Gen.App.tcApp we call tcSubTypeDS to match the result type. Without deep subsumption, unifyExpectedType would be sufficent. In all these cases note that the deep skolemisation must be done /first/.@@ -1260,9 +1277,9 @@ , text "ty_expected =" <+> ppr ty_expected ] ; go ty_actual ty_expected } where- -- NB: 'go' is not recursive, except for doing tcView- go ty_a ty_e | Just ty_a' <- tcView ty_a = go ty_a' ty_e- | Just ty_e' <- tcView ty_e = go ty_a ty_e'+ -- NB: 'go' is not recursive, except for doing coreView+ go ty_a ty_e | Just ty_a' <- coreView ty_a = go ty_a' ty_e+ | Just ty_e' <- coreView ty_e = go ty_a ty_e' go (TyVarTy tv_a) ty_e = do { lookup_res <- isFilledMetaTyVar_maybe tv_a@@ -1273,13 +1290,13 @@ ; tc_sub_type_ds unify inst_orig ctxt ty_a' ty_e } Nothing -> just_unify ty_actual ty_expected } - go ty_a@(FunTy { ft_af = VisArg, ft_mult = act_mult, ft_arg = act_arg, ft_res = act_res })- ty_e@(FunTy { ft_af = VisArg, ft_mult = exp_mult, ft_arg = exp_arg, ft_res = exp_res })- | isTauTy ty_a, isTauTy ty_e -- Short cut common case to avoid- = just_unify ty_actual ty_expected -- unnecessary eta expansion-- | otherwise- = -- This is where we do the co/contra thing, and generate a WpFun, which in turn+ go ty_a@(FunTy { ft_af = af1, ft_mult = act_mult, ft_arg = act_arg, ft_res = act_res })+ ty_e@(FunTy { ft_af = af2, ft_mult = exp_mult, ft_arg = exp_arg, ft_res = exp_res })+ | isVisibleFunArg af1, isVisibleFunArg af2+ = if (isTauTy ty_a && isTauTy ty_e) -- Short cut common case to avoid+ then just_unify ty_actual ty_expected -- unnecessary eta expansion+ else+ -- This is where we do the co/contra thing, and generate a WpFun, which in turn -- causes eta-expansion, which we don't like; hence encouraging NoDeepSubsumption do { arg_wrap <- tc_sub_type_deep unify given_orig GenSigCtxt exp_arg act_arg -- GenSigCtxt: See Note [Setting the argument context]@@ -1352,7 +1369,7 @@ deeplySkolemise skol_info ty = go init_subst ty where- init_subst = mkEmptyTCvSubst (mkInScopeSet (tyCoVarsOfType ty))+ init_subst = mkEmptySubst (mkInScopeSet (tyCoVarsOfType ty)) go subst ty | Just (arg_tys, tvs, theta, ty') <- tcDeepSplitSigmaTy_maybe ty@@ -1362,14 +1379,12 @@ ; ev_vars1 <- newEvVars (substTheta subst' theta) ; (wrap, tvs_prs2, ev_vars2, rho) <- go subst' ty' ; let tv_prs1 = map tyVarName tvs `zip` tvs1- ; return ( mkWpLams ids1- <.> mkWpTyLams tvs1- <.> mkWpLams ev_vars1- <.> wrap- <.> mkWpEvVarApps ids1+ ; return ( mkWpEta ids1 (mkWpTyLams tvs1+ <.> mkWpEvLams ev_vars1+ <.> wrap) , tv_prs1 ++ tvs_prs2 , ev_vars1 ++ ev_vars2- , mkVisFunTys arg_tys' rho ) }+ , mkScaledFunTys arg_tys' rho ) } | otherwise = return (idHsWrapper, [], [], substTy subst ty)@@ -1379,7 +1394,7 @@ deeplyInstantiate orig ty = go init_subst ty where- init_subst = mkEmptyTCvSubst (mkInScopeSet (tyCoVarsOfType ty))+ init_subst = mkEmptySubst (mkInScopeSet (tyCoVarsOfType ty)) go subst ty | Just (arg_tys, tvs, theta, rho) <- tcDeepSplitSigmaTy_maybe ty@@ -1389,11 +1404,8 @@ ; ids1 <- newSysLocalIds (fsLit "di") arg_tys' ; wrap1 <- instCall orig (mkTyVarTys tvs') theta' ; (wrap2, rho2) <- go subst' rho- ; return (mkWpLams ids1- <.> wrap2- <.> wrap1- <.> mkWpEvVarApps ids1,- mkVisFunTys arg_tys' rho2) }+ ; return (mkWpEta ids1 (wrap2 <.> wrap1),+ mkScaledFunTys arg_tys' rho2) } | otherwise = do { let ty' = substTy subst ty@@ -1465,8 +1477,8 @@ = do { deep_subsumption <- xoptM LangExt.DeepSubsumption ; let skolemise | deep_subsumption = deeplySkolemise | otherwise = topSkolemise- ; -- This (unpleasant) rec block allows us to pass skol_info to deeplySkolemise;- -- but skol_info can't be built until we have tv_prs+ ; -- rec {..}: see Note [Keeping SkolemInfo inside a SkolemTv]+ -- in GHC.Tc.Utils.TcType rec { (wrap, tv_prs, given, rho_ty) <- skolemise skol_info expected_ty ; skol_info <- mkSkolemInfo (SigSkol ctxt expected_ty tv_prs) } @@ -1483,7 +1495,9 @@ = do { res <- thing_inside expected_ty ; return (idHsWrapper, res) } | otherwise- = do { rec { (wrap, tv_prs, given, rho_ty) <- topSkolemise skol_info expected_ty+ = do { -- rec {..}: see Note [Keeping SkolemInfo inside a SkolemTv]+ -- in GHC.Tc.Utils.TcType+ rec { (wrap, tv_prs, given, rho_ty) <- topSkolemise skol_info expected_ty ; skol_info <- mkSkolemInfo (SigSkol ctxt expected_ty tv_prs) } ; let skol_tvs = map snd tv_prs@@ -1685,7 +1699,7 @@ -} unifyType :: Maybe TypedThing -- ^ If present, the thing that has type ty1- -> TcTauType -> TcTauType -- ty1, ty2+ -> TcTauType -> TcTauType -- ty1 (actual), ty2 (expected) -> TcM TcCoercionN -- :: ty1 ~# ty2 -- Actual and expected types -- Returns a coercion : ty1 ~ ty2@@ -1739,6 +1753,8 @@ -------------- -- It is always safe to defer unification to the main constraint solver -- See Note [Deferred unification]+-- ty1 is "actual"+-- ty2 is "expected" uType_defer t_or_k origin ty1 ty2 = do { co <- emitWantedEq origin t_or_k Nominal ty1 ty2 @@ -1815,17 +1831,18 @@ -- we'll end up saying "can't match Foo with Bool" -- rather than "can't match "Int with Bool". See #4535. go ty1 ty2- | Just ty1' <- tcView ty1 = go ty1' ty2- | Just ty2' <- tcView ty2 = go ty1 ty2'+ | Just ty1' <- coreView ty1 = go ty1' ty2+ | Just ty2' <- coreView ty2 = go ty1 ty2' -- Functions (t1 -> t2) just check the two parts -- Do not attempt (c => t); just defer- go (FunTy { ft_af = VisArg, ft_mult = w1, ft_arg = arg1, ft_res = res1 })- (FunTy { ft_af = VisArg, ft_mult = w2, ft_arg = arg2, ft_res = res2 })+ go (FunTy { ft_af = af1, ft_mult = w1, ft_arg = arg1, ft_res = res1 })+ (FunTy { ft_af = af2, ft_mult = w2, ft_arg = arg2, ft_res = res2 })+ | isVisibleFunArg af1, af1 == af2 = do { co_l <- uType t_or_k origin arg1 arg2 ; co_r <- uType t_or_k origin res1 res2 ; co_w <- uType t_or_k origin w1 w2- ; return $ mkFunCo Nominal co_w co_l co_r }+ ; return $ mkNakedFunCo1 Nominal af1 co_w co_l co_r } -- Always defer if a type synonym family (type function) -- is involved. (Data families behave rigidly.)@@ -1856,12 +1873,12 @@ go (AppTy s1 t1) (TyConApp tc2 ts2) | Just (ts2', t2') <- snocView ts2- = assert (not (mustBeSaturated tc2)) $+ = assert (not (tyConMustBeSaturated tc2)) $ go_app (isNextTyConArgVisible tc2 ts2') s1 t1 (TyConApp tc2 ts2') t2' go (TyConApp tc1 ts1) (AppTy s2 t2) | Just (ts1', t1') <- snocView ts1- = assert (not (mustBeSaturated tc1)) $+ = assert (not (tyConMustBeSaturated tc1)) $ go_app (isNextTyConArgVisible tc1 ts1') (TyConApp tc1 ts1') t1' s2 t2 go (CoercionTy co1) (CoercionTy co2)@@ -2012,7 +2029,7 @@ -> TcTauType -- Type 2, zonked -> TcM Coercion uUnfilledVar1 origin t_or_k swapped tv1 ty2- | Just tv2 <- tcGetTyVar_maybe ty2+ | Just tv2 <- getTyVar_maybe ty2 = go tv2 | otherwise@@ -2055,26 +2072,26 @@ -- See Note [Unification preconditions], (UNTOUCHABLE) wrinkles , cterHasNoProblem (checkTyVarEq tv1 ty2) -- See Note [Prevent unification with type families]- = do { mb_continue_solving <- startSolvingByUnification (metaTyVarInfo tv1) ty2- ; case mb_continue_solving of- { Nothing -> not_ok_so_defer- ; Just ty2 ->- do { co_k <- uType KindLevel kind_origin (tcTypeKind ty2) (tyVarKind tv1)+ = do { can_continue_solving <- startSolvingByUnification (metaTyVarInfo tv1) ty2+ ; if not can_continue_solving+ then not_ok_so_defer+ else+ do { co_k <- uType KindLevel kind_origin (typeKind ty2) (tyVarKind tv1) ; traceTc "uUnfilledVar2 ok" $ vcat [ ppr tv1 <+> dcolon <+> ppr (tyVarKind tv1)- , ppr ty2 <+> dcolon <+> ppr (tcTypeKind ty2)- , ppr (isTcReflCo co_k), ppr co_k ]+ , ppr ty2 <+> dcolon <+> ppr (typeKind ty2)+ , ppr (isReflCo co_k), ppr co_k ] - ; if isTcReflCo co_k+ ; if isReflCo co_k -- Only proceed if the kinds match -- NB: tv1 should still be unfilled, despite the kind unification -- because tv1 is not free in ty2 (or, hence, in its kind) then do { writeMetaTyVar tv1 ty2- ; return (mkTcNomReflCo ty2) }+ ; return (mkNomReflCo ty2) } - else defer }}} -- This cannot be solved now. See GHC.Tc.Solver.Canonical- -- Note [Equalities with incompatible kinds] for how- -- this will be dealt with in the solver+ else defer }} -- This cannot be solved now. See GHC.Tc.Solver.Canonical+ -- Note [Equalities with incompatible kinds] for how+ -- this will be dealt with in the solver | otherwise = not_ok_so_defer@@ -2094,38 +2111,39 @@ -- | Checks (TYVAR-TV), (COERCION-HOLE) and (CONCRETE) of -- Note [Unification preconditions]; returns True if these conditions -- are satisfied. But see the Note for other preconditions, too.-startSolvingByUnification :: MetaInfo -> TcType -- zonked- -> TcM (Maybe TcType)+startSolvingByUnification :: MetaInfo -> TcType -- zonked+ -> TcM Bool startSolvingByUnification _ xi | hasCoercionHoleTy xi -- (COERCION-HOLE) check- = return Nothing+ = return False startSolvingByUnification info xi = case info of- CycleBreakerTv -> return Nothing+ CycleBreakerTv -> return False ConcreteTv conc_orig ->- do { (xi, not_conc_reasons) <- makeTypeConcrete conc_orig xi+ do { (_, not_conc_reasons) <- makeTypeConcrete conc_orig xi -- NB: makeTypeConcrete has the side-effect of turning -- some TauTvs into ConcreteTvs, e.g. -- alpha[conc] ~# TYPE (TupleRep '[ beta[tau], IntRep ]) -- will write `beta[tau] := beta[conc]`. --- -- We return the new type, so that callers of this function- -- aren't required to zonk.+ -- We don't need to track these unifications for the purposes+ -- of constraint solving (e.g. updating tcs_unified or tcs_unif_lvl),+ -- as they don't unlock any further progress. ; case not_conc_reasons of- [] -> return $ Just xi- _ -> return Nothing }+ [] -> return True+ _ -> return False } TyVarTv ->- case tcGetTyVar_maybe xi of- Nothing -> return Nothing+ case getTyVar_maybe xi of+ Nothing -> return False Just tv -> case tcTyVarDetails tv of -- (TYVAR-TV) wrinkle- SkolemTv {} -> return $ Just xi- RuntimeUnk -> return $ Just xi+ SkolemTv {} -> return True+ RuntimeUnk -> return True MetaTv { mtv_info = info } -> case info of- TyVarTv -> return $ Just xi- _ -> return Nothing- _ -> return $ Just xi+ TyVarTv -> return True+ _ -> return False+ _ -> return True swapOverTyVars :: Bool -> TcTyVar -> TcTyVar -> Bool swapOverTyVars is_given tv1 tv2@@ -2200,7 +2218,7 @@ assumptions", section 2.2. We say that alpha[1] is "untouchable" inside this implication. - Bottom line: at amibient level 'l', when looking at a constraint+ Bottom line: at ambient level 'l', when looking at a constraint alpha[n] ~ ty, do not unify alpha := ty if there are any given equalities between levels 'n' and 'l'. @@ -2242,7 +2260,7 @@ is sensible, but it can lead to very confusing error messages. It's very much like a Wanted rewriting a Wanted. Even worse, unifying a variable essentially turns an equality into a Given,- and so we could not use the tracking mechansim in+ and so we could not use the tracking mechanism in Note [Wanteds rewrite Wanteds] in GHC.Tc.Types.Constraint. We thus simply do not unify in this case. @@ -2291,6 +2309,9 @@ Note [TyVar/TyVar orientation] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+See also Note [Fundeps with instances, and equality orientation]+where the kind equality orientation is important+ Given (a ~ b), should we orient the CEqCan as (a~b) or (b~a)? This is a surprisingly tricky question! This is invariant (TyEq:TV). @@ -2497,14 +2518,14 @@ matchExpectedFunKind :: TypedThing -- ^ type, only for errors -> Arity -- ^ n: number of desired arrows- -> TcKind -- ^ fun_ kind+ -> TcKind -- ^ fun_kind -> TcM Coercion -- ^ co :: fun_kind ~ (arg1 -> ... -> argn -> res) matchExpectedFunKind hs_ty n k = go n k where go 0 k = return (mkNomReflCo k) - go n k | Just k' <- tcView k = go n k'+ go n k | Just k' <- coreView k = go n k' go n k@(TyVarTy kvar) | isMetaTyVar kvar@@ -2513,9 +2534,10 @@ Indirect fun_kind -> go n fun_kind Flexi -> defer n k } - go n (FunTy { ft_mult = w, ft_arg = arg, ft_res = res })+ go n (FunTy { ft_af = af, ft_mult = w, ft_arg = arg, ft_res = res })+ | isVisibleFunArg af = do { co <- go (n-1) res- ; return (mkTcFunCo Nominal (mkTcNomReflCo w) (mkTcNomReflCo arg) co) }+ ; return (mkNakedFunCo1 Nominal af (mkNomReflCo w) (mkNomReflCo arg) co) } go n other = defer n other@@ -2639,7 +2661,7 @@ go (LitTy {}) = cteOK go (FunTy {ft_af = af, ft_mult = w, ft_arg = a, ft_res = r}) = go w S.<> go a S.<> go r S.<>- if not ghci_tv && af == InvisArg+ if not ghci_tv && isInvisibleFunArg af then impredicative else cteOK go (AppTy fun arg) = go fun S.<> go arg
compiler/GHC/Tc/Utils/Zonk.hs view
@@ -56,9 +56,8 @@ import GHC.Tc.Utils.TcMType import GHC.Tc.Utils.Env ( tcLookupGlobalOnly ) import GHC.Tc.Types.Evidence-import GHC.Tc.Errors.Types -import GHC.Core.TyCo.Ppr ( pprTyVar )+import GHC.Core.TyCo.Ppr ( pprTyVar ) import GHC.Core.TyCon import GHC.Core.Type import GHC.Core.Coercion@@ -79,7 +78,6 @@ import GHC.Types.Var import GHC.Types.Var.Env import GHC.Types.Id-import GHC.Types.Id.Info import GHC.Types.TypeEnv import GHC.Types.SourceText import GHC.Types.Basic@@ -355,7 +353,7 @@ -- immediately by creating a TypeEnv zonkLIdOcc :: ZonkEnv -> LocatedN TcId -> LocatedN Id-zonkLIdOcc env = mapLoc (zonkIdOcc env)+zonkLIdOcc env = fmap (zonkIdOcc env) zonkIdOcc :: ZonkEnv -> TcId -> Id -- Ids defined in this module should be in the envt;@@ -386,7 +384,7 @@ zonkIdBndr :: ZonkEnv -> TcId -> TcM Id zonkIdBndr env v = do Scaled w' ty' <- zonkScaledTcTypeToTypeX env (idScaledType v)- return (modifyIdInfo (`setLevityInfoWithType` ty') (setIdMult (setIdType v ty') w'))+ return (setIdMult (setIdType v ty') w') zonkIdBndrs :: ZonkEnv -> [TcId] -> TcM [Id] zonkIdBndrs env ids = mapM (zonkIdBndr env) ids@@ -537,12 +535,12 @@ zonk_bind :: ZonkEnv -> HsBind GhcTc -> TcM (HsBind GhcTc) zonk_bind env bind@(PatBind { pat_lhs = pat, pat_rhs = grhss- , pat_ext = ty})+ , pat_ext = (ty, ticks)}) = do { (_env, new_pat) <- zonkPat env pat -- Env already extended ; new_grhss <- zonkGRHSs env zonkLExpr grhss ; new_ty <- zonkTcTypeToTypeX env ty ; return (bind { pat_lhs = new_pat, pat_rhs = new_grhss- , pat_ext = new_ty }) }+ , pat_ext = (new_ty, ticks) }) } zonk_bind env (VarBind { var_ext = x , var_id = var, var_rhs = expr })@@ -554,13 +552,13 @@ zonk_bind env bind@(FunBind { fun_id = L loc var , fun_matches = ms- , fun_ext = co_fn })+ , fun_ext = (co_fn, ticks) }) = do { new_var <- zonkIdBndr env var ; (env1, new_co_fn) <- zonkCoFn env co_fn ; new_ms <- zonkMatchGroup env1 zonkLExpr ms ; return (bind { fun_id = L loc new_var , fun_matches = new_ms- , fun_ext = new_co_fn }) }+ , fun_ext = (new_co_fn, ticks) }) } zonk_bind env (XHsBindsLR (AbsBinds { abs_tvs = tyvars, abs_ev_vars = evs , abs_ev_binds = ev_binds@@ -587,7 +585,7 @@ | has_sig , (L loc bind@(FunBind { fun_id = (L mloc mono_id) , fun_matches = ms- , fun_ext = co_fn })) <- lbind+ , fun_ext = (co_fn, ticks) })) <- lbind = do { new_mono_id <- updateIdTypeAndMultM (zonkTcTypeToTypeX env) mono_id -- Specifically /not/ zonkIdBndr; we do not want to -- complain about a representation-polymorphic binder@@ -596,7 +594,7 @@ ; return $ L loc $ bind { fun_id = L mloc new_mono_id , fun_matches = new_ms- , fun_ext = new_co_fn } }+ , fun_ext = (new_co_fn, ticks) } } | otherwise = zonk_lbind env lbind -- The normal case @@ -676,14 +674,14 @@ -> MatchGroup GhcTc (LocatedA (body GhcTc)) -> TcM (MatchGroup GhcTc (LocatedA (body GhcTc))) zonkMatchGroup env zBody (MG { mg_alts = L l ms- , mg_ext = MatchGroupTc arg_tys res_ty- , mg_origin = origin })+ , mg_ext = MatchGroupTc arg_tys res_ty origin+ }) = do { ms' <- mapM (zonkMatch env zBody) ms ; arg_tys' <- zonkScaledTcTypesToTypesX env arg_tys ; res_ty' <- zonkTcTypeToTypeX env res_ty ; return (MG { mg_alts = L l ms'- , mg_ext = MatchGroupTc arg_tys' res_ty'- , mg_origin = origin }) }+ , mg_ext = MatchGroupTc arg_tys' res_ty' origin+ }) } zonkMatch :: Anno (GRHS GhcTc (LocatedA (body GhcTc))) ~ SrcAnn NoEpAnns => ZonkEnv@@ -747,7 +745,7 @@ zonkExpr _ (HsIPVar x _) = dataConCantHappen x -zonkExpr _ (HsOverLabel x _) = dataConCantHappen x+zonkExpr _ (HsOverLabel x _ _) = dataConCantHappen x zonkExpr env (HsLit x (HsRat e f ty)) = do new_ty <- zonkTcTypeToTypeX env ty@@ -773,10 +771,10 @@ new_e2 <- zonkLExpr env e2 return (HsApp x new_e1 new_e2) -zonkExpr env (HsAppType ty e t)+zonkExpr env (HsAppType ty e at t) = do new_e <- zonkLExpr env e new_ty <- zonkTcTypeToTypeX env ty- return (HsAppType new_ty new_e t)+ return (HsAppType new_ty new_e at t) -- NB: the type is an HsType; can't zonk that! zonkExpr env (HsTypedBracket hsb_tc body)@@ -785,10 +783,9 @@ zonkExpr env (HsUntypedBracket hsb_tc body) = (\x -> HsUntypedBracket x body) <$> zonkBracket env hsb_tc -zonkExpr env (HsSpliceE _ (XSplice (HsSplicedT s))) =- runTopSplice s >>= zonkExpr env+zonkExpr env (HsTypedSplice s _) = runTopSplice s >>= zonkExpr env -zonkExpr _ e@(HsSpliceE _ _) = pprPanic "zonkExpr: HsSpliceE" (ppr e)+zonkExpr _ e@(HsUntypedSplice _ _) = pprPanic "zonkExpr: HsUntypedSplice" (ppr e) zonkExpr _ (OpApp x _ _ _) = dataConCantHappen x @@ -859,32 +856,6 @@ ; return (expr { rcon_ext = new_con_expr , rcon_flds = new_rbinds }) } --- Record updates via dot syntax are replaced by desugared expressions--- in the renamer. See Note [Rebindable syntax and HsExpansion]. This--- is why we match on 'rupd_flds = Left rbinds' here and panic otherwise.-zonkExpr env (RecordUpd { rupd_flds = Left rbinds- , rupd_expr = expr- , rupd_ext = RecordUpdTc {- rupd_cons = cons- , rupd_in_tys = in_tys- , rupd_out_tys = out_tys- , rupd_wrap = req_wrap }})- = do { new_expr <- zonkLExpr env expr- ; new_in_tys <- mapM (zonkTcTypeToTypeX env) in_tys- ; new_out_tys <- mapM (zonkTcTypeToTypeX env) out_tys- ; new_rbinds <- zonkRecUpdFields env rbinds- ; (_, new_recwrap) <- zonkCoFn env req_wrap- ; return (- RecordUpd {- rupd_expr = new_expr- , rupd_flds = Left new_rbinds- , rupd_ext = RecordUpdTc {- rupd_cons = cons- , rupd_in_tys = new_in_tys- , rupd_out_tys = new_out_tys- , rupd_wrap = new_recwrap }}) }-zonkExpr _ (RecordUpd {}) = panic "GHC.Tc.Utils.Zonk: zonkExpr: The impossible happened!"- zonkExpr env (ExprWithTySig _ e ty) = do { e' <- zonkLExpr env e ; return (ExprWithTySig noExtField e' ty) }@@ -1038,7 +1009,7 @@ new_ty <- zonkTcTypeToTypeX env ty new_ids <- mapSndM (zonkExpr env) ids - massert (isLiftedTypeKind (tcTypeKind new_stack_tys))+ massert (isLiftedTypeKind (typeKind new_stack_tys)) -- desugarer assumes that this is not representation-polymorphic... -- but indeed it should always be lifted due to the typing -- rules for arrows@@ -1207,7 +1178,7 @@ ; (env1, bind_op') <- zonkSyntaxExpr env bind_op ; bind_arg_ty' <- zonkTcTypeToTypeX env1 bind_arg_ty ; (env2, stmts') <- zonkStmts env1 zonkLExpr stmts- ; by' <- fmapMaybeM (zonkLExpr env2) by+ ; by' <- traverse (zonkLExpr env2) by ; using' <- zonkLExpr env2 using ; (env3, return_op') <- zonkSyntaxExpr env2 return_op@@ -1311,16 +1282,6 @@ ; return (L l (fld { hfbLHS = new_id , hfbRHS = new_expr })) } -zonkRecUpdFields :: ZonkEnv -> [LHsRecUpdField GhcTc]- -> TcM [LHsRecUpdField GhcTc]-zonkRecUpdFields env = mapM zonk_rbind- where- zonk_rbind (L l fld)- = do { new_id <- wrapLocMA (zonkFieldOcc env) (hsRecUpdFieldOcc fld)- ; new_expr <- zonkLExpr env (hfbRHS fld)- ; return (L l (fld { hfbLHS = fmap ambiguousFieldOcc new_id- , hfbRHS = new_expr })) }- {- ************************************************************************ * *@@ -1356,10 +1317,10 @@ = do { (env', pat') <- zonkPat env pat ; return (env', BangPat x pat') } -zonk_pat env (AsPat x (L loc v) pat)+zonk_pat env (AsPat x (L loc v) at pat) = do { v' <- zonkIdBndr env v ; (env', pat') <- zonkPat (extendIdZonkEnv env v') pat- ; return (env', AsPat x (L loc v') pat') }+ ; return (env', AsPat x (L loc v') at pat') } zonk_pat env (ViewPat ty expr pat) = do { expr' <- zonkLExpr env expr@@ -1673,7 +1634,7 @@ ; term' <- case getEqPredTys_maybe (idType var') of Just (r, ty1, ty2) | ty1 `eqType` ty2- -> return (evCoercion (mkTcReflCo r ty1))+ -> return (evCoercion (mkReflCo r ty1)) _other -> zonkEvTerm env term ; return (bind { eb_lhs = var', eb_rhs = term' }) }@@ -1772,7 +1733,7 @@ T9198 and #19668. So yes, it seems worth it. -} -zonkTyVarOcc :: HasDebugCallStack => ZonkEnv -> TcTyVar -> TcM Type+zonkTyVarOcc :: ZonkEnv -> TcTyVar -> TcM Type zonkTyVarOcc env@(ZonkEnv { ze_flexi = flexi , ze_tv_env = tv_env , ze_meta_tv_env = mtv_env_ref }) tv@@ -1845,9 +1806,6 @@ | isMultiplicityTy zonked_kind -> do { traceTc "Defaulting flexi tyvar to Many:" (pprTyVar tv) ; return manyDataConTy }- | Just (ConcreteFRR origin) <- isConcreteTyVar_maybe tv- -> do { addErr $ TcRnCannotDefaultConcrete origin- ; return (anyTypeOfKind zonked_kind) } | otherwise -> do { traceTc "Defaulting flexi tyvar to Any:" (pprTyVar tv) ; return (anyTypeOfKind zonked_kind) }
compiler/GHC/Tc/Validity.hs view
@@ -1,4 +1,3 @@- {-# LANGUAGE DerivingStrategies #-} {-# OPTIONS_GHC -Wno-incomplete-record-updates #-}@@ -18,10 +17,10 @@ checkValidTyFamEqn, checkValidAssocTyFamDeflt, checkConsistentFamInst, arityErr, checkTyConTelescope,- allDistinctTyVars ) where import GHC.Prelude+import GHC.Data.FastString import GHC.Data.Maybe @@ -29,12 +28,19 @@ import GHC.Tc.Utils.Unify ( tcSubTypeAmbiguity ) import GHC.Tc.Solver ( simplifyAmbiguityCheck ) import GHC.Tc.Instance.Class ( matchGlobalInst, ClsInstResult(..), InstanceWhat(..), AssocInstInfo(..) )-import GHC.Core.TyCo.FVs-import GHC.Core.TyCo.Rep-import GHC.Core.TyCo.Ppr-import GHC.Tc.Utils.TcType hiding ( sizeType, sizeTypes )+import GHC.Tc.Utils.TcType+import GHC.Tc.Types.Origin+import GHC.Tc.Types.Rank+import GHC.Tc.Errors.Types+import GHC.Tc.Utils.Monad+import GHC.Tc.Utils.Env ( tcInitTidyEnv, tcInitOpenTidyEnv )+import GHC.Tc.Instance.FunDeps+import GHC.Tc.Instance.Family+ import GHC.Builtin.Types import GHC.Builtin.Names+import GHC.Builtin.Uniques ( mkAlphaTyVarUnique )+ import GHC.Core.Type import GHC.Core.Unify ( tcMatchTyX_BM, BindFlag(..) ) import GHC.Core.Coercion@@ -42,41 +48,41 @@ import GHC.Core.Class import GHC.Core.TyCon import GHC.Core.Predicate-import GHC.Tc.Types.Origin-import GHC.Tc.Types.Rank-import GHC.Tc.Errors.Types-import GHC.Types.Error+import GHC.Core.TyCo.FVs+import GHC.Core.TyCo.Rep+import GHC.Core.TyCo.Ppr+import GHC.Core.FamInstEnv ( isDominatedBy, injectiveBranches+ , InjectivityCheckResult(..) ) --- others: import GHC.Iface.Type ( pprIfaceType, pprIfaceTypeApp ) import GHC.CoreToIface ( toIfaceTyCon, toIfaceTcArgs, toIfaceType ) import GHC.Hs-import GHC.Tc.Utils.Monad-import GHC.Tc.Utils.Env ( tcInitTidyEnv, tcInitOpenTidyEnv )-import GHC.Tc.Instance.FunDeps-import GHC.Core.FamInstEnv- ( isDominatedBy, injectiveBranches, InjectivityCheckResult(..) )-import GHC.Tc.Instance.Family+import GHC.Driver.Session+import qualified GHC.LanguageExtensions as LangExt++import GHC.Types.Error import GHC.Types.Basic ( UnboxedTupleOrSum(..), unboxedTupleOrSumExtension ) import GHC.Types.Name import GHC.Types.Var.Env import GHC.Types.Var.Set-import GHC.Types.Var ( VarBndr(..), mkTyVar )+import GHC.Types.Var ( VarBndr(..), isInvisibleFunArg, mkTyVar )+import GHC.Types.SrcLoc+import GHC.Types.Unique.Set( isEmptyUniqSet )+ import GHC.Utils.FV import GHC.Utils.Error-import GHC.Driver.Session import GHC.Utils.Misc-import GHC.Data.List.SetOps-import GHC.Types.SrcLoc import GHC.Utils.Outputable as Outputable import GHC.Utils.Panic-import GHC.Builtin.Uniques ( mkAlphaTyVarUnique )-import qualified GHC.LanguageExtensions as LangExt +import GHC.Data.List.SetOps++import Language.Haskell.Syntax.Basic (FieldLabelString(..))+ import Control.Monad import Data.Foldable import Data.Function-import Data.List ( (\\), nub )+import Data.List ( (\\) ) import qualified Data.List.NonEmpty as NE {-@@ -361,11 +367,11 @@ checkValidType :: UserTypeCtxt -> Type -> TcM () -- Checks that a user-written type is valid for the given context -- Assumes argument is fully zonked--- Assumes arugment is well-kinded;+-- Assumes argument is well-kinded; -- that is, checkValidType doesn't need to do kind checking -- Not used for instance decls; checkValidInstance instead checkValidType ctxt ty- = do { traceTc "checkValidType" (ppr ty <+> text "::" <+> ppr (tcTypeKind ty))+ = do { traceTc "checkValidType" (ppr ty <+> text "::" <+> ppr (typeKind ty)) ; rankn_flag <- xoptM LangExt.RankNTypes ; impred_flag <- xoptM LangExt.ImpredicativeTypes ; let gen_rank :: Rank -> Rank@@ -431,7 +437,7 @@ -- and there may be nested foralls for the subtype test to examine ; checkAmbiguity ctxt ty - ; traceTc "checkValidType done" (ppr ty <+> text "::" <+> ppr (tcTypeKind ty)) }+ ; traceTc "checkValidType done" (ppr ty <+> text "::" <+> ppr (typeKind ty)) } checkValidMonoType :: Type -> TcM () -- Assumes argument is fully zonked@@ -444,10 +450,10 @@ checkTySynRhs :: UserTypeCtxt -> TcType -> TcM () checkTySynRhs ctxt ty- | tcReturnsConstraintKind actual_kind+ | returnsConstraintKind actual_kind = do { ck <- xoptM LangExt.ConstraintKinds ; if ck- then when (tcIsConstraintKind actual_kind)+ then when (isConstraintLikeKind actual_kind) (do { dflags <- getDynFlags ; expand <- initialExpandMode ; check_pred_ty emptyTidyEnv dflags ctxt expand ty })@@ -458,7 +464,7 @@ | otherwise = return () where- actual_kind = tcTypeKind ty+ actual_kind = typeKind ty {- Note [Check for escaping result kind] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -779,7 +785,7 @@ -- -- Critically, this case must come *after* the case for TyConApp. -- See Note [Liberal type synonyms].-check_type ve@(ValidityEnv{ ve_tidy_env = env, ve_ctxt = ctxt+check_type ve@(ValidityEnv{ ve_tidy_env = env , ve_rank = rank, ve_expand = expand }) ty | not (null tvbs && null theta) = do { traceTc "check_type" (ppr ty $$ ppr rank)@@ -791,9 +797,7 @@ -- Reject forall (a :: Eq b => b). blah -- In a kind signature we don't allow constraints - ; checkTcM (all (isInvisibleArgFlag . binderArgFlag) tvbs- || vdqAllowed ctxt)- (env, TcRnVDQInTermType (tidyType env ty))+ ; checkVdqOK ve tvbs ty -- Reject visible, dependent quantification in the type of a -- term (e.g., `f :: forall a -> a -> Maybe a`) @@ -811,12 +815,12 @@ where (tvbs, phi) = tcSplitForAllTyVarBinders ty (theta, tau) = tcSplitPhiTy phi- (env', _) = tidyTyCoVarBinders env tvbs+ (env', _) = tidyForAllTyBinders env tvbs check_type (ve@ValidityEnv{ ve_tidy_env = env, ve_ctxt = ctxt , ve_rank = rank }) ty@(FunTy _ mult arg_ty res_ty)- = do { failIfTcM (not (linearityAllowed ctxt) && not (isManyDataConTy mult))+ = do { failIfTcM (not (linearityAllowed ctxt) && not (isManyTy mult)) (env, TcRnLinearFuncInKind (tidyType env ty)) ; check_type (ve{ve_rank = arg_rank}) arg_ty ; check_type (ve{ve_rank = res_rank}) res_ty }@@ -868,7 +872,7 @@ check_expansion_only expand = assertPpr (isTypeSynonymTyCon tc) (ppr tc) $- case tcView ty of+ case coreView ty of Just ty' -> let err_ctxt = text "In the expansion of type synonym" <+> quotes (ppr tc) in addErrCtxt err_ctxt $@@ -987,6 +991,14 @@ checkTcM (all isEqPred theta) (env, TcRnConstraintInKind (tidyType env ty)) where env = ve_tidy_env ve +checkVdqOK :: ValidityEnv -> [TyVarBinder] -> Type -> TcM ()+checkVdqOK ve tvbs ty = do+ checkTcM (vdqAllowed ctxt || no_vdq)+ (env, TcRnVDQInTermType (Just (tidyType env ty)))+ where+ no_vdq = all (isInvisibleForAllTyFlag . binderFlag) tvbs+ ValidityEnv{ve_tidy_env = env, ve_ctxt = ctxt} = ve+ {- Note [Liberal type synonyms] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -1128,7 +1140,7 @@ -> DynFlags -> UserTypeCtxt -> PredType -> TcM () check_pred_help under_syn env dflags ctxt pred- | Just pred' <- tcView pred -- Switch on under_syn when going under a+ | Just pred' <- coreView pred -- Switch on under_syn when going under a -- synonym (#9838, yuk) = check_pred_help True env dflags ctxt pred' @@ -1140,7 +1152,7 @@ | otherwise -> check_class_pred env dflags ctxt pred cls tys EqPred _ _ _ -> pprPanic "check_pred_help" (ppr pred)- -- EqPreds, such as (t1 ~ #t2) or (t1 ~R# t2), don't even have kind Constraint+ -- EqPreds, such as (t1 ~# t2) or (t1 ~R# t2), don't even have kind Constraint -- and should never appear before the '=>' of a type. Thus -- f :: (a ~# b) => blah -- is wrong. For user written signatures, it'll be rejected by kind-checking@@ -1248,7 +1260,7 @@ = do { result <- matchGlobalInst dflags False cls tys ; case result of OneInst { cir_what = what }- -> let dia = TcRnUnknownMessage $+ -> let dia = mkTcRnUnknownMessage $ mkPlainDiagnostic (WarningWithFlag Opt_WarnSimplifiableClassConstraints) noHints (simplifiable_constraint_warn what)@@ -1376,7 +1388,7 @@ arityErr :: Outputable a => SDoc -> a -> Int -> Int -> TcRnMessage arityErr what name n m- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ hsep [ text "The" <+> what, quotes (ppr name), text "should have", n_arguments <> comma, text "but has been given", if m==0 then text "none" else int m]@@ -1428,7 +1440,7 @@ instance KnownNat T That says that any module satisfying M.hsig must provide a KnownNat-instance for T. We absolultely need that instance when compiling a+instance for T. We absolutely need that instance when compiling a module that imports M.hsig: see #15379 and Note [Fabricating Evidence for Literals in Backpack] in GHC.Tc.Instance.Class. @@ -1587,7 +1599,9 @@ tcInstHeadTyAppAllTyVars ty | Just (tc, tys) <- tcSplitTyConApp_maybe (dropCasts ty) = let tys' = filterOutInvisibleTypes tc tys -- avoid kinds- tys'' | tc == funTyCon, tys_h:tys_t <- tys', tys_h `eqType` manyDataConTy = tys_t+ tys'' | tc `hasKey` fUNTyConKey+ , ManyTy : tys_t <- tys'+ = tys_t | otherwise = tys' in ok tys'' | LitTy _ <- ty = True -- accept type literals (#13833)@@ -1598,7 +1612,7 @@ -- and that each is distinct ok tys = equalLength tvs tys && hasNoDups tvs where- tvs = mapMaybe tcGetTyVar_maybe tys+ tvs = mapMaybe getTyVar_maybe tys dropCasts :: Type -> Type -- See Note [Casts during validity checking]@@ -1617,7 +1631,7 @@ instTypeErr :: Class -> [Type] -> SDoc -> TcRnMessage instTypeErr cls tys msg- = TcRnUnknownMessage $ mkPlainError noHints $+ = mkTcRnUnknownMessage $ mkPlainError noHints $ hang (hang (text "Illegal instance declaration for") 2 (quotes (pprClassPred cls tys))) 2 msg@@ -1632,7 +1646,7 @@ -> whoops (text "Record data type may not be a data family") | otherwise -> case isStrLitTy x_ty of Just lbl- | isJust (lookupTyConFieldLabel lbl tc)+ | isJust (lookupTyConFieldLabel (FieldLabelString lbl) tc) -> whoops (ppr tc <+> text "already has a field" <+> quotes (ppr lbl)) | otherwise -> return ()@@ -1680,68 +1694,67 @@ Note [Valid 'deriving' predicate] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-validDerivPred checks for OK 'deriving' context. See Note [Exotic-derived instance contexts] in GHC.Tc.Deriv. However the predicate is-here because it uses sizeTypes, fvTypes.+validDerivPred checks for OK 'deriving' context.+See Note [Exotic derived instance contexts] in GHC.Tc.Deriv.Infer. However the predicate is+here because it is quite similar to checkInstTermination. -It checks for three things+It checks for two things: -(VD1) No repeated variables (hasNoDups fvs)+(VD1) The Paterson conditions; see Note [Paterson conditions]+ Not on do we want to check for termination (of course), but it also+ deals with a nasty corner case:+ instance C a b => D (T a) where ...+ Note that 'b' isn't a parameter of T. This gives rise to all sorts of+ problems; in particular, it's hard to compare solutions for equality+ when finding the fixpoint, and that means the inferContext loop does+ not converge. See #5287, #21302 -(VD2) No type constructors. This is done by comparing- sizeTypes tys == length (fvTypes tys)- sizeTypes counts variables and constructors; fvTypes returns variables.- So if they are the same, there must be no constructors. But there- might be applications thus (f (g x)).+(VD2) No type constructors; no foralls, no equalities:+ see Note [Exotic derived instance contexts] in GHC.Tc.Deriv.Infer - Note that tys only includes the visible arguments of the class type+ We check the no-type-constructor bit using tyConsOfType.+ Wrinkle: we look only at the /visible/ arguments of the class type constructor. Including the non-visible arguments can cause the following, perfectly valid instance to be rejected: class Category (cat :: k -> k -> *) where ... newtype T (c :: * -> * -> *) a b = MkT (c a b) instance Category c => Category (T c) where ...- since the first argument to Category is a non-visible *, which sizeTypes- would count as a constructor! See #11833.+ since the first argument to Category is a non-visible *, which has a+ type constructor! See #11833. -(VD3) Also check for a bizarre corner case, when the derived instance decl- would look like- instance C a b => D (T a) where ...- Note that 'b' isn't a parameter of T. This gives rise to all sorts of- problems; in particular, it's hard to compare solutions for equality- when finding the fixpoint, and that means the inferContext loop does- not converge. See #5287, #21302 Note [Equality class instances] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We can't have users writing instances for the equality classes. But we still need to be able to write instances for them ourselves. So we allow instances only in the defining module.- -} -validDerivPred :: TyVarSet -> PredType -> Bool+validDerivPred :: PatersonSize -> PredType -> Bool -- See Note [Valid 'deriving' predicate]-validDerivPred tv_set pred- | not (tyCoVarsOfType pred `subVarSet` tv_set)- = False -- Check (VD3)-- | otherwise+validDerivPred head_size pred = case classifyPredType pred of+ EqPred {} -> False -- Reject equality constraints+ ForAllPred {} -> False -- Rejects quantified predicates - ClassPred cls tys- | isTerminatingClass cls -> True- -- Typeable constraints are bigger than they appear due- -- to kind polymorphism, but that's OK+ ClassPred cls tys -> check_size (pSizeClassPred cls tys)+ && isEmptyUniqSet (tyConsOfTypes visible_tys)+ where+ visible_tys = filterOutInvisibleTypes (classTyCon cls) tys -- (VD2) - | otherwise -> hasNoDups visible_fvs -- Check (VD1)- && lengthIs visible_fvs (sizeTypes visible_tys) -- Check (VD2)- where- visible_tys = filterOutInvisibleTypes (classTyCon cls) tys- visible_fvs = fvTypes visible_tys+ IrredPred {} -> check_size (pSizeType pred)+ -- Very important that we do the "too many variable occurrences"+ -- check here, via check_size. Example (test T21302):+ -- instance c Eq a => Eq (BoxAssoc a)+ -- data BAD = BAD (BoxAssoc Int) deriving( Eq )+ -- We don't want to accept an inferred predicate (c0 Eq Int)+ -- from that `deriving(Eq)` clause, because the c0 is fresh,+ -- so we'll think it's a "new" one, and loop in+ -- GHC.Tc.Deriv.Infer.simplifyInstanceContexts - IrredPred {} -> True -- Accept (f a)- EqPred {} -> False -- Reject equality constraints- ForAllPred {} -> False -- Rejects quantified predicates+ where+ check_size pred_size = isNothing (pred_size `ltPatersonSize` head_size)+ -- Check (VD1) {- ************************************************************************@@ -1751,38 +1764,80 @@ ************************************************************************ -} -{- Note [Instances and constraint synonyms]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Currently, we don't allow instances for constraint synonyms at all.-Consider these (#13267):- type C1 a = Show (a -> Bool)- instance C1 Int where -- I1- show _ = "ur"+{- Note [Paterson conditions]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+The Paterson Conditions ensure termination of instance resolution.+Given an instance declaration+ instance (..., C t1.. tn, ...) => D s1 .. sm -This elicits "show is not a (visible) method of class C1", which isn't-a great message. But it comes from the renamer, so it's hard to improve.+we check that each constraint in the context of the instance is+"Paterson-smaller" than the instance head. The underlying idea of+Paterson-smaller is that -This needs a bit more care:- type C2 a = (Show a, Show Int)- instance C2 Int -- I2+ For any ground substitution S, for each constraint P in the+ context, S(P) has fewer type constructors, counting repetitions,+ than the head S(H) -If we use (splitTyConApp_maybe tau) in checkValidInstance to decompose-the instance head, we'll expand the synonym on fly, and it'll look like- instance (%,%) (Show Int, Show Int)-and we /really/ don't want that. So we carefully do /not/ expand-synonyms, by matching on TyConApp directly.--}+We implement this check by checking the following syntactic conditions: -checkValidInstance :: UserTypeCtxt -> LHsSigType GhcRn -> Type -> TcM ()-checkValidInstance ctxt hs_type ty- | not is_tc_app- = failWithTc (TcRnNoClassInstHead tau)+(PC1) No type variable has more (shallow) occurrences in P than in H. - | isNothing mb_cls- = failWithTc (TcRnIllegalClassInst (tyConFlavour tc))+ (If not, a substitution that replaces that variable with a big type+ would make P have many more type constructors than H. Side note: we+ could in principle skip this test for a variable of kind Bool,+ since there are no big ground types we can substitute for it.) - | otherwise- = do { setSrcSpanA head_loc $+(PC2) The constraint P has fewer constructors and variables (taken+ together and counting repetitions) than the head H. This size+ metric is computed by sizeType.++ (A substitution that replaces each variable with Int demonstrates+ the need.)++(PC3) The constraint P mentions no type functions.++ (A type function application can in principle expand to a type of+ arbitrary size, and so are rejected out of hand. See #15172.)++(See Section 5 of "Understanding functional dependencies via Constraint+Handling Rules", JFP Jan 2007; and the user manual section "Instance+termination rules".)++We measure "size" with the data type PatersonSize, in GHC.Tc.Utils.TcType.+ data PatersonSize+ = PS_TyFam TyCon+ | PS_Vanilla { ps_tvs :: [TyVar] -- Free tyvars, including repetitions;+ , ps_size :: Int} -- Number of type constructors and variables++* ps_tvs deals with (PC1)+* ps_size deals with (PC2)+* PS_TyFam deals with (PC3)++Note [Tuples in checkInstTermination]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Consider these two ways of giving the same instance decl (#8359):++ instance (Eq a, Num a) => C (T a)++ type X a = (Eq a, Num a)+ instance X a => C (T a)++In the former, `checkInstTermination` will check the size of two predicates:+(Eq a) and (Num a). In the latter, it will see only one: (X a). But we want+to treat the latter like the former.++So the `check` function in `checkInstTermination`, we simply recurse+if we find a constraint tuple.+-}+++checkValidInstance :: UserTypeCtxt -> LHsSigType GhcRn -> Type -> TcM ()+checkValidInstance ctxt hs_type ty = case tau of+ -- See Note [Instances and constraint synonyms]+ TyConApp tc inst_tys -> case tyConClass_maybe tc of+ Nothing -> failWithTc (TcRnIllegalClassInst (tyConFlavour tc))+ Just clas -> do+ { setSrcSpanA head_loc $ checkValidInstHead ctxt clas inst_tys ; traceTc "checkValidInstance {" (ppr ty)@@ -1791,7 +1846,7 @@ ; expand <- initialExpandMode ; check_valid_theta env0 ctxt expand theta - -- The Termination and Coverate Conditions+ -- The Termination and Coverage Conditions -- Check that instance inference will terminate (if we care) -- For Haskell 98 this will already have been done by checkValidTheta, -- but as we may be using other extensions we need to check.@@ -1815,43 +1870,45 @@ ; traceTc "End checkValidInstance }" empty ; return () }+ _ -> failWithTc (TcRnNoClassInstHead tau) where- (_tvs, theta, tau) = tcSplitSigmaTy ty- is_tc_app = case tau of { TyConApp {} -> True; _ -> False }- TyConApp tc inst_tys = tau -- See Note [Instances and constraint synonyms]- mb_cls = tyConClass_maybe tc- Just clas = mb_cls+ (theta, tau) = splitInstTyForValidity ty -- The location of the "head" of the instance head_loc = getLoc (getLHsInstDeclHead hs_type) -{--Note [Paterson conditions]-~~~~~~~~~~~~~~~~~~~~~~~~~~-Termination test: the so-called "Paterson conditions" (see Section 5 of-"Understanding functional dependencies via Constraint Handling Rules,-JFP Jan 2007).--We check that each assertion in the context satisfies:- (1) no variable has more occurrences in the assertion than in the head, and- (2) the assertion has fewer constructors and variables (taken together- and counting repetitions) than the head.-This is only needed with -fglasgow-exts, as Haskell 98 restrictions-(which have already been checked) guarantee termination.--The underlying idea is that+-- | Split an instance type of the form @forall tvbs. inst_ctxt => inst_head@+-- and return @(inst_ctxt, inst_head)@. This function makes no attempt to look+-- through type synonyms. See @Note [Instances and constraint synonyms]@.+splitInstTyForValidity :: Type -> (ThetaType, Type)+splitInstTyForValidity = split_context [] . drop_foralls+ where+ -- This is like 'dropForAlls', except that it does not look through type+ -- synonyms.+ drop_foralls :: Type -> Type+ drop_foralls (ForAllTy (Bndr _tv argf) ty)+ | isInvisibleForAllTyFlag argf = drop_foralls ty+ drop_foralls ty = ty - for any ground substitution, each assertion in the- context has fewer type constructors than the head.--}+ -- This is like 'tcSplitPhiTy', except that it does not look through type+ -- synonyms.+ split_context :: ThetaType -> Type -> (ThetaType, Type)+ split_context preds (FunTy { ft_af = af, ft_arg = pred, ft_res = tau })+ | isInvisibleFunArg af = split_context (pred:preds) tau+ split_context preds ty = (reverse preds, ty) checkInstTermination :: ThetaType -> TcPredType -> TcM () -- See Note [Paterson conditions] checkInstTermination theta head_pred = check_preds emptyVarSet theta where- head_fvs = fvType head_pred- head_size = sizeType head_pred+ head_size = pSizeType head_pred+ -- This is inconsistent and probably wrong. pSizeType does not filter out+ -- invisible type args (making the instance head look big), whereas the use of+ -- pSizeClassPredX below /does/ filter them out (making the tested constraints+ -- look smaller). I'm sure there is non termination lurking here, but see #15177+ -- for why I didn't change it. See Note [Invisible arguments and termination]+ -- in GHC.Tc.Utils.TcType check_preds :: VarSet -> [PredType] -> TcM () check_preds foralld_tvs preds = mapM_ (check foralld_tvs) preds@@ -1860,86 +1917,84 @@ check foralld_tvs pred = case classifyPredType pred of EqPred {} -> return () -- See #4200.- IrredPred {} -> check2 foralld_tvs pred (sizeType pred)- ClassPred cls tys- | isTerminatingClass cls- -> return ()+ IrredPred {} -> check2 (pSizeTypeX foralld_tvs pred) - | isCTupleClass cls -- Look inside tuple predicates; #8359+ ClassPred cls tys+ | isCTupleClass cls -- See Note [Tuples in checkInstTermination] -> check_preds foralld_tvs tys | otherwise -- Other ClassPreds- -> check2 foralld_tvs pred bogus_size- where- bogus_size = 1 + sizeTypes (filterOutInvisibleTypes (classTyCon cls) tys)- -- See Note [Invisible arguments and termination]+ -> check2 (pSizeClassPredX foralld_tvs cls tys) ForAllPred tvs _ head_pred' -> check (foralld_tvs `extendVarSetList` tvs) head_pred' -- Termination of the quantified predicate itself is checked -- when the predicates are individually checked for validity - check2 foralld_tvs pred pred_size- | not (null bad_tvs) = failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $- (noMoreMsg bad_tvs what (ppr head_pred))- | not (isTyFamFree pred) = failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $- (nestedMsg what)- | pred_size >= head_size = failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $- (smallerMsg what (ppr head_pred))- | otherwise = return ()- -- isTyFamFree: see Note [Type families in instance contexts]- where- what = text "constraint" <+> quotes (ppr pred)- bad_tvs = filterOut (`elemVarSet` foralld_tvs) (fvType pred)- \\ head_fvs+ where+ check2 pred_size+ = case pred_size `ltPatersonSize` head_size of+ Just ps_failure -> failWithTc $ mkInstSizeError ps_failure head_pred pred+ Nothing -> return () -smallerMsg :: SDoc -> SDoc -> SDoc-smallerMsg what inst_head- = vcat [ hang (text "The" <+> what)- 2 (sep [ text "is no smaller than"- , text "the instance head" <+> quotes inst_head ])- , parens undecidableMsg ] -noMoreMsg :: [TcTyVar] -> SDoc -> SDoc -> SDoc-noMoreMsg tvs what inst_head- = vcat [ hang (text "Variable" <> plural tvs1 <+> quotes (pprWithCommas ppr tvs1)- <+> occurs <+> text "more often")- 2 (sep [ text "in the" <+> what- , text "than in the instance head" <+> quotes inst_head ])+mkInstSizeError :: PatersonSizeFailure -> TcPredType -> TcPredType -> TcRnMessage+mkInstSizeError ps_failure head_pred pred+ = mkTcRnUnknownMessage $ mkPlainError noHints $+ vcat [ main_msg , parens undecidableMsg ] where- tvs1 = nub tvs- occurs = if isSingleton tvs1 then text "occurs"- else text "occur"+ pp_head = text "instance head" <+> quotes (ppr head_pred)+ pp_pred = text "constraint" <+> quotes (ppr pred) + main_msg = case ps_failure of+ PSF_TyFam tc -> -- See (PC3) of Note [Paterson conditions]+ hang (text "Illegal use of type family" <+> quotes (ppr tc))+ 2 (text "in the" <+> pp_pred)+ PSF_TyVar tvs -> hang (occMsg tvs)+ 2 (sep [ text "in the" <+> pp_pred+ , text "than in the" <+> pp_head ])+ PSF_Size -> hang (text "The" <+> pp_pred)+ 2 (sep [ text "is no smaller than", text "the" <+> pp_head ])++occMsg :: [TyVar] -> SDoc+occMsg tvs = text "Variable" <> plural tvs <+> quotes (pprWithCommas ppr tvs)+ <+> pp_occurs <+> text "more often"+ where+ pp_occurs | isSingleton tvs = text "occurs"+ | otherwise = text "occur"+ undecidableMsg :: SDoc undecidableMsg = text "Use UndecidableInstances to permit this" -{- Note [Type families in instance contexts]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-Are these OK?- type family F a- instance F a => C (Maybe [a]) where ...- instance C (F a) => C [[[a]]] where ... -No: the type family in the instance head might blow up to an-arbitrarily large type, depending on how 'a' is instantiated.-So we require UndecidableInstances if we have a type family-in the instance head. #15172.+{- Note [Instances and constraint synonyms]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Currently, we don't allow instances for constraint synonyms at all.+Consider these (#13267):+ type C1 a = Show (a -> Bool)+ instance C1 Int where -- I1+ show _ = "ur" -Note [Invisible arguments and termination]-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-When checking the Paterson conditions for termination an instance-declaration, we check for the number of "constructors and variables"-in the instance head and constraints. Question: Do we look at+This elicits "show is not a (visible) method of class C1", which isn't+a great message. But it comes from the renamer, so it's hard to improve. - * All the arguments, visible or invisible?- * Just the visible arguments?+This needs a bit more care:+ type C2 a = (Show a, Show Int)+ instance C2 Int -- I2 -I think both will ensure termination, provided we are consistent.-Currently we are /not/ consistent, which is really a bug. It's-described in #15177, which contains a number of examples.-The suspicious bits are the calls to filterOutInvisibleTypes.+If we use (splitTyConApp_maybe tau) in checkValidInstance to decompose+the instance head, we'll expand the synonym on fly, and it'll look like+ instance (%,%) (Show Int, Show Int)+and we /really/ don't want that. So we carefully do /not/ expand+synonyms, by matching on TyConApp directly.++For similar reasons, we do not use tcSplitSigmaTy when decomposing the instance+context, as the looks through type synonyms. If we looked through type+synonyms, then it could be possible to write an instance for a type synonym+involving a quantified constraint (see #22570). Instead, we define+splitInstTyForValidity, a specialized version of tcSplitSigmaTy (local to+GHC.Tc.Validity) that does not expand type synonyms. -} @@ -1967,7 +2022,7 @@ -- (b) failure of injectivity check_branch_compat prev_branches cur_branch | cur_branch `isDominatedBy` prev_branches- = do { let dia = TcRnUnknownMessage $+ = do { let dia = mkTcRnUnknownMessage $ mkPlainDiagnostic WarningWithoutFlag noHints (inaccessibleCoAxBranch fam_tc cur_branch) ; addDiagnosticAt (coAxBranchSpan cur_branch) dia ; return prev_branches }@@ -2069,23 +2124,23 @@ do { cpt_tvs <- zipWithM extract_tv pats pats_vis ; check_all_distinct_tvs $ zip cpt_tvs pats_vis } where- pats_vis :: [ArgFlag]- pats_vis = tyConArgFlags fam_tc pats+ pats_vis :: [ForAllTyFlag]+ pats_vis = tyConForAllTyFlags fam_tc pats -- Checks that a pattern on the LHS of a default is a type -- variable. If so, return the underlying type variable, and if -- not, throw an error. -- See Note [Type-checking default assoc decls]- extract_tv :: Type -- The particular type pattern from which to extract- -- its underlying type variable- -> ArgFlag -- The visibility of the type pattern- -- (only used for error message purposes)+ extract_tv :: Type -- The particular type pattern from which to+ -- extrace its underlying type variable+ -> ForAllTyFlag -- The visibility of the type pattern+ -- (only used for error message purposes) -> TcM TyVar extract_tv pat pat_vis = case getTyVar_maybe pat of Just tv -> pure tv- Nothing -> failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $- pprWithExplicitKindsWhen (isInvisibleArgFlag pat_vis) $+ Nothing -> failWithTc $ mkTcRnUnknownMessage $ mkPlainError noHints $+ pprWithExplicitKindsWhen (isInvisibleForAllTyFlag pat_vis) $ hang (text "Illegal argument" <+> quotes (ppr pat) <+> text "in:") 2 (vcat [ppr_eqn, suggestion]) @@ -2093,7 +2148,7 @@ -- duplicated. If that is the case, throw an error. -- See Note [Type-checking default assoc decls] check_all_distinct_tvs ::- [(TyVar, ArgFlag)] -- The type variable arguments in the associated+ [(TyVar, ForAllTyFlag)] -- The type variable arguments in the associated -- default declaration, along with their respective -- visibilities (the latter are only used for error -- message purposes)@@ -2102,8 +2157,8 @@ let dups = findDupsEq ((==) `on` fst) cpt_tvs_vis in traverse_ (\d -> let (pat_tv, pat_vis) = NE.head d in failWithTc $- TcRnUnknownMessage $ mkPlainError noHints $- pprWithExplicitKindsWhen (isInvisibleArgFlag pat_vis) $+ mkTcRnUnknownMessage $ mkPlainError noHints $+ pprWithExplicitKindsWhen (isInvisibleForAllTyFlag pat_vis) $ hang (text "Illegal duplicate variable" <+> quotes (ppr pat_tv) <+> text "in:") 2 (vcat [ppr_eqn, suggestion]))@@ -2118,34 +2173,47 @@ suggestion = text "The arguments to" <+> quotes (ppr fam_tc) <+> text "must all be distinct type variables" --- Make sure that each type family application is--- (1) strictly smaller than the lhs,--- (2) mentions no type variable more often than the lhs, and--- (3) does not contain any further type family instances.--- checkFamInstRhs :: TyCon -> [Type] -- LHS -> [(TyCon, [Type])] -- type family calls in RHS -> [TcRnMessage]+-- Ensure that the type family instance terminates. Specifically:+-- ensure that each type family application in the RHS is+-- (TF1) a call to a stuck family like (TypeError ...) or Any+-- See Note [Stuck type families] in GHC.Tc.Utils.TcType+-- or (TF2) obeys the Paterson conditions, namely:+-- - strictly smaller than the lhs,+-- - mentions no type variable more often than the lhs, and+-- - does not contain any further type family applications checkFamInstRhs lhs_tc lhs_tys famInsts- = map (TcRnUnknownMessage . mkPlainError noHints) $ mapMaybe check famInsts+ = mapMaybe check famInsts where- lhs_size = sizeTyConAppArgs lhs_tc lhs_tys- inst_head = pprType (TyConApp lhs_tc lhs_tys)- lhs_fvs = fvTypes lhs_tys+ lhs_size = pSizeTypes lhs_tys check (tc, tys)- | not (all isTyFamFree tys) = Just (nestedMsg what)- | not (null bad_tvs) = Just (noMoreMsg bad_tvs what inst_head)- | lhs_size <= fam_app_size = Just (smallerMsg what inst_head)- | otherwise = Nothing- where- what = text "type family application"- <+> quotes (pprType (TyConApp tc tys))- fam_app_size = sizeTyConAppArgs tc tys- bad_tvs = fvTypes tys \\ lhs_fvs- -- The (\\) is list difference; e.g.- -- [a,b,a,a] \\ [a,a] = [b,a]- -- So we are counting repetitions+ | not (isStuckTypeFamily tc) -- (TF1)+ , Just ps_failure <- pSizeTypes tys `ltPatersonSize` lhs_size -- (TF2)+ = Just $ mkFamSizeError ps_failure (TyConApp lhs_tc lhs_tys) (TyConApp tc tys)+ | otherwise+ = Nothing +mkFamSizeError :: PatersonSizeFailure -> Type -> Type -> TcRnMessage+mkFamSizeError ps_failure lhs fam_call+ = mkTcRnUnknownMessage $ mkPlainError noHints $+ vcat [ main_msg+ , parens undecidableMsg ]+ where+ pp_lhs = text "LHS of the family instance" <+> quotes (ppr lhs)+ pp_call = text "type-family application" <+> quotes (ppr fam_call)++ main_msg = case ps_failure of+ PSF_TyFam tc -> -- See (PC3) of Note [Paterson conditions]+ hang (text "Illegal nested use of type family" <+> quotes (ppr tc))+ 2 (text "in the arguments of the" <+> pp_call)+ PSF_TyVar tvs -> hang (occMsg tvs)+ 2 (sep [ text "in the" <+> pp_call+ , text "than in the" <+> pp_lhs ])+ PSF_Size -> hang (text "The" <+> pp_call)+ 2 (sep [ text "is no smaller than", text "the" <+> pp_lhs ])+ ----------------- checkFamPatBinders :: TyCon -> [TcTyVar] -- Bound on LHS of family instance@@ -2198,7 +2266,7 @@ dodgy_tvs = cpt_tvs `minusVarSet` inj_cpt_tvs check_tvs tvs what what2- = unless (null tvs) $ addErrAt (getSrcSpan (head tvs)) $ TcRnUnknownMessage $ mkPlainError noHints $+ = unless (null tvs) $ addErrAt (getSrcSpan (head tvs)) $ mkTcRnUnknownMessage $ mkPlainError noHints $ hang (text "Type variable" <> plural tvs <+> pprQuotedList tvs <+> isOrAre tvs <+> what <> comma) 2 (vcat [ text "but not" <+> what2 <+> text "the family instance"@@ -2229,7 +2297,7 @@ -- Ensure that no type family applications occur a type pattern ; case tcTyConAppTyFamInstsAndVis tc pat_ty_args of [] -> pure ()- ((tf_is_invis_arg, tf_tc, tf_args):_) -> failWithTc $ TcRnUnknownMessage $ mkPlainError noHints $+ ((tf_is_invis_arg, tf_tc, tf_args):_) -> failWithTc $ mkTcRnUnknownMessage $ mkPlainError noHints $ ty_fam_inst_illegal_err tf_is_invis_arg (mkTyConApp tf_tc tf_args) } where@@ -2249,11 +2317,6 @@ = text "Type family instance equation is overlapped:" $$ nest 2 (pprCoAxBranchUser fam_tc cur_branch) -nestedMsg :: SDoc -> SDoc-nestedMsg what- = sep [ text "Illegal nested" <+> what- , parens undecidableMsg ]- ------------------------- checkConsistentFamInst :: AssocInstInfo -> TyCon -- ^ Family tycon@@ -2285,16 +2348,16 @@ where (ax_tvs, ax_arg_tys, _) = etaExpandCoAxBranch branch - arg_triples :: [(Type,Type, ArgFlag)]+ arg_triples :: [(Type,Type, ForAllTyFlag)] arg_triples = [ (cls_arg_ty, at_arg_ty, vis) | (fam_tc_tv, vis, at_arg_ty) <- zip3 (tyConTyVars fam_tc)- (tyConArgFlags fam_tc ax_arg_tys)+ (tyConForAllTyFlags fam_tc ax_arg_tys) ax_arg_tys , Just cls_arg_ty <- [lookupVarEnv mini_env fam_tc_tv] ] pp_wrong_at_arg vis- = pprWithExplicitKindsWhen (isInvisibleArgFlag vis) $+ = pprWithExplicitKindsWhen (isInvisibleForAllTyFlag vis) $ vcat [ text "Type indexes must match class instance head" , text "Expected:" <+> pp_expected_ty , text " Actual:" <+> pp_actual_ty ]@@ -2317,12 +2380,12 @@ tidyTypes tidy_env2 ax_arg_tys mk_wildcard at_tv = mkTyVarTy (mkTyVar tv_name (tyVarKind at_tv))- tv_name = mkInternalName (mkAlphaTyVarUnique 1) (mkTyVarOcc "_") noSrcSpan+ tv_name = mkInternalName (mkAlphaTyVarUnique 1) (mkTyVarOccFS (fsLit "_")) noSrcSpan -- For check_match, bind_me, see -- Note [Matching in the consistent-instantiation check]- check_match :: [(Type,Type,ArgFlag)] -> TcM ()- check_match triples = go emptyTCvSubst emptyTCvSubst triples+ check_match :: [(Type,Type,ForAllTyFlag)] -> TcM ()+ check_match triples = go emptySubst emptySubst triples go _ _ [] = return () go lr_subst rl_subst ((ty1,ty2,vis):triples)@@ -2330,7 +2393,7 @@ , Just rl_subst1 <- tcMatchTyX_BM bind_me rl_subst ty2 ty1 = go lr_subst1 rl_subst1 triples | otherwise- = addErrTc (TcRnUnknownMessage $ mkPlainError noHints $ pp_wrong_at_arg vis)+ = addErrTc (mkTcRnUnknownMessage $ mkPlainError noHints $ pp_wrong_at_arg vis) -- The /scoped/ type variables from the class-instance header -- should not be alpha-renamed. Inferred ones can be.@@ -2554,7 +2617,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ It's remarkably painful to give a decent error message for conflicts with the class header. Consider- clase C b where+ class C b where type F a b c instance C [b] where type F x Int _ _ = ...@@ -2606,7 +2669,7 @@ first is caught by the renamer, but we catch the last two here. Perhaps one could argue that the second should be accepted, albeit with a warning, but consider the fact that in a type family instance, there is no way to interact-with such a varable. At least with @x :: forall a. Int@ we can use visibile+with such a variable. At least with @x :: forall a. Int@ we can use visible type application, like @x \@Bool 1@. (Of course it does nothing, but it is permissible.) In the type family case, the only sensible explanation is that the user has made a mistake -- thus we throw an error.@@ -2758,7 +2821,7 @@ checkTyConTelescope tc | bad_scope = -- See "Ill-scoped binders" in Note [Bad TyCon telescopes]- addErr $ TcRnUnknownMessage $ mkPlainError noHints $+ addErr $ mkTcRnUnknownMessage $ mkPlainError noHints $ vcat [ hang (text "The kind of" <+> quotes (ppr tc) <+> text "is ill-scoped") 2 pp_tc_kind , extra@@ -2783,9 +2846,9 @@ fkvs = tyCoVarsOfType (tyVarKind tv) inferred_tvs = [ binderVar tcb- | tcb <- tcbs, Inferred == tyConBinderArgFlag tcb ]+ | tcb <- tcbs, Inferred == tyConBinderForAllTyFlag tcb ] specified_tvs = [ binderVar tcb- | tcb <- tcbs, Specified == tyConBinderArgFlag tcb ]+ | tcb <- tcbs, Specified == tyConBinderForAllTyFlag tcb ] pp_inf = parens (text "namely:" <+> pprTyVars inferred_tvs) pp_spec = parens (text "namely:" <+> pprTyVars specified_tvs)@@ -2812,70 +2875,3 @@ 2 (vcat [ sep [ pp_inf, text "always come first"] , sep [text "then Specified variables", pp_spec]]) -{--************************************************************************-* *-\subsection{Auxiliary functions}-* *-************************************************************************--}---- Free variables of a type, retaining repetitions, and expanding synonyms--- This ignores coercions, as coercions aren't user-written-fvType :: Type -> [TyCoVar]-fvType ty | Just exp_ty <- tcView ty = fvType exp_ty-fvType (TyVarTy tv) = [tv]-fvType (TyConApp _ tys) = fvTypes tys-fvType (LitTy {}) = []-fvType (AppTy fun arg) = fvType fun ++ fvType arg-fvType (FunTy _ w arg res) = fvType w ++ fvType arg ++ fvType res-fvType (ForAllTy (Bndr tv _) ty)- = fvType (tyVarKind tv) ++- filter (/= tv) (fvType ty)-fvType (CastTy ty _) = fvType ty-fvType (CoercionTy {}) = []--fvTypes :: [Type] -> [TyVar]-fvTypes tys = concatMap fvType tys--sizeType :: Type -> Int--- Size of a type: the number of variables and constructors-sizeType ty | Just exp_ty <- tcView ty = sizeType exp_ty-sizeType (TyVarTy {}) = 1-sizeType (TyConApp tc tys) = 1 + sizeTyConAppArgs tc tys-sizeType (LitTy {}) = 1-sizeType (AppTy fun arg) = sizeType fun + sizeType arg-sizeType (FunTy _ w arg res) = sizeType w + sizeType arg + sizeType res + 1-sizeType (ForAllTy _ ty) = sizeType ty-sizeType (CastTy ty _) = sizeType ty-sizeType (CoercionTy _) = 0--sizeTypes :: [Type] -> Int-sizeTypes = foldr ((+) . sizeType) 0--sizeTyConAppArgs :: TyCon -> [Type] -> Int-sizeTyConAppArgs _tc tys = sizeTypes tys -- (filterOutInvisibleTypes tc tys)- -- See Note [Invisible arguments and termination]---- | When this says "True", ignore this class constraint during--- a termination check-isTerminatingClass :: Class -> Bool-isTerminatingClass cls- = isIPClass cls -- Implicit parameter constraints always terminate because- -- there are no instances for them --- they are only solved- -- by "local instances" in expressions- || isEqPredClass cls- || cls `hasKey` typeableClassKey- || cls `hasKey` coercibleTyConKey--allDistinctTyVars :: TyVarSet -> [KindOrType] -> Bool--- (allDistinctTyVars tvs tys) returns True if tys are--- a) all tyvars--- b) all distinct--- c) disjoint from tvs-allDistinctTyVars _ [] = True-allDistinctTyVars tkvs (ty : tys)- = case getTyVar_maybe ty of- Nothing -> False- Just tv | tv `elemVarSet` tkvs -> False- | otherwise -> allDistinctTyVars (tkvs `extendVarSet` tv) tys
compiler/GHC/ThToHs.hs view
@@ -3,14 +3,12 @@ {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiWayIf #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE ViewPatterns #-} {-# OPTIONS_GHC -Wno-incomplete-record-updates #-}-{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-} {- (c) The University of Glasgow 2006@@ -29,10 +27,11 @@ ) where -import GHC.Prelude+import GHC.Prelude hiding (head, init, last, tail) import GHC.Hs as Hs import GHC.Builtin.Names+import GHC.Tc.Errors.Types import GHC.Types.Name.Reader import qualified GHC.Types.Name as Name import GHC.Unit.Module@@ -42,21 +41,27 @@ import GHC.Core.Type as Hs import qualified GHC.Core.Coercion as Coercion ( Role(..) ) import GHC.Builtin.Types+import GHC.Builtin.Types.Prim( fUNTyCon ) import GHC.Types.Basic as Hs import GHC.Types.Fixity as Hs import GHC.Types.ForeignCall import GHC.Types.Unique import GHC.Types.SourceText-import GHC.Utils.Error import GHC.Data.Bag import GHC.Utils.Lexeme import GHC.Utils.Misc import GHC.Data.FastString-import GHC.Utils.Outputable as Outputable import GHC.Utils.Panic +import Language.Haskell.Syntax.Basic (FieldLabelString(..))+ import qualified Data.ByteString as BS import Control.Monad( unless, ap )+import Control.Applicative( (<|>) )+import Data.Bifunctor (first)+import Data.Foldable (for_)+import Data.List.NonEmpty( NonEmpty (..), nonEmpty )+import qualified Data.List.NonEmpty as NE import Data.Maybe( catMaybes, isNothing ) import Language.Haskell.TH as TH hiding (sigP) import Language.Haskell.TH.Syntax as TH@@ -67,30 +72,34 @@ ------------------------------------------------------------------- -- The external interface -convertToHsDecls :: Origin -> SrcSpan -> [TH.Dec] -> Either SDoc [LHsDecl GhcPs]-convertToHsDecls origin loc ds = initCvt origin loc (fmap catMaybes (mapM cvt_dec ds))+convertToHsDecls :: Origin -> SrcSpan -> [TH.Dec] -> Either RunSpliceFailReason [LHsDecl GhcPs]+convertToHsDecls origin loc ds =+ initCvt origin loc $ fmap catMaybes (mapM cvt_dec ds) where- cvt_dec d = wrapMsg "declaration" d (cvtDec d)+ cvt_dec d =+ wrapMsg (ConvDec d) $ cvtDec d -convertToHsExpr :: Origin -> SrcSpan -> TH.Exp -> Either SDoc (LHsExpr GhcPs)+convertToHsExpr :: Origin -> SrcSpan -> TH.Exp -> Either RunSpliceFailReason (LHsExpr GhcPs) convertToHsExpr origin loc e- = initCvt origin loc $ wrapMsg "expression" e $ cvtl e+ = initCvt origin loc $ wrapMsg (ConvExp e) $ cvtl e -convertToPat :: Origin -> SrcSpan -> TH.Pat -> Either SDoc (LPat GhcPs)+convertToPat :: Origin -> SrcSpan -> TH.Pat -> Either RunSpliceFailReason (LPat GhcPs) convertToPat origin loc p- = initCvt origin loc $ wrapMsg "pattern" p $ cvtPat p+ = initCvt origin loc $ wrapMsg (ConvPat p) $ cvtPat p -convertToHsType :: Origin -> SrcSpan -> TH.Type -> Either SDoc (LHsType GhcPs)+convertToHsType :: Origin -> SrcSpan -> TH.Type -> Either RunSpliceFailReason (LHsType GhcPs) convertToHsType origin loc t- = initCvt origin loc $ wrapMsg "type" t $ cvtType t+ = initCvt origin loc $ wrapMsg (ConvType t) $ cvtType t --------------------------------------------------------------------newtype CvtM a = CvtM { unCvtM :: Origin -> SrcSpan -> Either SDoc (SrcSpan, a) }+newtype CvtM' err a = CvtM { unCvtM :: Origin -> SrcSpan -> Either err (SrcSpan, a) } deriving (Functor) -- Push down the Origin (that is configurable by -- -fenable-th-splice-warnings) and source location; -- Can fail, with a single error message +type CvtM = CvtM' ConversionFailReason+ -- NB: If the conversion succeeds with (Right x), there should -- be no exception values hiding in x -- Reason: so a (head []) in TH code doesn't subsequently@@ -99,22 +108,25 @@ -- Use the SrcSpan everywhere, for lack of anything better. -- See Note [Source locations within TH splices]. -instance Applicative CvtM where+instance Applicative (CvtM' err) where pure x = CvtM $ \_ loc -> Right (loc,x) (<*>) = ap -instance Monad CvtM where+instance Monad (CvtM' err) where (CvtM m) >>= k = CvtM $ \origin loc -> case m origin loc of Left err -> Left err Right (loc',v) -> unCvtM (k v) origin loc' -initCvt :: Origin -> SrcSpan -> CvtM a -> Either SDoc a+mapCvtMError :: (err1 -> err2) -> CvtM' err1 a -> CvtM' err2 a+mapCvtMError f (CvtM m) = CvtM $ \origin loc -> first f $ m origin loc++initCvt :: Origin -> SrcSpan -> CvtM' err a -> Either err a initCvt origin loc (CvtM m) = fmap snd (m origin loc) force :: a -> CvtM () force a = a `seq` return () -failWith :: SDoc -> CvtM a+failWith :: ConversionFailReason -> CvtM a failWith m = CvtM (\_ _ -> Left m) getOrigin :: CvtM Origin@@ -137,19 +149,8 @@ wrapParLA :: (LocatedAn ann a -> b) -> a -> CvtM b wrapParLA add_par x = CvtM (\_ loc -> Right (loc, add_par (L (noAnnSrcSpan loc) x))) -wrapMsg :: (Show a, TH.Ppr a) => String -> a -> CvtM b -> CvtM b--- E.g wrapMsg "declaration" dec thing-wrapMsg what item (CvtM m)- = CvtM $ \origin loc -> case m origin loc of- Left err -> Left (err $$ msg)- Right v -> Right v- where- -- Show the item in pretty syntax normally,- -- but with all its constructors if you say -dppr-debug- msg = hang (text "When splicing a TH" <+> text what <> colon)- 2 (getPprDebug $ \case- True -> text (show item)- False -> text (pprint item))+wrapMsg :: ThingBeingConverted -> CvtM' ConversionFailReason a -> CvtM' RunSpliceFailReason a+wrapMsg what = mapCvtMError (ConversionFail what) wrapL :: CvtM a -> CvtM (Located a) wrapL (CvtM m) = CvtM $ \origin loc -> case m origin loc of@@ -216,18 +217,16 @@ | otherwise = do { pat' <- cvtPat pat ; body' <- cvtGuard body- ; ds' <- cvtLocalDecs (text "a where clause") ds+ ; ds' <- cvtLocalDecs WhereClause ds ; returnJustLA $ Hs.ValD noExtField $ PatBind { pat_lhs = pat' , pat_rhs = GRHSs emptyComments body' ds' , pat_ext = noAnn- , pat_ticks = ([],[]) } }+ } } cvtDec (TH.FunD nm cls) | null cls- = failWith (text "Function binding for"- <+> quotes (text (TH.pprint nm))- <+> text "has no equations")+ = failWith $ FunBindLacksEquations nm | otherwise = do { nm' <- vNameN nm ; cls' <- mapM (cvtClause (mkPrefixFunRhs nm')) cls@@ -271,42 +270,18 @@ , tcdRhs = rhs' } } cvtDec (DataD ctxt tc tvs ksig constrs derivs)- = do { let isGadtCon (GadtC _ _ _) = True- isGadtCon (RecGadtC _ _ _) = True- isGadtCon (ForallC _ _ c) = isGadtCon c- isGadtCon _ = False- isGadtDecl = all isGadtCon constrs- isH98Decl = all (not . isGadtCon) constrs- ; unless (isGadtDecl || isH98Decl)- (failWith (text "Cannot mix GADT constructors with Haskell 98"- <+> text "constructors"))- ; unless (isNothing ksig || isGadtDecl)- (failWith (text "Kind signatures are only allowed on GADTs"))- ; (ctxt', tc', tvs') <- cvt_tycl_hdr ctxt tc tvs- ; ksig' <- cvtKind `traverse` ksig- ; cons' <- mapM cvtConstr constrs- ; derivs' <- cvtDerivs derivs- ; let defn = HsDataDefn { dd_ext = noExtField- , dd_ND = DataType, dd_cType = Nothing- , dd_ctxt = mkHsContextMaybe ctxt'- , dd_kindSig = ksig'- , dd_cons = cons', dd_derivs = derivs' }- ; returnJustLA $ TyClD noExtField $- DataDecl { tcdDExt = noAnn- , tcdLName = tc', tcdTyVars = tvs'- , tcdFixity = Prefix- , tcdDataDefn = defn } }+ = cvtDataDec ctxt tc tvs ksig constrs derivs cvtDec (NewtypeD ctxt tc tvs ksig constr derivs) = do { (ctxt', tc', tvs') <- cvt_tycl_hdr ctxt tc tvs ; ksig' <- cvtKind `traverse` ksig- ; con' <- cvtConstr constr+ ; con' <- cvtConstr cNameN constr ; derivs' <- cvtDerivs derivs ; let defn = HsDataDefn { dd_ext = noExtField- , dd_ND = NewType, dd_cType = Nothing+ , dd_cType = Nothing , dd_ctxt = mkHsContextMaybe ctxt' , dd_kindSig = ksig'- , dd_cons = [con']+ , dd_cons = NewTypeCon con' , dd_derivs = derivs' } ; returnJustLA $ TyClD noExtField $ DataDecl { tcdDExt = noAnn@@ -314,16 +289,17 @@ , tcdFixity = Prefix , tcdDataDefn = defn } } +cvtDec (TypeDataD tc tvs ksig constrs)+ = cvtTypeDataDec tc tvs ksig constrs+ cvtDec (ClassD ctxt cl tvs fds decs) = do { (cxt', tc', tvs') <- cvt_tycl_hdr ctxt cl tvs ; fds' <- mapM cvt_fundep fds- ; (binds', sigs', fams', at_defs', adts') <- cvt_ci_decs (text "a class declaration") decs+ ; (binds', sigs', fams', at_defs', adts') <- cvt_ci_decs ClssDecl decs ; unless (null adts')- (failWith $ (text "Default data instance declarations"- <+> text "are not allowed:")- $$ (Outputable.ppr adts'))+ (failWith $ DefaultDataInstDecl adts') ; returnJustLA $ TyClD noExtField $- ClassDecl { tcdCExt = (noAnn, NoAnnSortKey, NoLayoutInfo)+ ClassDecl { tcdCExt = (noAnn, NoAnnSortKey), tcdLayout = NoLayoutInfo , tcdCtxt = mkHsContextMaybe cxt', tcdLName = tc', tcdTyVars = tvs' , tcdFixity = Prefix , tcdFDs = fds', tcdSigs = Hs.mkClassOpSigs sigs'@@ -333,9 +309,9 @@ } cvtDec (InstanceD o ctxt ty decs)- = do { let doc = text "an instance declaration"- ; (binds', sigs', fams', ats', adts') <- cvt_ci_decs doc decs- ; unless (null fams') (failWith (mkBadDecMsg doc fams'))+ = do { (binds', sigs', fams', ats', adts') <- cvt_ci_decs InstanceDecl decs+ ; for_ (nonEmpty fams') $ \ bad_fams ->+ failWith (IllegalDeclaration InstanceDecl $ IllegalFamDecls bad_fams) ; ctxt' <- cvtContext funPrec ctxt ; (L loc ty') <- cvtType ty ; let inst_ty' = L loc $ mkHsImplicitSigType $@@ -371,13 +347,14 @@ cvtDec (DataInstD ctxt bndrs tys ksig constrs derivs) = do { (ctxt', tc', bndrs', typats') <- cvt_datainst_hdr ctxt bndrs tys ; ksig' <- cvtKind `traverse` ksig- ; cons' <- mapM cvtConstr constrs+ ; cons' <- mapM (cvtConstr cNameN) constrs ; derivs' <- cvtDerivs derivs ; let defn = HsDataDefn { dd_ext = noExtField- , dd_ND = DataType, dd_cType = Nothing+ , dd_cType = Nothing , dd_ctxt = mkHsContextMaybe ctxt' , dd_kindSig = ksig'- , dd_cons = cons', dd_derivs = derivs' }+ , dd_cons = DataTypeCons False cons'+ , dd_derivs = derivs' } ; returnJustLA $ InstD noExtField $ DataFamInstD { dfid_ext = noExtField@@ -392,13 +369,13 @@ cvtDec (NewtypeInstD ctxt bndrs tys ksig constr derivs) = do { (ctxt', tc', bndrs', typats') <- cvt_datainst_hdr ctxt bndrs tys ; ksig' <- cvtKind `traverse` ksig- ; con' <- cvtConstr constr+ ; con' <- cvtConstr cNameN constr ; derivs' <- cvtDerivs derivs ; let defn = HsDataDefn { dd_ext = noExtField- , dd_ND = NewType, dd_cType = Nothing+ , dd_cType = Nothing , dd_ctxt = mkHsContextMaybe ctxt' , dd_kindSig = ksig'- , dd_cons = [con'], dd_derivs = derivs' }+ , dd_cons = NewTypeCon con', dd_derivs = derivs' } ; returnJustLA $ InstD noExtField $ DataFamInstD { dfid_ext = noExtField , dfid_inst = DataFamInstDecl { dfid_eqn =@@ -484,8 +461,61 @@ -- cvtImplicitParamBind. They are not allowed in any other scope, so -- reaching this case indicates an error. cvtDec (TH.ImplicitParamBindD _ _)- = failWith (text "Implicit parameter binding only allowed in let or where")+ = failWith InvalidImplicitParamBinding +-- Convert a @data@ declaration.+cvtDataDec :: TH.Cxt -> TH.Name -> [TH.TyVarBndr ()]+ -> Maybe TH.Kind -> [TH.Con] -> [TH.DerivClause]+ -> CvtM (Maybe (LHsDecl GhcPs))+cvtDataDec = cvtGenDataDec False++-- Convert a @type data@ declaration.+-- These have neither contexts nor derived clauses.+-- See Note [Type data declarations] in GHC.Rename.Module.+cvtTypeDataDec :: TH.Name -> [TH.TyVarBndr ()] -> Maybe TH.Kind -> [TH.Con]+ -> CvtM (Maybe (LHsDecl GhcPs))+cvtTypeDataDec tc tvs ksig constrs+ = cvtGenDataDec True [] tc tvs ksig constrs []++-- Convert a @data@ or @type data@ declaration (flagged by the Bool arg).+-- See Note [Type data declarations] in GHC.Rename.Module.+cvtGenDataDec :: Bool -> TH.Cxt -> TH.Name -> [TH.TyVarBndr ()]+ -> Maybe TH.Kind -> [TH.Con] -> [TH.DerivClause]+ -> CvtM (Maybe (LHsDecl GhcPs))+cvtGenDataDec type_data ctxt tc tvs ksig constrs derivs+ = do { let isGadtCon (GadtC _ _ _) = True+ isGadtCon (RecGadtC _ _ _) = True+ isGadtCon (ForallC _ _ c) = isGadtCon c+ isGadtCon _ = False+ isGadtDecl = all isGadtCon constrs+ isH98Decl = all (not . isGadtCon) constrs+ -- A constructor in a @data@ or @newtype@ declaration is+ -- a data constructor. A constructor in a @type data@+ -- declaration is a type constructor.+ -- See Note [Type data declarations] in GHC.Rename.Module.+ con_name+ | type_data = tconNameN+ | otherwise = cNameN+ ; unless (isGadtDecl || isH98Decl)+ (failWith CannotMixGADTConsWith98Cons)+ ; unless (isNothing ksig || isGadtDecl)+ (failWith KindSigsOnlyAllowedOnGADTs)+ ; (ctxt', tc', tvs') <- cvt_tycl_hdr ctxt tc tvs+ ; ksig' <- cvtKind `traverse` ksig+ ; cons' <- mapM (cvtConstr con_name) constrs+ ; derivs' <- cvtDerivs derivs+ ; let defn = HsDataDefn { dd_ext = noExtField+ , dd_cType = Nothing+ , dd_ctxt = mkHsContextMaybe ctxt'+ , dd_kindSig = ksig'+ , dd_cons = DataTypeCons type_data cons'+ , dd_derivs = derivs' }+ ; returnJustLA $ TyClD noExtField $+ DataDecl { tcdDExt = noAnn+ , tcdLName = tc', tcdTyVars = tvs'+ , tcdFixity = Prefix+ , tcdDataDefn = defn } }+ ---------------- cvtTySynEqn :: TySynEqn -> CvtM (LTyFamInstEqn GhcPs) cvtTySynEqn (TySynEqn mb_bndrs lhs rhs)@@ -514,12 +544,11 @@ (map HsValArg args') ++ args , feqn_fixity = Hs.Infix , feqn_rhs = rhs' } }- _ -> failWith $ text "Invalid type family instance LHS:"- <+> text (show lhs)+ _ -> failWith $ InvalidTyFamInstLHS lhs } -----------------cvt_ci_decs :: SDoc -> [TH.Dec]+cvt_ci_decs :: THDeclDescriptor -> [TH.Dec] -> CvtM (LHsBinds GhcPs, [LSig GhcPs], [LFamilyDecl GhcPs],@@ -527,14 +556,15 @@ [LDataFamInstDecl GhcPs]) -- Convert the declarations inside a class or instance decl -- ie signatures, bindings, and associated types-cvt_ci_decs doc decs+cvt_ci_decs declDescr decs = do { decs' <- cvtDecs decs ; let (ats', bind_sig_decs') = partitionWith is_tyfam_inst decs' ; let (adts', no_ats') = partitionWith is_datafam_inst bind_sig_decs' ; let (sigs', prob_binds') = partitionWith is_sig no_ats' ; let (binds', prob_fams') = partitionWith is_bind prob_binds' ; let (fams', bads) = partitionWith is_fam_decl prob_fams'- ; unless (null bads) (failWith (mkBadDecMsg doc bads))+ ; for_ (nonEmpty bads) $ \ bad_decls ->+ failWith (IllegalDeclaration declDescr $ IllegalDecls bad_decls) ; return (listToBag binds', sigs', fams', ats', adts') } ----------------@@ -567,8 +597,7 @@ ; args' <- mapM cvtType [t1,t2] ; return (cxt', nm', outer_bndrs, ((map HsValArg args') ++ args)) }- _ -> failWith $ text "Invalid type instance header:"- <+> text (show tys) }+ _ -> failWith $ InvalidTypeInstanceHeader tys } ---------------- cvt_tyfam_head :: TypeFamilyHead@@ -578,7 +607,7 @@ , Maybe (Hs.LInjectivityAnn GhcPs)) cvt_tyfam_head (TypeFamilyHead tc tyvars result injectivity)- = do {(_, tc', tyvars') <- cvt_tycl_hdr [] tc tyvars+ = do { (_, tc', tyvars') <- cvt_tycl_hdr [] tc tyvars ; result' <- cvtFamilyResultSig result ; injectivity' <- traverse cvtInjectivityAnnotation injectivity ; return (tc', tyvars', result', injectivity') }@@ -616,39 +645,35 @@ is_ip_bind (TH.ImplicitParamBindD n e) = Left (n, e) is_ip_bind decl = Right decl -mkBadDecMsg :: Outputable a => SDoc -> [a] -> SDoc-mkBadDecMsg doc bads- = sep [ text "Illegal declaration(s) in" <+> doc <> colon- , nest 2 (vcat (map Outputable.ppr bads)) ]- --------------------------------------------------- -- Data types --------------------------------------------------- -cvtConstr :: TH.Con -> CvtM (LConDecl GhcPs)+cvtConstr :: (TH.Name -> CvtM (LocatedN RdrName)) -- ^ convert constructor name+ -> TH.Con -> CvtM (LConDecl GhcPs) -cvtConstr (NormalC c strtys)- = do { c' <- cNameN c+cvtConstr con_name (NormalC c strtys)+ = do { c' <- con_name c ; tys' <- mapM cvt_arg strtys ; returnLA $ mkConDeclH98 noAnn c' Nothing Nothing (PrefixCon noTypeArgs (map hsLinear tys')) } -cvtConstr (RecC c varstrtys)- = do { c' <- cNameN c+cvtConstr con_name (RecC c varstrtys)+ = do { c' <- con_name c ; args' <- mapM cvt_id_arg varstrtys ; con_decl <- wrapParLA (mkConDeclH98 noAnn c' Nothing Nothing . RecCon) args' ; returnLA con_decl } -cvtConstr (InfixC st1 c st2)- = do { c' <- cNameN c+cvtConstr con_name (InfixC st1 c st2)+ = do { c' <- con_name c ; st1' <- cvt_arg st1 ; st2' <- cvt_arg st2 ; returnLA $ mkConDeclH98 noAnn c' Nothing Nothing (InfixCon (hsLinear st1') (hsLinear st2')) } -cvtConstr (ForallC tvs ctxt con)+cvtConstr con_name (ForallC tvs ctxt con) = do { tvs' <- cvtTvs tvs ; ctxt' <- cvtContext funPrec ctxt- ; L _ con' <- cvtConstr con+ ; L _ con' <- cvtConstr con_name con ; returnLA $ add_forall tvs' ctxt' con' } where add_cxt lcxt Nothing = mkHsContextMaybe lcxt@@ -676,32 +701,31 @@ where all_tvs = tvs' ++ ex_tvs -cvtConstr (GadtC [] _strtys _ty)- = failWith (text "GadtC must have at least one constructor name")--cvtConstr (GadtC c strtys ty)- = do { c' <- mapM cNameN c+cvtConstr con_name (GadtC c strtys ty) = case nonEmpty c of+ Nothing -> failWith GadtNoCons+ Just c -> do+ { c' <- mapM con_name c ; args <- mapM cvt_arg strtys ; ty' <- cvtType ty ; mk_gadt_decl c' (PrefixConGADT $ map hsLinear args) ty'} -cvtConstr (RecGadtC [] _varstrtys _ty)- = failWith (text "RecGadtC must have at least one constructor name")--cvtConstr (RecGadtC c varstrtys ty)- = do { c' <- mapM cNameN c+cvtConstr con_name (RecGadtC c varstrtys ty) = case nonEmpty c of+ Nothing -> failWith RecGadtNoCons+ Just c -> do+ { c' <- mapM con_name c ; ty' <- cvtType ty ; rec_flds <- mapM cvt_id_arg varstrtys ; lrec_flds <- returnLA rec_flds ; mk_gadt_decl c' (RecConGADT lrec_flds noHsUniTok) ty' } -mk_gadt_decl :: [LocatedN RdrName] -> HsConDeclGADTDetails GhcPs -> LHsType GhcPs+mk_gadt_decl :: NonEmpty (LocatedN RdrName) -> HsConDeclGADTDetails GhcPs -> LHsType GhcPs -> CvtM (LConDecl GhcPs) mk_gadt_decl names args res_ty = do bndrs <- returnLA mkHsOuterImplicit returnLA $ ConDeclGADT { con_g_ext = noAnn , con_names = names+ , con_dcolon = noHsUniTok , con_bndrs = bndrs , con_mb_cxt = Nothing , con_g_args = args@@ -756,17 +780,16 @@ ; if -- the prim and javascript calling conventions do not support headers -- and are inserted verbatim, analogous to mkImport in GHC.Parser.PostProcess | callconv == TH.Prim || callconv == TH.JavaScript- -> mk_imp (CImport (L l (cvt_conv callconv)) (L l safety') Nothing+ -> mk_imp (CImport (L l $ quotedSourceText from) (L l (cvt_conv callconv)) (L l safety') Nothing (CFunction (StaticTarget (SourceText from) (mkFastString from) Nothing- True))- (L l $ quotedSourceText from))+ True))) | Just impspec <- parseCImport (L l (cvt_conv callconv)) (L l safety') (mkFastString (TH.nameBase nm)) from (L l $ quotedSourceText from) -> mk_imp impspec | otherwise- -> failWith $ text (show from) <+> text "is not a valid ccall impent" }+ -> failWith $ InvalidCCallImpent from } where mk_imp impspec = do { nm' <- vNameN nm@@ -785,10 +808,9 @@ = do { nm' <- vNameN nm ; ty' <- cvtSigType ty ; l <- getL- ; let e = CExport (L l (CExportStatic (SourceText as)- (mkFastString as)- (cvt_conv callconv)))- (L l (SourceText as))+ ; let e = CExport (L l (SourceText as)) (L l (CExportStatic (SourceText as)+ (mkFastString as)+ (cvt_conv callconv))) ; return $ ForeignExport { fd_e_ext = noAnn , fd_name = nm' , fd_sig_ty = ty'@@ -855,18 +877,18 @@ cvtPragmaD (SpecialiseInstP ty) = do { ty' <- cvtSigType ty ; returnJustLA $ Hs.SigD noExtField $- SpecInstSig noAnn (SourceText "{-# SPECIALISE") ty' }+ SpecInstSig (noAnn, (SourceText "{-# SPECIALISE")) ty' } cvtPragmaD (RuleP nm ty_bndrs tm_bndrs lhs rhs phases) = do { let nm' = mkFastString nm- ; rd_name' <- returnLA (quotedSourceText nm,nm')+ ; rd_name' <- returnLA nm' ; let act = cvtPhases phases AlwaysActive ; ty_bndrs' <- traverse cvtTvs ty_bndrs ; tm_bndrs' <- mapM cvtRuleBndr tm_bndrs ; lhs' <- cvtl lhs ; rhs' <- cvtl rhs ; rule <- returnLA $- HsRule { rd_ext = noAnn+ HsRule { rd_ext = (noAnn, quotedSourceText nm) , rd_name = rd_name' , rd_act = act , rd_tyvs = ty_bndrs'@@ -874,8 +896,7 @@ , rd_lhs = lhs' , rd_rhs = rhs' } ; returnJustLA $ Hs.RuleD noExtField- $ HsRules { rds_ext = noAnn- , rds_src = SourceText "{-# RULES"+ $ HsRules { rds_ext = (noAnn, SourceText "{-# RULES") , rds_rules = [rule] } }@@ -891,7 +912,7 @@ n' <- vcName n wrapParLA ValueAnnProvenance n' ; returnJustLA $ Hs.AnnD noExtField- $ HsAnnotation noAnn (SourceText "{-# ANN") target' exp'+ $ HsAnnotation (noAnn, (SourceText "{-# ANN")) target' exp' } -- NB: This is the only place in GHC.ThToHs that makes use of the `setL`@@ -904,7 +925,7 @@ = do { cls' <- wrapL $ mapM cNameN cls ; mty' <- traverse tconNameN mty ; returnJustLA $ Hs.SigD noExtField- $ CompleteMatchSig noAnn NoSourceText cls' mty' }+ $ CompleteMatchSig (noAnn, NoSourceText) cls' mty' } dfltActivation :: TH.Inline -> Activation dfltActivation TH.NoInline = NeverActive@@ -937,21 +958,22 @@ -- Declarations --------------------------------------------------- -cvtLocalDecs :: SDoc -> [TH.Dec] -> CvtM (HsLocalBinds GhcPs)-cvtLocalDecs doc ds+cvtLocalDecs :: THDeclDescriptor -> [TH.Dec] -> CvtM (HsLocalBinds GhcPs)+cvtLocalDecs declDescr ds = case partitionWith is_ip_bind ds of ([], []) -> return (EmptyLocalBinds noExtField) ([], _) -> do ds' <- cvtDecs ds let (binds, prob_sigs) = partitionWith is_bind ds' let (sigs, bads) = partitionWith is_sig prob_sigs- unless (null bads) (failWith (mkBadDecMsg doc bads))+ for_ (nonEmpty bads) $ \ bad_decls ->+ failWith (IllegalDeclaration declDescr $ IllegalDecls bad_decls) return (HsValBinds noAnn (ValBinds NoAnnSortKey (listToBag binds) sigs)) (ip_binds, []) -> do binds <- mapM (uncurry cvtImplicitParamBind) ip_binds return (HsIPBinds noAnn (IPBinds noExtField binds)) ((_:_), (_:_)) ->- failWith (text "Implicit parameters mixed with other bindings")+ failWith ImplicitParamsWithOtherBinds cvtClause :: HsMatchContext GhcPs -> TH.Clause -> CvtM (Hs.LMatch GhcPs (LHsExpr GhcPs))@@ -959,7 +981,7 @@ = do { ps' <- cvtPats ps ; let pps = map (parenthesizePat appPrec) ps' ; g' <- cvtGuard body- ; ds' <- cvtLocalDecs (text "a where clause") wheres+ ; ds' <- cvtLocalDecs WhereClause wheres ; returnLA $ Hs.Match noAnn ctxt pps (GRHSs emptyComments g' ds') } cvtImplicitParamBind :: String -> TH.Exp -> CvtM (LIPBind GhcPs)@@ -991,17 +1013,13 @@ l' <- cvt_lit l let e' = mk_expr l' if is_compound_lit l' then wrapParLA gHsPar e' else pure e'- cvt (AppE x@(LamE _ _) y) = do { x' <- cvtl x; y' <- cvtl y- ; return $ HsApp noComments (mkLHsPar x')- (mkLHsPar y')}- cvt (AppE x y) = do { x' <- cvtl x; y' <- cvtl y- ; return $ HsApp noComments (mkLHsPar x')- (mkLHsPar y')}- cvt (AppTypeE e t) = do { e' <- cvtl e- ; t' <- cvtType t- ; let tp = parenthesizeHsType appPrec t'- ; return $ HsAppType noSrcSpan e'- $ mkHsWildCardBndrs tp }+ cvt (AppE e1 e2) = do { e1' <- parenthesizeHsExpr opPrec <$> cvtl e1+ ; e2' <- parenthesizeHsExpr appPrec <$> cvtl e2+ ; return $ HsApp noComments e1' e2' }+ cvt (AppTypeE e t) = do { e' <- parenthesizeHsExpr opPrec <$> cvtl e+ ; t' <- parenthesizeHsType appPrec <$> cvtType t+ ; return $ HsAppType noExtField e' noHsTok+ $ mkHsWildCardBndrs t' } cvt (LamE [] e) = cvt e -- Degenerate case. We convert the body as its -- own expression to avoid pretty-printing -- oddities that can result from zero-argument@@ -1016,7 +1034,7 @@ ; wrapParLA (HsLamCase noAnn LamCase . mkMatchGroup th_origin) ms' } cvt (LamCasesE ms)- | null ms = failWith (text "\\cases expression with no alternatives")+ | null ms = failWith CasesExprWithoutAlts | otherwise = do { ms' <- mapM (cvtClause $ LamCaseAlt LamCases) ms ; th_origin <- getOrigin ; wrapParLA (HsLamCase noAnn LamCases . mkMatchGroup th_origin) ms'@@ -1029,10 +1047,10 @@ cvt (CondE x y z) = do { x' <- cvtl x; y' <- cvtl y; z' <- cvtl z; ; return $ mkHsIf x' y' z' noAnn } cvt (MultiIfE alts)- | null alts = failWith (text "Multi-way if-expression with no alternatives")+ | null alts = failWith MultiWayIfWithoutAlts | otherwise = do { alts' <- mapM cvtpair alts ; return $ HsMultiIf noAnn alts' }- cvt (LetE ds e) = do { ds' <- cvtLocalDecs (text "a let expression") ds+ cvt (LetE ds e) = do { ds' <- cvtLocalDecs LetExpression ds ; e' <- cvtl e; return $ HsLet noAnn noHsTok ds' noHsTok e'} cvt (CaseE e ms) = do { e' <- cvtl e; ms' <- mapM (cvtMatch CaseAlt) ms ; th_origin <- getOrigin@@ -1104,11 +1122,13 @@ -- constructor names - see #14627. { s' <- vcName s ; wrapParLA (HsVar noExtField) s' }- cvt (LabelE s) = return $ HsOverLabel noComments (fsLit s)+ cvt (LabelE s) = return $ HsOverLabel noComments NoSourceText (fsLit s) cvt (ImplicitParamVarE n) = do { n' <- ipName n; return $ HsIPVar noComments n' } cvt (GetFieldE exp f) = do { e' <- cvtl exp- ; return $ HsGetField noComments e' (L noSrcSpanA (DotFieldOcc noAnn (L noSrcSpanA (fsLit f)))) }- cvt (ProjectionE xs) = return $ HsProjection noAnn $ fmap (L noSrcSpanA . DotFieldOcc noAnn . L noSrcSpanA . fsLit) xs+ ; return $ HsGetField noComments e'+ (L noSrcSpanA (DotFieldOcc noAnn (L noSrcSpanA (FieldLabelString (fsLit f))))) }+ cvt (ProjectionE xs) = return $ HsProjection noAnn $ fmap+ (L noSrcSpanA . DotFieldOcc noAnn . L noSrcSpanA . FieldLabelString . fsLit) xs {- | #16895 Ensure an infix expression's operator is a variable/constructor. Consider this example:@@ -1125,8 +1145,7 @@ ensureValidOpExp (VarE _n) m = m ensureValidOpExp (ConE _n) m = m ensureValidOpExp (UnboundVarE _n) m = m-ensureValidOpExp _e _m =- failWith (text "Non-variable expression is not allowed in an infix expression")+ensureValidOpExp _e _m = failWith NonVarInInfixExpr {- Note [Dropping constructors] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -1238,11 +1257,12 @@ ------------------------------------- cvtHsDo :: HsDoFlavour -> [TH.Stmt] -> CvtM (HsExpr GhcPs)-cvtHsDo do_or_lc stmts- | null stmts = failWith (text "Empty stmt list in do-block")- | otherwise- = do { stmts' <- cvtStmts stmts- ; let Just (stmts'', last') = snocView stmts'+cvtHsDo do_or_lc stmts = case nonEmpty stmts of+ Nothing -> failWith EmptyStmtListInDoBlock+ Just stmts -> do+ { stmts' <- traverse cvtStmt stmts+ ; let stmts'' = NE.init stmts'+ last' = NE.last stmts' ; last'' <- case last' of (L loc (BodyStmt _ body _ _))@@ -1251,9 +1271,7 @@ ; wrapParLA (HsDo noAnn do_or_lc) (stmts'' ++ [last'']) } where- bad_last stmt = vcat [ text "Illegal last statement of" <+> pprAHsDoFlavour do_or_lc <> colon- , nest 2 $ Outputable.ppr stmt- , text "(It should be an expression.)" ]+ bad_last stmt = IllegalLastStatement do_or_lc stmt cvtStmts :: [TH.Stmt] -> CvtM [Hs.LStmt GhcPs (LHsExpr GhcPs)] cvtStmts = mapM cvtStmt@@ -1261,7 +1279,7 @@ cvtStmt :: TH.Stmt -> CvtM (Hs.LStmt GhcPs (LHsExpr GhcPs)) cvtStmt (NoBindS e) = do { e' <- cvtl e; returnLA $ mkBodyStmt e' } cvtStmt (TH.BindS p e) = do { p' <- cvtPat p; e' <- cvtl e; returnLA $ mkPsBindStmt noAnn p' e' }-cvtStmt (TH.LetS ds) = do { ds' <- cvtLocalDecs (text "a let binding") ds+cvtStmt (TH.LetS ds) = do { ds' <- cvtLocalDecs LetBinding ds ; returnLA $ LetStmt noAnn ds' } cvtStmt (TH.ParS dss) = do { dss' <- mapM cvt_one dss ; returnLA $ ParStmt noExtField dss' noExpr noSyntaxExpr }@@ -1280,7 +1298,7 @@ (L loc SigPat{}) -> L loc (gParPat p') -- #14875 _ -> p' ; g' <- cvtGuard body- ; decs' <- cvtLocalDecs (text "a where clause") decs+ ; decs' <- cvtLocalDecs WhereClause decs ; returnLA $ Hs.Match noAnn ctxt [lp] (GRHSs emptyComments g' decs') } cvtGuard :: TH.Body -> CvtM [LGRHS GhcPs (LHsExpr GhcPs)]@@ -1385,10 +1403,11 @@ ; ps' <- cvtPats ps ; ts' <- mapM cvtType ts ; let pps = map (parenthesizePat appPrec) ps'+ pts = map (\t -> HsConPatTyArg noHsTok (mkHsPatSigType noAnn t)) ts' ; return $ ConPat { pat_con_ext = noAnn , pat_con = s'- , pat_args = PrefixCon (map (mkHsPatSigType noAnn) ts') pps+ , pat_args = PrefixCon pts pps } } cvtp (InfixP p1 s p2) = do { s' <- cNameN s; p1' <- cvtPat p1; p2' <- cvtPat p2@@ -1410,7 +1429,7 @@ cvtp (TildeP p) = do { p' <- cvtPat p; return $ LazyPat noAnn p' } cvtp (BangP p) = do { p' <- cvtPat p; return $ BangPat noAnn p' } cvtp (TH.AsP s p) = do { s' <- vNameN s; p' <- cvtPat p- ; return $ AsPat noAnn s' p' }+ ; return $ AsPat noAnn s' noHsTok p' } cvtp TH.WildP = return $ WildPat noExtField cvtp (RecP c fs) = do { c' <- cNameN c; fs' <- mapM cvtPatFld fs ; return $ ConPat@@ -1526,21 +1545,21 @@ returnLA $ Hs.ViaStrategy (XViaStrategyPs noAnn ty') cvtType :: TH.Type -> CvtM (LHsType GhcPs)-cvtType = cvtTypeKind "type"+cvtType = cvtTypeKind TypeLevel cvtSigType :: TH.Type -> CvtM (LHsSigType GhcPs)-cvtSigType = cvtSigTypeKind "type"+cvtSigType = cvtSigTypeKind TypeLevel -- | Convert a Template Haskell 'Type' to an 'LHsSigType'. To avoid duplicating -- the logic in 'cvtTypeKind' here, we simply reuse 'cvtTypeKind' and perform -- surgery on the 'LHsType' it returns to turn it into an 'LHsSigType'.-cvtSigTypeKind :: String -> TH.Type -> CvtM (LHsSigType GhcPs)-cvtSigTypeKind ty_str ty = do- ty' <- cvtTypeKind ty_str ty+cvtSigTypeKind :: TypeOrKind -> TH.Type -> CvtM (LHsSigType GhcPs)+cvtSigTypeKind typeOrKind ty = do+ ty' <- cvtTypeKind typeOrKind ty pure $ hsTypeToHsSigType $ parenthesizeHsType sigPrec ty' -cvtTypeKind :: String -> TH.Type -> CvtM (LHsType GhcPs)-cvtTypeKind ty_str ty+cvtTypeKind :: TypeOrKind -> TH.Type -> CvtM (LHsType GhcPs)+cvtTypeKind typeOrKind ty = do { (head_ty, tys') <- split_ty_app ty ; let m_normals = mapM extract_normal tys' where extract_normal (HsValArg ty) = Just ty@@ -1563,10 +1582,7 @@ ; mk_apps (HsTyVar noAnn NotPromoted tuple_tc) tys' } UnboxedSumT n | n < 2- -> failWith $- vcat [ text "Illegal sum arity:" <+> text (show n)- , nest 2 $- text "Sums must have an arity of at least 2" ]+ -> failWith $ IllegalSumArity n | Just normals <- m_normals , normals `lengthIs` n -- Saturated -> returnLA (HsSumTy noAnn normals)@@ -1600,7 +1616,7 @@ w'' = hsTypeToArrow w' returnLA (HsFunTy noAnn w'' x'' y'') | otherwise- -> do { fun_tc <- returnLA $ getRdrName funTyCon+ -> do { fun_tc <- returnLA $ getRdrName fUNTyCon ; mk_apps (HsTyVar noAnn NotPromoted fun_tc) tys' } ListT | Just normals <- m_normals@@ -1743,7 +1759,7 @@ ; returnLA (HsIParamTy noAnn (reLocA n') t') } - _ -> failWith (text "Malformed " <> text ty_str <+> text (show ty))+ _ -> failWith (MalformedType typeOrKind ty) } hsTypeToArrow :: LHsType GhcPs -> HsArrow GhcPs@@ -1796,8 +1812,9 @@ wrap_tyarg ta@(HsArgPar {}) = ta -- Already parenthesized -- ------------------------------------------------------------------------ Note [Adding parens for splices] {-+Note [Adding parens for splices]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The hsSyn representation of parsed source explicitly contains all the original parens, as written in the source. @@ -1828,7 +1845,7 @@ go (ParensT t) as' = do { loc <- getL; go t (HsArgPar loc: as') } go f as = return (f,as) -cvtTyLit :: TH.TyLit -> HsTyLit+cvtTyLit :: TH.TyLit -> HsTyLit (GhcPass p) cvtTyLit (TH.NumTyLit i) = HsNumTy NoSourceText i cvtTyLit (TH.StrTyLit s) = HsStrTy NoSourceText (fsLit s) cvtTyLit (TH.CharTyLit c) = HsCharTy NoSourceText c@@ -1853,10 +1870,10 @@ ; returnLA (mkHsOpTy prom x' op y) } cvtKind :: TH.Kind -> CvtM (LHsKind GhcPs)-cvtKind = cvtTypeKind "kind"+cvtKind = cvtTypeKind KindLevel cvtSigKind :: TH.Kind -> CvtM (LHsSigType GhcPs)-cvtSigKind = cvtSigTypeKind "kind"+cvtSigKind = cvtSigTypeKind KindLevel -- | Convert Maybe Kind to a type family result signature. Used with data -- families where naming of the result is not possible (thus only kind or no@@ -1933,14 +1950,11 @@ unboxedSumChecks :: TH.SumAlt -> TH.SumArity -> CvtM () unboxedSumChecks alt arity | alt > arity- = failWith $ text "Sum alternative" <+> text (show alt)- <+> text "exceeds its arity," <+> text (show arity)+ = failWith $ SumAltArityExceeded alt arity | alt <= 0- = failWith $ vcat [ text "Illegal sum alternative:" <+> text (show alt)- , nest 2 $ text "Sum alternatives must start from 1" ]+ = failWith $ IllegalSumAlt alt | arity < 2- = failWith $ vcat [ text "Illegal sum arity:" <+> text (show arity)- , nest 2 $ text "Sums must have an arity of at least 2" ]+ = failWith $ IllegalSumArity arity | otherwise = return () @@ -2039,12 +2053,12 @@ ipName :: String -> CvtM HsIPName ipName n- = do { unless (okVarOcc n) (failWith (badOcc OccName.varName n))+ = do { unless (okVarOcc n) (failWith (IllegalOccName OccName.varName n)) ; return (HsIPName (fsLit n)) } cvtName :: OccName.NameSpace -> TH.Name -> CvtM RdrName cvtName ctxt_ns (TH.Name occ flavour)- | not (okOcc ctxt_ns occ_str) = failWith (badOcc ctxt_ns occ_str)+ | not (okOcc ctxt_ns occ_str) = failWith (IllegalOccName ctxt_ns occ_str) | otherwise = do { loc <- getL ; let rdr_name = thRdrName loc ctxt_ns occ_str flavour@@ -2067,11 +2081,6 @@ "" -> False (c:_) -> startsVarId c || startsVarSym c -badOcc :: OccName.NameSpace -> String -> SDoc-badOcc ctxt_ns occ- = text "Illegal" <+> pprNameSpace ctxt_ns- <+> text "name:" <+> quotes (text occ)- thRdrName :: SrcSpan -> OccName.NameSpace -> String -> TH.NameFlavour -> RdrName -- This turns a TH Name into a RdrName; used for both binders and occurrences -- See Note [Binders in Template Haskell]@@ -2107,9 +2116,10 @@ thOrigRdrName :: String -> TH.NameSpace -> PkgName -> ModName -> RdrName thOrigRdrName occ th_ns pkg mod = let occ' = mk_occ (mk_ghc_ns th_ns) occ- in case isBuiltInOcc_maybe occ' of+ mod' = mkModule (mk_pkg pkg) (mk_mod mod)+ in case isBuiltInOcc_maybe occ' <|> isPunOcc_maybe mod' occ' of Just name -> nameRdrName name- Nothing -> (mkOrig $! (mkModule (mk_pkg pkg) (mk_mod mod))) $! occ'+ Nothing -> (mkOrig $! mod') $! occ' thRdrNameGuesses :: TH.Name -> [RdrName] thRdrNameGuesses (TH.Name occ flavour)
compiler/GHC/Types/TyThing/Ppr.hs view
@@ -21,7 +21,7 @@ import GHC.Types.TyThing ( TyThing(..), tyThingParent_maybe ) import GHC.Types.Name -import GHC.Core.Type ( ArgFlag(..), mkTyVarBinders )+import GHC.Core.Type ( ForAllTyFlag(..), mkTyVarBinders ) import GHC.Core.Coercion.Axiom ( coAxiomTyCon ) import GHC.Core.FamInstEnv( FamInst(..), FamFlavor(..) ) import GHC.Core.TyCo.Ppr ( pprUserForAll, pprTypeApp )@@ -31,8 +31,9 @@ import GHC.Iface.Make ( tyThingToIfaceDecl ) import GHC.Utils.Outputable-import GHC.Utils.Trace +import Data.Maybe ( isJust )+ -- ----------------------------------------------------------------------------- -- Pretty-printing entities that we get from the GHC API @@ -178,7 +179,7 @@ ppr_bndr :: Name -> Maybe (OccName -> SDoc) ppr_bndr name- | isBuiltInSyntax name+ | isBuiltInSyntax name || isJust (namePun_maybe name) = Nothing | otherwise = case nameModule_maybe name of
compiler/GHC/Utils/Asm.hs view
@@ -12,9 +12,10 @@ import GHC.Utils.Outputable -- | Generate a section type (e.g. @\@progbits@). See #13937.-sectionType :: Platform -- ^ Target platform+sectionType :: IsLine doc+ => Platform -- ^ Target platform -> String -- ^ section type- -> SDoc -- ^ pretty assembler fragment+ -> doc -- ^ pretty assembler fragment sectionType platform ty = case platformArch platform of ArchARM{} -> char '%' <> text ty
+ compiler/GHC/Wasm/ControlFlow.hs view
@@ -0,0 +1,49 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE DataKinds, GADTs, RankNTypes, KindSignatures #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE PatternSynonyms #-}++module GHC.Wasm.ControlFlow+ ( WasmControl(..), (<>), pattern WasmIf+ , BrTableInterval(..), inclusiveInterval++ , WasmType, WasmTypeTag(..)+ , TypeList(..)+ , WasmFunctionType(..)+ )+where++import GHC.Prelude++import GHC.CmmToAsm.Wasm.Types+import GHC.Utils.Panic++{-|+Module : GHC.Wasm.ControlFlow+Description : Representation of control-flow portion of the WebAssembly instruction set+-}++inclusiveInterval :: Integer -> Integer -> BrTableInterval+inclusiveInterval lo hi+ | lo <= hi = let count = hi - lo + 1+ in BrTableInterval lo count+ | otherwise = panic "GHC.Wasm.ControlFlow: empty interval"++(<>) :: forall s e pre mid post+ . WasmControl s e pre mid+ -> WasmControl s e mid post+ -> WasmControl s e pre post+(<>) = WasmSeq+-- N.B. Fallthrough can't be optimized away because of type checking.++++-- Syntactic sugar.+pattern WasmIf :: WasmFunctionType pre post+ -> e+ -> WasmControl s e pre post+ -> WasmControl s e pre post+ -> WasmControl s e pre post++pattern WasmIf ty e t f =+ WasmPush TagI32 e `WasmSeq` WasmIfTop ty t f
+ compiler/GHC/Wasm/ControlFlow/FromCmm.hs view
@@ -0,0 +1,360 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE DataKinds #-}++module GHC.Wasm.ControlFlow.FromCmm+ ( structuredControl+ )+where++import GHC.Prelude hiding (succ)++import Data.Function+import Data.List (sortBy)+import qualified Data.Tree as Tree++import GHC.Cmm+import GHC.Cmm.Dataflow.Block+import GHC.Cmm.Dataflow.Collections+import GHC.Cmm.Dominators+import GHC.Cmm.Dataflow.Graph+import GHC.Cmm.Dataflow.Label+import GHC.Cmm.Switch++import GHC.CmmToAsm.Wasm.Types++import GHC.Platform++import GHC.Utils.Misc+import GHC.Utils.Panic+import GHC.Utils.Outputable ( Outputable, text, (<+>), ppr+ , pprWithCommas+ )++import GHC.Wasm.ControlFlow+++{-|+Module : GHC.Wasm.ControlFlow.FromCmm+Description : Translation of (reducible) Cmm control flow to WebAssembly++Code in this module can translate any _reducible_ Cmm control-flow+graph to the structured control flow that is required by WebAssembly.+The algorithm is subtle and is described in detail in a draft paper+to be found at https://www.cs.tufts.edu/~nr/pubs/relooper.pdf.+-}++--------------------- Abstraction of Cmm control flow -----------------------++-- | Abstracts the kind of control flow we understand how to convert.+-- A block can be left in one of four ways:+--+-- * Unconditionally+--+-- * Conditionally on a predicate of type `e`+--+-- * To a location determined by the value of a scrutinee of type `e`+--+-- * Not at all.++data ControlFlow e = Unconditional Label+ | Conditional e Label Label+ | Switch { _scrutinee :: e+ , _range :: BrTableInterval+ , _targets :: [Maybe Label] -- from 0+ , _defaultTarget :: Maybe Label+ }+ | TailCall e++flowLeaving :: Platform -> CmmBlock -> ControlFlow CmmExpr+flowLeaving platform b =+ case lastNode b of+ CmmBranch l -> Unconditional l+ CmmCondBranch c t f _ -> Conditional c t f+ CmmSwitch e targets ->+ let (offset, target_labels) = switchTargetsToTable targets+ (lo, hi) = switchTargetsRange targets+ default_label = switchTargetsDefault targets+ scrutinee = smartExtend platform $ smartPlus platform e offset+ range = inclusiveInterval (lo+toInteger offset) (hi+toInteger offset)+ in Switch scrutinee range target_labels default_label+ CmmCall { cml_cont = Nothing, cml_target = e } -> TailCall e+ _ -> panic "flowLeaving: unreachable"++----------------------- Evaluation contexts ------------------------------++-- | The syntactic constructs in which Wasm code may be contained.+-- A list of these constructs represents an evaluation context,+-- which is used to determined what level of `br` instruction+-- reaches a given label.++data ContainingSyntax+ = BlockFollowedBy Label+ | LoopHeadedBy Label+ | IfThenElse (Maybe Label) -- ^ Carries the label that follows `if...end`, if any++matchesFrame :: Label -> ContainingSyntax -> Bool+matchesFrame label (BlockFollowedBy l) = label == l+matchesFrame label (LoopHeadedBy l) = label == l+matchesFrame label (IfThenElse (Just l)) = label == l+matchesFrame _ _ = False++data Context = Context { enclosing :: [ContainingSyntax]+ , fallthrough :: Maybe Label -- the label can+ -- be reached just by "falling through"+ -- the hole+ }++instance Outputable Context where+ ppr c | Just l <- fallthrough c =+ pprWithCommas ppr (enclosing c) <+> text "fallthrough to" <+> ppr l+ | otherwise = pprWithCommas ppr (enclosing c)++emptyContext :: Context+emptyContext = Context [] Nothing++inside :: ContainingSyntax -> Context -> Context+withFallthrough :: Context -> Label -> Context++inside frame c = c { enclosing = frame : enclosing c }+withFallthrough c l = c { fallthrough = Just l }++type CmmActions = Block CmmNode O O++type FT pre post = WasmFunctionType pre post++returns :: FT '[] '[ 'I32]+doesn'tReturn :: FT '[] '[]++returns = WasmFunctionType TypeListNil (TypeListCons TagI32 TypeListNil)+doesn'tReturn = WasmFunctionType TypeListNil TypeListNil++emptyPost :: FT pre post -> Bool+emptyPost (WasmFunctionType _ TypeListNil) = True+emptyPost _ = False++----------------------- Translation ------------------------------++-- | Convert a Cmm CFG to WebAssembly's structured control flow.++structuredControl :: forall expr stmt m .+ Applicative m+ => Platform -- ^ needed for offset calculation+ -> (Label -> CmmExpr -> m expr) -- ^ translator for expressions+ -> (Label -> CmmActions -> m stmt) -- ^ translator for straight-line code+ -> CmmGraph -- ^ CFG to be translated+ -> m (WasmControl stmt expr '[] '[ 'I32])+structuredControl platform txExpr txBlock g =+ doTree returns dominatorTree emptyContext+ where+ gwd :: GraphWithDominators CmmNode+ gwd = graphWithDominators g++ dominatorTree :: Tree.Tree CmmBlock-- Dominator tree in which children are sorted+ -- with highest reverse-postorder number first+ dominatorTree = fmap blockLabeled $ sortTree $ gwdDominatorTree gwd++ doTree :: FT '[] post -> Tree.Tree CmmBlock -> Context -> m (WasmControl stmt expr '[] post)+ nodeWithin :: forall post .+ FT '[] post -> CmmBlock -> [Tree.Tree CmmBlock] -> Maybe Label+ -> Context -> m (WasmControl stmt expr '[] post)+ doBranch :: FT '[] post -> Label -> Label -> Context -> m (WasmControl stmt expr '[] post)++ doTree fty (Tree.Node x children) context =+ let codeForX = nodeWithin fty x selectedChildren Nothing+ in if isLoopHeader x then+ WasmLoop fty <$> codeForX loopContext+ else+ codeForX context+ where selectedChildren = case lastNode x of+ CmmSwitch {} -> children+ -- N.B. Unlike `if`, translation of Switch uses only labels.+ _ -> filter hasMergeRoot children+ loopContext = LoopHeadedBy (entryLabel x) `inside` context+ hasMergeRoot = isMergeNode . Tree.rootLabel++ nodeWithin fty x (y_n:ys) (Just zlabel) context =+ WasmBlock fty <$> nodeWithin fty x (y_n:ys) Nothing context'+ where context' = BlockFollowedBy zlabel `inside` context+ nodeWithin fty x (y_n:ys) Nothing context =+ nodeWithin doesn'tReturn x ys (Just ylabel) (context `withFallthrough` ylabel) <<>>+ doTree fty y_n context+ where ylabel = treeEntryLabel y_n+ nodeWithin fty x [] (Just zlabel) context+ | not (generatesIf x) =+ WasmBlock fty <$> nodeWithin fty x [] Nothing context'+ where context' = BlockFollowedBy zlabel `inside` context+ nodeWithin fty x [] maybeMarks context =+ translationOfX context+ where xlabel = entryLabel x++ translationOfX :: Context -> m (WasmControl stmt expr '[] post)+ translationOfX context =+ (WasmActions <$> txBlock xlabel (nodeBody x)) <<>>+ case flowLeaving platform x of+ Unconditional l -> doBranch fty xlabel l context+ Conditional e t f ->+ WasmIf fty+ <$> txExpr xlabel e+ <*> doBranch fty xlabel t (IfThenElse maybeMarks `inside` context)+ <*> doBranch fty xlabel f (IfThenElse maybeMarks `inside` context)+ TailCall e -> WasmTailCall <$> txExpr xlabel e+ Switch e range targets default' ->+ WasmBrTable <$> txExpr xlabel e+ <$~> range+ <$~> map switchIndex targets+ <$~> switchIndex default'+ where switchIndex :: Maybe Label -> Int+ switchIndex Nothing = 0 -- arbitrary; GHC won't go here+ switchIndex (Just lbl) = index lbl (enclosing context)++ doBranch fty from to context+ | to `elem` fallthrough context && emptyPost fty = pure WasmFallthrough+ -- optimization: `br` is not needed, but it typechecks+ -- only if nothing is expected to be left on the stack++ | isBackward from to = pure $ WasmBr i -- continue+ | isMergeLabel to = pure $ WasmBr i -- exit+ | otherwise = doTree fty (subtreeAt to) context -- inline the code here+ where i = index to (enclosing context)++ generatesIf :: CmmBlock -> Bool+ generatesIf x = case flowLeaving platform x of Conditional {} -> True+ _ -> False++ ---- everything else is utility functions++ treeEntryLabel :: Tree.Tree CmmBlock -> Label+ treeEntryLabel = entryLabel . Tree.rootLabel++ sortTree :: Tree.Tree Label -> Tree.Tree Label+ -- Sort highest rpnum first+ sortTree (Tree.Node label children) =+ Tree.Node label $ sortBy (flip compare `on` (rpnum . Tree.rootLabel)) $+ map sortTree children++ subtreeAt :: Label -> Tree.Tree CmmBlock+ blockLabeled :: Label -> CmmBlock+ rpnum :: Label -> RPNum-- reverse postorder number of the labeled block+ isMergeLabel :: Label -> Bool+ isMergeNode :: CmmBlock -> Bool+ isLoopHeader :: CmmBlock -> Bool-- identify loop headers+ -- all nodes whose immediate dominator is the given block.+ -- They are produced with the largest RP number first,+ -- so the largest RP number is pushed on the context first.+ dominates :: Label -> Label -> Bool+ -- Domination relation (not just immediate domination)++ blockmap :: LabelMap CmmBlock+ GMany NothingO blockmap NothingO = g_graph g++ blockLabeled l = findLabelIn l blockmap++ rpblocks :: [CmmBlock]+ rpblocks = revPostorderFrom blockmap (g_entry g)++ foldEdges :: forall a . (Label -> Label -> a -> a) -> a -> a+ foldEdges f a =+ foldl (\a (from, to) -> f from to a)+ a+ [(entryLabel from, to) | from <- rpblocks, to <- successors from]++ isMergeLabel l = setMember l mergeBlockLabels+ isMergeNode = isMergeLabel . entryLabel++ isBackward :: Label -> Label -> Bool+ isBackward from to = rpnum to <= rpnum from -- self-edge counts as a backward edge++ subtreeAt label = findLabelIn label subtrees+ subtrees :: LabelMap (Tree.Tree CmmBlock)+ subtrees = addSubtree mapEmpty dominatorTree+ where addSubtree map t@(Tree.Node root children) =+ foldl addSubtree (mapInsert (entryLabel root) t map) children++ mergeBlockLabels :: LabelSet+ -- N.B. A block is a merge node if it is where control flow merges.+ -- That means it is entered by multiple control-flow edges, _except_+ -- back edges don't count. There must be multiple paths that enter the+ -- block _without_ passing through the block itself.+ mergeBlockLabels =+ setFromList [entryLabel n | n <- rpblocks, big (forwardPreds (entryLabel n))]+ where big [] = False+ big [_] = False+ big (_ : _ : _) = True++ forwardPreds :: Label -> [Label] -- reachable predecessors of reachable blocks,+ -- via forward edges only+ forwardPreds = \l -> mapFindWithDefault [] l predmap+ where predmap :: LabelMap [Label]+ predmap = foldEdges addForwardEdge mapEmpty+ addForwardEdge from to pm+ | isBackward from to = pm+ | otherwise = addToList (from :) to pm++ isLoopHeader = isHeaderLabel . entryLabel+ isHeaderLabel = (`setMember` headers) -- loop headers+ where headers :: LabelSet+ headers = foldMap headersPointedTo blockmap+ headersPointedTo block =+ setFromList [label | label <- successors block,+ dominates label (entryLabel block)]++ index :: Label -> [ContainingSyntax] -> Int+ index _ [] = panic "destination label not in evaluation context"+ index label (frame : context)+ | label `matchesFrame` frame = 0+ | otherwise = 1 + index label context++ rpnum = gwdRPNumber gwd+ dominates lbl blockname =+ lbl == blockname || dominatorsMember lbl (gwdDominatorsOf gwd blockname)++++nodeBody :: CmmBlock -> CmmActions+nodeBody (BlockCC _first middle _last) = middle++-- | A CmmSwitch scrutinee may have any width, but a br_table operand+-- must be exactly word sized, hence the extension here. (#22871)+smartExtend :: Platform -> CmmExpr -> CmmExpr+smartExtend p e | w0 == w1 = e+ | otherwise = CmmMachOp (MO_UU_Conv w0 w1) [e]+ where+ w0 = cmmExprWidth p e+ w1 = wordWidth p++smartPlus :: Platform -> CmmExpr -> Int -> CmmExpr+smartPlus _ e 0 = e+smartPlus platform e k =+ CmmMachOp (MO_Add width) [e, CmmLit (CmmInt (toInteger k) width)]+ where width = cmmExprWidth platform e++addToList :: (IsMap map) => ([a] -> [a]) -> KeyOf map -> map [a] -> map [a]+addToList consx = mapAlter add+ where add Nothing = Just (consx [])+ add (Just xs) = Just (consx xs)++------------------------------------------------------------------+--- everything below here is for diagnostics in case of panic++instance Outputable ContainingSyntax where+ ppr (BlockFollowedBy l) = text "node" <+> ppr l+ ppr (LoopHeadedBy l) = text "loop" <+> ppr l+ ppr (IfThenElse l) = text "if-then-else" <+> ppr l++findLabelIn :: HasDebugCallStack => Label -> LabelMap a -> a+findLabelIn lbl = mapFindWithDefault failed lbl+ where failed =+ pprPanic "label not found in control-flow graph" (ppr lbl)+++infixl 4 <$~>+(<$~>) :: Functor m => m (a -> b) -> a -> m b+(<$~>) f x = fmap ($ x) f++(<<>>) :: forall m s e pre mid post+ . Applicative m+ => m (WasmControl s e pre mid)+ -> m (WasmControl s e mid post)+ -> m (WasmControl s e pre post)+(<<>>) = liftA2 (<>)
compiler/MachRegs.h view
@@ -591,6 +591,92 @@ #define MAX_REAL_FLOAT_REG 6 #define MAX_REAL_DOUBLE_REG 6 +#elif defined(MACHREGS_wasm32)++#define REG_R1 1+#define REG_R2 2+#define REG_R3 3+#define REG_R4 4+#define REG_R5 5+#define REG_R6 6+#define REG_R7 7+#define REG_R8 8+#define REG_R9 9+#define REG_R10 10++#define REG_F1 11+#define REG_F2 12+#define REG_F3 13+#define REG_F4 14+#define REG_F5 15+#define REG_F6 16++#define REG_D1 17+#define REG_D2 18+#define REG_D3 19+#define REG_D4 20+#define REG_D5 21+#define REG_D6 22++#define REG_L1 23++#define REG_Sp 24+#define REG_SpLim 25+#define REG_Hp 26+#define REG_HpLim 27+#define REG_CCCS 28++/* -----------------------------------------------------------------------------+ The loongarch64 register mapping++ Register | Role(s) | Call effect+ ------------+-----------------------------------------+-------------+ zero | Hard-wired zero | -+ ra | Return address | caller-saved+ tp | Thread pointer | -+ sp | Stack pointer | callee-saved+ a0,a1 | Arguments / return values | caller-saved+ a2..a7 | Arguments | caller-saved+ t0..t8 | - | caller-saved+ u0 | Reserve | -+ fp | Frame pointer | callee-saved+ s0..s8 | - | callee-saved+ fa0,fa1 | Arguments / return values | caller-saved+ fa2..fa7 | Arguments | caller-saved+ ft0..ft15 | - | caller-saved+ fs0..fs7 | - | callee-saved++ Each general purpose register as well as each floating-point+ register is 64 bits wide, also, the u0 register is called r21 in some cases.++ -------------------------------------------------------------------------- */+#elif defined(MACHREGS_loongarch64)++#define REG(x) __asm__("$" #x)++#define REG_Base s0+#define REG_Sp s1+#define REG_Hp s2+#define REG_R1 s3+#define REG_R2 s4+#define REG_R3 s5+#define REG_R4 s6+#define REG_R5 s7+#define REG_SpLim s8++#define REG_F1 fs0+#define REG_F2 fs1+#define REG_F3 fs2+#define REG_F4 fs3++#define REG_D1 fs4+#define REG_D2 fs5+#define REG_D3 fs6+#define REG_D4 fs7++#define MAX_REAL_FLOAT_REG 4+#define MAX_REAL_DOUBLE_REG 4+ #else #error Cannot find platform to give register info for
compiler/ghc-llvm-version.h view
@@ -3,9 +3,9 @@ #define __GHC_LLVM_VERSION_H__ /* The maximum supported LLVM version number */-#define sUPPORTED_LLVM_VERSION_MAX (15)+#define sUPPORTED_LLVM_VERSION_MAX (16) /* The minimum supported LLVM version number */-#define sUPPORTED_LLVM_VERSION_MIN (10)+#define sUPPORTED_LLVM_VERSION_MIN (11) #endif /* __GHC_LLVM_VERSION_H__ */
ghc-lib.cabal view
@@ -1,8 +1,8 @@-cabal-version: 3.0+cabal-version: 2.0 build-type: Simple name: ghc-lib-version: 9.4.8.20231111-license: BSD-3-Clause+version: 9.6.1.20230312+license: BSD3 license-file: LICENSE category: Development author: The GHC Team and Digital Asset@@ -72,25 +72,25 @@ else build-depends: Win32 build-depends:- base >= 4.15 && < 4.18,- ghc-prim > 0.2 && < 0.10,- containers >= 0.5 && < 0.7,- bytestring >= 0.10 && < 0.12,+ base >= 4.16.1 && < 4.19,+ ghc-prim > 0.2 && < 0.11,+ bytestring >= 0.11.3 && < 0.12, time >= 1.4 && < 1.13, exceptions == 0.10.*, parsec,+ containers >= 0.5 && < 0.7, binary == 0.8.*, filepath >= 1 && < 1.5, directory >= 1 && < 1.4, array >= 0.1 && < 0.6,- deepseq >= 1.4 && < 1.6,+ deepseq >= 1.4 && < 1.5, pretty == 1.1.*, transformers >= 0.5 && < 0.7, process >= 1 && < 1.7, stm, rts, hpc == 0.6.*,- ghc-lib-parser == 9.4.8.20231111+ ghc-lib-parser == 9.6.1.20230312 build-tool-depends: alex:alex >= 3.1, happy:happy >= 1.19.4 other-extensions: BangPatterns@@ -127,13 +127,17 @@ UndecidableInstances default-extensions: BangPatterns+ ImplicitPrelude MonoLocalBinds NoImplicitPrelude ScopedTypeVariables TypeOperators hs-source-dirs:+ ghc-lib/stage0/libraries/ghc-boot/build ghc-lib/stage0/compiler/build+ libraries/template-haskell libraries/ghc-boot+ libraries/ghci compiler autogen-modules: Paths_ghc_lib@@ -156,9 +160,11 @@ GHC.Cmm.Expr, GHC.Cmm.MachOp, GHC.Cmm.Node,+ GHC.Cmm.Reg, GHC.Cmm.Switch, GHC.Cmm.Type, GHC.CmmToAsm.CFG.Weight,+ GHC.CmmToLlvm.Config, GHC.Core, GHC.Core.Class, GHC.Core.Coercion,@@ -170,6 +176,7 @@ GHC.Core.FamInstEnv, GHC.Core.InstEnv, GHC.Core.Lint,+ GHC.Core.Lint.Interactive, GHC.Core.Make, GHC.Core.Map.Expr, GHC.Core.Map.Type,@@ -179,17 +186,26 @@ GHC.Core.Opt.ConstantFold, GHC.Core.Opt.Monad, GHC.Core.Opt.OccurAnal,+ GHC.Core.Opt.Pipeline.Types,+ GHC.Core.Opt.Simplify,+ GHC.Core.Opt.Simplify.Env,+ GHC.Core.Opt.Simplify.Iteration,+ GHC.Core.Opt.Simplify.Monad,+ GHC.Core.Opt.Simplify.Utils,+ GHC.Core.Opt.Stats, GHC.Core.PatSyn, GHC.Core.Ppr, GHC.Core.Predicate, GHC.Core.Reduction, GHC.Core.RoughMap, GHC.Core.Rules,+ GHC.Core.Rules.Config, GHC.Core.Seq, GHC.Core.SimpleOpt, GHC.Core.Stats, GHC.Core.Subst, GHC.Core.Tidy,+ GHC.Core.TyCo.Compare, GHC.Core.TyCo.FVs, GHC.Core.TyCo.Ppr, GHC.Core.TyCo.Rep,@@ -216,6 +232,7 @@ GHC.Data.Graph.Directed, GHC.Data.Graph.UnVar, GHC.Data.IOEnv,+ GHC.Data.List.Infinite, GHC.Data.List.SetOps, GHC.Data.Maybe, GHC.Data.OrdList,@@ -227,10 +244,13 @@ GHC.Data.Strict, GHC.Data.StringBuffer, GHC.Data.TrieMap,+ GHC.Data.Unboxed, GHC.Driver.Backend,+ GHC.Driver.Backend.Internal, GHC.Driver.Backpack.Syntax, GHC.Driver.CmdLine, GHC.Driver.Config,+ GHC.Driver.Config.Core.Lint, GHC.Driver.Config.Diagnostic, GHC.Driver.Config.Logger, GHC.Driver.Config.Parser,@@ -242,11 +262,13 @@ GHC.Driver.Errors.Types, GHC.Driver.Flags, GHC.Driver.Hooks,+ GHC.Driver.LlvmConfigCache, GHC.Driver.Monad, GHC.Driver.Phases, GHC.Driver.Pipeline.Monad, GHC.Driver.Pipeline.Phases, GHC.Driver.Plugins,+ GHC.Driver.Plugins.External, GHC.Driver.Ppr, GHC.Driver.Session, GHC.Exts.Heap,@@ -311,6 +333,7 @@ GHC.Platform.ARM, GHC.Platform.ArchOS, GHC.Platform.Constants,+ GHC.Platform.LoongArch64, GHC.Platform.NoRegs, GHC.Platform.PPC, GHC.Platform.Profile,@@ -319,10 +342,12 @@ GHC.Platform.Reg.Class, GHC.Platform.Regs, GHC.Platform.S390X,+ GHC.Platform.Wasm32, GHC.Platform.Ways, GHC.Platform.X86, GHC.Platform.X86_64, GHC.Prelude,+ GHC.Prelude.Basic, GHC.Runtime.Context, GHC.Runtime.Eval.Types, GHC.Runtime.Heap.Layout,@@ -332,6 +357,7 @@ GHC.Settings, GHC.Settings.Config, GHC.Settings.Constants,+ GHC.Settings.Utils, GHC.Stg.InferTags.TagSig, GHC.Stg.Syntax, GHC.StgToCmm.Config,@@ -359,6 +385,7 @@ GHC.Types.Cpr, GHC.Types.Demand, GHC.Types.Error,+ GHC.Types.Error.Codes, GHC.Types.FieldLabel, GHC.Types.Fixity, GHC.Types.Fixity.Env,@@ -381,6 +408,7 @@ GHC.Types.Name.Reader, GHC.Types.Name.Set, GHC.Types.PkgQual,+ GHC.Types.ProfAuto, GHC.Types.RepType, GHC.Types.SafeHaskell, GHC.Types.SourceError,@@ -421,9 +449,9 @@ GHC.Unit.Module.ModGuts, GHC.Unit.Module.ModIface, GHC.Unit.Module.ModSummary,- GHC.Unit.Module.Name, GHC.Unit.Module.Status, GHC.Unit.Module.Warnings,+ GHC.Unit.Module.WholeCoreBindings, GHC.Unit.Parser, GHC.Unit.Ppr, GHC.Unit.State,@@ -434,6 +462,7 @@ GHC.Utils.CliOption, GHC.Utils.Constants, GHC.Utils.Encoding,+ GHC.Utils.Encoding.UTF8, GHC.Utils.Error, GHC.Utils.Exception, GHC.Utils.FV,@@ -462,11 +491,15 @@ GHCi.ResolvedBCO, GHCi.TH.Binary, Language.Haskell.Syntax,+ Language.Haskell.Syntax.Basic, Language.Haskell.Syntax.Binds,+ Language.Haskell.Syntax.Concrete, Language.Haskell.Syntax.Decls, Language.Haskell.Syntax.Expr, Language.Haskell.Syntax.Extension,+ Language.Haskell.Syntax.ImpExp, Language.Haskell.Syntax.Lit,+ Language.Haskell.Syntax.Module.Name, Language.Haskell.Syntax.Pat, Language.Haskell.Syntax.Type, Language.Haskell.TH,@@ -494,6 +527,7 @@ GHC.Cmm.ContFlowOpt GHC.Cmm.Dataflow GHC.Cmm.DebugBlock+ GHC.Cmm.Dominators GHC.Cmm.Graph GHC.Cmm.Info GHC.Cmm.Info.Build@@ -505,14 +539,14 @@ GHC.Cmm.Liveness GHC.Cmm.Opt GHC.Cmm.Parser+ GHC.Cmm.Parser.Config GHC.Cmm.Parser.Monad GHC.Cmm.Pipeline- GHC.Cmm.Ppr- GHC.Cmm.Ppr.Decl- GHC.Cmm.Ppr.Expr GHC.Cmm.ProcPoint+ GHC.Cmm.Reducibility GHC.Cmm.Sink GHC.Cmm.Switch.Implement+ GHC.Cmm.ThreadSanitizer GHC.Cmm.Utils GHC.CmmToAsm GHC.CmmToAsm.AArch64@@ -543,11 +577,14 @@ GHC.CmmToAsm.PPC.Regs GHC.CmmToAsm.Ppr GHC.CmmToAsm.Reg.Graph+ GHC.CmmToAsm.Reg.Graph.Base+ GHC.CmmToAsm.Reg.Graph.Coalesce GHC.CmmToAsm.Reg.Graph.Spill GHC.CmmToAsm.Reg.Graph.SpillClean GHC.CmmToAsm.Reg.Graph.SpillCost GHC.CmmToAsm.Reg.Graph.Stats GHC.CmmToAsm.Reg.Graph.TrivColorable+ GHC.CmmToAsm.Reg.Graph.X86 GHC.CmmToAsm.Reg.Linear GHC.CmmToAsm.Reg.Linear.AArch64 GHC.CmmToAsm.Reg.Linear.Base@@ -564,6 +601,11 @@ GHC.CmmToAsm.Reg.Utils GHC.CmmToAsm.Types GHC.CmmToAsm.Utils+ GHC.CmmToAsm.Wasm+ GHC.CmmToAsm.Wasm.Asm+ GHC.CmmToAsm.Wasm.FromCmm+ GHC.CmmToAsm.Wasm.Types+ GHC.CmmToAsm.Wasm.Utils GHC.CmmToAsm.X86 GHC.CmmToAsm.X86.CodeGen GHC.CmmToAsm.X86.Cond@@ -575,7 +617,6 @@ GHC.CmmToLlvm GHC.CmmToLlvm.Base GHC.CmmToLlvm.CodeGen- GHC.CmmToLlvm.Config GHC.CmmToLlvm.Data GHC.CmmToLlvm.Mangler GHC.CmmToLlvm.Ppr@@ -591,10 +632,6 @@ GHC.Core.Opt.LiberateCase GHC.Core.Opt.Pipeline GHC.Core.Opt.SetLevels- GHC.Core.Opt.Simplify- GHC.Core.Opt.Simplify.Env- GHC.Core.Opt.Simplify.Monad- GHC.Core.Opt.Simplify.Utils GHC.Core.Opt.SpecConstr GHC.Core.Opt.Specialise GHC.Core.Opt.StaticArgs@@ -605,38 +642,62 @@ GHC.CoreToStg.Prep GHC.Data.Bitmap GHC.Data.Graph.Base+ GHC.Data.Graph.Collapse GHC.Data.Graph.Color+ GHC.Data.Graph.Inductive.Graph+ GHC.Data.Graph.Inductive.PatriciaTree GHC.Data.Graph.Ops GHC.Data.Graph.Ppr GHC.Data.UnionFind+ GHC.Driver.Backpack GHC.Driver.CodeOutput GHC.Driver.Config.Cmm+ GHC.Driver.Config.Cmm.Parser GHC.Driver.Config.CmmToAsm GHC.Driver.Config.CmmToLlvm+ GHC.Driver.Config.Core.Lint.Interactive+ GHC.Driver.Config.Core.Opt.Arity+ GHC.Driver.Config.Core.Opt.LiberateCase+ GHC.Driver.Config.Core.Opt.Simplify+ GHC.Driver.Config.Core.Opt.WorkWrap+ GHC.Driver.Config.Core.Rules+ GHC.Driver.Config.CoreToStg+ GHC.Driver.Config.CoreToStg.Prep GHC.Driver.Config.Finder GHC.Driver.Config.HsToCore+ GHC.Driver.Config.HsToCore.Ticks+ GHC.Driver.Config.HsToCore.Usage+ GHC.Driver.Config.Linker GHC.Driver.Config.Stg.Debug GHC.Driver.Config.Stg.Lift GHC.Driver.Config.Stg.Pipeline GHC.Driver.Config.Stg.Ppr GHC.Driver.Config.StgToCmm+ GHC.Driver.Config.StgToJS GHC.Driver.Config.Tidy GHC.Driver.GenerateCgIPEStub GHC.Driver.Main GHC.Driver.Make+ GHC.Driver.MakeFile GHC.Driver.Pipeline GHC.Driver.Pipeline.Execute GHC.Driver.Pipeline.LogQueue+ GHC.HandleEncoding GHC.Hs.Stats GHC.Hs.Syn.Type GHC.HsToCore GHC.HsToCore.Arrows GHC.HsToCore.Binds+ GHC.HsToCore.Breakpoints GHC.HsToCore.Coverage GHC.HsToCore.Docs GHC.HsToCore.Expr+ GHC.HsToCore.Foreign.C GHC.HsToCore.Foreign.Call GHC.HsToCore.Foreign.Decl+ GHC.HsToCore.Foreign.JavaScript+ GHC.HsToCore.Foreign.Prim+ GHC.HsToCore.Foreign.Utils GHC.HsToCore.GuardedRHSs GHC.HsToCore.ListComp GHC.HsToCore.Match@@ -649,6 +710,7 @@ GHC.HsToCore.Pmc.Solver GHC.HsToCore.Pmc.Utils GHC.HsToCore.Quote+ GHC.HsToCore.Ticks GHC.HsToCore.Types GHC.HsToCore.Usage GHC.HsToCore.Utils@@ -668,6 +730,12 @@ GHC.Iface.Tidy GHC.Iface.Tidy.StaticPtrTable GHC.IfaceToCore+ GHC.JS.Make+ GHC.JS.Ppr+ GHC.JS.Syntax+ GHC.JS.Transform+ GHC.Linker+ GHC.Linker.Config GHC.Linker.Dynamic GHC.Linker.ExtraObj GHC.Linker.Loader@@ -681,6 +749,7 @@ GHC.Llvm.Syntax GHC.Llvm.Types GHC.Parser.Utils+ GHC.Platform.Host GHC.Plugins GHC.Rename.Bind GHC.Rename.Doc@@ -694,11 +763,11 @@ GHC.Rename.Splice GHC.Rename.Unbound GHC.Rename.Utils+ GHC.Runtime.Debugger GHC.Runtime.Eval GHC.Runtime.Heap.Inspect GHC.Runtime.Loader GHC.Settings.IO- GHC.Settings.Utils GHC.Stg.BcPrep GHC.Stg.CSE GHC.Stg.Debug@@ -739,8 +808,41 @@ GHC.StgToCmm.TagCheck GHC.StgToCmm.Ticky GHC.StgToCmm.Utils+ GHC.StgToJS+ GHC.StgToJS.Apply+ GHC.StgToJS.Arg+ GHC.StgToJS.Closure+ GHC.StgToJS.CodeGen+ GHC.StgToJS.CoreUtils+ GHC.StgToJS.DataCon+ GHC.StgToJS.Deps+ GHC.StgToJS.Expr+ GHC.StgToJS.ExprCtx+ GHC.StgToJS.FFI+ GHC.StgToJS.Heap+ GHC.StgToJS.Ids+ GHC.StgToJS.Linker.Linker+ GHC.StgToJS.Linker.Types+ GHC.StgToJS.Linker.Utils+ GHC.StgToJS.Literal+ GHC.StgToJS.Monad+ GHC.StgToJS.Object+ GHC.StgToJS.Prim+ GHC.StgToJS.Printer+ GHC.StgToJS.Profiling+ GHC.StgToJS.Regs+ GHC.StgToJS.Rts.Rts+ GHC.StgToJS.Rts.Types+ GHC.StgToJS.Sinker+ GHC.StgToJS.Stack+ GHC.StgToJS.StaticPtr+ GHC.StgToJS.StgUtils+ GHC.StgToJS.Symbols+ GHC.StgToJS.Types+ GHC.StgToJS.Utils GHC.SysTools GHC.SysTools.Ar+ GHC.SysTools.Cpp GHC.SysTools.Elf GHC.SysTools.Info GHC.SysTools.Process@@ -773,6 +875,7 @@ GHC.Tc.Instance.FunDeps GHC.Tc.Instance.Typeable GHC.Tc.Module+ GHC.Tc.Plugin GHC.Tc.Solver GHC.Tc.Solver.Canonical GHC.Tc.Solver.Interact@@ -800,3 +903,14 @@ GHC.Types.Unique.MemoFun GHC.Unit.Finder GHC.Utils.Asm+ GHC.Wasm.ControlFlow+ GHC.Wasm.ControlFlow.FromCmm+ GHCi.CreateBCO+ GHCi.InfoTable+ GHCi.ObjLink+ GHCi.Run+ GHCi.Signals+ GHCi.StaticPtrTable+ GHCi.TH+ Language.Haskell.TH.CodeDo+ Language.Haskell.TH.Quote
ghc-lib/stage0/compiler/build/primop-can-fail.hs-incl view
@@ -234,6 +234,9 @@ primOpCanFail AtomicModifyMutVar2Op = True primOpCanFail AtomicModifyMutVar_Op = True primOpCanFail RaiseOp = True+primOpCanFail RaiseUnderflowOp = True+primOpCanFail RaiseOverflowOp = True+primOpCanFail RaiseDivZeroOp = True primOpCanFail ReallyUnsafePtrEqualityOp = True primOpCanFail (VecInsertOp _ _ _) = True primOpCanFail (VecDivOp _ _ _) = True
ghc-lib/stage0/compiler/build/primop-code-size.hs-incl view
@@ -61,6 +61,9 @@ primOpCodeSize AddrToIntOp = 0 primOpCodeSize IntToAddrOp = 0 primOpCodeSize WriteMutVarOp = primOpCodeSizeForeignCall +primOpCodeSize RaiseUnderflowOp = primOpCodeSizeForeignCall +primOpCodeSize RaiseOverflowOp = primOpCodeSizeForeignCall +primOpCodeSize RaiseDivZeroOp = primOpCodeSizeForeignCall primOpCodeSize TouchOp = 0 primOpCodeSize ParOp = primOpCodeSizeForeignCall primOpCodeSize SparkOp = primOpCodeSizeForeignCall
ghc-lib/stage0/compiler/build/primop-data-decl.hs-incl view
@@ -563,11 +563,17 @@ | CasMutVarOp | CatchOp | RaiseOp+ | RaiseUnderflowOp+ | RaiseOverflowOp+ | RaiseDivZeroOp | RaiseIOOp | MaskAsyncExceptionsOp | MaskUninterruptibleOp | UnmaskAsyncExceptionsOp | MaskStatus+ | NewPromptTagOp+ | PromptOp+ | Control0Op | AtomicallyOp | RetryOp | CatchRetryOp@@ -598,7 +604,9 @@ | LabelThreadOp | IsCurrentThreadBoundOp | NoDuplicateOp+ | GetThreadLabelOp | ThreadStatusOp+ | ListThreadsOp | MkWeakOp | MkWeakNoFinalizerOp | AddCFinalizerToWeakOp
ghc-lib/stage0/compiler/build/primop-docs.hs-incl view
@@ -3,25 +3,25 @@ , ("timesInt2#","Return a triple (isHighNeeded,high,low) where high and low are respectively\n the high and low bits of the double-word result. isHighNeeded is a cheap way\n to test if the high word is a sign-extension of the low word (isHighNeeded =\n 0#) or not (isHighNeeded = 1#).") , ("mulIntMayOflo#","Return non-zero if there is any possibility that the upper word of a\n signed integer multiply might contain useful information. Return\n zero only if you are completely sure that no overflow can occur.\n On a 32-bit platform, the recommended implementation is to do a\n 32 x 32 -> 64 signed multiply, and subtract result[63:32] from\n (result[31] >>signed 31). If this is zero, meaning that the\n upper word is merely a sign extension of the lower one, no\n overflow can occur.\n\n On a 64-bit platform it is not always possible to\n acquire the top 64 bits of the result. Therefore, a recommended\n implementation is to take the absolute value of both operands, and\n return 0 iff bits[63:31] of them are zero, since that means that their\n magnitudes fit within 31 bits, so the magnitude of the product must fit\n into 62 bits.\n\n If in doubt, return non-zero, but do make an effort to create the\n correct answer for small args, since otherwise the performance of\n @(*) :: Integer -> Integer -> Integer@ will be poor.\n ") , ("quotInt#","Rounds towards zero. The behavior is undefined if the second argument is\n zero.\n ")- , ("remInt#","Satisfies @(quotInt\\# x y) *\\# y +\\# (remInt\\# x y) == x@. The\n behavior is undefined if the second argument is zero.\n ")+ , ("remInt#","Satisfies @('quotInt#' x y) '*#' y '+#' ('remInt#' x y) == x@. The\n behavior is undefined if the second argument is zero.\n ") , ("quotRemInt#","Rounds towards zero.") , ("andI#","Bitwise \"and\".") , ("orI#","Bitwise \"or\".") , ("xorI#","Bitwise \"xor\".") , ("notI#","Bitwise \"not\", also known as the binary complement.")- , ("negateInt#","Unary negation.\n Since the negative @Int#@ range extends one further than the\n positive range, @negateInt#@ of the most negative number is an\n identity operation. This way, @negateInt#@ is always its own inverse.")- , ("addIntC#","Add signed integers reporting overflow.\n First member of result is the sum truncated to an @Int#@;\n second member is zero if the true sum fits in an @Int#@,\n nonzero if overflow occurred (the sum is either too large\n or too small to fit in an @Int#@).")- , ("subIntC#","Subtract signed integers reporting overflow.\n First member of result is the difference truncated to an @Int#@;\n second member is zero if the true difference fits in an @Int#@,\n nonzero if overflow occurred (the difference is either too large\n or too small to fit in an @Int#@).")- , ("int2Float#","Convert an @Int#@ to the corresponding @Float#@ with the same\n integral value (up to truncation due to floating-point precision). e.g.\n @int2Float# 1# == 1.0#@")- , ("int2Double#","Convert an @Int#@ to the corresponding @Double#@ with the same\n integral value (up to truncation due to floating-point precision). e.g.\n @int2Double# 1# == 1.0##@")- , ("word2Float#","Convert an @Word#@ to the corresponding @Float#@ with the same\n integral value (up to truncation due to floating-point precision). e.g.\n @word2Float# 1## == 1.0#@")- , ("word2Double#","Convert an @Word#@ to the corresponding @Double#@ with the same\n integral value (up to truncation due to floating-point precision). e.g.\n @word2Double# 1## == 1.0##@")+ , ("negateInt#","Unary negation.\n Since the negative 'Int#' range extends one further than the\n positive range, 'negateInt#' of the most negative number is an\n identity operation. This way, 'negateInt#' is always its own inverse.")+ , ("addIntC#","Add signed integers reporting overflow.\n First member of result is the sum truncated to an 'Int#';\n second member is zero if the true sum fits in an 'Int#',\n nonzero if overflow occurred (the sum is either too large\n or too small to fit in an 'Int#').")+ , ("subIntC#","Subtract signed integers reporting overflow.\n First member of result is the difference truncated to an 'Int#';\n second member is zero if the true difference fits in an 'Int#',\n nonzero if overflow occurred (the difference is either too large\n or too small to fit in an 'Int#').")+ , ("int2Float#","Convert an 'Int#' to the corresponding 'Float#' with the same\n integral value (up to truncation due to floating-point precision). e.g.\n @'int2Float#' 1# == 1.0#@")+ , ("int2Double#","Convert an 'Int#' to the corresponding 'Double#' with the same\n integral value (up to truncation due to floating-point precision). e.g.\n @'int2Double#' 1# == 1.0##@")+ , ("word2Float#","Convert an 'Word#' to the corresponding 'Float#' with the same\n integral value (up to truncation due to floating-point precision). e.g.\n @'word2Float#' 1## == 1.0#@")+ , ("word2Double#","Convert an 'Word#' to the corresponding 'Double#' with the same\n integral value (up to truncation due to floating-point precision). e.g.\n @'word2Double#' 1## == 1.0##@") , ("uncheckedIShiftL#","Shift left. Result undefined if shift amount is not\n in the range 0 to word size - 1 inclusive.") , ("uncheckedIShiftRA#","Shift right arithmetic. Result undefined if shift amount is not\n in the range 0 to word size - 1 inclusive.") , ("uncheckedIShiftRL#","Shift right logical. Result undefined if shift amount is not\n in the range 0 to word size - 1 inclusive.")- , ("addWordC#","Add unsigned integers reporting overflow.\n The first element of the pair is the result. The second element is\n the carry flag, which is nonzero on overflow. See also @plusWord2#@.")+ , ("addWordC#","Add unsigned integers reporting overflow.\n The first element of the pair is the result. The second element is\n the carry flag, which is nonzero on overflow. See also 'plusWord2#'.") , ("subWordC#","Subtract unsigned integers reporting overflow.\n The first element of the pair is the result. The second element is\n the carry flag, which is nonzero on overflow.")- , ("plusWord2#","Add unsigned integers, with the high part (carry) in the first\n component of the returned pair and the low part in the second\n component of the pair. See also @addWordC#@.")+ , ("plusWord2#","Add unsigned integers, with the high part (carry) in the first\n component of the returned pair and the low part in the second\n component of the pair. See also 'addWordC#'.") , ("quotRemWord2#"," Takes high word of dividend, then low word of dividend, then divisor.\n Requires that high word < divisor.") , ("uncheckedShiftL#","Shift left logical. Result undefined if shift amount is not\n in the range 0 to word size - 1 inclusive.") , ("uncheckedShiftRL#","Shift right logical. Result undefined if shift amount is not\n in the range 0 to word size - 1 inclusive.")@@ -59,12 +59,12 @@ , ("bitReverse32#","Reverse the order of the bits in a 32-bit word.") , ("bitReverse64#","Reverse the order of the bits in a 64-bit word.") , ("bitReverse#","Reverse the order of the bits in a word.")- , ("double2Int#","Truncates a @Double#@ value to the nearest @Int#@.\n Results are undefined if the truncation if truncation yields\n a value outside the range of @Int#@.")+ , ("double2Int#","Truncates a 'Double#' value to the nearest 'Int#'.\n Results are undefined if the truncation if truncation yields\n a value outside the range of 'Int#'.") , ("**##","Exponentiation.") , ("decodeDouble_2Int#","Convert to integer.\n First component of the result is -1 or 1, indicating the sign of the\n mantissa. The next two are the high and low 32 bits of the mantissa\n respectively, and the last is the exponent.")- , ("decodeDouble_Int64#","Decode @Double\\#@ into mantissa and base-2 exponent.")- , ("float2Int#","Truncates a @Float#@ value to the nearest @Int#@.\n Results are undefined if the truncation if truncation yields\n a value outside the range of @Int#@.")- , ("decodeFloat_Int#","Convert to integers.\n First @Int\\#@ in result is the mantissa; second is the exponent.")+ , ("decodeDouble_Int64#","Decode 'Double#' into mantissa and base-2 exponent.")+ , ("float2Int#","Truncates a 'Float#' value to the nearest 'Int#'.\n Results are undefined if the truncation if truncation yields\n a value outside the range of 'Int#'.")+ , ("decodeFloat_Int#","Convert to integers.\n First 'Int#' in result is the mantissa; second is the exponent.") , ("newArray#","Create a new mutable array with the specified number of elements,\n in the specified state thread,\n with each element containing the specified initial value.") , ("readArray#","Read from specified index of mutable array. Result is not yet evaluated.") , ("writeArray#","Write to specified index of mutable array.")@@ -79,9 +79,9 @@ , ("cloneMutableArray#","Given a source array, an offset into the source array, and a number\n of elements to copy, create a new array with the elements from the\n source array. The provided array must fully contain the specified\n range, but this is not checked.") , ("freezeArray#","Given a source array, an offset into the source array, and a number\n of elements to copy, create a new array with the elements from the\n source array. The provided array must fully contain the specified\n range, but this is not checked.") , ("thawArray#","Given a source array, an offset into the source array, and a number\n of elements to copy, create a new array with the elements from the\n source array. The provided array must fully contain the specified\n range, but this is not checked.")- , ("casArray#","Given an array, an offset, the expected old value, and\n the new value, perform an atomic compare and swap (i.e. write the new\n value if the current value and the old value are the same pointer).\n Returns 0 if the swap succeeds and 1 if it fails. Additionally, returns\n the element at the offset after the operation completes. This means that\n on a success the new value is returned, and on a failure the actual old\n value (not the expected one) is returned. Implies a full memory barrier.\n The use of a pointer equality on a boxed value makes this function harder\n to use correctly than @casIntArray\\#@. All of the difficulties\n of using @reallyUnsafePtrEquality\\#@ correctly apply to\n @casArray\\#@ as well.\n ")+ , ("casArray#","Given an array, an offset, the expected old value, and\n the new value, perform an atomic compare and swap (i.e. write the new\n value if the current value and the old value are the same pointer).\n Returns 0 if the swap succeeds and 1 if it fails. Additionally, returns\n the element at the offset after the operation completes. This means that\n on a success the new value is returned, and on a failure the actual old\n value (not the expected one) is returned. Implies a full memory barrier.\n The use of a pointer equality on a boxed value makes this function harder\n to use correctly than 'casIntArray#'. All of the difficulties\n of using 'reallyUnsafePtrEquality#' correctly apply to\n 'casArray#' as well.\n ") , ("newSmallArray#","Create a new mutable array with the specified number of elements,\n in the specified state thread,\n with each element containing the specified initial value.")- , ("shrinkSmallMutableArray#","Shrink mutable array to new specified size, in\n the specified state thread. The new size argument must be less than or\n equal to the current size as reported by @getSizeofSmallMutableArray\\#@.")+ , ("shrinkSmallMutableArray#","Shrink mutable array to new specified size, in\n the specified state thread. The new size argument must be less than or\n equal to the current size as reported by 'getSizeofSmallMutableArray#'.") , ("readSmallArray#","Read from specified index of mutable array. Result is not yet evaluated.") , ("writeSmallArray#","Write to specified index of mutable array.") , ("sizeofSmallArray#","Return the number of elements in the array.")@@ -96,18 +96,18 @@ , ("cloneSmallMutableArray#","Given a source array, an offset into the source array, and a number\n of elements to copy, create a new array with the elements from the\n source array. The provided array must fully contain the specified\n range, but this is not checked.") , ("freezeSmallArray#","Given a source array, an offset into the source array, and a number\n of elements to copy, create a new array with the elements from the\n source array. The provided array must fully contain the specified\n range, but this is not checked.") , ("thawSmallArray#","Given a source array, an offset into the source array, and a number\n of elements to copy, create a new array with the elements from the\n source array. The provided array must fully contain the specified\n range, but this is not checked.")- , ("casSmallArray#","Unsafe, machine-level atomic compare and swap on an element within an array.\n See the documentation of @casArray\\#@.")- , ("ByteArray#","\n A boxed, unlifted datatype representing a region of raw memory in the garbage-collected heap,\n which is not scanned for pointers during garbage collection.\n\n It is created by freezing a 'MutableByteArray#' with 'unsafeFreezeByteArray#'.\n Freezing is essentially a no-op, as MutableByteArray# and ByteArray# share the same heap structure under the hood.\n\n The immutable and mutable variants are commonly used for scenarios requiring high-performance data structures,\n like Text, Primitive Vector, Unboxed Array, and ShortByteString.\n \n Another application of fundamental importance is 'Integer', which is backed by 'ByteArray#'.\n \n The representation on the heap of a Byte Array is:\n \n > +------------+-----------------+-----------------------+\n > | | | |\n > | HEADER | SIZE (in bytes) | PAYLOAD |\n > | | | |\n > +------------+-----------------+-----------------------+\n \n To obtain a pointer to actual payload (e.g., for FFI purposes) use 'byteArrayContents#' or 'mutableByteArrayContents#'.\n \n Alternatively, enabling the UnliftedFFITypes extension\n allows to mention 'ByteArray#' and 'MutableByteArray#' in FFI type signatures directly.\n")- , ("MutableByteArray#"," A mutable ByteAray#. It can be created in three ways:\n\n * 'newByteArray#': Create an unpinned array.\n * 'newPinnedByteArray#': This will create a pinned array,\n * 'newAlignedPinnedByteArray#': This will create a pinned array, with a custom alignment.\n\n Unpinned arrays can be moved around during garbage collection, so you must not store or pass pointers to these values\n if there is a chance for the garbage collector to kick in. That said, even unpinned arrays can be passed to unsafe FFI calls,\n because no garbage collection happens during these unsafe calls\n (see [Guaranteed Call Safety](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/ffi.html#guaranteed-call-safety)\n in the GHC Manual). For safe FFI calls, byte arrays must be not only pinned, but also kept alive by means of the keepAlive# function\n for the duration of a call (that's because garbage collection cannot move a pinned array, but is free to scrap it altogether).\n")+ , ("casSmallArray#","Unsafe, machine-level atomic compare and swap on an element within an array.\n See the documentation of 'casArray#'.")+ , ("ByteArray#","\n A boxed, unlifted datatype representing a region of raw memory in the garbage-collected heap,\n which is not scanned for pointers during garbage collection.\n\n It is created by freezing a 'MutableByteArray#' with 'unsafeFreezeByteArray#'.\n Freezing is essentially a no-op, as 'MutableByteArray#' and 'ByteArray#' share the same heap structure under the hood.\n\n The immutable and mutable variants are commonly used for scenarios requiring high-performance data structures,\n like @Text@, @Primitive Vector@, @Unboxed Array@, and @ShortByteString@.\n\n Another application of fundamental importance is 'Integer', which is backed by 'ByteArray#'.\n\n The representation on the heap of a Byte Array is:\n\n > +------------+-----------------+-----------------------+\n > | | | |\n > | HEADER | SIZE (in bytes) | PAYLOAD |\n > | | | |\n > +------------+-----------------+-----------------------+\n\n To obtain a pointer to actual payload (e.g., for FFI purposes) use 'byteArrayContents#' or 'mutableByteArrayContents#'.\n\n Alternatively, enabling the @UnliftedFFITypes@ extension\n allows to mention 'ByteArray#' and 'MutableByteArray#' in FFI type signatures directly.\n")+ , ("MutableByteArray#"," A mutable 'ByteAray#'. It can be created in three ways:\n\n * 'newByteArray#': Create an unpinned array.\n * 'newPinnedByteArray#': This will create a pinned array,\n * 'newAlignedPinnedByteArray#': This will create a pinned array, with a custom alignment.\n\n Unpinned arrays can be moved around during garbage collection, so you must not store or pass pointers to these values\n if there is a chance for the garbage collector to kick in. That said, even unpinned arrays can be passed to unsafe FFI calls,\n because no garbage collection happens during these unsafe calls\n (see [Guaranteed Call Safety](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/ffi.html#guaranteed-call-safety)\n in the GHC Manual). For safe FFI calls, byte arrays must be not only pinned, but also kept alive by means of the keepAlive# function\n for the duration of a call (that's because garbage collection cannot move a pinned array, but is free to scrap it altogether).\n") , ("newByteArray#","Create a new mutable byte array of specified size (in bytes), in\n the specified state thread. The size of the memory underlying the\n array will be rounded up to the platform's word size.") , ("newPinnedByteArray#","Like 'newByteArray#' but GC guarantees not to move it.") , ("newAlignedPinnedByteArray#","Like 'newPinnedByteArray#' but allow specifying an arbitrary\n alignment, which must be a power of two.")- , ("isMutableByteArrayPinned#","Determine whether a @MutableByteArray\\#@ is guaranteed not to move\n during GC.")- , ("isByteArrayPinned#","Determine whether a @ByteArray\\#@ is guaranteed not to move during GC.")+ , ("isMutableByteArrayPinned#","Determine whether a 'MutableByteArray#' is guaranteed not to move\n during GC.")+ , ("isByteArrayPinned#","Determine whether a 'ByteArray#' is guaranteed not to move during GC.") , ("byteArrayContents#","Intended for use with pinned arrays; otherwise very unsafe!") , ("mutableByteArrayContents#","Intended for use with pinned arrays; otherwise very unsafe!")- , ("shrinkMutableByteArray#","Shrink mutable byte array to new specified size (in bytes), in\n the specified state thread. The new size argument must be less than or\n equal to the current size as reported by @getSizeofMutableByteArray\\#@.")- , ("resizeMutableByteArray#","Resize (unpinned) mutable byte array to new specified size (in bytes).\n The returned @MutableByteArray\\#@ is either the original\n @MutableByteArray\\#@ resized in-place or, if not possible, a newly\n allocated (unpinned) @MutableByteArray\\#@ (with the original content\n copied over).\n\n To avoid undefined behaviour, the original @MutableByteArray\\#@ shall\n not be accessed anymore after a @resizeMutableByteArray\\#@ has been\n performed. Moreover, no reference to the old one should be kept in order\n to allow garbage collection of the original @MutableByteArray\\#@ in\n case a new @MutableByteArray\\#@ had to be allocated.")+ , ("shrinkMutableByteArray#","Shrink mutable byte array to new specified size (in bytes), in\n the specified state thread. The new size argument must be less than or\n equal to the current size as reported by 'getSizeofMutableByteArray#'.")+ , ("resizeMutableByteArray#","Resize (unpinned) mutable byte array to new specified size (in bytes).\n The returned 'MutableByteArray#' is either the original\n 'MutableByteArray#' resized in-place or, if not possible, a newly\n allocated (unpinned) 'MutableByteArray#' (with the original content\n copied over).\n\n To avoid undefined behaviour, the original 'MutableByteArray#' shall\n not be accessed anymore after a 'resizeMutableByteArray#' has been\n performed. Moreover, no reference to the old one should be kept in order\n to allow garbage collection of the original 'MutableByteArray#' in\n case a new 'MutableByteArray#' had to be allocated.") , ("unsafeFreezeByteArray#","Make a mutable byte array immutable, without copying.") , ("sizeofByteArray#","Return the size of the array in bytes.") , ("sizeofMutableByteArray#","Return the size of the array in bytes. Note that this is deprecated as it is\n unsafe in the presence of shrink and resize operations on the same mutable byte\n array.")@@ -119,7 +119,7 @@ , ("indexAddrArray#","Read a machine address; offset in machine words.") , ("indexFloatArray#","Read a single-precision floating-point value; offset in 4-byte words.") , ("indexDoubleArray#","Read a double-precision floating-point value; offset in 8-byte words.")- , ("indexStablePtrArray#","Read a @StablePtr#@ value; offset in machine words.")+ , ("indexStablePtrArray#","Read a 'StablePtr#' value; offset in machine words.") , ("indexInt8Array#","Read a 8-bit signed integer; offset in bytes.") , ("indexInt16Array#","Read a 16-bit signed integer; offset in 2-byte words.") , ("indexInt32Array#","Read a 32-bit signed integer; offset in 4-byte words.")@@ -135,7 +135,7 @@ , ("indexWord8ArrayAsAddr#","Read a machine address; offset in bytes.") , ("indexWord8ArrayAsFloat#","Read a single-precision floating-point value; offset in bytes.") , ("indexWord8ArrayAsDouble#","Read a double-precision floating-point value; offset in bytes.")- , ("indexWord8ArrayAsStablePtr#","Read a @StablePtr#@ value; offset in bytes.")+ , ("indexWord8ArrayAsStablePtr#","Read a 'StablePtr#' value; offset in bytes.") , ("indexWord8ArrayAsInt16#","Read a 16-bit signed integer; offset in bytes.") , ("indexWord8ArrayAsInt32#","Read a 32-bit signed integer; offset in bytes.") , ("indexWord8ArrayAsInt64#","Read a 64-bit signed integer; offset in bytes.")@@ -149,7 +149,7 @@ , ("readAddrArray#","Read a machine address; offset in machine words.") , ("readFloatArray#","Read a single-precision floating-point value; offset in 4-byte words.") , ("readDoubleArray#","Read a double-precision floating-point value; offset in 8-byte words.")- , ("readStablePtrArray#","Read a @StablePtr#@ value; offset in machine words.")+ , ("readStablePtrArray#","Read a 'StablePtr#' value; offset in machine words.") , ("readInt8Array#","Read a 8-bit signed integer; offset in bytes.") , ("readInt16Array#","Read a 16-bit signed integer; offset in 2-byte words.") , ("readInt32Array#","Read a 32-bit signed integer; offset in 4-byte words.")@@ -165,7 +165,7 @@ , ("readWord8ArrayAsAddr#","Read a machine address; offset in bytes.") , ("readWord8ArrayAsFloat#","Read a single-precision floating-point value; offset in bytes.") , ("readWord8ArrayAsDouble#","Read a double-precision floating-point value; offset in bytes.")- , ("readWord8ArrayAsStablePtr#","Read a @StablePtr#@ value; offset in bytes.")+ , ("readWord8ArrayAsStablePtr#","Read a 'StablePtr#' value; offset in bytes.") , ("readWord8ArrayAsInt16#","Read a 16-bit signed integer; offset in bytes.") , ("readWord8ArrayAsInt32#","Read a 32-bit signed integer; offset in bytes.") , ("readWord8ArrayAsInt64#","Read a 64-bit signed integer; offset in bytes.")@@ -179,7 +179,7 @@ , ("writeAddrArray#","Write a machine address; offset in machine words.") , ("writeFloatArray#","Write a single-precision floating-point value; offset in 4-byte words.") , ("writeDoubleArray#","Write a double-precision floating-point value; offset in 8-byte words.")- , ("writeStablePtrArray#","Write a @StablePtr#@ value; offset in machine words.")+ , ("writeStablePtrArray#","Write a 'StablePtr#' value; offset in machine words.") , ("writeInt8Array#","Write a 8-bit signed integer; offset in bytes.") , ("writeInt16Array#","Write a 16-bit signed integer; offset in 2-byte words.") , ("writeInt32Array#","Write a 32-bit signed integer; offset in 4-byte words.")@@ -195,20 +195,20 @@ , ("writeWord8ArrayAsAddr#","Write a machine address; offset in bytes.") , ("writeWord8ArrayAsFloat#","Write a single-precision floating-point value; offset in bytes.") , ("writeWord8ArrayAsDouble#","Write a double-precision floating-point value; offset in bytes.")- , ("writeWord8ArrayAsStablePtr#","Write a @StablePtr#@ value; offset in bytes.")+ , ("writeWord8ArrayAsStablePtr#","Write a 'StablePtr#' value; offset in bytes.") , ("writeWord8ArrayAsInt16#","Write a 16-bit signed integer; offset in bytes.") , ("writeWord8ArrayAsInt32#","Write a 32-bit signed integer; offset in bytes.") , ("writeWord8ArrayAsInt64#","Write a 64-bit signed integer; offset in bytes.") , ("writeWord8ArrayAsWord16#","Write a 16-bit unsigned integer; offset in bytes.") , ("writeWord8ArrayAsWord32#","Write a 32-bit unsigned integer; offset in bytes.") , ("writeWord8ArrayAsWord64#","Write a 64-bit unsigned integer; offset in bytes.")- , ("compareByteArrays#","@compareByteArrays# src1 src1_ofs src2 src2_ofs n@ compares\n @n@ bytes starting at offset @src1_ofs@ in the first\n @ByteArray#@ @src1@ to the range of @n@ bytes\n (i.e. same length) starting at offset @src2_ofs@ of the second\n @ByteArray#@ @src2@. Both arrays must fully contain the\n specified ranges, but this is not checked. Returns an @Int#@\n less than, equal to, or greater than zero if the range is found,\n respectively, to be byte-wise lexicographically less than, to\n match, or be greater than the second range.")- , ("copyByteArray#","@copyByteArray# src src_ofs dst dst_ofs n@ copies the range\n starting at offset @src_ofs@ of length @n@ from the\n @ByteArray#@ @src@ to the @MutableByteArray#@ @dst@\n starting at offset @dst_ofs@. Both arrays must fully contain\n the specified ranges, but this is not checked. The two arrays must\n not be the same array in different states, but this is not checked\n either.")+ , ("compareByteArrays#","@'compareByteArrays#' src1 src1_ofs src2 src2_ofs n@ compares\n @n@ bytes starting at offset @src1_ofs@ in the first\n 'ByteArray#' @src1@ to the range of @n@ bytes\n (i.e. same length) starting at offset @src2_ofs@ of the second\n 'ByteArray#' @src2@. Both arrays must fully contain the\n specified ranges, but this is not checked. Returns an 'Int#'\n less than, equal to, or greater than zero if the range is found,\n respectively, to be byte-wise lexicographically less than, to\n match, or be greater than the second range.")+ , ("copyByteArray#","@'copyByteArray#' src src_ofs dst dst_ofs n@ copies the range\n starting at offset @src_ofs@ of length @n@ from the\n 'ByteArray#' @src@ to the 'MutableByteArray#' @dst@\n starting at offset @dst_ofs@. Both arrays must fully contain\n the specified ranges, but this is not checked. The two arrays must\n not be the same array in different states, but this is not checked\n either.") , ("copyMutableByteArray#","Copy a range of the first MutableByteArray\\# to the specified region in the second MutableByteArray\\#.\n Both arrays must fully contain the specified ranges, but this is not checked. The regions are\n allowed to overlap, although this is only possible when the same array is provided\n as both the source and the destination.") , ("copyByteArrayToAddr#","Copy a range of the ByteArray\\# to the memory range starting at the Addr\\#.\n The ByteArray\\# and the memory region at Addr\\# must fully contain the\n specified ranges, but this is not checked. The Addr\\# must not point into the\n ByteArray\\# (e.g. if the ByteArray\\# were pinned), but this is not checked\n either.") , ("copyMutableByteArrayToAddr#","Copy a range of the MutableByteArray\\# to the memory range starting at the\n Addr\\#. The MutableByteArray\\# and the memory region at Addr\\# must fully\n contain the specified ranges, but this is not checked. The Addr\\# must not\n point into the MutableByteArray\\# (e.g. if the MutableByteArray\\# were\n pinned), but this is not checked either.") , ("copyAddrToByteArray#","Copy a memory range starting at the Addr\\# to the specified range in the\n MutableByteArray\\#. The memory region at Addr\\# and the ByteArray\\# must fully\n contain the specified ranges, but this is not checked. The Addr\\# must not\n point into the MutableByteArray\\# (e.g. if the MutableByteArray\\# were pinned),\n but this is not checked either.")- , ("setByteArray#","@setByteArray# ba off len c@ sets the byte range @[off, off+len)@ of\n the @MutableByteArray#@ to the byte @c@.")+ , ("setByteArray#","@'setByteArray#' ba off len c@ sets the byte range @[off, off+len)@ of\n the 'MutableByteArray#' to the byte @c@.") , ("atomicReadIntArray#","Given an array and an offset in machine words, read an element. The\n index is assumed to be in bounds. Implies a full memory barrier.") , ("atomicWriteIntArray#","Given an array and an offset in machine words, write an element. The\n index is assumed to be in bounds. Implies a full memory barrier.") , ("casIntArray#","Given an array, an offset in machine words, the expected old value, and\n the new value, perform an atomic compare and swap i.e. write the new\n value if the current value matches the provided old value. Returns\n the value of the element before the operation. Implies a full memory\n barrier.")@@ -224,8 +224,8 @@ , ("fetchXorIntArray#","Given an array, and offset in machine words, and a value to XOR,\n atomically XOR the value into the element. Returns the value of the\n element before the operation. Implies a full memory barrier.") , ("Addr#"," An arbitrary machine address assumed to point outside\n the garbage-collected heap. ") , ("nullAddr#"," The null address. ")- , ("minusAddr#","Result is meaningless if two @Addr\\#@s are so far apart that their\n difference doesn't fit in an @Int\\#@.")- , ("remAddr#","Return the remainder when the @Addr\\#@ arg, treated like an @Int\\#@,\n is divided by the @Int\\#@ arg.")+ , ("minusAddr#","Result is meaningless if two 'Addr#'s are so far apart that their\n difference doesn't fit in an 'Int#'.")+ , ("remAddr#","Return the remainder when the 'Addr#' arg, treated like an 'Int#',\n is divided by the 'Int#' arg.") , ("addr2Int#","Coerce directly from address to int.") , ("int2Addr#","Coerce directly from int to address.") , ("indexCharOffAddr#","Reads 8-bit character; offset in bytes.")@@ -248,76 +248,89 @@ , ("fetchXorWordAddr#","Given an address, and a value to XOR,\n atomically XOR the value into the element. Returns the value of the\n element before the operation. Implies a full memory barrier.") , ("atomicReadWordAddr#","Given an address, read a machine word. Implies a full memory barrier.") , ("atomicWriteWordAddr#","Given an address, write a machine word. Implies a full memory barrier.")- , ("MutVar#","A @MutVar\\#@ behaves like a single-element mutable array.")- , ("newMutVar#","Create @MutVar\\#@ with specified initial value in specified state thread.")- , ("readMutVar#","Read contents of @MutVar\\#@. Result is not yet evaluated.")- , ("writeMutVar#","Write contents of @MutVar\\#@.")- , ("atomicModifyMutVar2#"," Modify the contents of a @MutVar\\#@, returning the previous\n contents and the result of applying the given function to the\n previous contents. Note that this isn't strictly\n speaking the correct type for this function; it should really be\n @MutVar\\# s a -> (a -> (a,b)) -> State\\# s -> (\\# State\\# s, a, (a, b) \\#)@,\n but we don't know about pairs here. ")- , ("atomicModifyMutVar_#"," Modify the contents of a @MutVar\\#@, returning the previous\n contents and the result of applying the given function to the\n previous contents. ")- , ("casMutVar#"," Compare-and-swap: perform a pointer equality test between\n the first value passed to this function and the value\n stored inside the @MutVar\\#@. If the pointers are equal,\n replace the stored value with the second value passed to this\n function, otherwise do nothing.\n Returns the final value stored inside the @MutVar\\#@.\n The @Int\\#@ indicates whether a swap took place,\n with @1\\#@ meaning that we didn't swap, and @0\\#@\n that we did.\n Implies a full memory barrier.\n Because the comparison is done on the level of pointers,\n all of the difficulties of using\n @reallyUnsafePtrEquality\\#@ correctly apply to\n @casMutVar\\#@ as well.\n ")- , ("newTVar#","Create a new @TVar\\#@ holding a specified initial value.")- , ("readTVar#","Read contents of @TVar\\#@ inside an STM transaction,\n i.e. within a call to @atomically\\#@.\n Does not force evaluation of the result.")- , ("readTVarIO#","Read contents of @TVar\\#@ outside an STM transaction.\n Does not force evaluation of the result.")- , ("writeTVar#","Write contents of @TVar\\#@.")- , ("MVar#"," A shared mutable variable (/not/ the same as a @MutVar\\#@!).\n (Note: in a non-concurrent implementation, @(MVar\\# a)@ can be\n represented by @(MutVar\\# (Maybe a))@.) ")- , ("newMVar#","Create new @MVar\\#@; initially empty.")- , ("takeMVar#","If @MVar\\#@ is empty, block until it becomes full.\n Then remove and return its contents, and set it empty.")- , ("tryTakeMVar#","If @MVar\\#@ is empty, immediately return with integer 0 and value undefined.\n Otherwise, return with integer 1 and contents of @MVar\\#@, and set @MVar\\#@ empty.")- , ("putMVar#","If @MVar\\#@ is full, block until it becomes empty.\n Then store value arg as its new contents.")- , ("tryPutMVar#","If @MVar\\#@ is full, immediately return with integer 0.\n Otherwise, store value arg as @MVar\\#@'s new contents, and return with integer 1.")- , ("readMVar#","If @MVar\\#@ is empty, block until it becomes full.\n Then read its contents without modifying the MVar, without possibility\n of intervention from other threads.")- , ("tryReadMVar#","If @MVar\\#@ is empty, immediately return with integer 0 and value undefined.\n Otherwise, return with integer 1 and contents of @MVar\\#@.")- , ("isEmptyMVar#","Return 1 if @MVar\\#@ is empty; 0 otherwise.")- , ("IOPort#"," A shared I/O port is almost the same as a @MVar\\#@!).\n The main difference is that IOPort has no deadlock detection or\n deadlock breaking code that forcibly releases the lock. ")- , ("newIOPort#","Create new @IOPort\\#@; initially empty.")- , ("readIOPort#","If @IOPort\\#@ is empty, block until it becomes full.\n Then remove and return its contents, and set it empty.\n Throws an @IOPortException@ if another thread is already\n waiting to read this @IOPort\\#@.")- , ("writeIOPort#","If @IOPort\\#@ is full, immediately return with integer 0,\n throwing an @IOPortException@.\n Otherwise, store value arg as @IOPort\\#@'s new contents,\n and return with integer 1. ")+ , ("MutVar#","A 'MutVar#' behaves like a single-element mutable array.")+ , ("newMutVar#","Create 'MutVar#' with specified initial value in specified state thread.")+ , ("readMutVar#","Read contents of 'MutVar#'. Result is not yet evaluated.")+ , ("writeMutVar#","Write contents of 'MutVar#'.")+ , ("atomicModifyMutVar2#"," Modify the contents of a 'MutVar#', returning the previous\n contents and the result of applying the given function to the\n previous contents. Note that this isn't strictly\n speaking the correct type for this function; it should really be\n @'MutVar#' s a -> (a -> (a,b)) -> 'State#' s -> (# 'State#' s, a, (a, b) #)@,\n but we don't know about pairs here. ")+ , ("atomicModifyMutVar_#"," Modify the contents of a 'MutVar#', returning the previous\n contents and the result of applying the given function to the\n previous contents. ")+ , ("casMutVar#"," Compare-and-swap: perform a pointer equality test between\n the first value passed to this function and the value\n stored inside the 'MutVar#'. If the pointers are equal,\n replace the stored value with the second value passed to this\n function, otherwise do nothing.\n Returns the final value stored inside the 'MutVar#'.\n The 'Int#' indicates whether a swap took place,\n with @1#@ meaning that we didn't swap, and @0#@\n that we did.\n Implies a full memory barrier.\n Because the comparison is done on the level of pointers,\n all of the difficulties of using\n 'reallyUnsafePtrEquality#' correctly apply to\n 'casMutVar#' as well.\n ")+ , ("catch#"," @'catch#' k handler s@ evaluates @k s@, invoking @handler@ on any exceptions\n thrown.\n\n Note that the result type here isn't quite as unrestricted as the\n polymorphic type might suggest; see the section \\\"RuntimeRep polymorphism\n in continuation-style primops\\\" for details. ")+ , ("maskAsyncExceptions#"," @'maskAsyncExceptions#' k s@ evaluates @k s@ such that asynchronous\n exceptions are deferred until after evaluation has finished.\n\n Note that the result type here isn't quite as unrestricted as the\n polymorphic type might suggest; see the section \\\"RuntimeRep polymorphism\n in continuation-style primops\\\" for details. ")+ , ("maskUninterruptible#"," @'maskUninterruptible#' k s@ evaluates @k s@ such that asynchronous\n exceptions are deferred until after evaluation has finished.\n\n Note that the result type here isn't quite as unrestricted as the\n polymorphic type might suggest; see the section \\\"RuntimeRep polymorphism\n in continuation-style primops\\\" for details. ")+ , ("unmaskAsyncExceptions#"," @'unmaskAsyncUninterruptible#' k s@ evaluates @k s@ such that asynchronous\n exceptions are unmasked.\n\n Note that the result type here isn't quite as unrestricted as the\n polymorphic type might suggest; see the section \\\"RuntimeRep polymorphism\n in continuation-style primops\\\" for details. ")+ , ("PromptTag#"," See \"GHC.Prim#continuations\". ")+ , ("newPromptTag#"," See \"GHC.Prim#continuations\". ")+ , ("prompt#"," See \"GHC.Prim#continuations\". ")+ , ("control0#"," See \"GHC.Prim#continuations\". ")+ , ("newTVar#","Create a new 'TVar#' holding a specified initial value.")+ , ("readTVar#","Read contents of 'TVar#' inside an STM transaction,\n i.e. within a call to 'atomically#'.\n Does not force evaluation of the result.")+ , ("readTVarIO#","Read contents of 'TVar#' outside an STM transaction.\n Does not force evaluation of the result.")+ , ("writeTVar#","Write contents of 'TVar#'.")+ , ("MVar#"," A shared mutable variable (/not/ the same as a 'MutVar#'!).\n (Note: in a non-concurrent implementation, @('MVar#' a)@ can be\n represented by @('MutVar#' (Maybe a))@.) ")+ , ("newMVar#","Create new 'MVar#'; initially empty.")+ , ("takeMVar#","If 'MVar#' is empty, block until it becomes full.\n Then remove and return its contents, and set it empty.")+ , ("tryTakeMVar#","If 'MVar#' is empty, immediately return with integer 0 and value undefined.\n Otherwise, return with integer 1 and contents of 'MVar#', and set 'MVar#' empty.")+ , ("putMVar#","If 'MVar#' is full, block until it becomes empty.\n Then store value arg as its new contents.")+ , ("tryPutMVar#","If 'MVar#' is full, immediately return with integer 0.\n Otherwise, store value arg as 'MVar#''s new contents, and return with integer 1.")+ , ("readMVar#","If 'MVar#' is empty, block until it becomes full.\n Then read its contents without modifying the MVar, without possibility\n of intervention from other threads.")+ , ("tryReadMVar#","If 'MVar#' is empty, immediately return with integer 0 and value undefined.\n Otherwise, return with integer 1 and contents of 'MVar#'.")+ , ("isEmptyMVar#","Return 1 if 'MVar#' is empty; 0 otherwise.")+ , ("IOPort#"," A shared I/O port is almost the same as an 'MVar#'.\n The main difference is that IOPort has no deadlock detection or\n deadlock breaking code that forcibly releases the lock. ")+ , ("newIOPort#","Create new 'IOPort#'; initially empty.")+ , ("readIOPort#","If 'IOPort#' is empty, block until it becomes full.\n Then remove and return its contents, and set it empty.\n Throws an 'IOPortException' if another thread is already\n waiting to read this 'IOPort#'.")+ , ("writeIOPort#","If 'IOPort#' is full, immediately return with integer 0,\n throwing an 'IOPortException'.\n Otherwise, store value arg as 'IOPort#''s new contents,\n and return with integer 1. ") , ("delay#","Sleep specified number of microseconds.") , ("waitRead#","Block until input is available on specified file descriptor.") , ("waitWrite#","Block until output is possible on specified file descriptor.")- , ("State#"," @State\\#@ is the primitive, unlifted type of states. It has\n one type parameter, thus @State\\# RealWorld@, or @State\\# s@,\n where s is a type variable. The only purpose of the type parameter\n is to keep different state threads separate. It is represented by\n nothing at all. ")- , ("RealWorld"," @RealWorld@ is deeply magical. It is /primitive/, but it is not\n /unlifted/ (hence @ptrArg@). We never manipulate values of type\n @RealWorld@; it's only used in the type system, to parameterise @State\\#@. ")- , ("ThreadId#","(In a non-concurrent implementation, this can be a singleton\n type, whose (unique) value is returned by @myThreadId\\#@. The\n other operations can be omitted.)")- , ("mkWeak#"," @mkWeak# k v finalizer s@ creates a weak reference to value @k@,\n with an associated reference to some value @v@. If @k@ is still\n alive then @v@ can be retrieved using @deRefWeak#@. Note that\n the type of @k@ must be represented by a pointer (i.e. of kind @TYPE 'LiftedRep@ or @TYPE 'UnliftedRep@). ")- , ("addCFinalizerToWeak#"," @addCFinalizerToWeak# fptr ptr flag eptr w@ attaches a C\n function pointer @fptr@ to a weak pointer @w@ as a finalizer. If\n @flag@ is zero, @fptr@ will be called with one argument,\n @ptr@. Otherwise, it will be called with two arguments,\n @eptr@ and @ptr@. @addCFinalizerToWeak#@ returns\n 1 on success, or 0 if @w@ is already dead. ")- , ("finalizeWeak#"," Finalize a weak pointer. The return value is an unboxed tuple\n containing the new state of the world and an \"unboxed Maybe\",\n represented by an @Int#@ and a (possibly invalid) finalization\n action. An @Int#@ of @1@ indicates that the finalizer is valid. The\n return value @b@ from the finalizer should be ignored. ")+ , ("State#"," 'State#' is the primitive, unlifted type of states. It has\n one type parameter, thus @'State#' 'RealWorld'@, or @'State#' s@,\n where s is a type variable. The only purpose of the type parameter\n is to keep different state threads separate. It is represented by\n nothing at all. ")+ , ("RealWorld"," 'RealWorld' is deeply magical. It is /primitive/, but it is not\n /unlifted/ (hence @ptrArg@). We never manipulate values of type\n 'RealWorld'; it's only used in the type system, to parameterise 'State#'. ")+ , ("ThreadId#","(In a non-concurrent implementation, this can be a singleton\n type, whose (unique) value is returned by 'myThreadId#'. The\n other operations can be omitted.)")+ , ("labelThread#","Set the label of the given thread. The @ByteArray#@ should contain\n a UTF-8-encoded string.")+ , ("threadLabel#","Get the label of the given thread.\n Morally of type @ThreadId# -> IO (Maybe ByteArray#)@, with a @1#@ tag\n denoting @Just@.\n\n @since 0.10")+ , ("threadStatus#","Get the status of the given thread. Result is\n @(ThreadStatus, Capability, Locked)@ where\n @ThreadStatus@ is one of the status constants defined in\n @rts/Constants.h@, @Capability@ is the number of\n the capability which currently owns the thread, and\n @Locked@ is a boolean indicating whether the\n thread is bound to that capability.\n\n @since 0.9")+ , ("listThreads#"," Returns an array of the threads started by the program. Note that this\n threads which have finished execution may or may not be present in this\n list, depending upon whether they have been collected by the garbage collector.\n\n @since 0.10")+ , ("mkWeak#"," @'mkWeak#' k v finalizer s@ creates a weak reference to value @k@,\n with an associated reference to some value @v@. If @k@ is still\n alive then @v@ can be retrieved using 'deRefWeak#'. Note that\n the type of @k@ must be represented by a pointer (i.e. of kind\n @'TYPE' ''LiftedRep' or @'TYPE' ''UnliftedRep'@). ")+ , ("addCFinalizerToWeak#"," @'addCFinalizerToWeak#' fptr ptr flag eptr w@ attaches a C\n function pointer @fptr@ to a weak pointer @w@ as a finalizer. If\n @flag@ is zero, @fptr@ will be called with one argument,\n @ptr@. Otherwise, it will be called with two arguments,\n @eptr@ and @ptr@. 'addCFinalizerToWeak#' returns\n 1 on success, or 0 if @w@ is already dead. ")+ , ("finalizeWeak#"," Finalize a weak pointer. The return value is an unboxed tuple\n containing the new state of the world and an \"unboxed Maybe\",\n represented by an 'Int#' and a (possibly invalid) finalization\n action. An 'Int#' of @1@ indicates that the finalizer is valid. The\n return value @b@ from the finalizer should be ignored. ") , ("compactNew#"," Create a new CNF with a single compact block. The argument is\n the capacity of the compact block (in bytes, not words).\n The capacity is rounded up to a multiple of the allocator block size\n and is capped to one mega block. ") , ("compactResize#"," Set the new allocation size of the CNF. This value (in bytes)\n determines the capacity of each compact block in the CNF. It\n does not retroactively affect existing compact blocks in the CNF. ") , ("compactContains#"," Returns 1\\# if the object is contained in the CNF, 0\\# otherwise. ") , ("compactContainsAny#"," Returns 1\\# if the object is in any CNF at all, 0\\# otherwise. ") , ("compactGetFirstBlock#"," Returns the address and the utilized size (in bytes) of the\n first compact block of a CNF.")- , ("compactGetNextBlock#"," Given a CNF and the address of one its compact blocks, returns the\n next compact block and its utilized size, or @nullAddr\\#@ if the\n argument was the last compact block in the CNF. ")- , ("compactAllocateBlock#"," Attempt to allocate a compact block with the capacity (in\n bytes) given by the first argument. The @Addr\\#@ is a pointer\n to previous compact block of the CNF or @nullAddr\\#@ to create a\n new CNF with a single compact block.\n\n The resulting block is not known to the GC until\n @compactFixupPointers\\#@ is called on it, and care must be taken\n so that the address does not escape or memory will be leaked.\n ")+ , ("compactGetNextBlock#"," Given a CNF and the address of one its compact blocks, returns the\n next compact block and its utilized size, or 'nullAddr#' if the\n argument was the last compact block in the CNF. ")+ , ("compactAllocateBlock#"," Attempt to allocate a compact block with the capacity (in\n bytes) given by the first argument. The 'Addr#' is a pointer\n to previous compact block of the CNF or 'nullAddr#' to create a\n new CNF with a single compact block.\n\n The resulting block is not known to the GC until\n 'compactFixupPointers#' is called on it, and care must be taken\n so that the address does not escape or memory will be leaked.\n ") , ("compactFixupPointers#"," Given the pointer to the first block of a CNF and the\n address of the root object in the old address space, fix up\n the internal pointers inside the CNF to account for\n a different position in memory than when it was serialized.\n This method must be called exactly once after importing\n a serialized CNF. It returns the new CNF and the new adjusted\n root address. ")- , ("compactAdd#"," Recursively add a closure and its transitive closure to a\n @Compact\\#@ (a CNF), evaluating any unevaluated components\n at the same time. Note: @compactAdd\\#@ is not thread-safe, so\n only one thread may call @compactAdd\\#@ with a particular\n @Compact\\#@ at any given time. The primop does not\n enforce any mutual exclusion; the caller is expected to\n arrange this. ")- , ("compactAddWithSharing#"," Like @compactAdd\\#@, but retains sharing and cycles\n during compaction. ")+ , ("compactAdd#"," Recursively add a closure and its transitive closure to a\n 'Compact#' (a CNF), evaluating any unevaluated components\n at the same time. Note: 'compactAdd#' is not thread-safe, so\n only one thread may call 'compactAdd#' with a particular\n 'Compact#' at any given time. The primop does not\n enforce any mutual exclusion; the caller is expected to\n arrange this. ")+ , ("compactAddWithSharing#"," Like 'compactAdd#', but retains sharing and cycles\n during compaction. ") , ("compactSize#"," Return the total capacity (in bytes) of all the compact blocks\n in the CNF. ")- , ("reallyUnsafePtrEquality#"," Returns @1\\#@ if the given pointers are equal and @0\\#@ otherwise. ")+ , ("reallyUnsafePtrEquality#"," Returns @1#@ if the given pointers are equal and @0#@ otherwise. ") , ("numSparks#"," Returns the number of sparks in the local spark pool. ")- , ("keepAlive#"," \\tt{keepAlive# x s k} keeps the value \\tt{x} alive during the execution\n of the computation \\tt{k}.\n\n Note that the result type here isn't quite as unrestricted as the\n polymorphic type might suggest; ticket \\#21868 for details. ")+ , ("keepAlive#"," @'keepAlive#' x s k@ keeps the value @x@ alive during the execution\n of the computation @k@.\n\n Note that the result type here isn't quite as unrestricted as the\n polymorphic type might suggest; see the section \\\"RuntimeRep polymorphism\n in continuation-style primops\\\" for details. ")+ , ("dataToTag#"," Evaluates the argument and returns the tag of the result.\n Tags are Zero-indexed; the first constructor has tag zero. ") , ("BCO"," Primitive bytecode type. ")- , ("addrToAny#"," Convert an @Addr\\#@ to a followable Any type. ")- , ("anyToAddr#"," Retrieve the address of any Haskell value. This is\n essentially an @unsafeCoerce\\#@, but if implemented as such\n the core lint pass complains and fails to compile.\n As a primop, it is opaque to core/stg, and only appears\n in cmm (where the copy propagation pass will get rid of it).\n Note that \"a\" must be a value, not a thunk! It's too late\n for strictness analysis to enforce this, so you're on your\n own to guarantee this. Also note that @Addr\\#@ is not a GC\n pointer - up to you to guarantee that it does not become\n a dangling pointer immediately after you get it.")+ , ("addrToAny#"," Convert an 'Addr#' to a followable Any type. ")+ , ("anyToAddr#"," Retrieve the address of any Haskell value. This is\n essentially an 'unsafeCoerce#', but if implemented as such\n the core lint pass complains and fails to compile.\n As a primop, it is opaque to core/stg, and only appears\n in cmm (where the copy propagation pass will get rid of it).\n Note that \"a\" must be a value, not a thunk! It's too late\n for strictness analysis to enforce this, so you're on your\n own to guarantee this. Also note that 'Addr#' is not a GC\n pointer - up to you to guarantee that it does not become\n a dangling pointer immediately after you get it.") , ("mkApUpd0#"," Wrap a BCO in a @AP_UPD@ thunk which will be updated with the value of\n the BCO when evaluated. ")- , ("newBCO#"," @newBCO\\# instrs lits ptrs arity bitmap@ creates a new bytecode object. The\n resulting object encodes a function of the given arity with the instructions\n encoded in @instrs@, and a static reference table usage bitmap given by\n @bitmap@. ")- , ("unpackClosure#"," @unpackClosure\\# closure@ copies the closure and pointers in the\n payload of the given closure into two new arrays, and returns a pointer to\n the first word of the closure's info table, a non-pointer array for the raw\n bytes of the closure, and a pointer array for the pointers in the payload. ")- , ("closureSize#"," @closureSize\\# closure@ returns the size of the given closure in\n machine words. ")- , ("getCurrentCCS#"," Returns the current @CostCentreStack@ (value is @NULL@ if\n not profiling). Takes a dummy argument which can be used to\n avoid the call to @getCurrentCCS\\#@ being floated out by the\n simplifier, which would result in an uninformative stack\n (\"CAF\"). ")+ , ("newBCO#"," @'newBCO#' instrs lits ptrs arity bitmap@ creates a new bytecode object. The\n resulting object encodes a function of the given arity with the instructions\n encoded in @instrs@, and a static reference table usage bitmap given by\n @bitmap@. ")+ , ("unpackClosure#"," @'unpackClosure#' closure@ copies the closure and pointers in the\n payload of the given closure into two new arrays, and returns a pointer to\n the first word of the closure's info table, a non-pointer array for the raw\n bytes of the closure, and a pointer array for the pointers in the payload. ")+ , ("closureSize#"," @'closureSize#' closure@ returns the size of the given closure in\n machine words. ")+ , ("getCurrentCCS#"," Returns the current 'CostCentreStack' (value is @NULL@ if\n not profiling). Takes a dummy argument which can be used to\n avoid the call to 'getCurrentCCS#' being floated out by the\n simplifier, which would result in an uninformative stack\n (\"CAF\"). ") , ("clearCCS#"," Run the supplied IO action with an empty CCS. For example, this\n is used by the interpreter to run an interpreted computation\n without the call stack showing that it was invoked from GHC. ") , ("whereFrom#"," Returns the @InfoProvEnt @ for the info table of the given object\n (value is @NULL@ if the table does not exist or there is no information\n about the closure).")- , ("FUN","The builtin function type, written in infix form as @a # m -> b@.\n Values of this type are functions taking inputs of type @a@ and\n producing outputs of type @b@. The multiplicity of the input is\n @m@.\n\n Note that @FUN m a b@ permits representation polymorphism in both\n @a@ and @b@, so that types like @Int\\# -> Int\\#@ can still be\n well-kinded.\n ")- , ("realWorld#"," The token used in the implementation of the IO monad as a state monad.\n It does not pass any information at runtime.\n See also @GHC.Magic.runRW\\#@. ")- , ("void#"," This is an alias for the unboxed unit tuple constructor.\n In earlier versions of GHC, @void\\#@ was a value\n of the primitive type @Void\\#@, which is now defined to be @(\\# \\#)@.\n ")- , ("Proxy#"," The type constructor @Proxy#@ is used to bear witness to some\n type variable. It's used when you want to pass around proxy values\n for doing things like modelling type applications. A @Proxy#@\n is not only unboxed, it also has a polymorphic kind, and has no\n runtime representation, being totally free. ")- , ("proxy#"," Witness for an unboxed @Proxy#@ value, which has no runtime\n representation. ")- , ("seq"," The value of @seq a b@ is bottom if @a@ is bottom, and\n otherwise equal to @b@. In other words, it evaluates the first\n argument @a@ to weak head normal form (WHNF). @seq@ is usually\n introduced to improve performance by avoiding unneeded laziness.\n\n A note on evaluation order: the expression @seq a b@ does\n /not/ guarantee that @a@ will be evaluated before @b@.\n The only guarantee given by @seq@ is that the both @a@\n and @b@ will be evaluated before @seq@ returns a value.\n In particular, this means that @b@ may be evaluated before\n @a@. If you need to guarantee a specific order of evaluation,\n you must use the function @pseq@ from the \"parallel\" package. ")- , ("unsafeCoerce#"," The function @unsafeCoerce\\#@ allows you to side-step the typechecker entirely. That\n is, it allows you to coerce any type into any other type. If you use this function,\n you had better get it right, otherwise segmentation faults await. It is generally\n used when you want to write a program that you know is well-typed, but where Haskell's\n type system is not expressive enough to prove that it is well typed.\n\n The following uses of @unsafeCoerce\\#@ are supposed to work (i.e. not lead to\n spurious compile-time or run-time crashes):\n\n * Casting any lifted type to @Any@\n\n * Casting @Any@ back to the real type\n\n * Casting an unboxed type to another unboxed type of the same size.\n (Casting between floating-point and integral types does not work.\n See the @GHC.Float@ module for functions to do work.)\n\n * Casting between two types that have the same runtime representation. One case is when\n the two types differ only in \"phantom\" type parameters, for example\n @Ptr Int@ to @Ptr Float@, or @[Int]@ to @[Float]@ when the list is\n known to be empty. Also, a @newtype@ of a type @T@ has the same representation\n at runtime as @T@.\n\n Other uses of @unsafeCoerce\\#@ are undefined. In particular, you should not use\n @unsafeCoerce\\#@ to cast a T to an algebraic data type D, unless T is also\n an algebraic data type. For example, do not cast @Int->Int@ to @Bool@, even if\n you later cast that @Bool@ back to @Int->Int@ before applying it. The reasons\n have to do with GHC's internal representation details (for the cognoscenti, data values\n can be entered but function closures cannot). If you want a safe type to cast things\n to, use @Any@, which is not an algebraic data type.\n\n ")+ , ("FUN","The builtin function type, written in infix form as @a % m -> b@.\n Values of this type are functions taking inputs of type @a@ and\n producing outputs of type @b@. The multiplicity of the input is\n @m@.\n\n Note that @'FUN' m a b@ permits representation polymorphism in both\n @a@ and @b@, so that types like @'Int#' -> 'Int#'@ can still be\n well-kinded.\n ")+ , ("realWorld#"," The token used in the implementation of the IO monad as a state monad.\n It does not pass any information at runtime.\n See also 'GHC.Magic.runRW#'. ")+ , ("void#"," This is an alias for the unboxed unit tuple constructor.\n In earlier versions of GHC, 'void#' was a value\n of the primitive type 'Void#', which is now defined to be @(# #)@.\n ")+ , ("Proxy#"," The type constructor 'Proxy#' is used to bear witness to some\n type variable. It's used when you want to pass around proxy values\n for doing things like modelling type applications. A 'Proxy#'\n is not only unboxed, it also has a polymorphic kind, and has no\n runtime representation, being totally free. ")+ , ("proxy#"," Witness for an unboxed 'Proxy#' value, which has no runtime\n representation. ")+ , ("seq"," The value of @'seq' a b@ is bottom if @a@ is bottom, and\n otherwise equal to @b@. In other words, it evaluates the first\n argument @a@ to weak head normal form (WHNF). 'seq' is usually\n introduced to improve performance by avoiding unneeded laziness.\n\n A note on evaluation order: the expression @'seq' a b@ does\n /not/ guarantee that @a@ will be evaluated before @b@.\n The only guarantee given by 'seq' is that the both @a@\n and @b@ will be evaluated before 'seq' returns a value.\n In particular, this means that @b@ may be evaluated before\n @a@. If you need to guarantee a specific order of evaluation,\n you must use the function 'pseq' from the \"parallel\" package. ")+ , ("unsafeCoerce#"," The function 'unsafeCoerce#' allows you to side-step the typechecker entirely. That\n is, it allows you to coerce any type into any other type. If you use this function,\n you had better get it right, otherwise segmentation faults await. It is generally\n used when you want to write a program that you know is well-typed, but where Haskell's\n type system is not expressive enough to prove that it is well typed.\n\n The following uses of 'unsafeCoerce#' are supposed to work (i.e. not lead to\n spurious compile-time or run-time crashes):\n\n * Casting any lifted type to 'Any'\n\n * Casting 'Any' back to the real type\n\n * Casting an unboxed type to another unboxed type of the same size.\n (Casting between floating-point and integral types does not work.\n See the \"GHC.Float\" module for functions to do work.)\n\n * Casting between two types that have the same runtime representation. One case is when\n the two types differ only in \"phantom\" type parameters, for example\n @'Ptr' 'Int'@ to @'Ptr' 'Float'@, or @['Int']@ to @['Float']@ when the list is\n known to be empty. Also, a @newtype@ of a type @T@ has the same representation\n at runtime as @T@.\n\n Other uses of 'unsafeCoerce#' are undefined. In particular, you should not use\n 'unsafeCoerce#' to cast a T to an algebraic data type D, unless T is also\n an algebraic data type. For example, do not cast @'Int'->'Int'@ to 'Bool', even if\n you later cast that 'Bool' back to @'Int'->'Int'@ before applying it. The reasons\n have to do with GHC's internal representation details (for the cognoscenti, data values\n can be entered but function closures cannot). If you want a safe type to cast things\n to, use 'Any', which is not an algebraic data type.\n\n ") , ("traceEvent#"," Emits an event via the RTS tracing framework. The contents\n of the event is the zero-terminated byte string passed as the first\n argument. The event will be emitted either to the @.eventlog@ file,\n or to stderr, depending on the runtime RTS flags. ") , ("traceBinaryEvent#"," Emits an event via the RTS tracing framework. The contents\n of the event is the binary object passed as the first argument with\n the given length passed as the second argument. The event will be\n emitted to the @.eventlog@ file. ") , ("traceMarker#"," Emits a marker event via the RTS tracing framework. The contents\n of the event is the zero-terminated byte string passed as the first\n argument. The event will be emitted either to the @.eventlog@ file,\n or to stderr, depending on the runtime RTS flags. ") , ("setThreadAllocationCounter#"," Sets the allocation counter for the current thread to the given value. ")- , ("StackSnapshot#"," Haskell representation of a @StgStack*@ that was created (cloned)\n with a function in @GHC.Stack.CloneStack@. Please check the\n documentation in this module for more detailed explanations. ")- , ("coerce"," The function @coerce@ allows you to safely convert between values of\n types that have the same representation with no run-time overhead. In the\n simplest case you can use it instead of a newtype constructor, to go from\n the newtype's concrete type to the abstract type. But it also works in\n more complicated settings, e.g. converting a list of newtypes to a list of\n concrete types.\n\n This function is representation-polymorphic, but the\n @RuntimeRep@ type argument is marked as @Inferred@, meaning\n that it is not available for visible type application. This means\n the typechecker will accept @coerce @Int @Age 42@.\n ")+ , ("StackSnapshot#"," Haskell representation of a @StgStack*@ that was created (cloned)\n with a function in \"GHC.Stack.CloneStack\". Please check the\n documentation in that module for more detailed explanations. ")+ , ("coerce"," The function 'coerce' allows you to safely convert between values of\n types that have the same representation with no run-time overhead. In the\n simplest case you can use it instead of a newtype constructor, to go from\n the newtype's concrete type to the abstract type. But it also works in\n more complicated settings, e.g. converting a list of newtypes to a list of\n concrete types.\n\n This function is representation-polymorphic, but the\n 'RuntimeRep' type argument is marked as 'Inferred', meaning\n that it is not available for visible type application. This means\n the typechecker will accept @'coerce' @'Int' @Age 42@.\n ") , ("broadcastInt8X16#"," Broadcast a scalar to all elements of a vector. ") , ("broadcastInt16X8#"," Broadcast a scalar to all elements of a vector. ") , ("broadcastInt32X4#"," Broadcast a scalar to all elements of a vector. ")@@ -558,30 +571,30 @@ , ("quotWord16X32#"," Rounds towards zero element-wise. ") , ("quotWord32X16#"," Rounds towards zero element-wise. ") , ("quotWord64X8#"," Rounds towards zero element-wise. ")- , ("remInt8X16#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remInt16X8#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remInt32X4#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remInt64X2#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remInt8X32#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remInt16X16#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remInt32X8#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remInt64X4#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remInt8X64#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remInt16X32#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remInt32X16#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remInt64X8#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remWord8X16#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remWord16X8#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remWord32X4#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remWord64X2#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remWord8X32#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remWord16X16#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remWord32X8#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remWord64X4#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remWord8X64#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remWord16X32#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remWord32X16#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")- , ("remWord64X8#"," Satisfies @(quot\\# x y) times\\# y plus\\# (rem\\# x y) == x@. ")+ , ("remInt8X16#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remInt16X8#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remInt32X4#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remInt64X2#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remInt8X32#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remInt16X16#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remInt32X8#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remInt64X4#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remInt8X64#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remInt16X32#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remInt32X16#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remInt64X8#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remWord8X16#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remWord16X8#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remWord32X4#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remWord64X2#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remWord8X32#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remWord16X16#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remWord32X8#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remWord64X4#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remWord8X64#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remWord16X32#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remWord32X16#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ")+ , ("remWord64X8#"," Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x@. ") , ("negateInt8X16#"," Negate element-wise. ") , ("negateInt16X8#"," Negate element-wise. ") , ("negateInt32X4#"," Negate element-wise. ")
ghc-lib/stage0/compiler/build/primop-has-side-effects.hs-incl view
@@ -168,6 +168,9 @@ primOpHasSideEffects MaskUninterruptibleOp = True primOpHasSideEffects UnmaskAsyncExceptionsOp = True primOpHasSideEffects MaskStatus = True+primOpHasSideEffects NewPromptTagOp = True+primOpHasSideEffects PromptOp = True+primOpHasSideEffects Control0Op = True primOpHasSideEffects AtomicallyOp = True primOpHasSideEffects RetryOp = True primOpHasSideEffects CatchRetryOp = True@@ -199,6 +202,7 @@ primOpHasSideEffects IsCurrentThreadBoundOp = True primOpHasSideEffects NoDuplicateOp = True primOpHasSideEffects ThreadStatusOp = True+primOpHasSideEffects ListThreadsOp = True primOpHasSideEffects MkWeakOp = True primOpHasSideEffects MkWeakNoFinalizerOp = True primOpHasSideEffects AddCFinalizerToWeakOp = True
ghc-lib/stage0/compiler/build/primop-list.hs-incl view
@@ -562,11 +562,17 @@ , CasMutVarOp , CatchOp , RaiseOp+ , RaiseUnderflowOp+ , RaiseOverflowOp+ , RaiseDivZeroOp , RaiseIOOp , MaskAsyncExceptionsOp , MaskUninterruptibleOp , UnmaskAsyncExceptionsOp , MaskStatus+ , NewPromptTagOp+ , PromptOp+ , Control0Op , AtomicallyOp , RetryOp , CatchRetryOp@@ -597,7 +603,9 @@ , LabelThreadOp , IsCurrentThreadBoundOp , NoDuplicateOp+ , GetThreadLabelOp , ThreadStatusOp+ , ListThreadsOp , MkWeakOp , MkWeakNoFinalizerOp , AddCFinalizerToWeakOp
ghc-lib/stage0/compiler/build/primop-out-of-line.hs-incl view
@@ -33,11 +33,17 @@ primOpOutOfLine CasMutVarOp = True primOpOutOfLine CatchOp = True primOpOutOfLine RaiseOp = True+primOpOutOfLine RaiseUnderflowOp = True+primOpOutOfLine RaiseOverflowOp = True+primOpOutOfLine RaiseDivZeroOp = True primOpOutOfLine RaiseIOOp = True primOpOutOfLine MaskAsyncExceptionsOp = True primOpOutOfLine MaskUninterruptibleOp = True primOpOutOfLine UnmaskAsyncExceptionsOp = True primOpOutOfLine MaskStatus = True+primOpOutOfLine NewPromptTagOp = True+primOpOutOfLine PromptOp = True+primOpOutOfLine Control0Op = True primOpOutOfLine AtomicallyOp = True primOpOutOfLine RetryOp = True primOpOutOfLine CatchRetryOp = True@@ -67,7 +73,9 @@ primOpOutOfLine LabelThreadOp = True primOpOutOfLine IsCurrentThreadBoundOp = True primOpOutOfLine NoDuplicateOp = True+primOpOutOfLine GetThreadLabelOp = True primOpOutOfLine ThreadStatusOp = True+primOpOutOfLine ListThreadsOp = True primOpOutOfLine MkWeakOp = True primOpOutOfLine MkWeakNoFinalizerOp = True primOpOutOfLine AddCFinalizerToWeakOp = True
ghc-lib/stage0/compiler/build/primop-primop-info.hs-incl view
@@ -562,11 +562,17 @@ primOpInfo CasMutVarOp = mkGenPrimOp (fsLit "casMutVar#") [levity1TyVarInf, deltaTyVarSpec, levPolyAlphaTyVarSpec] [mkMutVarPrimTy deltaTy levPolyAlphaTy, levPolyAlphaTy, levPolyAlphaTy, mkStatePrimTy deltaTy] ((mkTupleTy Unboxed [mkStatePrimTy deltaTy, intPrimTy, levPolyAlphaTy])) primOpInfo CatchOp = mkGenPrimOp (fsLit "catch#") [runtimeRep1TyVarInf, levity2TyVarInf, openAlphaTyVarSpec, levPolyBetaTyVarSpec] [(mkVisFunTyMany (mkStatePrimTy realWorldTy) ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, openAlphaTy]))), (mkVisFunTyMany (levPolyBetaTy) ((mkVisFunTyMany (mkStatePrimTy realWorldTy) ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, openAlphaTy]))))), mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, openAlphaTy])) primOpInfo RaiseOp = mkGenPrimOp (fsLit "raise#") [levity1TyVarInf, runtimeRep2TyVarInf, levPolyAlphaTyVarSpec, openBetaTyVarSpec] [levPolyAlphaTy] (openBetaTy)+primOpInfo RaiseUnderflowOp = mkGenPrimOp (fsLit "raiseUnderflow#") [runtimeRep2TyVarInf, openBetaTyVarSpec] [(mkTupleTy Unboxed [])] (openBetaTy)+primOpInfo RaiseOverflowOp = mkGenPrimOp (fsLit "raiseOverflow#") [runtimeRep2TyVarInf, openBetaTyVarSpec] [(mkTupleTy Unboxed [])] (openBetaTy)+primOpInfo RaiseDivZeroOp = mkGenPrimOp (fsLit "raiseDivZero#") [runtimeRep2TyVarInf, openBetaTyVarSpec] [(mkTupleTy Unboxed [])] (openBetaTy) primOpInfo RaiseIOOp = mkGenPrimOp (fsLit "raiseIO#") [levity1TyVarInf, runtimeRep2TyVarInf, levPolyAlphaTyVarSpec, openBetaTyVarSpec] [levPolyAlphaTy, mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, openBetaTy])) primOpInfo MaskAsyncExceptionsOp = mkGenPrimOp (fsLit "maskAsyncExceptions#") [runtimeRep1TyVarInf, openAlphaTyVarSpec] [(mkVisFunTyMany (mkStatePrimTy realWorldTy) ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, openAlphaTy]))), mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, openAlphaTy])) primOpInfo MaskUninterruptibleOp = mkGenPrimOp (fsLit "maskUninterruptible#") [runtimeRep1TyVarInf, openAlphaTyVarSpec] [(mkVisFunTyMany (mkStatePrimTy realWorldTy) ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, openAlphaTy]))), mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, openAlphaTy])) primOpInfo UnmaskAsyncExceptionsOp = mkGenPrimOp (fsLit "unmaskAsyncExceptions#") [runtimeRep1TyVarInf, openAlphaTyVarSpec] [(mkVisFunTyMany (mkStatePrimTy realWorldTy) ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, openAlphaTy]))), mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, openAlphaTy])) primOpInfo MaskStatus = mkGenPrimOp (fsLit "getMaskingState#") [] [mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, intPrimTy]))+primOpInfo NewPromptTagOp = mkGenPrimOp (fsLit "newPromptTag#") [alphaTyVarSpec] [mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, mkPromptTagPrimTy alphaTy]))+primOpInfo PromptOp = mkGenPrimOp (fsLit "prompt#") [alphaTyVarSpec] [mkPromptTagPrimTy alphaTy, (mkVisFunTyMany (mkStatePrimTy realWorldTy) ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, alphaTy]))), mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, alphaTy]))+primOpInfo Control0Op = mkGenPrimOp (fsLit "control0#") [runtimeRep2TyVarInf, alphaTyVarSpec, openBetaTyVarSpec] [mkPromptTagPrimTy alphaTy, (mkVisFunTyMany ((mkVisFunTyMany ((mkVisFunTyMany (mkStatePrimTy realWorldTy) ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, openBetaTy])))) ((mkVisFunTyMany (mkStatePrimTy realWorldTy) ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, alphaTy])))))) ((mkVisFunTyMany (mkStatePrimTy realWorldTy) ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, alphaTy]))))), mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, openBetaTy])) primOpInfo AtomicallyOp = mkGenPrimOp (fsLit "atomically#") [levity1TyVarInf, levPolyAlphaTyVarSpec] [(mkVisFunTyMany (mkStatePrimTy realWorldTy) ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, levPolyAlphaTy]))), mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, levPolyAlphaTy])) primOpInfo RetryOp = mkGenPrimOp (fsLit "retry#") [levity1TyVarInf, levPolyAlphaTyVarSpec] [mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, levPolyAlphaTy])) primOpInfo CatchRetryOp = mkGenPrimOp (fsLit "catchRetry#") [levity1TyVarInf, levPolyAlphaTyVarSpec] [(mkVisFunTyMany (mkStatePrimTy realWorldTy) ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, levPolyAlphaTy]))), (mkVisFunTyMany (mkStatePrimTy realWorldTy) ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, levPolyAlphaTy]))), mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, levPolyAlphaTy]))@@ -594,10 +600,12 @@ primOpInfo KillThreadOp = mkGenPrimOp (fsLit "killThread#") [alphaTyVarSpec] [threadIdPrimTy, alphaTy, mkStatePrimTy realWorldTy] (mkStatePrimTy realWorldTy) primOpInfo YieldOp = mkGenPrimOp (fsLit "yield#") [] [mkStatePrimTy realWorldTy] (mkStatePrimTy realWorldTy) primOpInfo MyThreadIdOp = mkGenPrimOp (fsLit "myThreadId#") [] [mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, threadIdPrimTy]))-primOpInfo LabelThreadOp = mkGenPrimOp (fsLit "labelThread#") [] [threadIdPrimTy, addrPrimTy, mkStatePrimTy realWorldTy] (mkStatePrimTy realWorldTy)+primOpInfo LabelThreadOp = mkGenPrimOp (fsLit "labelThread#") [] [threadIdPrimTy, byteArrayPrimTy, mkStatePrimTy realWorldTy] (mkStatePrimTy realWorldTy) primOpInfo IsCurrentThreadBoundOp = mkGenPrimOp (fsLit "isCurrentThreadBound#") [] [mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, intPrimTy])) primOpInfo NoDuplicateOp = mkGenPrimOp (fsLit "noDuplicate#") [deltaTyVarSpec] [mkStatePrimTy deltaTy] (mkStatePrimTy deltaTy)+primOpInfo GetThreadLabelOp = mkGenPrimOp (fsLit "threadLabel#") [] [threadIdPrimTy, mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, intPrimTy, byteArrayPrimTy])) primOpInfo ThreadStatusOp = mkGenPrimOp (fsLit "threadStatus#") [] [threadIdPrimTy, mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, intPrimTy, intPrimTy, intPrimTy]))+primOpInfo ListThreadsOp = mkGenPrimOp (fsLit "listThreads#") [] [mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, mkArrayPrimTy threadIdPrimTy])) primOpInfo MkWeakOp = mkGenPrimOp (fsLit "mkWeak#") [levity1TyVarInf, levity2TyVarInf, levPolyAlphaTyVarSpec, levPolyBetaTyVarSpec, gammaTyVarSpec] [levPolyAlphaTy, levPolyBetaTy, (mkVisFunTyMany (mkStatePrimTy realWorldTy) ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, gammaTy]))), mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, mkWeakPrimTy levPolyBetaTy])) primOpInfo MkWeakNoFinalizerOp = mkGenPrimOp (fsLit "mkWeakNoFinalizer#") [levity1TyVarInf, levity2TyVarInf, levPolyAlphaTyVarSpec, levPolyBetaTyVarSpec] [levPolyAlphaTy, levPolyBetaTy, mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, mkWeakPrimTy levPolyBetaTy])) primOpInfo AddCFinalizerToWeakOp = mkGenPrimOp (fsLit "addCFinalizerToWeak#") [levity2TyVarInf, levPolyBetaTyVarSpec] [addrPrimTy, addrPrimTy, intPrimTy, addrPrimTy, mkWeakPrimTy levPolyBetaTy, mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, intPrimTy]))@@ -629,7 +637,7 @@ primOpInfo KeepAliveOp = mkGenPrimOp (fsLit "keepAlive#") [levity1TyVarInf, runtimeRep2TyVarInf, levPolyAlphaTyVarSpec, openBetaTyVarSpec] [levPolyAlphaTy, mkStatePrimTy realWorldTy, (mkVisFunTyMany (mkStatePrimTy realWorldTy) (openBetaTy))] (openBetaTy) primOpInfo DataToTagOp = mkGenPrimOp (fsLit "dataToTag#") [alphaTyVarSpec] [alphaTy] (intPrimTy) primOpInfo TagToEnumOp = mkGenPrimOp (fsLit "tagToEnum#") [alphaTyVarSpec] [intPrimTy] (alphaTy)-primOpInfo AddrToAnyOp = mkGenPrimOp (fsLit "addrToAny#") [alphaTyVarSpec] [addrPrimTy] ((mkTupleTy Unboxed [alphaTy]))+primOpInfo AddrToAnyOp = mkGenPrimOp (fsLit "addrToAny#") [levity1TyVarInf, levPolyAlphaTyVarSpec] [addrPrimTy] ((mkTupleTy Unboxed [levPolyAlphaTy])) primOpInfo AnyToAddrOp = mkGenPrimOp (fsLit "anyToAddr#") [alphaTyVarSpec] [alphaTy, mkStatePrimTy realWorldTy] ((mkTupleTy Unboxed [mkStatePrimTy realWorldTy, addrPrimTy])) primOpInfo MkApUpd0_Op = mkGenPrimOp (fsLit "mkApUpd0#") [alphaTyVarSpec] [bcoPrimTy] ((mkTupleTy Unboxed [alphaTy])) primOpInfo NewBCOOp = mkGenPrimOp (fsLit "newBCO#") [alphaTyVarSpec, deltaTyVarSpec] [byteArrayPrimTy, byteArrayPrimTy, mkArrayPrimTy alphaTy, intPrimTy, byteArrayPrimTy, mkStatePrimTy deltaTy] ((mkTupleTy Unboxed [mkStatePrimTy deltaTy, bcoPrimTy]))
ghc-lib/stage0/compiler/build/primop-strictness.hs-incl view
@@ -2,10 +2,15 @@ , lazyApply2Dmd , topDmd] topDiv primOpStrictness RaiseOp = \ _arity -> mkClosedDmdSig [topDmd] botDiv +primOpStrictness RaiseUnderflowOp = \ _arity -> mkClosedDmdSig [topDmd] botDiv +primOpStrictness RaiseOverflowOp = \ _arity -> mkClosedDmdSig [topDmd] botDiv +primOpStrictness RaiseDivZeroOp = \ _arity -> mkClosedDmdSig [topDmd] botDiv primOpStrictness RaiseIOOp = \ _arity -> mkClosedDmdSig [topDmd, topDmd] exnDiv primOpStrictness MaskAsyncExceptionsOp = \ _arity -> mkClosedDmdSig [strictOnceApply1Dmd,topDmd] topDiv primOpStrictness MaskUninterruptibleOp = \ _arity -> mkClosedDmdSig [strictOnceApply1Dmd,topDmd] topDiv primOpStrictness UnmaskAsyncExceptionsOp = \ _arity -> mkClosedDmdSig [strictOnceApply1Dmd,topDmd] topDiv +primOpStrictness PromptOp = \ _arity -> mkClosedDmdSig [topDmd, strictOnceApply1Dmd, topDmd] topDiv +primOpStrictness Control0Op = \ _arity -> mkClosedDmdSig [topDmd, lazyApply2Dmd, topDmd] topDiv primOpStrictness AtomicallyOp = \ _arity -> mkClosedDmdSig [strictManyApply1Dmd,topDmd] topDiv primOpStrictness RetryOp = \ _arity -> mkClosedDmdSig [topDmd] botDiv primOpStrictness CatchRetryOp = \ _arity -> mkClosedDmdSig [ lazyApply1Dmd
ghc-lib/stage0/compiler/build/primop-tag.hs-incl view
@@ -1,1307 +1,1315 @@ maxPrimOpTag :: Int-maxPrimOpTag = 1303-primOpTag :: PrimOp -> Int-primOpTag CharGtOp = 0-primOpTag CharGeOp = 1-primOpTag CharEqOp = 2-primOpTag CharNeOp = 3-primOpTag CharLtOp = 4-primOpTag CharLeOp = 5-primOpTag OrdOp = 6-primOpTag Int8ToIntOp = 7-primOpTag IntToInt8Op = 8-primOpTag Int8NegOp = 9-primOpTag Int8AddOp = 10-primOpTag Int8SubOp = 11-primOpTag Int8MulOp = 12-primOpTag Int8QuotOp = 13-primOpTag Int8RemOp = 14-primOpTag Int8QuotRemOp = 15-primOpTag Int8SllOp = 16-primOpTag Int8SraOp = 17-primOpTag Int8SrlOp = 18-primOpTag Int8ToWord8Op = 19-primOpTag Int8EqOp = 20-primOpTag Int8GeOp = 21-primOpTag Int8GtOp = 22-primOpTag Int8LeOp = 23-primOpTag Int8LtOp = 24-primOpTag Int8NeOp = 25-primOpTag Word8ToWordOp = 26-primOpTag WordToWord8Op = 27-primOpTag Word8AddOp = 28-primOpTag Word8SubOp = 29-primOpTag Word8MulOp = 30-primOpTag Word8QuotOp = 31-primOpTag Word8RemOp = 32-primOpTag Word8QuotRemOp = 33-primOpTag Word8AndOp = 34-primOpTag Word8OrOp = 35-primOpTag Word8XorOp = 36-primOpTag Word8NotOp = 37-primOpTag Word8SllOp = 38-primOpTag Word8SrlOp = 39-primOpTag Word8ToInt8Op = 40-primOpTag Word8EqOp = 41-primOpTag Word8GeOp = 42-primOpTag Word8GtOp = 43-primOpTag Word8LeOp = 44-primOpTag Word8LtOp = 45-primOpTag Word8NeOp = 46-primOpTag Int16ToIntOp = 47-primOpTag IntToInt16Op = 48-primOpTag Int16NegOp = 49-primOpTag Int16AddOp = 50-primOpTag Int16SubOp = 51-primOpTag Int16MulOp = 52-primOpTag Int16QuotOp = 53-primOpTag Int16RemOp = 54-primOpTag Int16QuotRemOp = 55-primOpTag Int16SllOp = 56-primOpTag Int16SraOp = 57-primOpTag Int16SrlOp = 58-primOpTag Int16ToWord16Op = 59-primOpTag Int16EqOp = 60-primOpTag Int16GeOp = 61-primOpTag Int16GtOp = 62-primOpTag Int16LeOp = 63-primOpTag Int16LtOp = 64-primOpTag Int16NeOp = 65-primOpTag Word16ToWordOp = 66-primOpTag WordToWord16Op = 67-primOpTag Word16AddOp = 68-primOpTag Word16SubOp = 69-primOpTag Word16MulOp = 70-primOpTag Word16QuotOp = 71-primOpTag Word16RemOp = 72-primOpTag Word16QuotRemOp = 73-primOpTag Word16AndOp = 74-primOpTag Word16OrOp = 75-primOpTag Word16XorOp = 76-primOpTag Word16NotOp = 77-primOpTag Word16SllOp = 78-primOpTag Word16SrlOp = 79-primOpTag Word16ToInt16Op = 80-primOpTag Word16EqOp = 81-primOpTag Word16GeOp = 82-primOpTag Word16GtOp = 83-primOpTag Word16LeOp = 84-primOpTag Word16LtOp = 85-primOpTag Word16NeOp = 86-primOpTag Int32ToIntOp = 87-primOpTag IntToInt32Op = 88-primOpTag Int32NegOp = 89-primOpTag Int32AddOp = 90-primOpTag Int32SubOp = 91-primOpTag Int32MulOp = 92-primOpTag Int32QuotOp = 93-primOpTag Int32RemOp = 94-primOpTag Int32QuotRemOp = 95-primOpTag Int32SllOp = 96-primOpTag Int32SraOp = 97-primOpTag Int32SrlOp = 98-primOpTag Int32ToWord32Op = 99-primOpTag Int32EqOp = 100-primOpTag Int32GeOp = 101-primOpTag Int32GtOp = 102-primOpTag Int32LeOp = 103-primOpTag Int32LtOp = 104-primOpTag Int32NeOp = 105-primOpTag Word32ToWordOp = 106-primOpTag WordToWord32Op = 107-primOpTag Word32AddOp = 108-primOpTag Word32SubOp = 109-primOpTag Word32MulOp = 110-primOpTag Word32QuotOp = 111-primOpTag Word32RemOp = 112-primOpTag Word32QuotRemOp = 113-primOpTag Word32AndOp = 114-primOpTag Word32OrOp = 115-primOpTag Word32XorOp = 116-primOpTag Word32NotOp = 117-primOpTag Word32SllOp = 118-primOpTag Word32SrlOp = 119-primOpTag Word32ToInt32Op = 120-primOpTag Word32EqOp = 121-primOpTag Word32GeOp = 122-primOpTag Word32GtOp = 123-primOpTag Word32LeOp = 124-primOpTag Word32LtOp = 125-primOpTag Word32NeOp = 126-primOpTag Int64ToIntOp = 127-primOpTag IntToInt64Op = 128-primOpTag Int64NegOp = 129-primOpTag Int64AddOp = 130-primOpTag Int64SubOp = 131-primOpTag Int64MulOp = 132-primOpTag Int64QuotOp = 133-primOpTag Int64RemOp = 134-primOpTag Int64SllOp = 135-primOpTag Int64SraOp = 136-primOpTag Int64SrlOp = 137-primOpTag Int64ToWord64Op = 138-primOpTag Int64EqOp = 139-primOpTag Int64GeOp = 140-primOpTag Int64GtOp = 141-primOpTag Int64LeOp = 142-primOpTag Int64LtOp = 143-primOpTag Int64NeOp = 144-primOpTag Word64ToWordOp = 145-primOpTag WordToWord64Op = 146-primOpTag Word64AddOp = 147-primOpTag Word64SubOp = 148-primOpTag Word64MulOp = 149-primOpTag Word64QuotOp = 150-primOpTag Word64RemOp = 151-primOpTag Word64AndOp = 152-primOpTag Word64OrOp = 153-primOpTag Word64XorOp = 154-primOpTag Word64NotOp = 155-primOpTag Word64SllOp = 156-primOpTag Word64SrlOp = 157-primOpTag Word64ToInt64Op = 158-primOpTag Word64EqOp = 159-primOpTag Word64GeOp = 160-primOpTag Word64GtOp = 161-primOpTag Word64LeOp = 162-primOpTag Word64LtOp = 163-primOpTag Word64NeOp = 164-primOpTag IntAddOp = 165-primOpTag IntSubOp = 166-primOpTag IntMulOp = 167-primOpTag IntMul2Op = 168-primOpTag IntMulMayOfloOp = 169-primOpTag IntQuotOp = 170-primOpTag IntRemOp = 171-primOpTag IntQuotRemOp = 172-primOpTag IntAndOp = 173-primOpTag IntOrOp = 174-primOpTag IntXorOp = 175-primOpTag IntNotOp = 176-primOpTag IntNegOp = 177-primOpTag IntAddCOp = 178-primOpTag IntSubCOp = 179-primOpTag IntGtOp = 180-primOpTag IntGeOp = 181-primOpTag IntEqOp = 182-primOpTag IntNeOp = 183-primOpTag IntLtOp = 184-primOpTag IntLeOp = 185-primOpTag ChrOp = 186-primOpTag IntToWordOp = 187-primOpTag IntToFloatOp = 188-primOpTag IntToDoubleOp = 189-primOpTag WordToFloatOp = 190-primOpTag WordToDoubleOp = 191-primOpTag IntSllOp = 192-primOpTag IntSraOp = 193-primOpTag IntSrlOp = 194-primOpTag WordAddOp = 195-primOpTag WordAddCOp = 196-primOpTag WordSubCOp = 197-primOpTag WordAdd2Op = 198-primOpTag WordSubOp = 199-primOpTag WordMulOp = 200-primOpTag WordMul2Op = 201-primOpTag WordQuotOp = 202-primOpTag WordRemOp = 203-primOpTag WordQuotRemOp = 204-primOpTag WordQuotRem2Op = 205-primOpTag WordAndOp = 206-primOpTag WordOrOp = 207-primOpTag WordXorOp = 208-primOpTag WordNotOp = 209-primOpTag WordSllOp = 210-primOpTag WordSrlOp = 211-primOpTag WordToIntOp = 212-primOpTag WordGtOp = 213-primOpTag WordGeOp = 214-primOpTag WordEqOp = 215-primOpTag WordNeOp = 216-primOpTag WordLtOp = 217-primOpTag WordLeOp = 218-primOpTag PopCnt8Op = 219-primOpTag PopCnt16Op = 220-primOpTag PopCnt32Op = 221-primOpTag PopCnt64Op = 222-primOpTag PopCntOp = 223-primOpTag Pdep8Op = 224-primOpTag Pdep16Op = 225-primOpTag Pdep32Op = 226-primOpTag Pdep64Op = 227-primOpTag PdepOp = 228-primOpTag Pext8Op = 229-primOpTag Pext16Op = 230-primOpTag Pext32Op = 231-primOpTag Pext64Op = 232-primOpTag PextOp = 233-primOpTag Clz8Op = 234-primOpTag Clz16Op = 235-primOpTag Clz32Op = 236-primOpTag Clz64Op = 237-primOpTag ClzOp = 238-primOpTag Ctz8Op = 239-primOpTag Ctz16Op = 240-primOpTag Ctz32Op = 241-primOpTag Ctz64Op = 242-primOpTag CtzOp = 243-primOpTag BSwap16Op = 244-primOpTag BSwap32Op = 245-primOpTag BSwap64Op = 246-primOpTag BSwapOp = 247-primOpTag BRev8Op = 248-primOpTag BRev16Op = 249-primOpTag BRev32Op = 250-primOpTag BRev64Op = 251-primOpTag BRevOp = 252-primOpTag Narrow8IntOp = 253-primOpTag Narrow16IntOp = 254-primOpTag Narrow32IntOp = 255-primOpTag Narrow8WordOp = 256-primOpTag Narrow16WordOp = 257-primOpTag Narrow32WordOp = 258-primOpTag DoubleGtOp = 259-primOpTag DoubleGeOp = 260-primOpTag DoubleEqOp = 261-primOpTag DoubleNeOp = 262-primOpTag DoubleLtOp = 263-primOpTag DoubleLeOp = 264-primOpTag DoubleAddOp = 265-primOpTag DoubleSubOp = 266-primOpTag DoubleMulOp = 267-primOpTag DoubleDivOp = 268-primOpTag DoubleNegOp = 269-primOpTag DoubleFabsOp = 270-primOpTag DoubleToIntOp = 271-primOpTag DoubleToFloatOp = 272-primOpTag DoubleExpOp = 273-primOpTag DoubleExpM1Op = 274-primOpTag DoubleLogOp = 275-primOpTag DoubleLog1POp = 276-primOpTag DoubleSqrtOp = 277-primOpTag DoubleSinOp = 278-primOpTag DoubleCosOp = 279-primOpTag DoubleTanOp = 280-primOpTag DoubleAsinOp = 281-primOpTag DoubleAcosOp = 282-primOpTag DoubleAtanOp = 283-primOpTag DoubleSinhOp = 284-primOpTag DoubleCoshOp = 285-primOpTag DoubleTanhOp = 286-primOpTag DoubleAsinhOp = 287-primOpTag DoubleAcoshOp = 288-primOpTag DoubleAtanhOp = 289-primOpTag DoublePowerOp = 290-primOpTag DoubleDecode_2IntOp = 291-primOpTag DoubleDecode_Int64Op = 292-primOpTag FloatGtOp = 293-primOpTag FloatGeOp = 294-primOpTag FloatEqOp = 295-primOpTag FloatNeOp = 296-primOpTag FloatLtOp = 297-primOpTag FloatLeOp = 298-primOpTag FloatAddOp = 299-primOpTag FloatSubOp = 300-primOpTag FloatMulOp = 301-primOpTag FloatDivOp = 302-primOpTag FloatNegOp = 303-primOpTag FloatFabsOp = 304-primOpTag FloatToIntOp = 305-primOpTag FloatExpOp = 306-primOpTag FloatExpM1Op = 307-primOpTag FloatLogOp = 308-primOpTag FloatLog1POp = 309-primOpTag FloatSqrtOp = 310-primOpTag FloatSinOp = 311-primOpTag FloatCosOp = 312-primOpTag FloatTanOp = 313-primOpTag FloatAsinOp = 314-primOpTag FloatAcosOp = 315-primOpTag FloatAtanOp = 316-primOpTag FloatSinhOp = 317-primOpTag FloatCoshOp = 318-primOpTag FloatTanhOp = 319-primOpTag FloatAsinhOp = 320-primOpTag FloatAcoshOp = 321-primOpTag FloatAtanhOp = 322-primOpTag FloatPowerOp = 323-primOpTag FloatToDoubleOp = 324-primOpTag FloatDecode_IntOp = 325-primOpTag NewArrayOp = 326-primOpTag ReadArrayOp = 327-primOpTag WriteArrayOp = 328-primOpTag SizeofArrayOp = 329-primOpTag SizeofMutableArrayOp = 330-primOpTag IndexArrayOp = 331-primOpTag UnsafeFreezeArrayOp = 332-primOpTag UnsafeThawArrayOp = 333-primOpTag CopyArrayOp = 334-primOpTag CopyMutableArrayOp = 335-primOpTag CloneArrayOp = 336-primOpTag CloneMutableArrayOp = 337-primOpTag FreezeArrayOp = 338-primOpTag ThawArrayOp = 339-primOpTag CasArrayOp = 340-primOpTag NewSmallArrayOp = 341-primOpTag ShrinkSmallMutableArrayOp_Char = 342-primOpTag ReadSmallArrayOp = 343-primOpTag WriteSmallArrayOp = 344-primOpTag SizeofSmallArrayOp = 345-primOpTag SizeofSmallMutableArrayOp = 346-primOpTag GetSizeofSmallMutableArrayOp = 347-primOpTag IndexSmallArrayOp = 348-primOpTag UnsafeFreezeSmallArrayOp = 349-primOpTag UnsafeThawSmallArrayOp = 350-primOpTag CopySmallArrayOp = 351-primOpTag CopySmallMutableArrayOp = 352-primOpTag CloneSmallArrayOp = 353-primOpTag CloneSmallMutableArrayOp = 354-primOpTag FreezeSmallArrayOp = 355-primOpTag ThawSmallArrayOp = 356-primOpTag CasSmallArrayOp = 357-primOpTag NewByteArrayOp_Char = 358-primOpTag NewPinnedByteArrayOp_Char = 359-primOpTag NewAlignedPinnedByteArrayOp_Char = 360-primOpTag MutableByteArrayIsPinnedOp = 361-primOpTag ByteArrayIsPinnedOp = 362-primOpTag ByteArrayContents_Char = 363-primOpTag MutableByteArrayContents_Char = 364-primOpTag ShrinkMutableByteArrayOp_Char = 365-primOpTag ResizeMutableByteArrayOp_Char = 366-primOpTag UnsafeFreezeByteArrayOp = 367-primOpTag SizeofByteArrayOp = 368-primOpTag SizeofMutableByteArrayOp = 369-primOpTag GetSizeofMutableByteArrayOp = 370-primOpTag IndexByteArrayOp_Char = 371-primOpTag IndexByteArrayOp_WideChar = 372-primOpTag IndexByteArrayOp_Int = 373-primOpTag IndexByteArrayOp_Word = 374-primOpTag IndexByteArrayOp_Addr = 375-primOpTag IndexByteArrayOp_Float = 376-primOpTag IndexByteArrayOp_Double = 377-primOpTag IndexByteArrayOp_StablePtr = 378-primOpTag IndexByteArrayOp_Int8 = 379-primOpTag IndexByteArrayOp_Int16 = 380-primOpTag IndexByteArrayOp_Int32 = 381-primOpTag IndexByteArrayOp_Int64 = 382-primOpTag IndexByteArrayOp_Word8 = 383-primOpTag IndexByteArrayOp_Word16 = 384-primOpTag IndexByteArrayOp_Word32 = 385-primOpTag IndexByteArrayOp_Word64 = 386-primOpTag IndexByteArrayOp_Word8AsChar = 387-primOpTag IndexByteArrayOp_Word8AsWideChar = 388-primOpTag IndexByteArrayOp_Word8AsInt = 389-primOpTag IndexByteArrayOp_Word8AsWord = 390-primOpTag IndexByteArrayOp_Word8AsAddr = 391-primOpTag IndexByteArrayOp_Word8AsFloat = 392-primOpTag IndexByteArrayOp_Word8AsDouble = 393-primOpTag IndexByteArrayOp_Word8AsStablePtr = 394-primOpTag IndexByteArrayOp_Word8AsInt16 = 395-primOpTag IndexByteArrayOp_Word8AsInt32 = 396-primOpTag IndexByteArrayOp_Word8AsInt64 = 397-primOpTag IndexByteArrayOp_Word8AsWord16 = 398-primOpTag IndexByteArrayOp_Word8AsWord32 = 399-primOpTag IndexByteArrayOp_Word8AsWord64 = 400-primOpTag ReadByteArrayOp_Char = 401-primOpTag ReadByteArrayOp_WideChar = 402-primOpTag ReadByteArrayOp_Int = 403-primOpTag ReadByteArrayOp_Word = 404-primOpTag ReadByteArrayOp_Addr = 405-primOpTag ReadByteArrayOp_Float = 406-primOpTag ReadByteArrayOp_Double = 407-primOpTag ReadByteArrayOp_StablePtr = 408-primOpTag ReadByteArrayOp_Int8 = 409-primOpTag ReadByteArrayOp_Int16 = 410-primOpTag ReadByteArrayOp_Int32 = 411-primOpTag ReadByteArrayOp_Int64 = 412-primOpTag ReadByteArrayOp_Word8 = 413-primOpTag ReadByteArrayOp_Word16 = 414-primOpTag ReadByteArrayOp_Word32 = 415-primOpTag ReadByteArrayOp_Word64 = 416-primOpTag ReadByteArrayOp_Word8AsChar = 417-primOpTag ReadByteArrayOp_Word8AsWideChar = 418-primOpTag ReadByteArrayOp_Word8AsInt = 419-primOpTag ReadByteArrayOp_Word8AsWord = 420-primOpTag ReadByteArrayOp_Word8AsAddr = 421-primOpTag ReadByteArrayOp_Word8AsFloat = 422-primOpTag ReadByteArrayOp_Word8AsDouble = 423-primOpTag ReadByteArrayOp_Word8AsStablePtr = 424-primOpTag ReadByteArrayOp_Word8AsInt16 = 425-primOpTag ReadByteArrayOp_Word8AsInt32 = 426-primOpTag ReadByteArrayOp_Word8AsInt64 = 427-primOpTag ReadByteArrayOp_Word8AsWord16 = 428-primOpTag ReadByteArrayOp_Word8AsWord32 = 429-primOpTag ReadByteArrayOp_Word8AsWord64 = 430-primOpTag WriteByteArrayOp_Char = 431-primOpTag WriteByteArrayOp_WideChar = 432-primOpTag WriteByteArrayOp_Int = 433-primOpTag WriteByteArrayOp_Word = 434-primOpTag WriteByteArrayOp_Addr = 435-primOpTag WriteByteArrayOp_Float = 436-primOpTag WriteByteArrayOp_Double = 437-primOpTag WriteByteArrayOp_StablePtr = 438-primOpTag WriteByteArrayOp_Int8 = 439-primOpTag WriteByteArrayOp_Int16 = 440-primOpTag WriteByteArrayOp_Int32 = 441-primOpTag WriteByteArrayOp_Int64 = 442-primOpTag WriteByteArrayOp_Word8 = 443-primOpTag WriteByteArrayOp_Word16 = 444-primOpTag WriteByteArrayOp_Word32 = 445-primOpTag WriteByteArrayOp_Word64 = 446-primOpTag WriteByteArrayOp_Word8AsChar = 447-primOpTag WriteByteArrayOp_Word8AsWideChar = 448-primOpTag WriteByteArrayOp_Word8AsInt = 449-primOpTag WriteByteArrayOp_Word8AsWord = 450-primOpTag WriteByteArrayOp_Word8AsAddr = 451-primOpTag WriteByteArrayOp_Word8AsFloat = 452-primOpTag WriteByteArrayOp_Word8AsDouble = 453-primOpTag WriteByteArrayOp_Word8AsStablePtr = 454-primOpTag WriteByteArrayOp_Word8AsInt16 = 455-primOpTag WriteByteArrayOp_Word8AsInt32 = 456-primOpTag WriteByteArrayOp_Word8AsInt64 = 457-primOpTag WriteByteArrayOp_Word8AsWord16 = 458-primOpTag WriteByteArrayOp_Word8AsWord32 = 459-primOpTag WriteByteArrayOp_Word8AsWord64 = 460-primOpTag CompareByteArraysOp = 461-primOpTag CopyByteArrayOp = 462-primOpTag CopyMutableByteArrayOp = 463-primOpTag CopyByteArrayToAddrOp = 464-primOpTag CopyMutableByteArrayToAddrOp = 465-primOpTag CopyAddrToByteArrayOp = 466-primOpTag SetByteArrayOp = 467-primOpTag AtomicReadByteArrayOp_Int = 468-primOpTag AtomicWriteByteArrayOp_Int = 469-primOpTag CasByteArrayOp_Int = 470-primOpTag CasByteArrayOp_Int8 = 471-primOpTag CasByteArrayOp_Int16 = 472-primOpTag CasByteArrayOp_Int32 = 473-primOpTag CasByteArrayOp_Int64 = 474-primOpTag FetchAddByteArrayOp_Int = 475-primOpTag FetchSubByteArrayOp_Int = 476-primOpTag FetchAndByteArrayOp_Int = 477-primOpTag FetchNandByteArrayOp_Int = 478-primOpTag FetchOrByteArrayOp_Int = 479-primOpTag FetchXorByteArrayOp_Int = 480-primOpTag AddrAddOp = 481-primOpTag AddrSubOp = 482-primOpTag AddrRemOp = 483-primOpTag AddrToIntOp = 484-primOpTag IntToAddrOp = 485-primOpTag AddrGtOp = 486-primOpTag AddrGeOp = 487-primOpTag AddrEqOp = 488-primOpTag AddrNeOp = 489-primOpTag AddrLtOp = 490-primOpTag AddrLeOp = 491-primOpTag IndexOffAddrOp_Char = 492-primOpTag IndexOffAddrOp_WideChar = 493-primOpTag IndexOffAddrOp_Int = 494-primOpTag IndexOffAddrOp_Word = 495-primOpTag IndexOffAddrOp_Addr = 496-primOpTag IndexOffAddrOp_Float = 497-primOpTag IndexOffAddrOp_Double = 498-primOpTag IndexOffAddrOp_StablePtr = 499-primOpTag IndexOffAddrOp_Int8 = 500-primOpTag IndexOffAddrOp_Int16 = 501-primOpTag IndexOffAddrOp_Int32 = 502-primOpTag IndexOffAddrOp_Int64 = 503-primOpTag IndexOffAddrOp_Word8 = 504-primOpTag IndexOffAddrOp_Word16 = 505-primOpTag IndexOffAddrOp_Word32 = 506-primOpTag IndexOffAddrOp_Word64 = 507-primOpTag ReadOffAddrOp_Char = 508-primOpTag ReadOffAddrOp_WideChar = 509-primOpTag ReadOffAddrOp_Int = 510-primOpTag ReadOffAddrOp_Word = 511-primOpTag ReadOffAddrOp_Addr = 512-primOpTag ReadOffAddrOp_Float = 513-primOpTag ReadOffAddrOp_Double = 514-primOpTag ReadOffAddrOp_StablePtr = 515-primOpTag ReadOffAddrOp_Int8 = 516-primOpTag ReadOffAddrOp_Int16 = 517-primOpTag ReadOffAddrOp_Int32 = 518-primOpTag ReadOffAddrOp_Int64 = 519-primOpTag ReadOffAddrOp_Word8 = 520-primOpTag ReadOffAddrOp_Word16 = 521-primOpTag ReadOffAddrOp_Word32 = 522-primOpTag ReadOffAddrOp_Word64 = 523-primOpTag WriteOffAddrOp_Char = 524-primOpTag WriteOffAddrOp_WideChar = 525-primOpTag WriteOffAddrOp_Int = 526-primOpTag WriteOffAddrOp_Word = 527-primOpTag WriteOffAddrOp_Addr = 528-primOpTag WriteOffAddrOp_Float = 529-primOpTag WriteOffAddrOp_Double = 530-primOpTag WriteOffAddrOp_StablePtr = 531-primOpTag WriteOffAddrOp_Int8 = 532-primOpTag WriteOffAddrOp_Int16 = 533-primOpTag WriteOffAddrOp_Int32 = 534-primOpTag WriteOffAddrOp_Int64 = 535-primOpTag WriteOffAddrOp_Word8 = 536-primOpTag WriteOffAddrOp_Word16 = 537-primOpTag WriteOffAddrOp_Word32 = 538-primOpTag WriteOffAddrOp_Word64 = 539-primOpTag InterlockedExchange_Addr = 540-primOpTag InterlockedExchange_Word = 541-primOpTag CasAddrOp_Addr = 542-primOpTag CasAddrOp_Word = 543-primOpTag CasAddrOp_Word8 = 544-primOpTag CasAddrOp_Word16 = 545-primOpTag CasAddrOp_Word32 = 546-primOpTag CasAddrOp_Word64 = 547-primOpTag FetchAddAddrOp_Word = 548-primOpTag FetchSubAddrOp_Word = 549-primOpTag FetchAndAddrOp_Word = 550-primOpTag FetchNandAddrOp_Word = 551-primOpTag FetchOrAddrOp_Word = 552-primOpTag FetchXorAddrOp_Word = 553-primOpTag AtomicReadAddrOp_Word = 554-primOpTag AtomicWriteAddrOp_Word = 555-primOpTag NewMutVarOp = 556-primOpTag ReadMutVarOp = 557-primOpTag WriteMutVarOp = 558-primOpTag AtomicModifyMutVar2Op = 559-primOpTag AtomicModifyMutVar_Op = 560-primOpTag CasMutVarOp = 561-primOpTag CatchOp = 562-primOpTag RaiseOp = 563-primOpTag RaiseIOOp = 564-primOpTag MaskAsyncExceptionsOp = 565-primOpTag MaskUninterruptibleOp = 566-primOpTag UnmaskAsyncExceptionsOp = 567-primOpTag MaskStatus = 568-primOpTag AtomicallyOp = 569-primOpTag RetryOp = 570-primOpTag CatchRetryOp = 571-primOpTag CatchSTMOp = 572-primOpTag NewTVarOp = 573-primOpTag ReadTVarOp = 574-primOpTag ReadTVarIOOp = 575-primOpTag WriteTVarOp = 576-primOpTag NewMVarOp = 577-primOpTag TakeMVarOp = 578-primOpTag TryTakeMVarOp = 579-primOpTag PutMVarOp = 580-primOpTag TryPutMVarOp = 581-primOpTag ReadMVarOp = 582-primOpTag TryReadMVarOp = 583-primOpTag IsEmptyMVarOp = 584-primOpTag NewIOPortOp = 585-primOpTag ReadIOPortOp = 586-primOpTag WriteIOPortOp = 587-primOpTag DelayOp = 588-primOpTag WaitReadOp = 589-primOpTag WaitWriteOp = 590-primOpTag ForkOp = 591-primOpTag ForkOnOp = 592-primOpTag KillThreadOp = 593-primOpTag YieldOp = 594-primOpTag MyThreadIdOp = 595-primOpTag LabelThreadOp = 596-primOpTag IsCurrentThreadBoundOp = 597-primOpTag NoDuplicateOp = 598-primOpTag ThreadStatusOp = 599-primOpTag MkWeakOp = 600-primOpTag MkWeakNoFinalizerOp = 601-primOpTag AddCFinalizerToWeakOp = 602-primOpTag DeRefWeakOp = 603-primOpTag FinalizeWeakOp = 604-primOpTag TouchOp = 605-primOpTag MakeStablePtrOp = 606-primOpTag DeRefStablePtrOp = 607-primOpTag EqStablePtrOp = 608-primOpTag MakeStableNameOp = 609-primOpTag StableNameToIntOp = 610-primOpTag CompactNewOp = 611-primOpTag CompactResizeOp = 612-primOpTag CompactContainsOp = 613-primOpTag CompactContainsAnyOp = 614-primOpTag CompactGetFirstBlockOp = 615-primOpTag CompactGetNextBlockOp = 616-primOpTag CompactAllocateBlockOp = 617-primOpTag CompactFixupPointersOp = 618-primOpTag CompactAdd = 619-primOpTag CompactAddWithSharing = 620-primOpTag CompactSize = 621-primOpTag ReallyUnsafePtrEqualityOp = 622-primOpTag ParOp = 623-primOpTag SparkOp = 624-primOpTag SeqOp = 625-primOpTag GetSparkOp = 626-primOpTag NumSparks = 627-primOpTag KeepAliveOp = 628-primOpTag DataToTagOp = 629-primOpTag TagToEnumOp = 630-primOpTag AddrToAnyOp = 631-primOpTag AnyToAddrOp = 632-primOpTag MkApUpd0_Op = 633-primOpTag NewBCOOp = 634-primOpTag UnpackClosureOp = 635-primOpTag ClosureSizeOp = 636-primOpTag GetApStackValOp = 637-primOpTag GetCCSOfOp = 638-primOpTag GetCurrentCCSOp = 639-primOpTag ClearCCSOp = 640-primOpTag WhereFromOp = 641-primOpTag TraceEventOp = 642-primOpTag TraceEventBinaryOp = 643-primOpTag TraceMarkerOp = 644-primOpTag SetThreadAllocationCounter = 645-primOpTag (VecBroadcastOp IntVec 16 W8) = 646-primOpTag (VecBroadcastOp IntVec 8 W16) = 647-primOpTag (VecBroadcastOp IntVec 4 W32) = 648-primOpTag (VecBroadcastOp IntVec 2 W64) = 649-primOpTag (VecBroadcastOp IntVec 32 W8) = 650-primOpTag (VecBroadcastOp IntVec 16 W16) = 651-primOpTag (VecBroadcastOp IntVec 8 W32) = 652-primOpTag (VecBroadcastOp IntVec 4 W64) = 653-primOpTag (VecBroadcastOp IntVec 64 W8) = 654-primOpTag (VecBroadcastOp IntVec 32 W16) = 655-primOpTag (VecBroadcastOp IntVec 16 W32) = 656-primOpTag (VecBroadcastOp IntVec 8 W64) = 657-primOpTag (VecBroadcastOp WordVec 16 W8) = 658-primOpTag (VecBroadcastOp WordVec 8 W16) = 659-primOpTag (VecBroadcastOp WordVec 4 W32) = 660-primOpTag (VecBroadcastOp WordVec 2 W64) = 661-primOpTag (VecBroadcastOp WordVec 32 W8) = 662-primOpTag (VecBroadcastOp WordVec 16 W16) = 663-primOpTag (VecBroadcastOp WordVec 8 W32) = 664-primOpTag (VecBroadcastOp WordVec 4 W64) = 665-primOpTag (VecBroadcastOp WordVec 64 W8) = 666-primOpTag (VecBroadcastOp WordVec 32 W16) = 667-primOpTag (VecBroadcastOp WordVec 16 W32) = 668-primOpTag (VecBroadcastOp WordVec 8 W64) = 669-primOpTag (VecBroadcastOp FloatVec 4 W32) = 670-primOpTag (VecBroadcastOp FloatVec 2 W64) = 671-primOpTag (VecBroadcastOp FloatVec 8 W32) = 672-primOpTag (VecBroadcastOp FloatVec 4 W64) = 673-primOpTag (VecBroadcastOp FloatVec 16 W32) = 674-primOpTag (VecBroadcastOp FloatVec 8 W64) = 675-primOpTag (VecPackOp IntVec 16 W8) = 676-primOpTag (VecPackOp IntVec 8 W16) = 677-primOpTag (VecPackOp IntVec 4 W32) = 678-primOpTag (VecPackOp IntVec 2 W64) = 679-primOpTag (VecPackOp IntVec 32 W8) = 680-primOpTag (VecPackOp IntVec 16 W16) = 681-primOpTag (VecPackOp IntVec 8 W32) = 682-primOpTag (VecPackOp IntVec 4 W64) = 683-primOpTag (VecPackOp IntVec 64 W8) = 684-primOpTag (VecPackOp IntVec 32 W16) = 685-primOpTag (VecPackOp IntVec 16 W32) = 686-primOpTag (VecPackOp IntVec 8 W64) = 687-primOpTag (VecPackOp WordVec 16 W8) = 688-primOpTag (VecPackOp WordVec 8 W16) = 689-primOpTag (VecPackOp WordVec 4 W32) = 690-primOpTag (VecPackOp WordVec 2 W64) = 691-primOpTag (VecPackOp WordVec 32 W8) = 692-primOpTag (VecPackOp WordVec 16 W16) = 693-primOpTag (VecPackOp WordVec 8 W32) = 694-primOpTag (VecPackOp WordVec 4 W64) = 695-primOpTag (VecPackOp WordVec 64 W8) = 696-primOpTag (VecPackOp WordVec 32 W16) = 697-primOpTag (VecPackOp WordVec 16 W32) = 698-primOpTag (VecPackOp WordVec 8 W64) = 699-primOpTag (VecPackOp FloatVec 4 W32) = 700-primOpTag (VecPackOp FloatVec 2 W64) = 701-primOpTag (VecPackOp FloatVec 8 W32) = 702-primOpTag (VecPackOp FloatVec 4 W64) = 703-primOpTag (VecPackOp FloatVec 16 W32) = 704-primOpTag (VecPackOp FloatVec 8 W64) = 705-primOpTag (VecUnpackOp IntVec 16 W8) = 706-primOpTag (VecUnpackOp IntVec 8 W16) = 707-primOpTag (VecUnpackOp IntVec 4 W32) = 708-primOpTag (VecUnpackOp IntVec 2 W64) = 709-primOpTag (VecUnpackOp IntVec 32 W8) = 710-primOpTag (VecUnpackOp IntVec 16 W16) = 711-primOpTag (VecUnpackOp IntVec 8 W32) = 712-primOpTag (VecUnpackOp IntVec 4 W64) = 713-primOpTag (VecUnpackOp IntVec 64 W8) = 714-primOpTag (VecUnpackOp IntVec 32 W16) = 715-primOpTag (VecUnpackOp IntVec 16 W32) = 716-primOpTag (VecUnpackOp IntVec 8 W64) = 717-primOpTag (VecUnpackOp WordVec 16 W8) = 718-primOpTag (VecUnpackOp WordVec 8 W16) = 719-primOpTag (VecUnpackOp WordVec 4 W32) = 720-primOpTag (VecUnpackOp WordVec 2 W64) = 721-primOpTag (VecUnpackOp WordVec 32 W8) = 722-primOpTag (VecUnpackOp WordVec 16 W16) = 723-primOpTag (VecUnpackOp WordVec 8 W32) = 724-primOpTag (VecUnpackOp WordVec 4 W64) = 725-primOpTag (VecUnpackOp WordVec 64 W8) = 726-primOpTag (VecUnpackOp WordVec 32 W16) = 727-primOpTag (VecUnpackOp WordVec 16 W32) = 728-primOpTag (VecUnpackOp WordVec 8 W64) = 729-primOpTag (VecUnpackOp FloatVec 4 W32) = 730-primOpTag (VecUnpackOp FloatVec 2 W64) = 731-primOpTag (VecUnpackOp FloatVec 8 W32) = 732-primOpTag (VecUnpackOp FloatVec 4 W64) = 733-primOpTag (VecUnpackOp FloatVec 16 W32) = 734-primOpTag (VecUnpackOp FloatVec 8 W64) = 735-primOpTag (VecInsertOp IntVec 16 W8) = 736-primOpTag (VecInsertOp IntVec 8 W16) = 737-primOpTag (VecInsertOp IntVec 4 W32) = 738-primOpTag (VecInsertOp IntVec 2 W64) = 739-primOpTag (VecInsertOp IntVec 32 W8) = 740-primOpTag (VecInsertOp IntVec 16 W16) = 741-primOpTag (VecInsertOp IntVec 8 W32) = 742-primOpTag (VecInsertOp IntVec 4 W64) = 743-primOpTag (VecInsertOp IntVec 64 W8) = 744-primOpTag (VecInsertOp IntVec 32 W16) = 745-primOpTag (VecInsertOp IntVec 16 W32) = 746-primOpTag (VecInsertOp IntVec 8 W64) = 747-primOpTag (VecInsertOp WordVec 16 W8) = 748-primOpTag (VecInsertOp WordVec 8 W16) = 749-primOpTag (VecInsertOp WordVec 4 W32) = 750-primOpTag (VecInsertOp WordVec 2 W64) = 751-primOpTag (VecInsertOp WordVec 32 W8) = 752-primOpTag (VecInsertOp WordVec 16 W16) = 753-primOpTag (VecInsertOp WordVec 8 W32) = 754-primOpTag (VecInsertOp WordVec 4 W64) = 755-primOpTag (VecInsertOp WordVec 64 W8) = 756-primOpTag (VecInsertOp WordVec 32 W16) = 757-primOpTag (VecInsertOp WordVec 16 W32) = 758-primOpTag (VecInsertOp WordVec 8 W64) = 759-primOpTag (VecInsertOp FloatVec 4 W32) = 760-primOpTag (VecInsertOp FloatVec 2 W64) = 761-primOpTag (VecInsertOp FloatVec 8 W32) = 762-primOpTag (VecInsertOp FloatVec 4 W64) = 763-primOpTag (VecInsertOp FloatVec 16 W32) = 764-primOpTag (VecInsertOp FloatVec 8 W64) = 765-primOpTag (VecAddOp IntVec 16 W8) = 766-primOpTag (VecAddOp IntVec 8 W16) = 767-primOpTag (VecAddOp IntVec 4 W32) = 768-primOpTag (VecAddOp IntVec 2 W64) = 769-primOpTag (VecAddOp IntVec 32 W8) = 770-primOpTag (VecAddOp IntVec 16 W16) = 771-primOpTag (VecAddOp IntVec 8 W32) = 772-primOpTag (VecAddOp IntVec 4 W64) = 773-primOpTag (VecAddOp IntVec 64 W8) = 774-primOpTag (VecAddOp IntVec 32 W16) = 775-primOpTag (VecAddOp IntVec 16 W32) = 776-primOpTag (VecAddOp IntVec 8 W64) = 777-primOpTag (VecAddOp WordVec 16 W8) = 778-primOpTag (VecAddOp WordVec 8 W16) = 779-primOpTag (VecAddOp WordVec 4 W32) = 780-primOpTag (VecAddOp WordVec 2 W64) = 781-primOpTag (VecAddOp WordVec 32 W8) = 782-primOpTag (VecAddOp WordVec 16 W16) = 783-primOpTag (VecAddOp WordVec 8 W32) = 784-primOpTag (VecAddOp WordVec 4 W64) = 785-primOpTag (VecAddOp WordVec 64 W8) = 786-primOpTag (VecAddOp WordVec 32 W16) = 787-primOpTag (VecAddOp WordVec 16 W32) = 788-primOpTag (VecAddOp WordVec 8 W64) = 789-primOpTag (VecAddOp FloatVec 4 W32) = 790-primOpTag (VecAddOp FloatVec 2 W64) = 791-primOpTag (VecAddOp FloatVec 8 W32) = 792-primOpTag (VecAddOp FloatVec 4 W64) = 793-primOpTag (VecAddOp FloatVec 16 W32) = 794-primOpTag (VecAddOp FloatVec 8 W64) = 795-primOpTag (VecSubOp IntVec 16 W8) = 796-primOpTag (VecSubOp IntVec 8 W16) = 797-primOpTag (VecSubOp IntVec 4 W32) = 798-primOpTag (VecSubOp IntVec 2 W64) = 799-primOpTag (VecSubOp IntVec 32 W8) = 800-primOpTag (VecSubOp IntVec 16 W16) = 801-primOpTag (VecSubOp IntVec 8 W32) = 802-primOpTag (VecSubOp IntVec 4 W64) = 803-primOpTag (VecSubOp IntVec 64 W8) = 804-primOpTag (VecSubOp IntVec 32 W16) = 805-primOpTag (VecSubOp IntVec 16 W32) = 806-primOpTag (VecSubOp IntVec 8 W64) = 807-primOpTag (VecSubOp WordVec 16 W8) = 808-primOpTag (VecSubOp WordVec 8 W16) = 809-primOpTag (VecSubOp WordVec 4 W32) = 810-primOpTag (VecSubOp WordVec 2 W64) = 811-primOpTag (VecSubOp WordVec 32 W8) = 812-primOpTag (VecSubOp WordVec 16 W16) = 813-primOpTag (VecSubOp WordVec 8 W32) = 814-primOpTag (VecSubOp WordVec 4 W64) = 815-primOpTag (VecSubOp WordVec 64 W8) = 816-primOpTag (VecSubOp WordVec 32 W16) = 817-primOpTag (VecSubOp WordVec 16 W32) = 818-primOpTag (VecSubOp WordVec 8 W64) = 819-primOpTag (VecSubOp FloatVec 4 W32) = 820-primOpTag (VecSubOp FloatVec 2 W64) = 821-primOpTag (VecSubOp FloatVec 8 W32) = 822-primOpTag (VecSubOp FloatVec 4 W64) = 823-primOpTag (VecSubOp FloatVec 16 W32) = 824-primOpTag (VecSubOp FloatVec 8 W64) = 825-primOpTag (VecMulOp IntVec 16 W8) = 826-primOpTag (VecMulOp IntVec 8 W16) = 827-primOpTag (VecMulOp IntVec 4 W32) = 828-primOpTag (VecMulOp IntVec 2 W64) = 829-primOpTag (VecMulOp IntVec 32 W8) = 830-primOpTag (VecMulOp IntVec 16 W16) = 831-primOpTag (VecMulOp IntVec 8 W32) = 832-primOpTag (VecMulOp IntVec 4 W64) = 833-primOpTag (VecMulOp IntVec 64 W8) = 834-primOpTag (VecMulOp IntVec 32 W16) = 835-primOpTag (VecMulOp IntVec 16 W32) = 836-primOpTag (VecMulOp IntVec 8 W64) = 837-primOpTag (VecMulOp WordVec 16 W8) = 838-primOpTag (VecMulOp WordVec 8 W16) = 839-primOpTag (VecMulOp WordVec 4 W32) = 840-primOpTag (VecMulOp WordVec 2 W64) = 841-primOpTag (VecMulOp WordVec 32 W8) = 842-primOpTag (VecMulOp WordVec 16 W16) = 843-primOpTag (VecMulOp WordVec 8 W32) = 844-primOpTag (VecMulOp WordVec 4 W64) = 845-primOpTag (VecMulOp WordVec 64 W8) = 846-primOpTag (VecMulOp WordVec 32 W16) = 847-primOpTag (VecMulOp WordVec 16 W32) = 848-primOpTag (VecMulOp WordVec 8 W64) = 849-primOpTag (VecMulOp FloatVec 4 W32) = 850-primOpTag (VecMulOp FloatVec 2 W64) = 851-primOpTag (VecMulOp FloatVec 8 W32) = 852-primOpTag (VecMulOp FloatVec 4 W64) = 853-primOpTag (VecMulOp FloatVec 16 W32) = 854-primOpTag (VecMulOp FloatVec 8 W64) = 855-primOpTag (VecDivOp FloatVec 4 W32) = 856-primOpTag (VecDivOp FloatVec 2 W64) = 857-primOpTag (VecDivOp FloatVec 8 W32) = 858-primOpTag (VecDivOp FloatVec 4 W64) = 859-primOpTag (VecDivOp FloatVec 16 W32) = 860-primOpTag (VecDivOp FloatVec 8 W64) = 861-primOpTag (VecQuotOp IntVec 16 W8) = 862-primOpTag (VecQuotOp IntVec 8 W16) = 863-primOpTag (VecQuotOp IntVec 4 W32) = 864-primOpTag (VecQuotOp IntVec 2 W64) = 865-primOpTag (VecQuotOp IntVec 32 W8) = 866-primOpTag (VecQuotOp IntVec 16 W16) = 867-primOpTag (VecQuotOp IntVec 8 W32) = 868-primOpTag (VecQuotOp IntVec 4 W64) = 869-primOpTag (VecQuotOp IntVec 64 W8) = 870-primOpTag (VecQuotOp IntVec 32 W16) = 871-primOpTag (VecQuotOp IntVec 16 W32) = 872-primOpTag (VecQuotOp IntVec 8 W64) = 873-primOpTag (VecQuotOp WordVec 16 W8) = 874-primOpTag (VecQuotOp WordVec 8 W16) = 875-primOpTag (VecQuotOp WordVec 4 W32) = 876-primOpTag (VecQuotOp WordVec 2 W64) = 877-primOpTag (VecQuotOp WordVec 32 W8) = 878-primOpTag (VecQuotOp WordVec 16 W16) = 879-primOpTag (VecQuotOp WordVec 8 W32) = 880-primOpTag (VecQuotOp WordVec 4 W64) = 881-primOpTag (VecQuotOp WordVec 64 W8) = 882-primOpTag (VecQuotOp WordVec 32 W16) = 883-primOpTag (VecQuotOp WordVec 16 W32) = 884-primOpTag (VecQuotOp WordVec 8 W64) = 885-primOpTag (VecRemOp IntVec 16 W8) = 886-primOpTag (VecRemOp IntVec 8 W16) = 887-primOpTag (VecRemOp IntVec 4 W32) = 888-primOpTag (VecRemOp IntVec 2 W64) = 889-primOpTag (VecRemOp IntVec 32 W8) = 890-primOpTag (VecRemOp IntVec 16 W16) = 891-primOpTag (VecRemOp IntVec 8 W32) = 892-primOpTag (VecRemOp IntVec 4 W64) = 893-primOpTag (VecRemOp IntVec 64 W8) = 894-primOpTag (VecRemOp IntVec 32 W16) = 895-primOpTag (VecRemOp IntVec 16 W32) = 896-primOpTag (VecRemOp IntVec 8 W64) = 897-primOpTag (VecRemOp WordVec 16 W8) = 898-primOpTag (VecRemOp WordVec 8 W16) = 899-primOpTag (VecRemOp WordVec 4 W32) = 900-primOpTag (VecRemOp WordVec 2 W64) = 901-primOpTag (VecRemOp WordVec 32 W8) = 902-primOpTag (VecRemOp WordVec 16 W16) = 903-primOpTag (VecRemOp WordVec 8 W32) = 904-primOpTag (VecRemOp WordVec 4 W64) = 905-primOpTag (VecRemOp WordVec 64 W8) = 906-primOpTag (VecRemOp WordVec 32 W16) = 907-primOpTag (VecRemOp WordVec 16 W32) = 908-primOpTag (VecRemOp WordVec 8 W64) = 909-primOpTag (VecNegOp IntVec 16 W8) = 910-primOpTag (VecNegOp IntVec 8 W16) = 911-primOpTag (VecNegOp IntVec 4 W32) = 912-primOpTag (VecNegOp IntVec 2 W64) = 913-primOpTag (VecNegOp IntVec 32 W8) = 914-primOpTag (VecNegOp IntVec 16 W16) = 915-primOpTag (VecNegOp IntVec 8 W32) = 916-primOpTag (VecNegOp IntVec 4 W64) = 917-primOpTag (VecNegOp IntVec 64 W8) = 918-primOpTag (VecNegOp IntVec 32 W16) = 919-primOpTag (VecNegOp IntVec 16 W32) = 920-primOpTag (VecNegOp IntVec 8 W64) = 921-primOpTag (VecNegOp FloatVec 4 W32) = 922-primOpTag (VecNegOp FloatVec 2 W64) = 923-primOpTag (VecNegOp FloatVec 8 W32) = 924-primOpTag (VecNegOp FloatVec 4 W64) = 925-primOpTag (VecNegOp FloatVec 16 W32) = 926-primOpTag (VecNegOp FloatVec 8 W64) = 927-primOpTag (VecIndexByteArrayOp IntVec 16 W8) = 928-primOpTag (VecIndexByteArrayOp IntVec 8 W16) = 929-primOpTag (VecIndexByteArrayOp IntVec 4 W32) = 930-primOpTag (VecIndexByteArrayOp IntVec 2 W64) = 931-primOpTag (VecIndexByteArrayOp IntVec 32 W8) = 932-primOpTag (VecIndexByteArrayOp IntVec 16 W16) = 933-primOpTag (VecIndexByteArrayOp IntVec 8 W32) = 934-primOpTag (VecIndexByteArrayOp IntVec 4 W64) = 935-primOpTag (VecIndexByteArrayOp IntVec 64 W8) = 936-primOpTag (VecIndexByteArrayOp IntVec 32 W16) = 937-primOpTag (VecIndexByteArrayOp IntVec 16 W32) = 938-primOpTag (VecIndexByteArrayOp IntVec 8 W64) = 939-primOpTag (VecIndexByteArrayOp WordVec 16 W8) = 940-primOpTag (VecIndexByteArrayOp WordVec 8 W16) = 941-primOpTag (VecIndexByteArrayOp WordVec 4 W32) = 942-primOpTag (VecIndexByteArrayOp WordVec 2 W64) = 943-primOpTag (VecIndexByteArrayOp WordVec 32 W8) = 944-primOpTag (VecIndexByteArrayOp WordVec 16 W16) = 945-primOpTag (VecIndexByteArrayOp WordVec 8 W32) = 946-primOpTag (VecIndexByteArrayOp WordVec 4 W64) = 947-primOpTag (VecIndexByteArrayOp WordVec 64 W8) = 948-primOpTag (VecIndexByteArrayOp WordVec 32 W16) = 949-primOpTag (VecIndexByteArrayOp WordVec 16 W32) = 950-primOpTag (VecIndexByteArrayOp WordVec 8 W64) = 951-primOpTag (VecIndexByteArrayOp FloatVec 4 W32) = 952-primOpTag (VecIndexByteArrayOp FloatVec 2 W64) = 953-primOpTag (VecIndexByteArrayOp FloatVec 8 W32) = 954-primOpTag (VecIndexByteArrayOp FloatVec 4 W64) = 955-primOpTag (VecIndexByteArrayOp FloatVec 16 W32) = 956-primOpTag (VecIndexByteArrayOp FloatVec 8 W64) = 957-primOpTag (VecReadByteArrayOp IntVec 16 W8) = 958-primOpTag (VecReadByteArrayOp IntVec 8 W16) = 959-primOpTag (VecReadByteArrayOp IntVec 4 W32) = 960-primOpTag (VecReadByteArrayOp IntVec 2 W64) = 961-primOpTag (VecReadByteArrayOp IntVec 32 W8) = 962-primOpTag (VecReadByteArrayOp IntVec 16 W16) = 963-primOpTag (VecReadByteArrayOp IntVec 8 W32) = 964-primOpTag (VecReadByteArrayOp IntVec 4 W64) = 965-primOpTag (VecReadByteArrayOp IntVec 64 W8) = 966-primOpTag (VecReadByteArrayOp IntVec 32 W16) = 967-primOpTag (VecReadByteArrayOp IntVec 16 W32) = 968-primOpTag (VecReadByteArrayOp IntVec 8 W64) = 969-primOpTag (VecReadByteArrayOp WordVec 16 W8) = 970-primOpTag (VecReadByteArrayOp WordVec 8 W16) = 971-primOpTag (VecReadByteArrayOp WordVec 4 W32) = 972-primOpTag (VecReadByteArrayOp WordVec 2 W64) = 973-primOpTag (VecReadByteArrayOp WordVec 32 W8) = 974-primOpTag (VecReadByteArrayOp WordVec 16 W16) = 975-primOpTag (VecReadByteArrayOp WordVec 8 W32) = 976-primOpTag (VecReadByteArrayOp WordVec 4 W64) = 977-primOpTag (VecReadByteArrayOp WordVec 64 W8) = 978-primOpTag (VecReadByteArrayOp WordVec 32 W16) = 979-primOpTag (VecReadByteArrayOp WordVec 16 W32) = 980-primOpTag (VecReadByteArrayOp WordVec 8 W64) = 981-primOpTag (VecReadByteArrayOp FloatVec 4 W32) = 982-primOpTag (VecReadByteArrayOp FloatVec 2 W64) = 983-primOpTag (VecReadByteArrayOp FloatVec 8 W32) = 984-primOpTag (VecReadByteArrayOp FloatVec 4 W64) = 985-primOpTag (VecReadByteArrayOp FloatVec 16 W32) = 986-primOpTag (VecReadByteArrayOp FloatVec 8 W64) = 987-primOpTag (VecWriteByteArrayOp IntVec 16 W8) = 988-primOpTag (VecWriteByteArrayOp IntVec 8 W16) = 989-primOpTag (VecWriteByteArrayOp IntVec 4 W32) = 990-primOpTag (VecWriteByteArrayOp IntVec 2 W64) = 991-primOpTag (VecWriteByteArrayOp IntVec 32 W8) = 992-primOpTag (VecWriteByteArrayOp IntVec 16 W16) = 993-primOpTag (VecWriteByteArrayOp IntVec 8 W32) = 994-primOpTag (VecWriteByteArrayOp IntVec 4 W64) = 995-primOpTag (VecWriteByteArrayOp IntVec 64 W8) = 996-primOpTag (VecWriteByteArrayOp IntVec 32 W16) = 997-primOpTag (VecWriteByteArrayOp IntVec 16 W32) = 998-primOpTag (VecWriteByteArrayOp IntVec 8 W64) = 999-primOpTag (VecWriteByteArrayOp WordVec 16 W8) = 1000-primOpTag (VecWriteByteArrayOp WordVec 8 W16) = 1001-primOpTag (VecWriteByteArrayOp WordVec 4 W32) = 1002-primOpTag (VecWriteByteArrayOp WordVec 2 W64) = 1003-primOpTag (VecWriteByteArrayOp WordVec 32 W8) = 1004-primOpTag (VecWriteByteArrayOp WordVec 16 W16) = 1005-primOpTag (VecWriteByteArrayOp WordVec 8 W32) = 1006-primOpTag (VecWriteByteArrayOp WordVec 4 W64) = 1007-primOpTag (VecWriteByteArrayOp WordVec 64 W8) = 1008-primOpTag (VecWriteByteArrayOp WordVec 32 W16) = 1009-primOpTag (VecWriteByteArrayOp WordVec 16 W32) = 1010-primOpTag (VecWriteByteArrayOp WordVec 8 W64) = 1011-primOpTag (VecWriteByteArrayOp FloatVec 4 W32) = 1012-primOpTag (VecWriteByteArrayOp FloatVec 2 W64) = 1013-primOpTag (VecWriteByteArrayOp FloatVec 8 W32) = 1014-primOpTag (VecWriteByteArrayOp FloatVec 4 W64) = 1015-primOpTag (VecWriteByteArrayOp FloatVec 16 W32) = 1016-primOpTag (VecWriteByteArrayOp FloatVec 8 W64) = 1017-primOpTag (VecIndexOffAddrOp IntVec 16 W8) = 1018-primOpTag (VecIndexOffAddrOp IntVec 8 W16) = 1019-primOpTag (VecIndexOffAddrOp IntVec 4 W32) = 1020-primOpTag (VecIndexOffAddrOp IntVec 2 W64) = 1021-primOpTag (VecIndexOffAddrOp IntVec 32 W8) = 1022-primOpTag (VecIndexOffAddrOp IntVec 16 W16) = 1023-primOpTag (VecIndexOffAddrOp IntVec 8 W32) = 1024-primOpTag (VecIndexOffAddrOp IntVec 4 W64) = 1025-primOpTag (VecIndexOffAddrOp IntVec 64 W8) = 1026-primOpTag (VecIndexOffAddrOp IntVec 32 W16) = 1027-primOpTag (VecIndexOffAddrOp IntVec 16 W32) = 1028-primOpTag (VecIndexOffAddrOp IntVec 8 W64) = 1029-primOpTag (VecIndexOffAddrOp WordVec 16 W8) = 1030-primOpTag (VecIndexOffAddrOp WordVec 8 W16) = 1031-primOpTag (VecIndexOffAddrOp WordVec 4 W32) = 1032-primOpTag (VecIndexOffAddrOp WordVec 2 W64) = 1033-primOpTag (VecIndexOffAddrOp WordVec 32 W8) = 1034-primOpTag (VecIndexOffAddrOp WordVec 16 W16) = 1035-primOpTag (VecIndexOffAddrOp WordVec 8 W32) = 1036-primOpTag (VecIndexOffAddrOp WordVec 4 W64) = 1037-primOpTag (VecIndexOffAddrOp WordVec 64 W8) = 1038-primOpTag (VecIndexOffAddrOp WordVec 32 W16) = 1039-primOpTag (VecIndexOffAddrOp WordVec 16 W32) = 1040-primOpTag (VecIndexOffAddrOp WordVec 8 W64) = 1041-primOpTag (VecIndexOffAddrOp FloatVec 4 W32) = 1042-primOpTag (VecIndexOffAddrOp FloatVec 2 W64) = 1043-primOpTag (VecIndexOffAddrOp FloatVec 8 W32) = 1044-primOpTag (VecIndexOffAddrOp FloatVec 4 W64) = 1045-primOpTag (VecIndexOffAddrOp FloatVec 16 W32) = 1046-primOpTag (VecIndexOffAddrOp FloatVec 8 W64) = 1047-primOpTag (VecReadOffAddrOp IntVec 16 W8) = 1048-primOpTag (VecReadOffAddrOp IntVec 8 W16) = 1049-primOpTag (VecReadOffAddrOp IntVec 4 W32) = 1050-primOpTag (VecReadOffAddrOp IntVec 2 W64) = 1051-primOpTag (VecReadOffAddrOp IntVec 32 W8) = 1052-primOpTag (VecReadOffAddrOp IntVec 16 W16) = 1053-primOpTag (VecReadOffAddrOp IntVec 8 W32) = 1054-primOpTag (VecReadOffAddrOp IntVec 4 W64) = 1055-primOpTag (VecReadOffAddrOp IntVec 64 W8) = 1056-primOpTag (VecReadOffAddrOp IntVec 32 W16) = 1057-primOpTag (VecReadOffAddrOp IntVec 16 W32) = 1058-primOpTag (VecReadOffAddrOp IntVec 8 W64) = 1059-primOpTag (VecReadOffAddrOp WordVec 16 W8) = 1060-primOpTag (VecReadOffAddrOp WordVec 8 W16) = 1061-primOpTag (VecReadOffAddrOp WordVec 4 W32) = 1062-primOpTag (VecReadOffAddrOp WordVec 2 W64) = 1063-primOpTag (VecReadOffAddrOp WordVec 32 W8) = 1064-primOpTag (VecReadOffAddrOp WordVec 16 W16) = 1065-primOpTag (VecReadOffAddrOp WordVec 8 W32) = 1066-primOpTag (VecReadOffAddrOp WordVec 4 W64) = 1067-primOpTag (VecReadOffAddrOp WordVec 64 W8) = 1068-primOpTag (VecReadOffAddrOp WordVec 32 W16) = 1069-primOpTag (VecReadOffAddrOp WordVec 16 W32) = 1070-primOpTag (VecReadOffAddrOp WordVec 8 W64) = 1071-primOpTag (VecReadOffAddrOp FloatVec 4 W32) = 1072-primOpTag (VecReadOffAddrOp FloatVec 2 W64) = 1073-primOpTag (VecReadOffAddrOp FloatVec 8 W32) = 1074-primOpTag (VecReadOffAddrOp FloatVec 4 W64) = 1075-primOpTag (VecReadOffAddrOp FloatVec 16 W32) = 1076-primOpTag (VecReadOffAddrOp FloatVec 8 W64) = 1077-primOpTag (VecWriteOffAddrOp IntVec 16 W8) = 1078-primOpTag (VecWriteOffAddrOp IntVec 8 W16) = 1079-primOpTag (VecWriteOffAddrOp IntVec 4 W32) = 1080-primOpTag (VecWriteOffAddrOp IntVec 2 W64) = 1081-primOpTag (VecWriteOffAddrOp IntVec 32 W8) = 1082-primOpTag (VecWriteOffAddrOp IntVec 16 W16) = 1083-primOpTag (VecWriteOffAddrOp IntVec 8 W32) = 1084-primOpTag (VecWriteOffAddrOp IntVec 4 W64) = 1085-primOpTag (VecWriteOffAddrOp IntVec 64 W8) = 1086-primOpTag (VecWriteOffAddrOp IntVec 32 W16) = 1087-primOpTag (VecWriteOffAddrOp IntVec 16 W32) = 1088-primOpTag (VecWriteOffAddrOp IntVec 8 W64) = 1089-primOpTag (VecWriteOffAddrOp WordVec 16 W8) = 1090-primOpTag (VecWriteOffAddrOp WordVec 8 W16) = 1091-primOpTag (VecWriteOffAddrOp WordVec 4 W32) = 1092-primOpTag (VecWriteOffAddrOp WordVec 2 W64) = 1093-primOpTag (VecWriteOffAddrOp WordVec 32 W8) = 1094-primOpTag (VecWriteOffAddrOp WordVec 16 W16) = 1095-primOpTag (VecWriteOffAddrOp WordVec 8 W32) = 1096-primOpTag (VecWriteOffAddrOp WordVec 4 W64) = 1097-primOpTag (VecWriteOffAddrOp WordVec 64 W8) = 1098-primOpTag (VecWriteOffAddrOp WordVec 32 W16) = 1099-primOpTag (VecWriteOffAddrOp WordVec 16 W32) = 1100-primOpTag (VecWriteOffAddrOp WordVec 8 W64) = 1101-primOpTag (VecWriteOffAddrOp FloatVec 4 W32) = 1102-primOpTag (VecWriteOffAddrOp FloatVec 2 W64) = 1103-primOpTag (VecWriteOffAddrOp FloatVec 8 W32) = 1104-primOpTag (VecWriteOffAddrOp FloatVec 4 W64) = 1105-primOpTag (VecWriteOffAddrOp FloatVec 16 W32) = 1106-primOpTag (VecWriteOffAddrOp FloatVec 8 W64) = 1107-primOpTag (VecIndexScalarByteArrayOp IntVec 16 W8) = 1108-primOpTag (VecIndexScalarByteArrayOp IntVec 8 W16) = 1109-primOpTag (VecIndexScalarByteArrayOp IntVec 4 W32) = 1110-primOpTag (VecIndexScalarByteArrayOp IntVec 2 W64) = 1111-primOpTag (VecIndexScalarByteArrayOp IntVec 32 W8) = 1112-primOpTag (VecIndexScalarByteArrayOp IntVec 16 W16) = 1113-primOpTag (VecIndexScalarByteArrayOp IntVec 8 W32) = 1114-primOpTag (VecIndexScalarByteArrayOp IntVec 4 W64) = 1115-primOpTag (VecIndexScalarByteArrayOp IntVec 64 W8) = 1116-primOpTag (VecIndexScalarByteArrayOp IntVec 32 W16) = 1117-primOpTag (VecIndexScalarByteArrayOp IntVec 16 W32) = 1118-primOpTag (VecIndexScalarByteArrayOp IntVec 8 W64) = 1119-primOpTag (VecIndexScalarByteArrayOp WordVec 16 W8) = 1120-primOpTag (VecIndexScalarByteArrayOp WordVec 8 W16) = 1121-primOpTag (VecIndexScalarByteArrayOp WordVec 4 W32) = 1122-primOpTag (VecIndexScalarByteArrayOp WordVec 2 W64) = 1123-primOpTag (VecIndexScalarByteArrayOp WordVec 32 W8) = 1124-primOpTag (VecIndexScalarByteArrayOp WordVec 16 W16) = 1125-primOpTag (VecIndexScalarByteArrayOp WordVec 8 W32) = 1126-primOpTag (VecIndexScalarByteArrayOp WordVec 4 W64) = 1127-primOpTag (VecIndexScalarByteArrayOp WordVec 64 W8) = 1128-primOpTag (VecIndexScalarByteArrayOp WordVec 32 W16) = 1129-primOpTag (VecIndexScalarByteArrayOp WordVec 16 W32) = 1130-primOpTag (VecIndexScalarByteArrayOp WordVec 8 W64) = 1131-primOpTag (VecIndexScalarByteArrayOp FloatVec 4 W32) = 1132-primOpTag (VecIndexScalarByteArrayOp FloatVec 2 W64) = 1133-primOpTag (VecIndexScalarByteArrayOp FloatVec 8 W32) = 1134-primOpTag (VecIndexScalarByteArrayOp FloatVec 4 W64) = 1135-primOpTag (VecIndexScalarByteArrayOp FloatVec 16 W32) = 1136-primOpTag (VecIndexScalarByteArrayOp FloatVec 8 W64) = 1137-primOpTag (VecReadScalarByteArrayOp IntVec 16 W8) = 1138-primOpTag (VecReadScalarByteArrayOp IntVec 8 W16) = 1139-primOpTag (VecReadScalarByteArrayOp IntVec 4 W32) = 1140-primOpTag (VecReadScalarByteArrayOp IntVec 2 W64) = 1141-primOpTag (VecReadScalarByteArrayOp IntVec 32 W8) = 1142-primOpTag (VecReadScalarByteArrayOp IntVec 16 W16) = 1143-primOpTag (VecReadScalarByteArrayOp IntVec 8 W32) = 1144-primOpTag (VecReadScalarByteArrayOp IntVec 4 W64) = 1145-primOpTag (VecReadScalarByteArrayOp IntVec 64 W8) = 1146-primOpTag (VecReadScalarByteArrayOp IntVec 32 W16) = 1147-primOpTag (VecReadScalarByteArrayOp IntVec 16 W32) = 1148-primOpTag (VecReadScalarByteArrayOp IntVec 8 W64) = 1149-primOpTag (VecReadScalarByteArrayOp WordVec 16 W8) = 1150-primOpTag (VecReadScalarByteArrayOp WordVec 8 W16) = 1151-primOpTag (VecReadScalarByteArrayOp WordVec 4 W32) = 1152-primOpTag (VecReadScalarByteArrayOp WordVec 2 W64) = 1153-primOpTag (VecReadScalarByteArrayOp WordVec 32 W8) = 1154-primOpTag (VecReadScalarByteArrayOp WordVec 16 W16) = 1155-primOpTag (VecReadScalarByteArrayOp WordVec 8 W32) = 1156-primOpTag (VecReadScalarByteArrayOp WordVec 4 W64) = 1157-primOpTag (VecReadScalarByteArrayOp WordVec 64 W8) = 1158-primOpTag (VecReadScalarByteArrayOp WordVec 32 W16) = 1159-primOpTag (VecReadScalarByteArrayOp WordVec 16 W32) = 1160-primOpTag (VecReadScalarByteArrayOp WordVec 8 W64) = 1161-primOpTag (VecReadScalarByteArrayOp FloatVec 4 W32) = 1162-primOpTag (VecReadScalarByteArrayOp FloatVec 2 W64) = 1163-primOpTag (VecReadScalarByteArrayOp FloatVec 8 W32) = 1164-primOpTag (VecReadScalarByteArrayOp FloatVec 4 W64) = 1165-primOpTag (VecReadScalarByteArrayOp FloatVec 16 W32) = 1166-primOpTag (VecReadScalarByteArrayOp FloatVec 8 W64) = 1167-primOpTag (VecWriteScalarByteArrayOp IntVec 16 W8) = 1168-primOpTag (VecWriteScalarByteArrayOp IntVec 8 W16) = 1169-primOpTag (VecWriteScalarByteArrayOp IntVec 4 W32) = 1170-primOpTag (VecWriteScalarByteArrayOp IntVec 2 W64) = 1171-primOpTag (VecWriteScalarByteArrayOp IntVec 32 W8) = 1172-primOpTag (VecWriteScalarByteArrayOp IntVec 16 W16) = 1173-primOpTag (VecWriteScalarByteArrayOp IntVec 8 W32) = 1174-primOpTag (VecWriteScalarByteArrayOp IntVec 4 W64) = 1175-primOpTag (VecWriteScalarByteArrayOp IntVec 64 W8) = 1176-primOpTag (VecWriteScalarByteArrayOp IntVec 32 W16) = 1177-primOpTag (VecWriteScalarByteArrayOp IntVec 16 W32) = 1178-primOpTag (VecWriteScalarByteArrayOp IntVec 8 W64) = 1179-primOpTag (VecWriteScalarByteArrayOp WordVec 16 W8) = 1180-primOpTag (VecWriteScalarByteArrayOp WordVec 8 W16) = 1181-primOpTag (VecWriteScalarByteArrayOp WordVec 4 W32) = 1182-primOpTag (VecWriteScalarByteArrayOp WordVec 2 W64) = 1183-primOpTag (VecWriteScalarByteArrayOp WordVec 32 W8) = 1184-primOpTag (VecWriteScalarByteArrayOp WordVec 16 W16) = 1185-primOpTag (VecWriteScalarByteArrayOp WordVec 8 W32) = 1186-primOpTag (VecWriteScalarByteArrayOp WordVec 4 W64) = 1187-primOpTag (VecWriteScalarByteArrayOp WordVec 64 W8) = 1188-primOpTag (VecWriteScalarByteArrayOp WordVec 32 W16) = 1189-primOpTag (VecWriteScalarByteArrayOp WordVec 16 W32) = 1190-primOpTag (VecWriteScalarByteArrayOp WordVec 8 W64) = 1191-primOpTag (VecWriteScalarByteArrayOp FloatVec 4 W32) = 1192-primOpTag (VecWriteScalarByteArrayOp FloatVec 2 W64) = 1193-primOpTag (VecWriteScalarByteArrayOp FloatVec 8 W32) = 1194-primOpTag (VecWriteScalarByteArrayOp FloatVec 4 W64) = 1195-primOpTag (VecWriteScalarByteArrayOp FloatVec 16 W32) = 1196-primOpTag (VecWriteScalarByteArrayOp FloatVec 8 W64) = 1197-primOpTag (VecIndexScalarOffAddrOp IntVec 16 W8) = 1198-primOpTag (VecIndexScalarOffAddrOp IntVec 8 W16) = 1199-primOpTag (VecIndexScalarOffAddrOp IntVec 4 W32) = 1200-primOpTag (VecIndexScalarOffAddrOp IntVec 2 W64) = 1201-primOpTag (VecIndexScalarOffAddrOp IntVec 32 W8) = 1202-primOpTag (VecIndexScalarOffAddrOp IntVec 16 W16) = 1203-primOpTag (VecIndexScalarOffAddrOp IntVec 8 W32) = 1204-primOpTag (VecIndexScalarOffAddrOp IntVec 4 W64) = 1205-primOpTag (VecIndexScalarOffAddrOp IntVec 64 W8) = 1206-primOpTag (VecIndexScalarOffAddrOp IntVec 32 W16) = 1207-primOpTag (VecIndexScalarOffAddrOp IntVec 16 W32) = 1208-primOpTag (VecIndexScalarOffAddrOp IntVec 8 W64) = 1209-primOpTag (VecIndexScalarOffAddrOp WordVec 16 W8) = 1210-primOpTag (VecIndexScalarOffAddrOp WordVec 8 W16) = 1211-primOpTag (VecIndexScalarOffAddrOp WordVec 4 W32) = 1212-primOpTag (VecIndexScalarOffAddrOp WordVec 2 W64) = 1213-primOpTag (VecIndexScalarOffAddrOp WordVec 32 W8) = 1214-primOpTag (VecIndexScalarOffAddrOp WordVec 16 W16) = 1215-primOpTag (VecIndexScalarOffAddrOp WordVec 8 W32) = 1216-primOpTag (VecIndexScalarOffAddrOp WordVec 4 W64) = 1217-primOpTag (VecIndexScalarOffAddrOp WordVec 64 W8) = 1218-primOpTag (VecIndexScalarOffAddrOp WordVec 32 W16) = 1219-primOpTag (VecIndexScalarOffAddrOp WordVec 16 W32) = 1220-primOpTag (VecIndexScalarOffAddrOp WordVec 8 W64) = 1221-primOpTag (VecIndexScalarOffAddrOp FloatVec 4 W32) = 1222-primOpTag (VecIndexScalarOffAddrOp FloatVec 2 W64) = 1223-primOpTag (VecIndexScalarOffAddrOp FloatVec 8 W32) = 1224-primOpTag (VecIndexScalarOffAddrOp FloatVec 4 W64) = 1225-primOpTag (VecIndexScalarOffAddrOp FloatVec 16 W32) = 1226-primOpTag (VecIndexScalarOffAddrOp FloatVec 8 W64) = 1227-primOpTag (VecReadScalarOffAddrOp IntVec 16 W8) = 1228-primOpTag (VecReadScalarOffAddrOp IntVec 8 W16) = 1229-primOpTag (VecReadScalarOffAddrOp IntVec 4 W32) = 1230-primOpTag (VecReadScalarOffAddrOp IntVec 2 W64) = 1231-primOpTag (VecReadScalarOffAddrOp IntVec 32 W8) = 1232-primOpTag (VecReadScalarOffAddrOp IntVec 16 W16) = 1233-primOpTag (VecReadScalarOffAddrOp IntVec 8 W32) = 1234-primOpTag (VecReadScalarOffAddrOp IntVec 4 W64) = 1235-primOpTag (VecReadScalarOffAddrOp IntVec 64 W8) = 1236-primOpTag (VecReadScalarOffAddrOp IntVec 32 W16) = 1237-primOpTag (VecReadScalarOffAddrOp IntVec 16 W32) = 1238-primOpTag (VecReadScalarOffAddrOp IntVec 8 W64) = 1239-primOpTag (VecReadScalarOffAddrOp WordVec 16 W8) = 1240-primOpTag (VecReadScalarOffAddrOp WordVec 8 W16) = 1241-primOpTag (VecReadScalarOffAddrOp WordVec 4 W32) = 1242-primOpTag (VecReadScalarOffAddrOp WordVec 2 W64) = 1243-primOpTag (VecReadScalarOffAddrOp WordVec 32 W8) = 1244-primOpTag (VecReadScalarOffAddrOp WordVec 16 W16) = 1245-primOpTag (VecReadScalarOffAddrOp WordVec 8 W32) = 1246-primOpTag (VecReadScalarOffAddrOp WordVec 4 W64) = 1247-primOpTag (VecReadScalarOffAddrOp WordVec 64 W8) = 1248-primOpTag (VecReadScalarOffAddrOp WordVec 32 W16) = 1249-primOpTag (VecReadScalarOffAddrOp WordVec 16 W32) = 1250-primOpTag (VecReadScalarOffAddrOp WordVec 8 W64) = 1251-primOpTag (VecReadScalarOffAddrOp FloatVec 4 W32) = 1252-primOpTag (VecReadScalarOffAddrOp FloatVec 2 W64) = 1253-primOpTag (VecReadScalarOffAddrOp FloatVec 8 W32) = 1254-primOpTag (VecReadScalarOffAddrOp FloatVec 4 W64) = 1255-primOpTag (VecReadScalarOffAddrOp FloatVec 16 W32) = 1256-primOpTag (VecReadScalarOffAddrOp FloatVec 8 W64) = 1257-primOpTag (VecWriteScalarOffAddrOp IntVec 16 W8) = 1258-primOpTag (VecWriteScalarOffAddrOp IntVec 8 W16) = 1259-primOpTag (VecWriteScalarOffAddrOp IntVec 4 W32) = 1260-primOpTag (VecWriteScalarOffAddrOp IntVec 2 W64) = 1261-primOpTag (VecWriteScalarOffAddrOp IntVec 32 W8) = 1262-primOpTag (VecWriteScalarOffAddrOp IntVec 16 W16) = 1263-primOpTag (VecWriteScalarOffAddrOp IntVec 8 W32) = 1264-primOpTag (VecWriteScalarOffAddrOp IntVec 4 W64) = 1265-primOpTag (VecWriteScalarOffAddrOp IntVec 64 W8) = 1266-primOpTag (VecWriteScalarOffAddrOp IntVec 32 W16) = 1267-primOpTag (VecWriteScalarOffAddrOp IntVec 16 W32) = 1268-primOpTag (VecWriteScalarOffAddrOp IntVec 8 W64) = 1269-primOpTag (VecWriteScalarOffAddrOp WordVec 16 W8) = 1270-primOpTag (VecWriteScalarOffAddrOp WordVec 8 W16) = 1271-primOpTag (VecWriteScalarOffAddrOp WordVec 4 W32) = 1272-primOpTag (VecWriteScalarOffAddrOp WordVec 2 W64) = 1273-primOpTag (VecWriteScalarOffAddrOp WordVec 32 W8) = 1274-primOpTag (VecWriteScalarOffAddrOp WordVec 16 W16) = 1275-primOpTag (VecWriteScalarOffAddrOp WordVec 8 W32) = 1276-primOpTag (VecWriteScalarOffAddrOp WordVec 4 W64) = 1277-primOpTag (VecWriteScalarOffAddrOp WordVec 64 W8) = 1278-primOpTag (VecWriteScalarOffAddrOp WordVec 32 W16) = 1279-primOpTag (VecWriteScalarOffAddrOp WordVec 16 W32) = 1280-primOpTag (VecWriteScalarOffAddrOp WordVec 8 W64) = 1281-primOpTag (VecWriteScalarOffAddrOp FloatVec 4 W32) = 1282-primOpTag (VecWriteScalarOffAddrOp FloatVec 2 W64) = 1283-primOpTag (VecWriteScalarOffAddrOp FloatVec 8 W32) = 1284-primOpTag (VecWriteScalarOffAddrOp FloatVec 4 W64) = 1285-primOpTag (VecWriteScalarOffAddrOp FloatVec 16 W32) = 1286-primOpTag (VecWriteScalarOffAddrOp FloatVec 8 W64) = 1287-primOpTag PrefetchByteArrayOp3 = 1288-primOpTag PrefetchMutableByteArrayOp3 = 1289-primOpTag PrefetchAddrOp3 = 1290-primOpTag PrefetchValueOp3 = 1291-primOpTag PrefetchByteArrayOp2 = 1292-primOpTag PrefetchMutableByteArrayOp2 = 1293-primOpTag PrefetchAddrOp2 = 1294-primOpTag PrefetchValueOp2 = 1295-primOpTag PrefetchByteArrayOp1 = 1296-primOpTag PrefetchMutableByteArrayOp1 = 1297-primOpTag PrefetchAddrOp1 = 1298-primOpTag PrefetchValueOp1 = 1299-primOpTag PrefetchByteArrayOp0 = 1300-primOpTag PrefetchMutableByteArrayOp0 = 1301-primOpTag PrefetchAddrOp0 = 1302-primOpTag PrefetchValueOp0 = 1303+maxPrimOpTag = 1311+primOpTag :: PrimOp -> Int+primOpTag CharGtOp = 0+primOpTag CharGeOp = 1+primOpTag CharEqOp = 2+primOpTag CharNeOp = 3+primOpTag CharLtOp = 4+primOpTag CharLeOp = 5+primOpTag OrdOp = 6+primOpTag Int8ToIntOp = 7+primOpTag IntToInt8Op = 8+primOpTag Int8NegOp = 9+primOpTag Int8AddOp = 10+primOpTag Int8SubOp = 11+primOpTag Int8MulOp = 12+primOpTag Int8QuotOp = 13+primOpTag Int8RemOp = 14+primOpTag Int8QuotRemOp = 15+primOpTag Int8SllOp = 16+primOpTag Int8SraOp = 17+primOpTag Int8SrlOp = 18+primOpTag Int8ToWord8Op = 19+primOpTag Int8EqOp = 20+primOpTag Int8GeOp = 21+primOpTag Int8GtOp = 22+primOpTag Int8LeOp = 23+primOpTag Int8LtOp = 24+primOpTag Int8NeOp = 25+primOpTag Word8ToWordOp = 26+primOpTag WordToWord8Op = 27+primOpTag Word8AddOp = 28+primOpTag Word8SubOp = 29+primOpTag Word8MulOp = 30+primOpTag Word8QuotOp = 31+primOpTag Word8RemOp = 32+primOpTag Word8QuotRemOp = 33+primOpTag Word8AndOp = 34+primOpTag Word8OrOp = 35+primOpTag Word8XorOp = 36+primOpTag Word8NotOp = 37+primOpTag Word8SllOp = 38+primOpTag Word8SrlOp = 39+primOpTag Word8ToInt8Op = 40+primOpTag Word8EqOp = 41+primOpTag Word8GeOp = 42+primOpTag Word8GtOp = 43+primOpTag Word8LeOp = 44+primOpTag Word8LtOp = 45+primOpTag Word8NeOp = 46+primOpTag Int16ToIntOp = 47+primOpTag IntToInt16Op = 48+primOpTag Int16NegOp = 49+primOpTag Int16AddOp = 50+primOpTag Int16SubOp = 51+primOpTag Int16MulOp = 52+primOpTag Int16QuotOp = 53+primOpTag Int16RemOp = 54+primOpTag Int16QuotRemOp = 55+primOpTag Int16SllOp = 56+primOpTag Int16SraOp = 57+primOpTag Int16SrlOp = 58+primOpTag Int16ToWord16Op = 59+primOpTag Int16EqOp = 60+primOpTag Int16GeOp = 61+primOpTag Int16GtOp = 62+primOpTag Int16LeOp = 63+primOpTag Int16LtOp = 64+primOpTag Int16NeOp = 65+primOpTag Word16ToWordOp = 66+primOpTag WordToWord16Op = 67+primOpTag Word16AddOp = 68+primOpTag Word16SubOp = 69+primOpTag Word16MulOp = 70+primOpTag Word16QuotOp = 71+primOpTag Word16RemOp = 72+primOpTag Word16QuotRemOp = 73+primOpTag Word16AndOp = 74+primOpTag Word16OrOp = 75+primOpTag Word16XorOp = 76+primOpTag Word16NotOp = 77+primOpTag Word16SllOp = 78+primOpTag Word16SrlOp = 79+primOpTag Word16ToInt16Op = 80+primOpTag Word16EqOp = 81+primOpTag Word16GeOp = 82+primOpTag Word16GtOp = 83+primOpTag Word16LeOp = 84+primOpTag Word16LtOp = 85+primOpTag Word16NeOp = 86+primOpTag Int32ToIntOp = 87+primOpTag IntToInt32Op = 88+primOpTag Int32NegOp = 89+primOpTag Int32AddOp = 90+primOpTag Int32SubOp = 91+primOpTag Int32MulOp = 92+primOpTag Int32QuotOp = 93+primOpTag Int32RemOp = 94+primOpTag Int32QuotRemOp = 95+primOpTag Int32SllOp = 96+primOpTag Int32SraOp = 97+primOpTag Int32SrlOp = 98+primOpTag Int32ToWord32Op = 99+primOpTag Int32EqOp = 100+primOpTag Int32GeOp = 101+primOpTag Int32GtOp = 102+primOpTag Int32LeOp = 103+primOpTag Int32LtOp = 104+primOpTag Int32NeOp = 105+primOpTag Word32ToWordOp = 106+primOpTag WordToWord32Op = 107+primOpTag Word32AddOp = 108+primOpTag Word32SubOp = 109+primOpTag Word32MulOp = 110+primOpTag Word32QuotOp = 111+primOpTag Word32RemOp = 112+primOpTag Word32QuotRemOp = 113+primOpTag Word32AndOp = 114+primOpTag Word32OrOp = 115+primOpTag Word32XorOp = 116+primOpTag Word32NotOp = 117+primOpTag Word32SllOp = 118+primOpTag Word32SrlOp = 119+primOpTag Word32ToInt32Op = 120+primOpTag Word32EqOp = 121+primOpTag Word32GeOp = 122+primOpTag Word32GtOp = 123+primOpTag Word32LeOp = 124+primOpTag Word32LtOp = 125+primOpTag Word32NeOp = 126+primOpTag Int64ToIntOp = 127+primOpTag IntToInt64Op = 128+primOpTag Int64NegOp = 129+primOpTag Int64AddOp = 130+primOpTag Int64SubOp = 131+primOpTag Int64MulOp = 132+primOpTag Int64QuotOp = 133+primOpTag Int64RemOp = 134+primOpTag Int64SllOp = 135+primOpTag Int64SraOp = 136+primOpTag Int64SrlOp = 137+primOpTag Int64ToWord64Op = 138+primOpTag Int64EqOp = 139+primOpTag Int64GeOp = 140+primOpTag Int64GtOp = 141+primOpTag Int64LeOp = 142+primOpTag Int64LtOp = 143+primOpTag Int64NeOp = 144+primOpTag Word64ToWordOp = 145+primOpTag WordToWord64Op = 146+primOpTag Word64AddOp = 147+primOpTag Word64SubOp = 148+primOpTag Word64MulOp = 149+primOpTag Word64QuotOp = 150+primOpTag Word64RemOp = 151+primOpTag Word64AndOp = 152+primOpTag Word64OrOp = 153+primOpTag Word64XorOp = 154+primOpTag Word64NotOp = 155+primOpTag Word64SllOp = 156+primOpTag Word64SrlOp = 157+primOpTag Word64ToInt64Op = 158+primOpTag Word64EqOp = 159+primOpTag Word64GeOp = 160+primOpTag Word64GtOp = 161+primOpTag Word64LeOp = 162+primOpTag Word64LtOp = 163+primOpTag Word64NeOp = 164+primOpTag IntAddOp = 165+primOpTag IntSubOp = 166+primOpTag IntMulOp = 167+primOpTag IntMul2Op = 168+primOpTag IntMulMayOfloOp = 169+primOpTag IntQuotOp = 170+primOpTag IntRemOp = 171+primOpTag IntQuotRemOp = 172+primOpTag IntAndOp = 173+primOpTag IntOrOp = 174+primOpTag IntXorOp = 175+primOpTag IntNotOp = 176+primOpTag IntNegOp = 177+primOpTag IntAddCOp = 178+primOpTag IntSubCOp = 179+primOpTag IntGtOp = 180+primOpTag IntGeOp = 181+primOpTag IntEqOp = 182+primOpTag IntNeOp = 183+primOpTag IntLtOp = 184+primOpTag IntLeOp = 185+primOpTag ChrOp = 186+primOpTag IntToWordOp = 187+primOpTag IntToFloatOp = 188+primOpTag IntToDoubleOp = 189+primOpTag WordToFloatOp = 190+primOpTag WordToDoubleOp = 191+primOpTag IntSllOp = 192+primOpTag IntSraOp = 193+primOpTag IntSrlOp = 194+primOpTag WordAddOp = 195+primOpTag WordAddCOp = 196+primOpTag WordSubCOp = 197+primOpTag WordAdd2Op = 198+primOpTag WordSubOp = 199+primOpTag WordMulOp = 200+primOpTag WordMul2Op = 201+primOpTag WordQuotOp = 202+primOpTag WordRemOp = 203+primOpTag WordQuotRemOp = 204+primOpTag WordQuotRem2Op = 205+primOpTag WordAndOp = 206+primOpTag WordOrOp = 207+primOpTag WordXorOp = 208+primOpTag WordNotOp = 209+primOpTag WordSllOp = 210+primOpTag WordSrlOp = 211+primOpTag WordToIntOp = 212+primOpTag WordGtOp = 213+primOpTag WordGeOp = 214+primOpTag WordEqOp = 215+primOpTag WordNeOp = 216+primOpTag WordLtOp = 217+primOpTag WordLeOp = 218+primOpTag PopCnt8Op = 219+primOpTag PopCnt16Op = 220+primOpTag PopCnt32Op = 221+primOpTag PopCnt64Op = 222+primOpTag PopCntOp = 223+primOpTag Pdep8Op = 224+primOpTag Pdep16Op = 225+primOpTag Pdep32Op = 226+primOpTag Pdep64Op = 227+primOpTag PdepOp = 228+primOpTag Pext8Op = 229+primOpTag Pext16Op = 230+primOpTag Pext32Op = 231+primOpTag Pext64Op = 232+primOpTag PextOp = 233+primOpTag Clz8Op = 234+primOpTag Clz16Op = 235+primOpTag Clz32Op = 236+primOpTag Clz64Op = 237+primOpTag ClzOp = 238+primOpTag Ctz8Op = 239+primOpTag Ctz16Op = 240+primOpTag Ctz32Op = 241+primOpTag Ctz64Op = 242+primOpTag CtzOp = 243+primOpTag BSwap16Op = 244+primOpTag BSwap32Op = 245+primOpTag BSwap64Op = 246+primOpTag BSwapOp = 247+primOpTag BRev8Op = 248+primOpTag BRev16Op = 249+primOpTag BRev32Op = 250+primOpTag BRev64Op = 251+primOpTag BRevOp = 252+primOpTag Narrow8IntOp = 253+primOpTag Narrow16IntOp = 254+primOpTag Narrow32IntOp = 255+primOpTag Narrow8WordOp = 256+primOpTag Narrow16WordOp = 257+primOpTag Narrow32WordOp = 258+primOpTag DoubleGtOp = 259+primOpTag DoubleGeOp = 260+primOpTag DoubleEqOp = 261+primOpTag DoubleNeOp = 262+primOpTag DoubleLtOp = 263+primOpTag DoubleLeOp = 264+primOpTag DoubleAddOp = 265+primOpTag DoubleSubOp = 266+primOpTag DoubleMulOp = 267+primOpTag DoubleDivOp = 268+primOpTag DoubleNegOp = 269+primOpTag DoubleFabsOp = 270+primOpTag DoubleToIntOp = 271+primOpTag DoubleToFloatOp = 272+primOpTag DoubleExpOp = 273+primOpTag DoubleExpM1Op = 274+primOpTag DoubleLogOp = 275+primOpTag DoubleLog1POp = 276+primOpTag DoubleSqrtOp = 277+primOpTag DoubleSinOp = 278+primOpTag DoubleCosOp = 279+primOpTag DoubleTanOp = 280+primOpTag DoubleAsinOp = 281+primOpTag DoubleAcosOp = 282+primOpTag DoubleAtanOp = 283+primOpTag DoubleSinhOp = 284+primOpTag DoubleCoshOp = 285+primOpTag DoubleTanhOp = 286+primOpTag DoubleAsinhOp = 287+primOpTag DoubleAcoshOp = 288+primOpTag DoubleAtanhOp = 289+primOpTag DoublePowerOp = 290+primOpTag DoubleDecode_2IntOp = 291+primOpTag DoubleDecode_Int64Op = 292+primOpTag FloatGtOp = 293+primOpTag FloatGeOp = 294+primOpTag FloatEqOp = 295+primOpTag FloatNeOp = 296+primOpTag FloatLtOp = 297+primOpTag FloatLeOp = 298+primOpTag FloatAddOp = 299+primOpTag FloatSubOp = 300+primOpTag FloatMulOp = 301+primOpTag FloatDivOp = 302+primOpTag FloatNegOp = 303+primOpTag FloatFabsOp = 304+primOpTag FloatToIntOp = 305+primOpTag FloatExpOp = 306+primOpTag FloatExpM1Op = 307+primOpTag FloatLogOp = 308+primOpTag FloatLog1POp = 309+primOpTag FloatSqrtOp = 310+primOpTag FloatSinOp = 311+primOpTag FloatCosOp = 312+primOpTag FloatTanOp = 313+primOpTag FloatAsinOp = 314+primOpTag FloatAcosOp = 315+primOpTag FloatAtanOp = 316+primOpTag FloatSinhOp = 317+primOpTag FloatCoshOp = 318+primOpTag FloatTanhOp = 319+primOpTag FloatAsinhOp = 320+primOpTag FloatAcoshOp = 321+primOpTag FloatAtanhOp = 322+primOpTag FloatPowerOp = 323+primOpTag FloatToDoubleOp = 324+primOpTag FloatDecode_IntOp = 325+primOpTag NewArrayOp = 326+primOpTag ReadArrayOp = 327+primOpTag WriteArrayOp = 328+primOpTag SizeofArrayOp = 329+primOpTag SizeofMutableArrayOp = 330+primOpTag IndexArrayOp = 331+primOpTag UnsafeFreezeArrayOp = 332+primOpTag UnsafeThawArrayOp = 333+primOpTag CopyArrayOp = 334+primOpTag CopyMutableArrayOp = 335+primOpTag CloneArrayOp = 336+primOpTag CloneMutableArrayOp = 337+primOpTag FreezeArrayOp = 338+primOpTag ThawArrayOp = 339+primOpTag CasArrayOp = 340+primOpTag NewSmallArrayOp = 341+primOpTag ShrinkSmallMutableArrayOp_Char = 342+primOpTag ReadSmallArrayOp = 343+primOpTag WriteSmallArrayOp = 344+primOpTag SizeofSmallArrayOp = 345+primOpTag SizeofSmallMutableArrayOp = 346+primOpTag GetSizeofSmallMutableArrayOp = 347+primOpTag IndexSmallArrayOp = 348+primOpTag UnsafeFreezeSmallArrayOp = 349+primOpTag UnsafeThawSmallArrayOp = 350+primOpTag CopySmallArrayOp = 351+primOpTag CopySmallMutableArrayOp = 352+primOpTag CloneSmallArrayOp = 353+primOpTag CloneSmallMutableArrayOp = 354+primOpTag FreezeSmallArrayOp = 355+primOpTag ThawSmallArrayOp = 356+primOpTag CasSmallArrayOp = 357+primOpTag NewByteArrayOp_Char = 358+primOpTag NewPinnedByteArrayOp_Char = 359+primOpTag NewAlignedPinnedByteArrayOp_Char = 360+primOpTag MutableByteArrayIsPinnedOp = 361+primOpTag ByteArrayIsPinnedOp = 362+primOpTag ByteArrayContents_Char = 363+primOpTag MutableByteArrayContents_Char = 364+primOpTag ShrinkMutableByteArrayOp_Char = 365+primOpTag ResizeMutableByteArrayOp_Char = 366+primOpTag UnsafeFreezeByteArrayOp = 367+primOpTag SizeofByteArrayOp = 368+primOpTag SizeofMutableByteArrayOp = 369+primOpTag GetSizeofMutableByteArrayOp = 370+primOpTag IndexByteArrayOp_Char = 371+primOpTag IndexByteArrayOp_WideChar = 372+primOpTag IndexByteArrayOp_Int = 373+primOpTag IndexByteArrayOp_Word = 374+primOpTag IndexByteArrayOp_Addr = 375+primOpTag IndexByteArrayOp_Float = 376+primOpTag IndexByteArrayOp_Double = 377+primOpTag IndexByteArrayOp_StablePtr = 378+primOpTag IndexByteArrayOp_Int8 = 379+primOpTag IndexByteArrayOp_Int16 = 380+primOpTag IndexByteArrayOp_Int32 = 381+primOpTag IndexByteArrayOp_Int64 = 382+primOpTag IndexByteArrayOp_Word8 = 383+primOpTag IndexByteArrayOp_Word16 = 384+primOpTag IndexByteArrayOp_Word32 = 385+primOpTag IndexByteArrayOp_Word64 = 386+primOpTag IndexByteArrayOp_Word8AsChar = 387+primOpTag IndexByteArrayOp_Word8AsWideChar = 388+primOpTag IndexByteArrayOp_Word8AsInt = 389+primOpTag IndexByteArrayOp_Word8AsWord = 390+primOpTag IndexByteArrayOp_Word8AsAddr = 391+primOpTag IndexByteArrayOp_Word8AsFloat = 392+primOpTag IndexByteArrayOp_Word8AsDouble = 393+primOpTag IndexByteArrayOp_Word8AsStablePtr = 394+primOpTag IndexByteArrayOp_Word8AsInt16 = 395+primOpTag IndexByteArrayOp_Word8AsInt32 = 396+primOpTag IndexByteArrayOp_Word8AsInt64 = 397+primOpTag IndexByteArrayOp_Word8AsWord16 = 398+primOpTag IndexByteArrayOp_Word8AsWord32 = 399+primOpTag IndexByteArrayOp_Word8AsWord64 = 400+primOpTag ReadByteArrayOp_Char = 401+primOpTag ReadByteArrayOp_WideChar = 402+primOpTag ReadByteArrayOp_Int = 403+primOpTag ReadByteArrayOp_Word = 404+primOpTag ReadByteArrayOp_Addr = 405+primOpTag ReadByteArrayOp_Float = 406+primOpTag ReadByteArrayOp_Double = 407+primOpTag ReadByteArrayOp_StablePtr = 408+primOpTag ReadByteArrayOp_Int8 = 409+primOpTag ReadByteArrayOp_Int16 = 410+primOpTag ReadByteArrayOp_Int32 = 411+primOpTag ReadByteArrayOp_Int64 = 412+primOpTag ReadByteArrayOp_Word8 = 413+primOpTag ReadByteArrayOp_Word16 = 414+primOpTag ReadByteArrayOp_Word32 = 415+primOpTag ReadByteArrayOp_Word64 = 416+primOpTag ReadByteArrayOp_Word8AsChar = 417+primOpTag ReadByteArrayOp_Word8AsWideChar = 418+primOpTag ReadByteArrayOp_Word8AsInt = 419+primOpTag ReadByteArrayOp_Word8AsWord = 420+primOpTag ReadByteArrayOp_Word8AsAddr = 421+primOpTag ReadByteArrayOp_Word8AsFloat = 422+primOpTag ReadByteArrayOp_Word8AsDouble = 423+primOpTag ReadByteArrayOp_Word8AsStablePtr = 424+primOpTag ReadByteArrayOp_Word8AsInt16 = 425+primOpTag ReadByteArrayOp_Word8AsInt32 = 426+primOpTag ReadByteArrayOp_Word8AsInt64 = 427+primOpTag ReadByteArrayOp_Word8AsWord16 = 428+primOpTag ReadByteArrayOp_Word8AsWord32 = 429+primOpTag ReadByteArrayOp_Word8AsWord64 = 430+primOpTag WriteByteArrayOp_Char = 431+primOpTag WriteByteArrayOp_WideChar = 432+primOpTag WriteByteArrayOp_Int = 433+primOpTag WriteByteArrayOp_Word = 434+primOpTag WriteByteArrayOp_Addr = 435+primOpTag WriteByteArrayOp_Float = 436+primOpTag WriteByteArrayOp_Double = 437+primOpTag WriteByteArrayOp_StablePtr = 438+primOpTag WriteByteArrayOp_Int8 = 439+primOpTag WriteByteArrayOp_Int16 = 440+primOpTag WriteByteArrayOp_Int32 = 441+primOpTag WriteByteArrayOp_Int64 = 442+primOpTag WriteByteArrayOp_Word8 = 443+primOpTag WriteByteArrayOp_Word16 = 444+primOpTag WriteByteArrayOp_Word32 = 445+primOpTag WriteByteArrayOp_Word64 = 446+primOpTag WriteByteArrayOp_Word8AsChar = 447+primOpTag WriteByteArrayOp_Word8AsWideChar = 448+primOpTag WriteByteArrayOp_Word8AsInt = 449+primOpTag WriteByteArrayOp_Word8AsWord = 450+primOpTag WriteByteArrayOp_Word8AsAddr = 451+primOpTag WriteByteArrayOp_Word8AsFloat = 452+primOpTag WriteByteArrayOp_Word8AsDouble = 453+primOpTag WriteByteArrayOp_Word8AsStablePtr = 454+primOpTag WriteByteArrayOp_Word8AsInt16 = 455+primOpTag WriteByteArrayOp_Word8AsInt32 = 456+primOpTag WriteByteArrayOp_Word8AsInt64 = 457+primOpTag WriteByteArrayOp_Word8AsWord16 = 458+primOpTag WriteByteArrayOp_Word8AsWord32 = 459+primOpTag WriteByteArrayOp_Word8AsWord64 = 460+primOpTag CompareByteArraysOp = 461+primOpTag CopyByteArrayOp = 462+primOpTag CopyMutableByteArrayOp = 463+primOpTag CopyByteArrayToAddrOp = 464+primOpTag CopyMutableByteArrayToAddrOp = 465+primOpTag CopyAddrToByteArrayOp = 466+primOpTag SetByteArrayOp = 467+primOpTag AtomicReadByteArrayOp_Int = 468+primOpTag AtomicWriteByteArrayOp_Int = 469+primOpTag CasByteArrayOp_Int = 470+primOpTag CasByteArrayOp_Int8 = 471+primOpTag CasByteArrayOp_Int16 = 472+primOpTag CasByteArrayOp_Int32 = 473+primOpTag CasByteArrayOp_Int64 = 474+primOpTag FetchAddByteArrayOp_Int = 475+primOpTag FetchSubByteArrayOp_Int = 476+primOpTag FetchAndByteArrayOp_Int = 477+primOpTag FetchNandByteArrayOp_Int = 478+primOpTag FetchOrByteArrayOp_Int = 479+primOpTag FetchXorByteArrayOp_Int = 480+primOpTag AddrAddOp = 481+primOpTag AddrSubOp = 482+primOpTag AddrRemOp = 483+primOpTag AddrToIntOp = 484+primOpTag IntToAddrOp = 485+primOpTag AddrGtOp = 486+primOpTag AddrGeOp = 487+primOpTag AddrEqOp = 488+primOpTag AddrNeOp = 489+primOpTag AddrLtOp = 490+primOpTag AddrLeOp = 491+primOpTag IndexOffAddrOp_Char = 492+primOpTag IndexOffAddrOp_WideChar = 493+primOpTag IndexOffAddrOp_Int = 494+primOpTag IndexOffAddrOp_Word = 495+primOpTag IndexOffAddrOp_Addr = 496+primOpTag IndexOffAddrOp_Float = 497+primOpTag IndexOffAddrOp_Double = 498+primOpTag IndexOffAddrOp_StablePtr = 499+primOpTag IndexOffAddrOp_Int8 = 500+primOpTag IndexOffAddrOp_Int16 = 501+primOpTag IndexOffAddrOp_Int32 = 502+primOpTag IndexOffAddrOp_Int64 = 503+primOpTag IndexOffAddrOp_Word8 = 504+primOpTag IndexOffAddrOp_Word16 = 505+primOpTag IndexOffAddrOp_Word32 = 506+primOpTag IndexOffAddrOp_Word64 = 507+primOpTag ReadOffAddrOp_Char = 508+primOpTag ReadOffAddrOp_WideChar = 509+primOpTag ReadOffAddrOp_Int = 510+primOpTag ReadOffAddrOp_Word = 511+primOpTag ReadOffAddrOp_Addr = 512+primOpTag ReadOffAddrOp_Float = 513+primOpTag ReadOffAddrOp_Double = 514+primOpTag ReadOffAddrOp_StablePtr = 515+primOpTag ReadOffAddrOp_Int8 = 516+primOpTag ReadOffAddrOp_Int16 = 517+primOpTag ReadOffAddrOp_Int32 = 518+primOpTag ReadOffAddrOp_Int64 = 519+primOpTag ReadOffAddrOp_Word8 = 520+primOpTag ReadOffAddrOp_Word16 = 521+primOpTag ReadOffAddrOp_Word32 = 522+primOpTag ReadOffAddrOp_Word64 = 523+primOpTag WriteOffAddrOp_Char = 524+primOpTag WriteOffAddrOp_WideChar = 525+primOpTag WriteOffAddrOp_Int = 526+primOpTag WriteOffAddrOp_Word = 527+primOpTag WriteOffAddrOp_Addr = 528+primOpTag WriteOffAddrOp_Float = 529+primOpTag WriteOffAddrOp_Double = 530+primOpTag WriteOffAddrOp_StablePtr = 531+primOpTag WriteOffAddrOp_Int8 = 532+primOpTag WriteOffAddrOp_Int16 = 533+primOpTag WriteOffAddrOp_Int32 = 534+primOpTag WriteOffAddrOp_Int64 = 535+primOpTag WriteOffAddrOp_Word8 = 536+primOpTag WriteOffAddrOp_Word16 = 537+primOpTag WriteOffAddrOp_Word32 = 538+primOpTag WriteOffAddrOp_Word64 = 539+primOpTag InterlockedExchange_Addr = 540+primOpTag InterlockedExchange_Word = 541+primOpTag CasAddrOp_Addr = 542+primOpTag CasAddrOp_Word = 543+primOpTag CasAddrOp_Word8 = 544+primOpTag CasAddrOp_Word16 = 545+primOpTag CasAddrOp_Word32 = 546+primOpTag CasAddrOp_Word64 = 547+primOpTag FetchAddAddrOp_Word = 548+primOpTag FetchSubAddrOp_Word = 549+primOpTag FetchAndAddrOp_Word = 550+primOpTag FetchNandAddrOp_Word = 551+primOpTag FetchOrAddrOp_Word = 552+primOpTag FetchXorAddrOp_Word = 553+primOpTag AtomicReadAddrOp_Word = 554+primOpTag AtomicWriteAddrOp_Word = 555+primOpTag NewMutVarOp = 556+primOpTag ReadMutVarOp = 557+primOpTag WriteMutVarOp = 558+primOpTag AtomicModifyMutVar2Op = 559+primOpTag AtomicModifyMutVar_Op = 560+primOpTag CasMutVarOp = 561+primOpTag CatchOp = 562+primOpTag RaiseOp = 563+primOpTag RaiseUnderflowOp = 564+primOpTag RaiseOverflowOp = 565+primOpTag RaiseDivZeroOp = 566+primOpTag RaiseIOOp = 567+primOpTag MaskAsyncExceptionsOp = 568+primOpTag MaskUninterruptibleOp = 569+primOpTag UnmaskAsyncExceptionsOp = 570+primOpTag MaskStatus = 571+primOpTag NewPromptTagOp = 572+primOpTag PromptOp = 573+primOpTag Control0Op = 574+primOpTag AtomicallyOp = 575+primOpTag RetryOp = 576+primOpTag CatchRetryOp = 577+primOpTag CatchSTMOp = 578+primOpTag NewTVarOp = 579+primOpTag ReadTVarOp = 580+primOpTag ReadTVarIOOp = 581+primOpTag WriteTVarOp = 582+primOpTag NewMVarOp = 583+primOpTag TakeMVarOp = 584+primOpTag TryTakeMVarOp = 585+primOpTag PutMVarOp = 586+primOpTag TryPutMVarOp = 587+primOpTag ReadMVarOp = 588+primOpTag TryReadMVarOp = 589+primOpTag IsEmptyMVarOp = 590+primOpTag NewIOPortOp = 591+primOpTag ReadIOPortOp = 592+primOpTag WriteIOPortOp = 593+primOpTag DelayOp = 594+primOpTag WaitReadOp = 595+primOpTag WaitWriteOp = 596+primOpTag ForkOp = 597+primOpTag ForkOnOp = 598+primOpTag KillThreadOp = 599+primOpTag YieldOp = 600+primOpTag MyThreadIdOp = 601+primOpTag LabelThreadOp = 602+primOpTag IsCurrentThreadBoundOp = 603+primOpTag NoDuplicateOp = 604+primOpTag GetThreadLabelOp = 605+primOpTag ThreadStatusOp = 606+primOpTag ListThreadsOp = 607+primOpTag MkWeakOp = 608+primOpTag MkWeakNoFinalizerOp = 609+primOpTag AddCFinalizerToWeakOp = 610+primOpTag DeRefWeakOp = 611+primOpTag FinalizeWeakOp = 612+primOpTag TouchOp = 613+primOpTag MakeStablePtrOp = 614+primOpTag DeRefStablePtrOp = 615+primOpTag EqStablePtrOp = 616+primOpTag MakeStableNameOp = 617+primOpTag StableNameToIntOp = 618+primOpTag CompactNewOp = 619+primOpTag CompactResizeOp = 620+primOpTag CompactContainsOp = 621+primOpTag CompactContainsAnyOp = 622+primOpTag CompactGetFirstBlockOp = 623+primOpTag CompactGetNextBlockOp = 624+primOpTag CompactAllocateBlockOp = 625+primOpTag CompactFixupPointersOp = 626+primOpTag CompactAdd = 627+primOpTag CompactAddWithSharing = 628+primOpTag CompactSize = 629+primOpTag ReallyUnsafePtrEqualityOp = 630+primOpTag ParOp = 631+primOpTag SparkOp = 632+primOpTag SeqOp = 633+primOpTag GetSparkOp = 634+primOpTag NumSparks = 635+primOpTag KeepAliveOp = 636+primOpTag DataToTagOp = 637+primOpTag TagToEnumOp = 638+primOpTag AddrToAnyOp = 639+primOpTag AnyToAddrOp = 640+primOpTag MkApUpd0_Op = 641+primOpTag NewBCOOp = 642+primOpTag UnpackClosureOp = 643+primOpTag ClosureSizeOp = 644+primOpTag GetApStackValOp = 645+primOpTag GetCCSOfOp = 646+primOpTag GetCurrentCCSOp = 647+primOpTag ClearCCSOp = 648+primOpTag WhereFromOp = 649+primOpTag TraceEventOp = 650+primOpTag TraceEventBinaryOp = 651+primOpTag TraceMarkerOp = 652+primOpTag SetThreadAllocationCounter = 653+primOpTag (VecBroadcastOp IntVec 16 W8) = 654+primOpTag (VecBroadcastOp IntVec 8 W16) = 655+primOpTag (VecBroadcastOp IntVec 4 W32) = 656+primOpTag (VecBroadcastOp IntVec 2 W64) = 657+primOpTag (VecBroadcastOp IntVec 32 W8) = 658+primOpTag (VecBroadcastOp IntVec 16 W16) = 659+primOpTag (VecBroadcastOp IntVec 8 W32) = 660+primOpTag (VecBroadcastOp IntVec 4 W64) = 661+primOpTag (VecBroadcastOp IntVec 64 W8) = 662+primOpTag (VecBroadcastOp IntVec 32 W16) = 663+primOpTag (VecBroadcastOp IntVec 16 W32) = 664+primOpTag (VecBroadcastOp IntVec 8 W64) = 665+primOpTag (VecBroadcastOp WordVec 16 W8) = 666+primOpTag (VecBroadcastOp WordVec 8 W16) = 667+primOpTag (VecBroadcastOp WordVec 4 W32) = 668+primOpTag (VecBroadcastOp WordVec 2 W64) = 669+primOpTag (VecBroadcastOp WordVec 32 W8) = 670+primOpTag (VecBroadcastOp WordVec 16 W16) = 671+primOpTag (VecBroadcastOp WordVec 8 W32) = 672+primOpTag (VecBroadcastOp WordVec 4 W64) = 673+primOpTag (VecBroadcastOp WordVec 64 W8) = 674+primOpTag (VecBroadcastOp WordVec 32 W16) = 675+primOpTag (VecBroadcastOp WordVec 16 W32) = 676+primOpTag (VecBroadcastOp WordVec 8 W64) = 677+primOpTag (VecBroadcastOp FloatVec 4 W32) = 678+primOpTag (VecBroadcastOp FloatVec 2 W64) = 679+primOpTag (VecBroadcastOp FloatVec 8 W32) = 680+primOpTag (VecBroadcastOp FloatVec 4 W64) = 681+primOpTag (VecBroadcastOp FloatVec 16 W32) = 682+primOpTag (VecBroadcastOp FloatVec 8 W64) = 683+primOpTag (VecPackOp IntVec 16 W8) = 684+primOpTag (VecPackOp IntVec 8 W16) = 685+primOpTag (VecPackOp IntVec 4 W32) = 686+primOpTag (VecPackOp IntVec 2 W64) = 687+primOpTag (VecPackOp IntVec 32 W8) = 688+primOpTag (VecPackOp IntVec 16 W16) = 689+primOpTag (VecPackOp IntVec 8 W32) = 690+primOpTag (VecPackOp IntVec 4 W64) = 691+primOpTag (VecPackOp IntVec 64 W8) = 692+primOpTag (VecPackOp IntVec 32 W16) = 693+primOpTag (VecPackOp IntVec 16 W32) = 694+primOpTag (VecPackOp IntVec 8 W64) = 695+primOpTag (VecPackOp WordVec 16 W8) = 696+primOpTag (VecPackOp WordVec 8 W16) = 697+primOpTag (VecPackOp WordVec 4 W32) = 698+primOpTag (VecPackOp WordVec 2 W64) = 699+primOpTag (VecPackOp WordVec 32 W8) = 700+primOpTag (VecPackOp WordVec 16 W16) = 701+primOpTag (VecPackOp WordVec 8 W32) = 702+primOpTag (VecPackOp WordVec 4 W64) = 703+primOpTag (VecPackOp WordVec 64 W8) = 704+primOpTag (VecPackOp WordVec 32 W16) = 705+primOpTag (VecPackOp WordVec 16 W32) = 706+primOpTag (VecPackOp WordVec 8 W64) = 707+primOpTag (VecPackOp FloatVec 4 W32) = 708+primOpTag (VecPackOp FloatVec 2 W64) = 709+primOpTag (VecPackOp FloatVec 8 W32) = 710+primOpTag (VecPackOp FloatVec 4 W64) = 711+primOpTag (VecPackOp FloatVec 16 W32) = 712+primOpTag (VecPackOp FloatVec 8 W64) = 713+primOpTag (VecUnpackOp IntVec 16 W8) = 714+primOpTag (VecUnpackOp IntVec 8 W16) = 715+primOpTag (VecUnpackOp IntVec 4 W32) = 716+primOpTag (VecUnpackOp IntVec 2 W64) = 717+primOpTag (VecUnpackOp IntVec 32 W8) = 718+primOpTag (VecUnpackOp IntVec 16 W16) = 719+primOpTag (VecUnpackOp IntVec 8 W32) = 720+primOpTag (VecUnpackOp IntVec 4 W64) = 721+primOpTag (VecUnpackOp IntVec 64 W8) = 722+primOpTag (VecUnpackOp IntVec 32 W16) = 723+primOpTag (VecUnpackOp IntVec 16 W32) = 724+primOpTag (VecUnpackOp IntVec 8 W64) = 725+primOpTag (VecUnpackOp WordVec 16 W8) = 726+primOpTag (VecUnpackOp WordVec 8 W16) = 727+primOpTag (VecUnpackOp WordVec 4 W32) = 728+primOpTag (VecUnpackOp WordVec 2 W64) = 729+primOpTag (VecUnpackOp WordVec 32 W8) = 730+primOpTag (VecUnpackOp WordVec 16 W16) = 731+primOpTag (VecUnpackOp WordVec 8 W32) = 732+primOpTag (VecUnpackOp WordVec 4 W64) = 733+primOpTag (VecUnpackOp WordVec 64 W8) = 734+primOpTag (VecUnpackOp WordVec 32 W16) = 735+primOpTag (VecUnpackOp WordVec 16 W32) = 736+primOpTag (VecUnpackOp WordVec 8 W64) = 737+primOpTag (VecUnpackOp FloatVec 4 W32) = 738+primOpTag (VecUnpackOp FloatVec 2 W64) = 739+primOpTag (VecUnpackOp FloatVec 8 W32) = 740+primOpTag (VecUnpackOp FloatVec 4 W64) = 741+primOpTag (VecUnpackOp FloatVec 16 W32) = 742+primOpTag (VecUnpackOp FloatVec 8 W64) = 743+primOpTag (VecInsertOp IntVec 16 W8) = 744+primOpTag (VecInsertOp IntVec 8 W16) = 745+primOpTag (VecInsertOp IntVec 4 W32) = 746+primOpTag (VecInsertOp IntVec 2 W64) = 747+primOpTag (VecInsertOp IntVec 32 W8) = 748+primOpTag (VecInsertOp IntVec 16 W16) = 749+primOpTag (VecInsertOp IntVec 8 W32) = 750+primOpTag (VecInsertOp IntVec 4 W64) = 751+primOpTag (VecInsertOp IntVec 64 W8) = 752+primOpTag (VecInsertOp IntVec 32 W16) = 753+primOpTag (VecInsertOp IntVec 16 W32) = 754+primOpTag (VecInsertOp IntVec 8 W64) = 755+primOpTag (VecInsertOp WordVec 16 W8) = 756+primOpTag (VecInsertOp WordVec 8 W16) = 757+primOpTag (VecInsertOp WordVec 4 W32) = 758+primOpTag (VecInsertOp WordVec 2 W64) = 759+primOpTag (VecInsertOp WordVec 32 W8) = 760+primOpTag (VecInsertOp WordVec 16 W16) = 761+primOpTag (VecInsertOp WordVec 8 W32) = 762+primOpTag (VecInsertOp WordVec 4 W64) = 763+primOpTag (VecInsertOp WordVec 64 W8) = 764+primOpTag (VecInsertOp WordVec 32 W16) = 765+primOpTag (VecInsertOp WordVec 16 W32) = 766+primOpTag (VecInsertOp WordVec 8 W64) = 767+primOpTag (VecInsertOp FloatVec 4 W32) = 768+primOpTag (VecInsertOp FloatVec 2 W64) = 769+primOpTag (VecInsertOp FloatVec 8 W32) = 770+primOpTag (VecInsertOp FloatVec 4 W64) = 771+primOpTag (VecInsertOp FloatVec 16 W32) = 772+primOpTag (VecInsertOp FloatVec 8 W64) = 773+primOpTag (VecAddOp IntVec 16 W8) = 774+primOpTag (VecAddOp IntVec 8 W16) = 775+primOpTag (VecAddOp IntVec 4 W32) = 776+primOpTag (VecAddOp IntVec 2 W64) = 777+primOpTag (VecAddOp IntVec 32 W8) = 778+primOpTag (VecAddOp IntVec 16 W16) = 779+primOpTag (VecAddOp IntVec 8 W32) = 780+primOpTag (VecAddOp IntVec 4 W64) = 781+primOpTag (VecAddOp IntVec 64 W8) = 782+primOpTag (VecAddOp IntVec 32 W16) = 783+primOpTag (VecAddOp IntVec 16 W32) = 784+primOpTag (VecAddOp IntVec 8 W64) = 785+primOpTag (VecAddOp WordVec 16 W8) = 786+primOpTag (VecAddOp WordVec 8 W16) = 787+primOpTag (VecAddOp WordVec 4 W32) = 788+primOpTag (VecAddOp WordVec 2 W64) = 789+primOpTag (VecAddOp WordVec 32 W8) = 790+primOpTag (VecAddOp WordVec 16 W16) = 791+primOpTag (VecAddOp WordVec 8 W32) = 792+primOpTag (VecAddOp WordVec 4 W64) = 793+primOpTag (VecAddOp WordVec 64 W8) = 794+primOpTag (VecAddOp WordVec 32 W16) = 795+primOpTag (VecAddOp WordVec 16 W32) = 796+primOpTag (VecAddOp WordVec 8 W64) = 797+primOpTag (VecAddOp FloatVec 4 W32) = 798+primOpTag (VecAddOp FloatVec 2 W64) = 799+primOpTag (VecAddOp FloatVec 8 W32) = 800+primOpTag (VecAddOp FloatVec 4 W64) = 801+primOpTag (VecAddOp FloatVec 16 W32) = 802+primOpTag (VecAddOp FloatVec 8 W64) = 803+primOpTag (VecSubOp IntVec 16 W8) = 804+primOpTag (VecSubOp IntVec 8 W16) = 805+primOpTag (VecSubOp IntVec 4 W32) = 806+primOpTag (VecSubOp IntVec 2 W64) = 807+primOpTag (VecSubOp IntVec 32 W8) = 808+primOpTag (VecSubOp IntVec 16 W16) = 809+primOpTag (VecSubOp IntVec 8 W32) = 810+primOpTag (VecSubOp IntVec 4 W64) = 811+primOpTag (VecSubOp IntVec 64 W8) = 812+primOpTag (VecSubOp IntVec 32 W16) = 813+primOpTag (VecSubOp IntVec 16 W32) = 814+primOpTag (VecSubOp IntVec 8 W64) = 815+primOpTag (VecSubOp WordVec 16 W8) = 816+primOpTag (VecSubOp WordVec 8 W16) = 817+primOpTag (VecSubOp WordVec 4 W32) = 818+primOpTag (VecSubOp WordVec 2 W64) = 819+primOpTag (VecSubOp WordVec 32 W8) = 820+primOpTag (VecSubOp WordVec 16 W16) = 821+primOpTag (VecSubOp WordVec 8 W32) = 822+primOpTag (VecSubOp WordVec 4 W64) = 823+primOpTag (VecSubOp WordVec 64 W8) = 824+primOpTag (VecSubOp WordVec 32 W16) = 825+primOpTag (VecSubOp WordVec 16 W32) = 826+primOpTag (VecSubOp WordVec 8 W64) = 827+primOpTag (VecSubOp FloatVec 4 W32) = 828+primOpTag (VecSubOp FloatVec 2 W64) = 829+primOpTag (VecSubOp FloatVec 8 W32) = 830+primOpTag (VecSubOp FloatVec 4 W64) = 831+primOpTag (VecSubOp FloatVec 16 W32) = 832+primOpTag (VecSubOp FloatVec 8 W64) = 833+primOpTag (VecMulOp IntVec 16 W8) = 834+primOpTag (VecMulOp IntVec 8 W16) = 835+primOpTag (VecMulOp IntVec 4 W32) = 836+primOpTag (VecMulOp IntVec 2 W64) = 837+primOpTag (VecMulOp IntVec 32 W8) = 838+primOpTag (VecMulOp IntVec 16 W16) = 839+primOpTag (VecMulOp IntVec 8 W32) = 840+primOpTag (VecMulOp IntVec 4 W64) = 841+primOpTag (VecMulOp IntVec 64 W8) = 842+primOpTag (VecMulOp IntVec 32 W16) = 843+primOpTag (VecMulOp IntVec 16 W32) = 844+primOpTag (VecMulOp IntVec 8 W64) = 845+primOpTag (VecMulOp WordVec 16 W8) = 846+primOpTag (VecMulOp WordVec 8 W16) = 847+primOpTag (VecMulOp WordVec 4 W32) = 848+primOpTag (VecMulOp WordVec 2 W64) = 849+primOpTag (VecMulOp WordVec 32 W8) = 850+primOpTag (VecMulOp WordVec 16 W16) = 851+primOpTag (VecMulOp WordVec 8 W32) = 852+primOpTag (VecMulOp WordVec 4 W64) = 853+primOpTag (VecMulOp WordVec 64 W8) = 854+primOpTag (VecMulOp WordVec 32 W16) = 855+primOpTag (VecMulOp WordVec 16 W32) = 856+primOpTag (VecMulOp WordVec 8 W64) = 857+primOpTag (VecMulOp FloatVec 4 W32) = 858+primOpTag (VecMulOp FloatVec 2 W64) = 859+primOpTag (VecMulOp FloatVec 8 W32) = 860+primOpTag (VecMulOp FloatVec 4 W64) = 861+primOpTag (VecMulOp FloatVec 16 W32) = 862+primOpTag (VecMulOp FloatVec 8 W64) = 863+primOpTag (VecDivOp FloatVec 4 W32) = 864+primOpTag (VecDivOp FloatVec 2 W64) = 865+primOpTag (VecDivOp FloatVec 8 W32) = 866+primOpTag (VecDivOp FloatVec 4 W64) = 867+primOpTag (VecDivOp FloatVec 16 W32) = 868+primOpTag (VecDivOp FloatVec 8 W64) = 869+primOpTag (VecQuotOp IntVec 16 W8) = 870+primOpTag (VecQuotOp IntVec 8 W16) = 871+primOpTag (VecQuotOp IntVec 4 W32) = 872+primOpTag (VecQuotOp IntVec 2 W64) = 873+primOpTag (VecQuotOp IntVec 32 W8) = 874+primOpTag (VecQuotOp IntVec 16 W16) = 875+primOpTag (VecQuotOp IntVec 8 W32) = 876+primOpTag (VecQuotOp IntVec 4 W64) = 877+primOpTag (VecQuotOp IntVec 64 W8) = 878+primOpTag (VecQuotOp IntVec 32 W16) = 879+primOpTag (VecQuotOp IntVec 16 W32) = 880+primOpTag (VecQuotOp IntVec 8 W64) = 881+primOpTag (VecQuotOp WordVec 16 W8) = 882+primOpTag (VecQuotOp WordVec 8 W16) = 883+primOpTag (VecQuotOp WordVec 4 W32) = 884+primOpTag (VecQuotOp WordVec 2 W64) = 885+primOpTag (VecQuotOp WordVec 32 W8) = 886+primOpTag (VecQuotOp WordVec 16 W16) = 887+primOpTag (VecQuotOp WordVec 8 W32) = 888+primOpTag (VecQuotOp WordVec 4 W64) = 889+primOpTag (VecQuotOp WordVec 64 W8) = 890+primOpTag (VecQuotOp WordVec 32 W16) = 891+primOpTag (VecQuotOp WordVec 16 W32) = 892+primOpTag (VecQuotOp WordVec 8 W64) = 893+primOpTag (VecRemOp IntVec 16 W8) = 894+primOpTag (VecRemOp IntVec 8 W16) = 895+primOpTag (VecRemOp IntVec 4 W32) = 896+primOpTag (VecRemOp IntVec 2 W64) = 897+primOpTag (VecRemOp IntVec 32 W8) = 898+primOpTag (VecRemOp IntVec 16 W16) = 899+primOpTag (VecRemOp IntVec 8 W32) = 900+primOpTag (VecRemOp IntVec 4 W64) = 901+primOpTag (VecRemOp IntVec 64 W8) = 902+primOpTag (VecRemOp IntVec 32 W16) = 903+primOpTag (VecRemOp IntVec 16 W32) = 904+primOpTag (VecRemOp IntVec 8 W64) = 905+primOpTag (VecRemOp WordVec 16 W8) = 906+primOpTag (VecRemOp WordVec 8 W16) = 907+primOpTag (VecRemOp WordVec 4 W32) = 908+primOpTag (VecRemOp WordVec 2 W64) = 909+primOpTag (VecRemOp WordVec 32 W8) = 910+primOpTag (VecRemOp WordVec 16 W16) = 911+primOpTag (VecRemOp WordVec 8 W32) = 912+primOpTag (VecRemOp WordVec 4 W64) = 913+primOpTag (VecRemOp WordVec 64 W8) = 914+primOpTag (VecRemOp WordVec 32 W16) = 915+primOpTag (VecRemOp WordVec 16 W32) = 916+primOpTag (VecRemOp WordVec 8 W64) = 917+primOpTag (VecNegOp IntVec 16 W8) = 918+primOpTag (VecNegOp IntVec 8 W16) = 919+primOpTag (VecNegOp IntVec 4 W32) = 920+primOpTag (VecNegOp IntVec 2 W64) = 921+primOpTag (VecNegOp IntVec 32 W8) = 922+primOpTag (VecNegOp IntVec 16 W16) = 923+primOpTag (VecNegOp IntVec 8 W32) = 924+primOpTag (VecNegOp IntVec 4 W64) = 925+primOpTag (VecNegOp IntVec 64 W8) = 926+primOpTag (VecNegOp IntVec 32 W16) = 927+primOpTag (VecNegOp IntVec 16 W32) = 928+primOpTag (VecNegOp IntVec 8 W64) = 929+primOpTag (VecNegOp FloatVec 4 W32) = 930+primOpTag (VecNegOp FloatVec 2 W64) = 931+primOpTag (VecNegOp FloatVec 8 W32) = 932+primOpTag (VecNegOp FloatVec 4 W64) = 933+primOpTag (VecNegOp FloatVec 16 W32) = 934+primOpTag (VecNegOp FloatVec 8 W64) = 935+primOpTag (VecIndexByteArrayOp IntVec 16 W8) = 936+primOpTag (VecIndexByteArrayOp IntVec 8 W16) = 937+primOpTag (VecIndexByteArrayOp IntVec 4 W32) = 938+primOpTag (VecIndexByteArrayOp IntVec 2 W64) = 939+primOpTag (VecIndexByteArrayOp IntVec 32 W8) = 940+primOpTag (VecIndexByteArrayOp IntVec 16 W16) = 941+primOpTag (VecIndexByteArrayOp IntVec 8 W32) = 942+primOpTag (VecIndexByteArrayOp IntVec 4 W64) = 943+primOpTag (VecIndexByteArrayOp IntVec 64 W8) = 944+primOpTag (VecIndexByteArrayOp IntVec 32 W16) = 945+primOpTag (VecIndexByteArrayOp IntVec 16 W32) = 946+primOpTag (VecIndexByteArrayOp IntVec 8 W64) = 947+primOpTag (VecIndexByteArrayOp WordVec 16 W8) = 948+primOpTag (VecIndexByteArrayOp WordVec 8 W16) = 949+primOpTag (VecIndexByteArrayOp WordVec 4 W32) = 950+primOpTag (VecIndexByteArrayOp WordVec 2 W64) = 951+primOpTag (VecIndexByteArrayOp WordVec 32 W8) = 952+primOpTag (VecIndexByteArrayOp WordVec 16 W16) = 953+primOpTag (VecIndexByteArrayOp WordVec 8 W32) = 954+primOpTag (VecIndexByteArrayOp WordVec 4 W64) = 955+primOpTag (VecIndexByteArrayOp WordVec 64 W8) = 956+primOpTag (VecIndexByteArrayOp WordVec 32 W16) = 957+primOpTag (VecIndexByteArrayOp WordVec 16 W32) = 958+primOpTag (VecIndexByteArrayOp WordVec 8 W64) = 959+primOpTag (VecIndexByteArrayOp FloatVec 4 W32) = 960+primOpTag (VecIndexByteArrayOp FloatVec 2 W64) = 961+primOpTag (VecIndexByteArrayOp FloatVec 8 W32) = 962+primOpTag (VecIndexByteArrayOp FloatVec 4 W64) = 963+primOpTag (VecIndexByteArrayOp FloatVec 16 W32) = 964+primOpTag (VecIndexByteArrayOp FloatVec 8 W64) = 965+primOpTag (VecReadByteArrayOp IntVec 16 W8) = 966+primOpTag (VecReadByteArrayOp IntVec 8 W16) = 967+primOpTag (VecReadByteArrayOp IntVec 4 W32) = 968+primOpTag (VecReadByteArrayOp IntVec 2 W64) = 969+primOpTag (VecReadByteArrayOp IntVec 32 W8) = 970+primOpTag (VecReadByteArrayOp IntVec 16 W16) = 971+primOpTag (VecReadByteArrayOp IntVec 8 W32) = 972+primOpTag (VecReadByteArrayOp IntVec 4 W64) = 973+primOpTag (VecReadByteArrayOp IntVec 64 W8) = 974+primOpTag (VecReadByteArrayOp IntVec 32 W16) = 975+primOpTag (VecReadByteArrayOp IntVec 16 W32) = 976+primOpTag (VecReadByteArrayOp IntVec 8 W64) = 977+primOpTag (VecReadByteArrayOp WordVec 16 W8) = 978+primOpTag (VecReadByteArrayOp WordVec 8 W16) = 979+primOpTag (VecReadByteArrayOp WordVec 4 W32) = 980+primOpTag (VecReadByteArrayOp WordVec 2 W64) = 981+primOpTag (VecReadByteArrayOp WordVec 32 W8) = 982+primOpTag (VecReadByteArrayOp WordVec 16 W16) = 983+primOpTag (VecReadByteArrayOp WordVec 8 W32) = 984+primOpTag (VecReadByteArrayOp WordVec 4 W64) = 985+primOpTag (VecReadByteArrayOp WordVec 64 W8) = 986+primOpTag (VecReadByteArrayOp WordVec 32 W16) = 987+primOpTag (VecReadByteArrayOp WordVec 16 W32) = 988+primOpTag (VecReadByteArrayOp WordVec 8 W64) = 989+primOpTag (VecReadByteArrayOp FloatVec 4 W32) = 990+primOpTag (VecReadByteArrayOp FloatVec 2 W64) = 991+primOpTag (VecReadByteArrayOp FloatVec 8 W32) = 992+primOpTag (VecReadByteArrayOp FloatVec 4 W64) = 993+primOpTag (VecReadByteArrayOp FloatVec 16 W32) = 994+primOpTag (VecReadByteArrayOp FloatVec 8 W64) = 995+primOpTag (VecWriteByteArrayOp IntVec 16 W8) = 996+primOpTag (VecWriteByteArrayOp IntVec 8 W16) = 997+primOpTag (VecWriteByteArrayOp IntVec 4 W32) = 998+primOpTag (VecWriteByteArrayOp IntVec 2 W64) = 999+primOpTag (VecWriteByteArrayOp IntVec 32 W8) = 1000+primOpTag (VecWriteByteArrayOp IntVec 16 W16) = 1001+primOpTag (VecWriteByteArrayOp IntVec 8 W32) = 1002+primOpTag (VecWriteByteArrayOp IntVec 4 W64) = 1003+primOpTag (VecWriteByteArrayOp IntVec 64 W8) = 1004+primOpTag (VecWriteByteArrayOp IntVec 32 W16) = 1005+primOpTag (VecWriteByteArrayOp IntVec 16 W32) = 1006+primOpTag (VecWriteByteArrayOp IntVec 8 W64) = 1007+primOpTag (VecWriteByteArrayOp WordVec 16 W8) = 1008+primOpTag (VecWriteByteArrayOp WordVec 8 W16) = 1009+primOpTag (VecWriteByteArrayOp WordVec 4 W32) = 1010+primOpTag (VecWriteByteArrayOp WordVec 2 W64) = 1011+primOpTag (VecWriteByteArrayOp WordVec 32 W8) = 1012+primOpTag (VecWriteByteArrayOp WordVec 16 W16) = 1013+primOpTag (VecWriteByteArrayOp WordVec 8 W32) = 1014+primOpTag (VecWriteByteArrayOp WordVec 4 W64) = 1015+primOpTag (VecWriteByteArrayOp WordVec 64 W8) = 1016+primOpTag (VecWriteByteArrayOp WordVec 32 W16) = 1017+primOpTag (VecWriteByteArrayOp WordVec 16 W32) = 1018+primOpTag (VecWriteByteArrayOp WordVec 8 W64) = 1019+primOpTag (VecWriteByteArrayOp FloatVec 4 W32) = 1020+primOpTag (VecWriteByteArrayOp FloatVec 2 W64) = 1021+primOpTag (VecWriteByteArrayOp FloatVec 8 W32) = 1022+primOpTag (VecWriteByteArrayOp FloatVec 4 W64) = 1023+primOpTag (VecWriteByteArrayOp FloatVec 16 W32) = 1024+primOpTag (VecWriteByteArrayOp FloatVec 8 W64) = 1025+primOpTag (VecIndexOffAddrOp IntVec 16 W8) = 1026+primOpTag (VecIndexOffAddrOp IntVec 8 W16) = 1027+primOpTag (VecIndexOffAddrOp IntVec 4 W32) = 1028+primOpTag (VecIndexOffAddrOp IntVec 2 W64) = 1029+primOpTag (VecIndexOffAddrOp IntVec 32 W8) = 1030+primOpTag (VecIndexOffAddrOp IntVec 16 W16) = 1031+primOpTag (VecIndexOffAddrOp IntVec 8 W32) = 1032+primOpTag (VecIndexOffAddrOp IntVec 4 W64) = 1033+primOpTag (VecIndexOffAddrOp IntVec 64 W8) = 1034+primOpTag (VecIndexOffAddrOp IntVec 32 W16) = 1035+primOpTag (VecIndexOffAddrOp IntVec 16 W32) = 1036+primOpTag (VecIndexOffAddrOp IntVec 8 W64) = 1037+primOpTag (VecIndexOffAddrOp WordVec 16 W8) = 1038+primOpTag (VecIndexOffAddrOp WordVec 8 W16) = 1039+primOpTag (VecIndexOffAddrOp WordVec 4 W32) = 1040+primOpTag (VecIndexOffAddrOp WordVec 2 W64) = 1041+primOpTag (VecIndexOffAddrOp WordVec 32 W8) = 1042+primOpTag (VecIndexOffAddrOp WordVec 16 W16) = 1043+primOpTag (VecIndexOffAddrOp WordVec 8 W32) = 1044+primOpTag (VecIndexOffAddrOp WordVec 4 W64) = 1045+primOpTag (VecIndexOffAddrOp WordVec 64 W8) = 1046+primOpTag (VecIndexOffAddrOp WordVec 32 W16) = 1047+primOpTag (VecIndexOffAddrOp WordVec 16 W32) = 1048+primOpTag (VecIndexOffAddrOp WordVec 8 W64) = 1049+primOpTag (VecIndexOffAddrOp FloatVec 4 W32) = 1050+primOpTag (VecIndexOffAddrOp FloatVec 2 W64) = 1051+primOpTag (VecIndexOffAddrOp FloatVec 8 W32) = 1052+primOpTag (VecIndexOffAddrOp FloatVec 4 W64) = 1053+primOpTag (VecIndexOffAddrOp FloatVec 16 W32) = 1054+primOpTag (VecIndexOffAddrOp FloatVec 8 W64) = 1055+primOpTag (VecReadOffAddrOp IntVec 16 W8) = 1056+primOpTag (VecReadOffAddrOp IntVec 8 W16) = 1057+primOpTag (VecReadOffAddrOp IntVec 4 W32) = 1058+primOpTag (VecReadOffAddrOp IntVec 2 W64) = 1059+primOpTag (VecReadOffAddrOp IntVec 32 W8) = 1060+primOpTag (VecReadOffAddrOp IntVec 16 W16) = 1061+primOpTag (VecReadOffAddrOp IntVec 8 W32) = 1062+primOpTag (VecReadOffAddrOp IntVec 4 W64) = 1063+primOpTag (VecReadOffAddrOp IntVec 64 W8) = 1064+primOpTag (VecReadOffAddrOp IntVec 32 W16) = 1065+primOpTag (VecReadOffAddrOp IntVec 16 W32) = 1066+primOpTag (VecReadOffAddrOp IntVec 8 W64) = 1067+primOpTag (VecReadOffAddrOp WordVec 16 W8) = 1068+primOpTag (VecReadOffAddrOp WordVec 8 W16) = 1069+primOpTag (VecReadOffAddrOp WordVec 4 W32) = 1070+primOpTag (VecReadOffAddrOp WordVec 2 W64) = 1071+primOpTag (VecReadOffAddrOp WordVec 32 W8) = 1072+primOpTag (VecReadOffAddrOp WordVec 16 W16) = 1073+primOpTag (VecReadOffAddrOp WordVec 8 W32) = 1074+primOpTag (VecReadOffAddrOp WordVec 4 W64) = 1075+primOpTag (VecReadOffAddrOp WordVec 64 W8) = 1076+primOpTag (VecReadOffAddrOp WordVec 32 W16) = 1077+primOpTag (VecReadOffAddrOp WordVec 16 W32) = 1078+primOpTag (VecReadOffAddrOp WordVec 8 W64) = 1079+primOpTag (VecReadOffAddrOp FloatVec 4 W32) = 1080+primOpTag (VecReadOffAddrOp FloatVec 2 W64) = 1081+primOpTag (VecReadOffAddrOp FloatVec 8 W32) = 1082+primOpTag (VecReadOffAddrOp FloatVec 4 W64) = 1083+primOpTag (VecReadOffAddrOp FloatVec 16 W32) = 1084+primOpTag (VecReadOffAddrOp FloatVec 8 W64) = 1085+primOpTag (VecWriteOffAddrOp IntVec 16 W8) = 1086+primOpTag (VecWriteOffAddrOp IntVec 8 W16) = 1087+primOpTag (VecWriteOffAddrOp IntVec 4 W32) = 1088+primOpTag (VecWriteOffAddrOp IntVec 2 W64) = 1089+primOpTag (VecWriteOffAddrOp IntVec 32 W8) = 1090+primOpTag (VecWriteOffAddrOp IntVec 16 W16) = 1091+primOpTag (VecWriteOffAddrOp IntVec 8 W32) = 1092+primOpTag (VecWriteOffAddrOp IntVec 4 W64) = 1093+primOpTag (VecWriteOffAddrOp IntVec 64 W8) = 1094+primOpTag (VecWriteOffAddrOp IntVec 32 W16) = 1095+primOpTag (VecWriteOffAddrOp IntVec 16 W32) = 1096+primOpTag (VecWriteOffAddrOp IntVec 8 W64) = 1097+primOpTag (VecWriteOffAddrOp WordVec 16 W8) = 1098+primOpTag (VecWriteOffAddrOp WordVec 8 W16) = 1099+primOpTag (VecWriteOffAddrOp WordVec 4 W32) = 1100+primOpTag (VecWriteOffAddrOp WordVec 2 W64) = 1101+primOpTag (VecWriteOffAddrOp WordVec 32 W8) = 1102+primOpTag (VecWriteOffAddrOp WordVec 16 W16) = 1103+primOpTag (VecWriteOffAddrOp WordVec 8 W32) = 1104+primOpTag (VecWriteOffAddrOp WordVec 4 W64) = 1105+primOpTag (VecWriteOffAddrOp WordVec 64 W8) = 1106+primOpTag (VecWriteOffAddrOp WordVec 32 W16) = 1107+primOpTag (VecWriteOffAddrOp WordVec 16 W32) = 1108+primOpTag (VecWriteOffAddrOp WordVec 8 W64) = 1109+primOpTag (VecWriteOffAddrOp FloatVec 4 W32) = 1110+primOpTag (VecWriteOffAddrOp FloatVec 2 W64) = 1111+primOpTag (VecWriteOffAddrOp FloatVec 8 W32) = 1112+primOpTag (VecWriteOffAddrOp FloatVec 4 W64) = 1113+primOpTag (VecWriteOffAddrOp FloatVec 16 W32) = 1114+primOpTag (VecWriteOffAddrOp FloatVec 8 W64) = 1115+primOpTag (VecIndexScalarByteArrayOp IntVec 16 W8) = 1116+primOpTag (VecIndexScalarByteArrayOp IntVec 8 W16) = 1117+primOpTag (VecIndexScalarByteArrayOp IntVec 4 W32) = 1118+primOpTag (VecIndexScalarByteArrayOp IntVec 2 W64) = 1119+primOpTag (VecIndexScalarByteArrayOp IntVec 32 W8) = 1120+primOpTag (VecIndexScalarByteArrayOp IntVec 16 W16) = 1121+primOpTag (VecIndexScalarByteArrayOp IntVec 8 W32) = 1122+primOpTag (VecIndexScalarByteArrayOp IntVec 4 W64) = 1123+primOpTag (VecIndexScalarByteArrayOp IntVec 64 W8) = 1124+primOpTag (VecIndexScalarByteArrayOp IntVec 32 W16) = 1125+primOpTag (VecIndexScalarByteArrayOp IntVec 16 W32) = 1126+primOpTag (VecIndexScalarByteArrayOp IntVec 8 W64) = 1127+primOpTag (VecIndexScalarByteArrayOp WordVec 16 W8) = 1128+primOpTag (VecIndexScalarByteArrayOp WordVec 8 W16) = 1129+primOpTag (VecIndexScalarByteArrayOp WordVec 4 W32) = 1130+primOpTag (VecIndexScalarByteArrayOp WordVec 2 W64) = 1131+primOpTag (VecIndexScalarByteArrayOp WordVec 32 W8) = 1132+primOpTag (VecIndexScalarByteArrayOp WordVec 16 W16) = 1133+primOpTag (VecIndexScalarByteArrayOp WordVec 8 W32) = 1134+primOpTag (VecIndexScalarByteArrayOp WordVec 4 W64) = 1135+primOpTag (VecIndexScalarByteArrayOp WordVec 64 W8) = 1136+primOpTag (VecIndexScalarByteArrayOp WordVec 32 W16) = 1137+primOpTag (VecIndexScalarByteArrayOp WordVec 16 W32) = 1138+primOpTag (VecIndexScalarByteArrayOp WordVec 8 W64) = 1139+primOpTag (VecIndexScalarByteArrayOp FloatVec 4 W32) = 1140+primOpTag (VecIndexScalarByteArrayOp FloatVec 2 W64) = 1141+primOpTag (VecIndexScalarByteArrayOp FloatVec 8 W32) = 1142+primOpTag (VecIndexScalarByteArrayOp FloatVec 4 W64) = 1143+primOpTag (VecIndexScalarByteArrayOp FloatVec 16 W32) = 1144+primOpTag (VecIndexScalarByteArrayOp FloatVec 8 W64) = 1145+primOpTag (VecReadScalarByteArrayOp IntVec 16 W8) = 1146+primOpTag (VecReadScalarByteArrayOp IntVec 8 W16) = 1147+primOpTag (VecReadScalarByteArrayOp IntVec 4 W32) = 1148+primOpTag (VecReadScalarByteArrayOp IntVec 2 W64) = 1149+primOpTag (VecReadScalarByteArrayOp IntVec 32 W8) = 1150+primOpTag (VecReadScalarByteArrayOp IntVec 16 W16) = 1151+primOpTag (VecReadScalarByteArrayOp IntVec 8 W32) = 1152+primOpTag (VecReadScalarByteArrayOp IntVec 4 W64) = 1153+primOpTag (VecReadScalarByteArrayOp IntVec 64 W8) = 1154+primOpTag (VecReadScalarByteArrayOp IntVec 32 W16) = 1155+primOpTag (VecReadScalarByteArrayOp IntVec 16 W32) = 1156+primOpTag (VecReadScalarByteArrayOp IntVec 8 W64) = 1157+primOpTag (VecReadScalarByteArrayOp WordVec 16 W8) = 1158+primOpTag (VecReadScalarByteArrayOp WordVec 8 W16) = 1159+primOpTag (VecReadScalarByteArrayOp WordVec 4 W32) = 1160+primOpTag (VecReadScalarByteArrayOp WordVec 2 W64) = 1161+primOpTag (VecReadScalarByteArrayOp WordVec 32 W8) = 1162+primOpTag (VecReadScalarByteArrayOp WordVec 16 W16) = 1163+primOpTag (VecReadScalarByteArrayOp WordVec 8 W32) = 1164+primOpTag (VecReadScalarByteArrayOp WordVec 4 W64) = 1165+primOpTag (VecReadScalarByteArrayOp WordVec 64 W8) = 1166+primOpTag (VecReadScalarByteArrayOp WordVec 32 W16) = 1167+primOpTag (VecReadScalarByteArrayOp WordVec 16 W32) = 1168+primOpTag (VecReadScalarByteArrayOp WordVec 8 W64) = 1169+primOpTag (VecReadScalarByteArrayOp FloatVec 4 W32) = 1170+primOpTag (VecReadScalarByteArrayOp FloatVec 2 W64) = 1171+primOpTag (VecReadScalarByteArrayOp FloatVec 8 W32) = 1172+primOpTag (VecReadScalarByteArrayOp FloatVec 4 W64) = 1173+primOpTag (VecReadScalarByteArrayOp FloatVec 16 W32) = 1174+primOpTag (VecReadScalarByteArrayOp FloatVec 8 W64) = 1175+primOpTag (VecWriteScalarByteArrayOp IntVec 16 W8) = 1176+primOpTag (VecWriteScalarByteArrayOp IntVec 8 W16) = 1177+primOpTag (VecWriteScalarByteArrayOp IntVec 4 W32) = 1178+primOpTag (VecWriteScalarByteArrayOp IntVec 2 W64) = 1179+primOpTag (VecWriteScalarByteArrayOp IntVec 32 W8) = 1180+primOpTag (VecWriteScalarByteArrayOp IntVec 16 W16) = 1181+primOpTag (VecWriteScalarByteArrayOp IntVec 8 W32) = 1182+primOpTag (VecWriteScalarByteArrayOp IntVec 4 W64) = 1183+primOpTag (VecWriteScalarByteArrayOp IntVec 64 W8) = 1184+primOpTag (VecWriteScalarByteArrayOp IntVec 32 W16) = 1185+primOpTag (VecWriteScalarByteArrayOp IntVec 16 W32) = 1186+primOpTag (VecWriteScalarByteArrayOp IntVec 8 W64) = 1187+primOpTag (VecWriteScalarByteArrayOp WordVec 16 W8) = 1188+primOpTag (VecWriteScalarByteArrayOp WordVec 8 W16) = 1189+primOpTag (VecWriteScalarByteArrayOp WordVec 4 W32) = 1190+primOpTag (VecWriteScalarByteArrayOp WordVec 2 W64) = 1191+primOpTag (VecWriteScalarByteArrayOp WordVec 32 W8) = 1192+primOpTag (VecWriteScalarByteArrayOp WordVec 16 W16) = 1193+primOpTag (VecWriteScalarByteArrayOp WordVec 8 W32) = 1194+primOpTag (VecWriteScalarByteArrayOp WordVec 4 W64) = 1195+primOpTag (VecWriteScalarByteArrayOp WordVec 64 W8) = 1196+primOpTag (VecWriteScalarByteArrayOp WordVec 32 W16) = 1197+primOpTag (VecWriteScalarByteArrayOp WordVec 16 W32) = 1198+primOpTag (VecWriteScalarByteArrayOp WordVec 8 W64) = 1199+primOpTag (VecWriteScalarByteArrayOp FloatVec 4 W32) = 1200+primOpTag (VecWriteScalarByteArrayOp FloatVec 2 W64) = 1201+primOpTag (VecWriteScalarByteArrayOp FloatVec 8 W32) = 1202+primOpTag (VecWriteScalarByteArrayOp FloatVec 4 W64) = 1203+primOpTag (VecWriteScalarByteArrayOp FloatVec 16 W32) = 1204+primOpTag (VecWriteScalarByteArrayOp FloatVec 8 W64) = 1205+primOpTag (VecIndexScalarOffAddrOp IntVec 16 W8) = 1206+primOpTag (VecIndexScalarOffAddrOp IntVec 8 W16) = 1207+primOpTag (VecIndexScalarOffAddrOp IntVec 4 W32) = 1208+primOpTag (VecIndexScalarOffAddrOp IntVec 2 W64) = 1209+primOpTag (VecIndexScalarOffAddrOp IntVec 32 W8) = 1210+primOpTag (VecIndexScalarOffAddrOp IntVec 16 W16) = 1211+primOpTag (VecIndexScalarOffAddrOp IntVec 8 W32) = 1212+primOpTag (VecIndexScalarOffAddrOp IntVec 4 W64) = 1213+primOpTag (VecIndexScalarOffAddrOp IntVec 64 W8) = 1214+primOpTag (VecIndexScalarOffAddrOp IntVec 32 W16) = 1215+primOpTag (VecIndexScalarOffAddrOp IntVec 16 W32) = 1216+primOpTag (VecIndexScalarOffAddrOp IntVec 8 W64) = 1217+primOpTag (VecIndexScalarOffAddrOp WordVec 16 W8) = 1218+primOpTag (VecIndexScalarOffAddrOp WordVec 8 W16) = 1219+primOpTag (VecIndexScalarOffAddrOp WordVec 4 W32) = 1220+primOpTag (VecIndexScalarOffAddrOp WordVec 2 W64) = 1221+primOpTag (VecIndexScalarOffAddrOp WordVec 32 W8) = 1222+primOpTag (VecIndexScalarOffAddrOp WordVec 16 W16) = 1223+primOpTag (VecIndexScalarOffAddrOp WordVec 8 W32) = 1224+primOpTag (VecIndexScalarOffAddrOp WordVec 4 W64) = 1225+primOpTag (VecIndexScalarOffAddrOp WordVec 64 W8) = 1226+primOpTag (VecIndexScalarOffAddrOp WordVec 32 W16) = 1227+primOpTag (VecIndexScalarOffAddrOp WordVec 16 W32) = 1228+primOpTag (VecIndexScalarOffAddrOp WordVec 8 W64) = 1229+primOpTag (VecIndexScalarOffAddrOp FloatVec 4 W32) = 1230+primOpTag (VecIndexScalarOffAddrOp FloatVec 2 W64) = 1231+primOpTag (VecIndexScalarOffAddrOp FloatVec 8 W32) = 1232+primOpTag (VecIndexScalarOffAddrOp FloatVec 4 W64) = 1233+primOpTag (VecIndexScalarOffAddrOp FloatVec 16 W32) = 1234+primOpTag (VecIndexScalarOffAddrOp FloatVec 8 W64) = 1235+primOpTag (VecReadScalarOffAddrOp IntVec 16 W8) = 1236+primOpTag (VecReadScalarOffAddrOp IntVec 8 W16) = 1237+primOpTag (VecReadScalarOffAddrOp IntVec 4 W32) = 1238+primOpTag (VecReadScalarOffAddrOp IntVec 2 W64) = 1239+primOpTag (VecReadScalarOffAddrOp IntVec 32 W8) = 1240+primOpTag (VecReadScalarOffAddrOp IntVec 16 W16) = 1241+primOpTag (VecReadScalarOffAddrOp IntVec 8 W32) = 1242+primOpTag (VecReadScalarOffAddrOp IntVec 4 W64) = 1243+primOpTag (VecReadScalarOffAddrOp IntVec 64 W8) = 1244+primOpTag (VecReadScalarOffAddrOp IntVec 32 W16) = 1245+primOpTag (VecReadScalarOffAddrOp IntVec 16 W32) = 1246+primOpTag (VecReadScalarOffAddrOp IntVec 8 W64) = 1247+primOpTag (VecReadScalarOffAddrOp WordVec 16 W8) = 1248+primOpTag (VecReadScalarOffAddrOp WordVec 8 W16) = 1249+primOpTag (VecReadScalarOffAddrOp WordVec 4 W32) = 1250+primOpTag (VecReadScalarOffAddrOp WordVec 2 W64) = 1251+primOpTag (VecReadScalarOffAddrOp WordVec 32 W8) = 1252+primOpTag (VecReadScalarOffAddrOp WordVec 16 W16) = 1253+primOpTag (VecReadScalarOffAddrOp WordVec 8 W32) = 1254+primOpTag (VecReadScalarOffAddrOp WordVec 4 W64) = 1255+primOpTag (VecReadScalarOffAddrOp WordVec 64 W8) = 1256+primOpTag (VecReadScalarOffAddrOp WordVec 32 W16) = 1257+primOpTag (VecReadScalarOffAddrOp WordVec 16 W32) = 1258+primOpTag (VecReadScalarOffAddrOp WordVec 8 W64) = 1259+primOpTag (VecReadScalarOffAddrOp FloatVec 4 W32) = 1260+primOpTag (VecReadScalarOffAddrOp FloatVec 2 W64) = 1261+primOpTag (VecReadScalarOffAddrOp FloatVec 8 W32) = 1262+primOpTag (VecReadScalarOffAddrOp FloatVec 4 W64) = 1263+primOpTag (VecReadScalarOffAddrOp FloatVec 16 W32) = 1264+primOpTag (VecReadScalarOffAddrOp FloatVec 8 W64) = 1265+primOpTag (VecWriteScalarOffAddrOp IntVec 16 W8) = 1266+primOpTag (VecWriteScalarOffAddrOp IntVec 8 W16) = 1267+primOpTag (VecWriteScalarOffAddrOp IntVec 4 W32) = 1268+primOpTag (VecWriteScalarOffAddrOp IntVec 2 W64) = 1269+primOpTag (VecWriteScalarOffAddrOp IntVec 32 W8) = 1270+primOpTag (VecWriteScalarOffAddrOp IntVec 16 W16) = 1271+primOpTag (VecWriteScalarOffAddrOp IntVec 8 W32) = 1272+primOpTag (VecWriteScalarOffAddrOp IntVec 4 W64) = 1273+primOpTag (VecWriteScalarOffAddrOp IntVec 64 W8) = 1274+primOpTag (VecWriteScalarOffAddrOp IntVec 32 W16) = 1275+primOpTag (VecWriteScalarOffAddrOp IntVec 16 W32) = 1276+primOpTag (VecWriteScalarOffAddrOp IntVec 8 W64) = 1277+primOpTag (VecWriteScalarOffAddrOp WordVec 16 W8) = 1278+primOpTag (VecWriteScalarOffAddrOp WordVec 8 W16) = 1279+primOpTag (VecWriteScalarOffAddrOp WordVec 4 W32) = 1280+primOpTag (VecWriteScalarOffAddrOp WordVec 2 W64) = 1281+primOpTag (VecWriteScalarOffAddrOp WordVec 32 W8) = 1282+primOpTag (VecWriteScalarOffAddrOp WordVec 16 W16) = 1283+primOpTag (VecWriteScalarOffAddrOp WordVec 8 W32) = 1284+primOpTag (VecWriteScalarOffAddrOp WordVec 4 W64) = 1285+primOpTag (VecWriteScalarOffAddrOp WordVec 64 W8) = 1286+primOpTag (VecWriteScalarOffAddrOp WordVec 32 W16) = 1287+primOpTag (VecWriteScalarOffAddrOp WordVec 16 W32) = 1288+primOpTag (VecWriteScalarOffAddrOp WordVec 8 W64) = 1289+primOpTag (VecWriteScalarOffAddrOp FloatVec 4 W32) = 1290+primOpTag (VecWriteScalarOffAddrOp FloatVec 2 W64) = 1291+primOpTag (VecWriteScalarOffAddrOp FloatVec 8 W32) = 1292+primOpTag (VecWriteScalarOffAddrOp FloatVec 4 W64) = 1293+primOpTag (VecWriteScalarOffAddrOp FloatVec 16 W32) = 1294+primOpTag (VecWriteScalarOffAddrOp FloatVec 8 W64) = 1295+primOpTag PrefetchByteArrayOp3 = 1296+primOpTag PrefetchMutableByteArrayOp3 = 1297+primOpTag PrefetchAddrOp3 = 1298+primOpTag PrefetchValueOp3 = 1299+primOpTag PrefetchByteArrayOp2 = 1300+primOpTag PrefetchMutableByteArrayOp2 = 1301+primOpTag PrefetchAddrOp2 = 1302+primOpTag PrefetchValueOp2 = 1303+primOpTag PrefetchByteArrayOp1 = 1304+primOpTag PrefetchMutableByteArrayOp1 = 1305+primOpTag PrefetchAddrOp1 = 1306+primOpTag PrefetchValueOp1 = 1307+primOpTag PrefetchByteArrayOp0 = 1308+primOpTag PrefetchMutableByteArrayOp0 = 1309+primOpTag PrefetchAddrOp0 = 1310+primOpTag PrefetchValueOp0 = 1311
ghc-lib/stage0/lib/llvm-passes view
@@ -1,5 +1,5 @@ [-(0, "-mem2reg -globalopt -lower-expect"),-(1, "-O1 -globalopt"),-(2, "-O2")+(0, "-enable-new-pm=0 -mem2reg -globalopt -lower-expect"),+(1, "-enable-new-pm=0 -O1 -globalopt"),+(2, "-enable-new-pm=0 -O2") ]
ghc-lib/stage0/lib/llvm-targets view
@@ -1,26 +1,29 @@ [("i386-unknown-windows", ("e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:32-n8:16:32-a:0:32-S32", "pentium4", "")) ,("i686-unknown-windows", ("e-m:x-p:32:32-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:32-n8:16:32-a:0:32-S32", "pentium4", "")) ,("x86_64-unknown-windows", ("e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", "x86-64", ""))-,("arm-unknown-linux-gnueabi", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm7tdmi", "-vfp2 -vfp2sp -vfp3 -vfp3d16 -vfp3d16sp -vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 -fp64 -d32 -neon -sha2 -aes -dotprod -fp16fml -bf16 -mve.fp -fpregs +strict-align"))-,("arm-unknown-linux-gnueabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm1176jzf-s", "+vfp2 +vfp2sp -vfp3 -vfp3d16 -vfp3d16sp -vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 -d32 -neon -sha2 -aes -fp16fml +strict-align"))-,("arm-unknown-linux-musleabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm1176jzf-s", "+vfp2 +vfp2sp -vfp3 -vfp3d16 -vfp3d16sp -vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 -d32 -neon -sha2 -aes -fp16fml +strict-align"))-,("armv6-unknown-linux-gnueabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm1136jf-s", "+vfp2 +vfp2sp -vfp3 -vfp3d16 -vfp3d16sp -vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 -d32 -neon -sha2 -aes -fp16fml +strict-align"))-,("armv6-unknown-linux-musleabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm1136jf-s", "+vfp2 +vfp2sp -vfp3 -vfp3d16 -vfp3d16sp -vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 -d32 -neon -sha2 -aes -fp16fml +strict-align"))-,("armv6l-unknown-linux-gnueabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm1176jzf-s", "+vfp2 +vfp2sp -vfp3 -vfp3d16 -vfp3d16sp -vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 -d32 -neon -sha2 -aes -fp16fml +strict-align"))-,("armv6l-unknown-linux-musleabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm1176jzf-s", "+vfp2 +vfp2sp -vfp3 -vfp3d16 -vfp3d16sp -vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 -d32 -neon -sha2 -aes -fp16fml +strict-align"))-,("armv7-unknown-linux-gnueabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", "+vfp2 +vfp2sp +vfp3 +vfp3d16 +vfp3d16sp +vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 +d32 +neon -sha2 -aes -fp16fml"))-,("armv7-unknown-linux-musleabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", "+vfp2 +vfp2sp +vfp3 +vfp3d16 +vfp3d16sp +vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 +d32 +neon -sha2 -aes -fp16fml"))-,("armv7a-unknown-linux-gnueabi", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", "+vfp2 +vfp2sp +vfp3 +vfp3d16 +vfp3d16sp +vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 +d32 +neon -sha2 -aes -fp16fml"))-,("armv7a-unknown-linux-musleabi", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", "+vfp2 +vfp2sp +vfp3 +vfp3d16 +vfp3d16sp +vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 +d32 +neon -sha2 -aes -fp16fml"))-,("armv7a-unknown-linux-gnueabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", "+vfp2 +vfp2sp +vfp3 +vfp3d16 +vfp3d16sp +vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 +d32 +neon -sha2 -aes -fp16fml"))-,("armv7a-unknown-linux-musleabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", "+vfp2 +vfp2sp +vfp3 +vfp3d16 +vfp3d16sp +vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 +d32 +neon -sha2 -aes -fp16fml"))-,("armv7l-unknown-linux-gnueabi", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", "+vfp2 +vfp2sp +vfp3 +vfp3d16 +vfp3d16sp +vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 +d32 +neon -sha2 -aes -fp16fml"))-,("armv7l-unknown-linux-musleabi", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", "+vfp2 +vfp2sp +vfp3 +vfp3d16 +vfp3d16sp +vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 +d32 +neon -sha2 -aes -fp16fml"))-,("armv7l-unknown-linux-gnueabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", "+vfp2 +vfp2sp +vfp3 +vfp3d16 +vfp3d16sp +vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 +d32 +neon -sha2 -aes -fp16fml"))-,("armv7l-unknown-linux-musleabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", "+vfp2 +vfp2sp +vfp3 +vfp3d16 +vfp3d16sp +vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 +d32 +neon -sha2 -aes -fp16fml"))+,("arm-unknown-linux-gnueabi", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm7tdmi", "+strict-align"))+,("arm-unknown-linux-gnueabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm1176jzf-s", "+strict-align"))+,("arm-unknown-linux-musleabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm1176jzf-s", "+strict-align"))+,("armv6-unknown-linux-gnueabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm1136jf-s", "+strict-align"))+,("armv6-unknown-linux-musleabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm1136jf-s", "+strict-align"))+,("armv6l-unknown-linux-gnueabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm1176jzf-s", "+strict-align"))+,("armv6l-unknown-linux-musleabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm1176jzf-s", "+strict-align"))+,("armv7-unknown-linux-gnueabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", ""))+,("armv7-unknown-linux-musleabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", ""))+,("armv7a-unknown-linux-gnueabi", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", ""))+,("armv7a-unknown-linux-musleabi", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", ""))+,("armv7a-unknown-linux-gnueabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", ""))+,("armv7a-unknown-linux-musleabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", ""))+,("armv7l-unknown-linux-gnueabi", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", ""))+,("armv7l-unknown-linux-musleabi", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", ""))+,("armv7l-unknown-linux-gnueabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", ""))+,("armv7l-unknown-linux-musleabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", "")) ,("aarch64-unknown-linux-gnu", ("e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", "generic", "+neon")) ,("aarch64-unknown-linux-musl", ("e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", "generic", "+neon")) ,("aarch64-unknown-linux", ("e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", "generic", "+neon"))+,("aarch64_be-unknown-linux-gnu", ("E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", "generic", "+neon"))+,("aarch64_be-unknown-linux-musl", ("E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", "generic", "+neon"))+,("aarch64_be-unknown-linux", ("E-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", "generic", "+neon")) ,("i386-unknown-linux-gnu", ("e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128", "pentium4", "")) ,("i386-unknown-linux-musl", ("e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128", "pentium4", "")) ,("i386-unknown-linux", ("e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128", "pentium4", ""))@@ -31,29 +34,30 @@ ,("x86_64-unknown-linux-musl", ("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", "x86-64", "")) ,("x86_64-unknown-linux", ("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", "x86-64", "")) ,("x86_64-unknown-linux-android", ("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", "x86-64", "+sse4.2 +popcnt +cx16"))-,("armv7-unknown-linux-androideabi", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", "+vfp2 +vfp2sp +vfp3 +vfp3d16 +vfp3d16sp +vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 +d32 +neon -sha2 -aes -fp16fml"))-,("aarch64-unknown-linux-android", ("e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", "generic", "+neon +outline-atomics"))-,("armv7a-unknown-linux-androideabi", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", "+vfp2 +vfp2sp +vfp3 +vfp3d16 +vfp3d16sp +vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 +d32 +neon -sha2 -aes -fp16fml"))-,("powerpc64le-unknown-linux-gnu", ("e-m:e-i64:64-n32:64-S128-v256:256:256-v512:512:512", "ppc64le", ""))-,("powerpc64le-unknown-linux-musl", ("e-m:e-i64:64-n32:64-S128-v256:256:256-v512:512:512", "ppc64le", "+secure-plt"))-,("powerpc64le-unknown-linux", ("e-m:e-i64:64-n32:64-S128-v256:256:256-v512:512:512", "ppc64le", ""))+,("armv7-unknown-linux-androideabi", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", "+fpregs +vfp2 +vfp2sp +vfp3 +vfp3d16 +vfp3d16sp +vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 +d32 +neon -crypto -fp16fml"))+,("aarch64-unknown-linux-android", ("e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", "generic", "+neon"))+,("armv7a-unknown-linux-androideabi", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", "+fpregs +vfp2 +vfp2sp +vfp3 +vfp3d16 +vfp3d16sp +vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 +d32 +neon -crypto -fp16fml"))+,("powerpc64le-unknown-linux-gnu", ("e-m:e-i64:64-n32:64", "ppc64le", ""))+,("powerpc64le-unknown-linux-musl", ("e-m:e-i64:64-n32:64", "ppc64le", "+secure-plt"))+,("powerpc64le-unknown-linux", ("e-m:e-i64:64-n32:64", "ppc64le", "")) ,("s390x-ibm-linux", ("E-m:e-i1:8:16-i8:8:16-i64:64-f128:64-a:8:16-n32:64", "z10", ""))-,("riscv64-unknown-linux-gnu", ("e-m:e-p:64:64-i64:64-i128:128-n64-S128", "", "+m +a +f +d +c +relax -save-restore"))-,("riscv64-unknown-linux", ("e-m:e-p:64:64-i64:64-i128:128-n64-S128", "", "+m +a +f +d +c +relax -save-restore"))-,("i386-apple-darwin", ("e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:128-n8:16:32-S128", "yonah", ""))-,("x86_64-apple-darwin", ("e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", "core2", ""))-,("arm64-apple-darwin", ("e-m:o-i64:64-i128:128-n32:64-S128", "apple-a7", "+fp-armv8 +neon +crypto +zcm +zcz +sha2 +aes"))-,("armv7-apple-ios", ("e-m:o-p:32:32-Fi8-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32", "generic", "+vfp2 +vfp2sp +vfp3 +vfp3d16 +vfp3d16sp +vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 +d32 +neon -sha2 -aes -fp16fml"))+,("riscv64-unknown-linux-gnu", ("e-m:e-p:64:64-i64:64-i128:128-n64-S128", "", "+m +a +f +d +c +relax"))+,("riscv64-unknown-linux", ("e-m:e-p:64:64-i64:64-i128:128-n64-S128", "", "+m +a +f +d +c +relax"))+,("loongarch64-unknown-linux-gnu", ("e-m:e-p:64:64-i64:64-i128:128-n64-S128", "", "+f +d"))+,("loongarch64-unknown-linux", ("e-m:e-p:64:64-i64:64-i128:128-n64-S128", "", "+f +d"))+,("i386-apple-darwin", ("e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:128-n8:16:32-S128", "penryn", ""))+,("x86_64-apple-darwin", ("e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", "penryn", ""))+,("arm64-apple-darwin", ("e-m:o-i64:64-i128:128-n32:64-S128", "generic", "+v8.3a +fp-armv8 +neon +crc +crypto +fullfp16 +ras +lse +rdm +rcpc +zcm +zcz +sha2 +aes"))+,("armv7-apple-ios", ("e-m:o-p:32:32-Fi8-f64:32:64-v64:32:64-v128:32:128-a:0:32-n32-S32", "generic", "")) ,("aarch64-apple-ios", ("e-m:o-i64:64-i128:128-n32:64-S128", "apple-a7", "+fp-armv8 +neon +crypto +zcm +zcz +sha2 +aes")) ,("i386-apple-ios", ("e-m:o-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:128-n8:16:32-S128", "yonah", "")) ,("x86_64-apple-ios", ("e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", "core2", ""))-,("x86_64-portbld-freebsd", ("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", "x86-64", ""))+,("amd64-portbld-freebsd", ("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", "x86-64", "")) ,("x86_64-unknown-freebsd", ("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", "x86-64", "")) ,("aarch64-unknown-freebsd", ("e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", "generic", "+neon"))-,("armv6-unknown-freebsd-gnueabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm1176jzf-s", "+vfp2 +vfp2sp -vfp3 -vfp3d16 -vfp3d16sp -vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 -d32 -neon -sha2 -aes -fp16fml +strict-align"))-,("armv7-unknown-freebsd-gnueabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "cortex-a8", "+vfp2 +vfp2sp +vfp3 +vfp3d16 +vfp3d16sp +vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 +fp64 +d32 +neon -sha2 -aes -fp16fml +strict-align"))+,("armv6-unknown-freebsd-gnueabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm1176jzf-s", "+strict-align"))+,("armv7-unknown-freebsd-gnueabihf", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "generic", "+strict-align")) ,("aarch64-unknown-netbsd", ("e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", "generic", "+neon"))-,("x86_64-unknown-openbsd", ("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", "x86-64", ""))-,("i386-unknown-openbsd", ("e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128", "i586", ""))-,("arm-unknown-nto-qnx-eabi", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm7tdmi", "-vfp2 -vfp2sp -vfp3 -vfp3d16 -vfp3d16sp -vfp3sp -fp16 -vfp4 -vfp4d16 -vfp4d16sp -vfp4sp -fp-armv8 -fp-armv8d16 -fp-armv8d16sp -fp-armv8sp -fullfp16 -fp64 -d32 -neon -sha2 -aes -dotprod -fp16fml -bf16 -mve.fp -fpregs +strict-align"))+,("x86_64-unknown-openbsd", ("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128", "x86-64", "+retpoline-indirect-calls +retpoline-indirect-branches"))+,("arm-unknown-nto-qnx-eabi", ("e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64", "arm7tdmi", "+strict-align")) ]
ghc-lib/stage0/lib/settings view
@@ -10,7 +10,6 @@ ,("ld command", "ld") ,("ld flags", "") ,("ld supports compact unwind", "YES")-,("ld supports build-id", "NO") ,("ld supports filelist", "YES") ,("ld is GNU ld", "NO") ,("Merge objects command", "ld")@@ -25,7 +24,6 @@ ,("touch command", "touch") ,("dllwrap command", "/bin/false") ,("windres command", "/bin/false")-,("libtool command", "libtool") ,("unlit command", "$topdir/bin/unlit") ,("cross compiling", "NO") ,("target platform string", "x86_64-apple-darwin")@@ -46,7 +44,7 @@ ,("Use inplace MinGW toolchain", "NO") ,("Use interpreter", "YES") ,("Support SMP", "YES")-,("RTS ways", "v thr")+,("RTS ways", "v") ,("Tables next to code", "YES") ,("Leading underscore", "YES") ,("Use LibFFI", "NO")
+ ghc-lib/stage0/libraries/ghc-boot/build/GHC/Platform/Host.hs view
@@ -0,0 +1,12 @@+module GHC.Platform.Host where++import GHC.Platform.ArchOS++hostPlatformArch :: Arch+hostPlatformArch = ArchX86_64++hostPlatformOS :: OS+hostPlatformOS = OSDarwin++hostPlatformArchOS :: ArchOS+hostPlatformArchOS = ArchOS hostPlatformArch hostPlatformOS
ghc-lib/stage0/rts/build/include/GhclibDerivedConstants.h view
@@ -1,6 +1,6 @@ /* This file is created automatically. Do not edit by hand.*/ -#define HS_CONSTANTS "291,1,2,4096,252,9,0,8,16,24,32,40,48,56,64,72,80,84,88,92,96,100,104,112,120,128,136,144,152,168,184,200,216,232,248,280,312,344,376,408,440,504,568,632,696,760,824,832,840,848,856,864,872,888,904,-24,-16,-8,24,0,8,48,46,96,72,8,48,8,8,16,8,64,8,16,8,0,72,56,8,16,0,8,8,0,8,0,96,112,16,8,16,0,4,4,24,20,4,15,7,1,-16,255,0,255,7,10,6,6,1,6,6,6,6,6,0,16384,21,1024,8,4,8,8,6,3,30,1152921503533105152,0,1152921504606846976,1"+#define HS_CONSTANTS "291,1,2,4096,252,9,0,8,16,24,32,40,48,56,64,72,80,84,88,92,96,100,104,112,120,128,136,144,152,168,184,200,216,232,248,280,312,344,376,408,440,504,568,632,696,760,824,832,840,848,856,864,872,888,904,-24,-16,-8,24,0,8,48,46,96,72,8,48,8,8,16,8,64,8,16,8,0,72,56,8,16,0,8,8,0,8,0,104,120,16,8,16,0,4,4,24,20,4,15,7,1,-16,255,0,255,7,10,6,6,1,6,6,6,6,6,0,16384,21,1024,8,4,8,8,6,3,30,1152921503533105152,0,1152921504606846976,1" #define CONTROL_GROUP_CONST_291 291 #define STD_HDR_SIZE 1 #define PROF_HDR_SIZE 2@@ -68,29 +68,29 @@ #define OFFSET_stgGCEnter1 -16 #define OFFSET_stgGCFun -8 #define OFFSET_Capability_r 24-#define OFFSET_Capability_lock 1224+#define OFFSET_Capability_lock 1216 #define OFFSET_Capability_no 944 #define REP_Capability_no b32 #define Capability_no(__ptr__) REP_Capability_no[__ptr__+OFFSET_Capability_no] #define OFFSET_Capability_mut_lists 1016 #define REP_Capability_mut_lists b64 #define Capability_mut_lists(__ptr__) REP_Capability_mut_lists[__ptr__+OFFSET_Capability_mut_lists]-#define OFFSET_Capability_context_switch 1192+#define OFFSET_Capability_context_switch 1184 #define REP_Capability_context_switch b32 #define Capability_context_switch(__ptr__) REP_Capability_context_switch[__ptr__+OFFSET_Capability_context_switch]-#define OFFSET_Capability_interrupt 1196+#define OFFSET_Capability_interrupt 1188 #define REP_Capability_interrupt b32 #define Capability_interrupt(__ptr__) REP_Capability_interrupt[__ptr__+OFFSET_Capability_interrupt]-#define OFFSET_Capability_sparks 1328+#define OFFSET_Capability_sparks 1320 #define REP_Capability_sparks b64 #define Capability_sparks(__ptr__) REP_Capability_sparks[__ptr__+OFFSET_Capability_sparks]-#define OFFSET_Capability_total_allocated 1200+#define OFFSET_Capability_total_allocated 1192 #define REP_Capability_total_allocated b64 #define Capability_total_allocated(__ptr__) REP_Capability_total_allocated[__ptr__+OFFSET_Capability_total_allocated]-#define OFFSET_Capability_weak_ptr_list_hd 1176+#define OFFSET_Capability_weak_ptr_list_hd 1168 #define REP_Capability_weak_ptr_list_hd b64 #define Capability_weak_ptr_list_hd(__ptr__) REP_Capability_weak_ptr_list_hd[__ptr__+OFFSET_Capability_weak_ptr_list_hd]-#define OFFSET_Capability_weak_ptr_list_tl 1184+#define OFFSET_Capability_weak_ptr_list_tl 1176 #define REP_Capability_weak_ptr_list_tl b64 #define Capability_weak_ptr_list_tl(__ptr__) REP_Capability_weak_ptr_list_tl[__ptr__+OFFSET_Capability_weak_ptr_list_tl] #define OFFSET_bdescr_start 0@@ -111,7 +111,7 @@ #define OFFSET_bdescr_flags 46 #define REP_bdescr_flags b16 #define bdescr_flags(__ptr__) REP_bdescr_flags[__ptr__+OFFSET_bdescr_flags]-#define SIZEOF_generation 384+#define SIZEOF_generation 368 #define OFFSET_generation_n_new_large_words 56 #define REP_generation_n_new_large_words b64 #define generation_n_new_large_words(__ptr__) REP_generation_n_new_large_words[__ptr__+OFFSET_generation_n_new_large_words]@@ -170,6 +170,11 @@ #define SIZEOF_StgCatchFrame (SIZEOF_StgHeader+16) #define SIZEOF_StgStopFrame_NoHdr 0 #define SIZEOF_StgStopFrame (SIZEOF_StgHeader+0)+#define SIZEOF_StgDeadThreadFrame_NoHdr 8+#define SIZEOF_StgDeadThreadFrame (SIZEOF_StgHeader+8)+#define OFFSET_StgDeadThreadFrame_result 0+#define REP_StgDeadThreadFrame_result b64+#define StgDeadThreadFrame_result(__ptr__) REP_StgDeadThreadFrame_result[__ptr__+SIZEOF_StgHeader+OFFSET_StgDeadThreadFrame_result] #define SIZEOF_StgMutArrPtrs_NoHdr 16 #define SIZEOF_StgMutArrPtrs (SIZEOF_StgHeader+16) #define OFFSET_StgMutArrPtrs_ptrs 0@@ -205,7 +210,7 @@ #define OFFSET_StgTSO_block_info 32 #define REP_StgTSO_block_info b64 #define StgTSO_block_info(__ptr__) REP_StgTSO_block_info[__ptr__+SIZEOF_StgHeader+OFFSET_StgTSO_block_info]-#define OFFSET_StgTSO_blocked_exceptions 80+#define OFFSET_StgTSO_blocked_exceptions 88 #define REP_StgTSO_blocked_exceptions b64 #define StgTSO_blocked_exceptions(__ptr__) REP_StgTSO_blocked_exceptions[__ptr__+SIZEOF_StgHeader+OFFSET_StgTSO_blocked_exceptions] #define OFFSET_StgTSO_id 40@@ -226,13 +231,16 @@ #define OFFSET_StgTSO_dirty 52 #define REP_StgTSO_dirty b32 #define StgTSO_dirty(__ptr__) REP_StgTSO_dirty[__ptr__+SIZEOF_StgHeader+OFFSET_StgTSO_dirty]-#define OFFSET_StgTSO_bq 88+#define OFFSET_StgTSO_bq 96 #define REP_StgTSO_bq b64 #define StgTSO_bq(__ptr__) REP_StgTSO_bq[__ptr__+SIZEOF_StgHeader+OFFSET_StgTSO_bq]-#define OFFSET_StgTSO_alloc_limit 96+#define OFFSET_StgTSO_label 80+#define REP_StgTSO_label b64+#define StgTSO_label(__ptr__) REP_StgTSO_label[__ptr__+SIZEOF_StgHeader+OFFSET_StgTSO_label]+#define OFFSET_StgTSO_alloc_limit 104 #define REP_StgTSO_alloc_limit b64 #define StgTSO_alloc_limit(__ptr__) REP_StgTSO_alloc_limit[__ptr__+SIZEOF_StgHeader+OFFSET_StgTSO_alloc_limit]-#define OFFSET_StgTSO_cccs 112+#define OFFSET_StgTSO_cccs 120 #define REP_StgTSO_cccs b64 #define StgTSO_cccs(__ptr__) REP_StgTSO_cccs[__ptr__+SIZEOF_StgHeader+OFFSET_StgTSO_cccs] #define OFFSET_StgTSO_stackobj 16@@ -293,6 +301,19 @@ #define StgAP_STACK_fun(__ptr__) REP_StgAP_STACK_fun[__ptr__+SIZEOF_StgHeader+OFFSET_StgAP_STACK_fun] #define OFFSET_StgAP_STACK_payload 24 #define StgAP_STACK_payload(__ptr__,__ix__) W_[__ptr__+SIZEOF_StgHeader+OFFSET_StgAP_STACK_payload + WDS(__ix__)]+#define SIZEOF_StgContinuation_NoHdr 24+#define SIZEOF_StgContinuation (SIZEOF_StgHeader+24)+#define OFFSET_StgContinuation_apply_mask_frame 0+#define REP_StgContinuation_apply_mask_frame b64+#define StgContinuation_apply_mask_frame(__ptr__) REP_StgContinuation_apply_mask_frame[__ptr__+SIZEOF_StgHeader+OFFSET_StgContinuation_apply_mask_frame]+#define OFFSET_StgContinuation_mask_frame_offset 8+#define REP_StgContinuation_mask_frame_offset b64+#define StgContinuation_mask_frame_offset(__ptr__) REP_StgContinuation_mask_frame_offset[__ptr__+SIZEOF_StgHeader+OFFSET_StgContinuation_mask_frame_offset]+#define OFFSET_StgContinuation_stack_size 16+#define REP_StgContinuation_stack_size b64+#define StgContinuation_stack_size(__ptr__) REP_StgContinuation_stack_size[__ptr__+SIZEOF_StgHeader+OFFSET_StgContinuation_stack_size]+#define OFFSET_StgContinuation_stack 24+#define StgContinuation_stack(__ptr__,__ix__) W_[__ptr__+SIZEOF_StgHeader+OFFSET_StgContinuation_stack + WDS(__ix__)] #define SIZEOF_StgSelector_NoThunkHdr 8 #define SIZEOF_StgSelector_NoHdr 16 #define SIZEOF_StgSelector (SIZEOF_StgHeader+16)
ghc-lib/stage0/rts/build/include/ghcautoconf.h view
@@ -85,6 +85,9 @@ significant byte first */ /* #undef FLOAT_WORDS_BIGENDIAN */ +/* Has musttail */+#define HAS_MUSTTAIL 1+ /* Has visibility hidden */ #define HAS_VISIBILITY_HIDDEN 1 @@ -129,6 +132,10 @@ don't. */ /* #undef HAVE_DECL_MAP_NORESERVE */ +/* Define to 1 if you have the declaration of `program_invocation_short_name',+ and to 0 if you don't. */+#define HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME 0+ /* Define to 1 if you have the <dirent.h> header file. */ #define HAVE_DIRENT_H 1 @@ -162,12 +169,18 @@ /* Define to 1 if you have the `GetModuleFileName' function. */ /* #undef HAVE_GETMODULEFILENAME */ +/* Define to 1 if you have the `getpid' function. */+#define HAVE_GETPID 1+ /* Define to 1 if you have the `getrusage' function. */ #define HAVE_GETRUSAGE 1 /* Define to 1 if you have the `gettimeofday' function. */ #define HAVE_GETTIMEOFDAY 1 +/* Define to 1 if you have the `getuid' function. */+#define HAVE_GETUID 1+ /* Define to 1 if you have the <grp.h> header file. */ #define HAVE_GRP_H 1 @@ -186,9 +199,6 @@ /* Define to 1 if you need to link with libm */ #define HAVE_LIBM 1 -/* Define to 1 if you have the `mingwex' library (-lmingwex). */-/* #undef HAVE_LIBMINGWEX */- /* Define to 1 if you have libnuma */ #define HAVE_LIBNUMA 0 @@ -246,6 +256,9 @@ /* Define to 1 if you have the <pwd.h> header file. */ #define HAVE_PWD_H 1 +/* Define to 1 if you have the `raise' function. */+#define HAVE_RAISE 1+ /* Define to 1 if you have the `sched_getaffinity' function. */ /* #undef HAVE_SCHED_GETAFFINITY */ @@ -345,9 +358,6 @@ /* Define to 1 if you have the `times' function. */ #define HAVE_TIMES 1 -/* Define to 1 if you have the <time.h> header file. */-#define HAVE_TIME_H 1- /* Define to 1 if you have the <unistd.h> header file. */ #define HAVE_UNISTD_H 1 @@ -491,10 +501,6 @@ /* Define to 1 if info tables are laid out next to code */ #define TABLES_NEXT_TO_CODE 1 -/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. This- macro is obsolete. */-#define TIME_WITH_SYS_TIME 1- /* Compile-in ASSERTs in all ways. */ /* #undef USE_ASSERTS_ALL_WAYS */ @@ -626,10 +632,10 @@ /* #undef pid_t */ /* The maximum supported LLVM version number */-#define sUPPORTED_LLVM_VERSION_MAX (15)+#define sUPPORTED_LLVM_VERSION_MAX (16) /* The minimum supported LLVM version number */-#define sUPPORTED_LLVM_VERSION_MIN (10)+#define sUPPORTED_LLVM_VERSION_MIN (11) /* Define to `unsigned int' if <sys/types.h> does not define. */ /* #undef size_t */
+ libraries/ghc-boot/GHC/HandleEncoding.hs view
@@ -0,0 +1,32 @@+-- | See GHC #10762 and #15021.+module GHC.HandleEncoding (configureHandleEncoding) where++import Prelude -- See note [Why do we import Prelude here?]+import GHC.IO.Encoding (textEncodingName)+import System.Environment+import System.IO++-- | Handle GHC-specific character encoding flags, allowing us to control how+-- GHC produces output regardless of OS.+configureHandleEncoding :: IO ()+configureHandleEncoding = do+ mb_val <- lookupEnv "GHC_CHARENC"+ case mb_val of+ Just "UTF-8" -> do+ hSetEncoding stdout utf8+ hSetEncoding stderr utf8+ _ -> do+ -- Avoid GHC erroring out when trying to display unhandled characters+ hSetTranslit stdout+ hSetTranslit stderr++-- | Change the character encoding of the given Handle to transliterate+-- on unsupported characters instead of throwing an exception+hSetTranslit :: Handle -> IO ()+hSetTranslit h = do+ menc <- hGetEncoding h+ case fmap textEncodingName menc of+ Just name | '/' `notElem` name -> do+ enc' <- mkTextEncoding $ name ++ "//TRANSLIT"+ hSetEncoding h enc'+ _ -> return ()
− libraries/ghc-boot/GHC/Settings/Utils.hs
@@ -1,72 +0,0 @@-module GHC.Settings.Utils where--import Prelude -- See Note [Why do we import Prelude here?]--import Data.Char (isSpace)-import Data.Map (Map)-import qualified Data.Map as Map--import GHC.BaseDir-import GHC.Platform.ArchOS--maybeRead :: Read a => String -> Maybe a-maybeRead str = case reads str of- [(x, "")] -> Just x- _ -> Nothing--maybeReadFuzzy :: Read a => String -> Maybe a-maybeReadFuzzy str = case reads str of- [(x, s)] | all isSpace s -> Just x- _ -> Nothing----- Note [Settings file]--- ~~~~~~~~~~~~~~~~~~~~------ GHC has a file, `${top_dir}/settings`, which is the main source of run-time--- configuration. ghc-pkg needs just a little bit of it: the target platform CPU--- arch and OS. It uses that to figure out what subdirectory of `~/.ghc` is--- associated with the current version/target platform.------ This module has just enough code to read key value pairs from the settings--- file, and read the target platform from those pairs.--type RawSettings = Map String String---- | Read target Arch/OS from the settings-getTargetArchOS- :: FilePath -- ^ Settings filepath (for error messages)- -> RawSettings -- ^ Raw settings file contents- -> Either String ArchOS-getTargetArchOS settingsFile settings =- ArchOS <$> readRawSetting settingsFile settings "target arch"- <*> readRawSetting settingsFile settings "target os"---getRawSetting- :: FilePath -> RawSettings -> String -> Either String String-getRawSetting settingsFile settings key = case Map.lookup key settings of- Just xs -> Right xs- Nothing -> Left $ "No entry for " ++ show key ++ " in " ++ show settingsFile--getRawFilePathSetting- :: FilePath -> FilePath -> RawSettings -> String -> Either String String-getRawFilePathSetting top_dir settingsFile settings key =- expandTopDir top_dir <$> getRawSetting settingsFile settings key--getRawBooleanSetting- :: FilePath -> RawSettings -> String -> Either String Bool-getRawBooleanSetting settingsFile settings key = do- rawValue <- getRawSetting settingsFile settings key- case rawValue of- "YES" -> Right True- "NO" -> Right False- xs -> Left $ "Bad value for " ++ show key ++ ": " ++ show xs--readRawSetting- :: (Show a, Read a) => FilePath -> RawSettings -> String -> Either String a-readRawSetting settingsFile settings key = case Map.lookup key settings of- Just xs -> case maybeRead xs of- Just v -> Right v- Nothing -> Left $ "Failed to read " ++ show key ++ " value " ++ show xs- Nothing -> Left $ "No entry for " ++ show key ++ " in " ++ show settingsFile
+ libraries/ghci/GHCi/CreateBCO.hs view
@@ -0,0 +1,211 @@+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP #-}++--+-- (c) The University of Glasgow 2002-2006+--++-- | Create real byte-code objects from 'ResolvedBCO's.+module GHCi.CreateBCO (createBCOs) where++import Prelude -- See note [Why do we import Prelude here?]+import GHCi.ResolvedBCO+import GHCi.RemoteTypes+import GHCi.BreakArray+import GHC.Data.SizedSeq++import System.IO (fixIO)+import Control.Monad+import Data.Array.Base+import Foreign hiding (newArray)+import Unsafe.Coerce (unsafeCoerce)+import GHC.Arr ( Array(..) )+import GHC.Exts+import GHC.IO+import Control.Exception ( ErrorCall(..) )++createBCOs :: [ResolvedBCO] -> IO [HValueRef]+createBCOs bcos = do+ let n_bcos = length bcos+ hvals <- fixIO $ \hvs -> do+ let arr = listArray (0, n_bcos-1) hvs+ mapM (createBCO arr) bcos+ mapM mkRemoteRef hvals++createBCO :: Array Int HValue -> ResolvedBCO -> IO HValue+createBCO _ ResolvedBCO{..} | resolvedBCOIsLE /= isLittleEndian+ = throwIO (ErrorCall $+ unlines [ "The endianness of the ResolvedBCO does not match"+ , "the systems endianness. Using ghc and iserv in a"+ , "mixed endianness setup is not supported!"+ ])+createBCO arr bco+ = +#if MIN_VERSION_ghc_prim(0, 7, 0)+ do linked_bco <- linkBCO' arr bco+#else+ do BCO bco# <- linkBCO' arr bco+#endif++ -- Note [Updatable CAF BCOs]+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~+ -- Why do we need mkApUpd0 here? Otherwise top-level+ -- interpreted CAFs don't get updated after evaluation. A+ -- top-level BCO will evaluate itself and return its value+ -- when entered, but it won't update itself. Wrapping the BCO+ -- in an AP_UPD thunk will take care of the update for us.+ --+ -- Furthermore:+ -- (a) An AP thunk *must* point directly to a BCO+ -- (b) A zero-arity BCO *must* be wrapped in an AP thunk+ -- (c) An AP is always fully saturated, so we *can't* wrap+ -- non-zero arity BCOs in an AP thunk.+ --+ -- See #17424.+ if (resolvedBCOArity bco > 0)+ +#if MIN_VERSION_ghc_prim(0, 7, 0)+ then return (HValue (unsafeCoerce linked_bco))+ else case mkApUpd0# linked_bco of { (# final_bco #) ->+#else+ then return (HValue (unsafeCoerce# bco#))+ else case mkApUpd0# bco# of { (# final_bco #) ->+#endif++ return (HValue final_bco) }+++toWordArray :: UArray Int Word64 -> UArray Int Word+toWordArray = amap fromIntegral++linkBCO' :: Array Int HValue -> ResolvedBCO -> IO BCO+linkBCO' arr ResolvedBCO{..} = do+ let+ ptrs = ssElts resolvedBCOPtrs+ n_ptrs = sizeSS resolvedBCOPtrs++ !(I# arity#) = resolvedBCOArity++ !(EmptyArr empty#) = emptyArr -- See Note [BCO empty array]++ barr a = case a of UArray _lo _hi n b -> if n == 0 then empty# else b+ insns_barr = barr resolvedBCOInstrs+ bitmap_barr = barr (toWordArray resolvedBCOBitmap)+ literals_barr = barr (toWordArray resolvedBCOLits)++ PtrsArr marr <- mkPtrsArray arr n_ptrs ptrs+ IO $ \s ->+ case unsafeFreezeArray# marr s of { (# s, arr #) ->+ case newBCO insns_barr literals_barr arr arity# bitmap_barr of { IO io ->+ io s+ }}+++-- we recursively link any sub-BCOs while making the ptrs array+mkPtrsArray :: Array Int HValue -> Word -> [ResolvedBCOPtr] -> IO PtrsArr+mkPtrsArray arr n_ptrs ptrs = do+ marr <- newPtrsArray (fromIntegral n_ptrs)+ let+ fill (ResolvedBCORef n) i =+ writePtrsArrayHValue i (arr ! n) marr -- must be lazy!+ fill (ResolvedBCOPtr r) i = do+ hv <- localRef r+ writePtrsArrayHValue i hv marr+ fill (ResolvedBCOStaticPtr r) i = do+ writePtrsArrayPtr i (fromRemotePtr r) marr+ fill (ResolvedBCOPtrBCO bco) i = do+ +#if MIN_VERSION_ghc_prim(0, 7, 0)+ bco <- linkBCO' arr bco+ writePtrsArrayBCO i bco marr+#else+ BCO bco# <- linkBCO' arr bco+ writePtrsArrayBCO i bco# marr+#endif++ fill (ResolvedBCOPtrBreakArray r) i = do+ BA mba <- localRef r+ writePtrsArrayMBA i mba marr+ zipWithM_ fill ptrs [0..]+ return marr++data PtrsArr = PtrsArr (MutableArray# RealWorld HValue)++newPtrsArray :: Int -> IO PtrsArr+newPtrsArray (I# i) = IO $ \s ->+ case newArray# i undefined s of (# s', arr #) -> (# s', PtrsArr arr #)++writePtrsArrayHValue :: Int -> HValue -> PtrsArr -> IO ()+writePtrsArrayHValue (I# i) hv (PtrsArr arr) = IO $ \s ->+ case writeArray# arr i hv s of s' -> (# s', () #)++writePtrsArrayPtr :: Int -> Ptr a -> PtrsArr -> IO ()+writePtrsArrayPtr (I# i) (Ptr a#) (PtrsArr arr) = IO $ \s ->+ case writeArrayAddr# arr i a# s of s' -> (# s', () #)++-- This is rather delicate: convincing GHC to pass an Addr# as an Any but+-- without making a thunk turns out to be surprisingly tricky.+{-# NOINLINE writeArrayAddr# #-}+writeArrayAddr# :: MutableArray# s a -> Int# -> Addr# -> State# s -> State# s+#if defined(javascript_HOST_ARCH)+-- Addr# isn't coercible with Any with the JS backend.+writeArrayAddr# = error "writeArrayAddr#: currently unsupported with the JS backend"+#else+writeArrayAddr# marr i addr s = unsafeCoerce# writeArray# marr i addr s+#endif+++#if MIN_VERSION_ghc_prim(0, 7, 0)+writePtrsArrayBCO :: Int -> BCO -> PtrsArr -> IO ()+#else+writePtrsArrayBCO :: Int -> BCO# -> PtrsArr -> IO ()+#endif++writePtrsArrayBCO (I# i) bco (PtrsArr arr) = IO $ \s ->+ case (unsafeCoerce# writeArray#) arr i bco s of s' -> (# s', () #)+++#if MIN_VERSION_ghc_prim(0, 7, 0)+writePtrsArrayMBA :: Int -> MutableByteArray# s -> PtrsArr -> IO ()+#else+data BCO = BCO BCO#+writePtrsArrayMBA :: Int -> MutableByteArray# s -> PtrsArr -> IO ()+#endif++writePtrsArrayMBA (I# i) mba (PtrsArr arr) = IO $ \s ->+ case (unsafeCoerce# writeArray#) arr i mba s of s' -> (# s', () #)++newBCO :: ByteArray# -> ByteArray# -> Array# a -> Int# -> ByteArray# -> IO BCO+newBCO instrs lits ptrs arity bitmap = IO $ \s ->+ +#if MIN_VERSION_ghc_prim(0, 7, 0)+ newBCO# instrs lits ptrs arity bitmap s+#else+ case newBCO# instrs lits ptrs arity bitmap s of+ (# s1, bco #) -> (# s1, BCO bco #)+#endif+++{- Note [BCO empty array]+ ~~~~~~~~~~~~~~~~~~~~~~+Lots of BCOs have empty ptrs or nptrs, but empty arrays are not free:+they are 2-word heap objects. So let's make a single empty array and+share it between all BCOs.+-}++data EmptyArr = EmptyArr ByteArray#++{-# NOINLINE emptyArr #-}+emptyArr :: EmptyArr+emptyArr = unsafeDupablePerformIO $ IO $ \s ->+ case newByteArray# 0# s of { (# s, arr #) ->+ case unsafeFreezeByteArray# arr s of { (# s, farr #) ->+ (# s, EmptyArr farr #)+ }}
+ libraries/ghci/GHCi/InfoTable.hsc view
@@ -0,0 +1,419 @@+{-# LANGUAGE CPP, MagicHash, ScopedTypeVariables #-}++-- Get definitions for the structs, constants & config etc.+#include "Rts.h"++-- |+-- Run-time info table support. This module provides support for+-- creating and reading info tables /in the running program/.+-- We use the RTS data structures directly via hsc2hs.+--+module GHCi.InfoTable+ (+ mkConInfoTable+ ) where++import Prelude hiding (fail) -- See note [Why do we import Prelude here?]++import Foreign+import Foreign.C+import GHC.Ptr+import GHC.Exts+import GHC.Exts.Heap+import Data.ByteString (ByteString)+import Control.Monad.Fail+import qualified Data.ByteString as BS+import GHC.Platform.Host (hostPlatformArch)+import GHC.Platform.ArchOS++-- NOTE: Must return a pointer acceptable for use in the header of a closure.+-- If tables_next_to_code is enabled, then it must point the 'code' field.+-- Otherwise, it should point to the start of the StgInfoTable.+mkConInfoTable+ :: Bool -- TABLES_NEXT_TO_CODE+ -> Int -- ptr words+ -> Int -- non-ptr words+ -> Int -- constr tag+ -> Int -- pointer tag+ -> ByteString -- con desc+ -> IO (Ptr StgInfoTable)+ -- resulting info table is allocated with allocateExecPage(), and+ -- should be freed with freeExecPage().++mkConInfoTable tables_next_to_code ptr_words nonptr_words tag ptrtag con_desc = do+ let entry_addr = interpConstrEntry !! ptrtag+ code' <- if tables_next_to_code+ then Just <$> mkJumpToAddr entry_addr+ else pure Nothing+ let+ itbl = StgInfoTable {+ entry = if tables_next_to_code+ then Nothing+ else Just entry_addr,+ ptrs = fromIntegral ptr_words,+ nptrs = fromIntegral nonptr_words,+ tipe = CONSTR,+ srtlen = fromIntegral tag,+ code = code'+ }+ castFunPtrToPtr <$> newExecConItbl tables_next_to_code itbl con_desc+++-- -----------------------------------------------------------------------------+-- Building machine code fragments for a constructor's entry code++funPtrToInt :: FunPtr a -> Int+funPtrToInt (FunPtr a) = I## (addr2Int## a)++mkJumpToAddr :: MonadFail m => EntryFunPtr-> m ItblCodes+mkJumpToAddr a = case hostPlatformArch of+ ArchPPC -> pure $+ -- We'll use r12, for no particular reason.+ -- 0xDEADBEEF stands for the address:+ -- 3D80DEAD lis r12,0xDEAD+ -- 618CBEEF ori r12,r12,0xBEEF+ -- 7D8903A6 mtctr r12+ -- 4E800420 bctr++ let w32 = fromIntegral (funPtrToInt a)+ hi16 x = (x `shiftR` 16) .&. 0xFFFF+ lo16 x = x .&. 0xFFFF+ in Right [ 0x3D800000 .|. hi16 w32,+ 0x618C0000 .|. lo16 w32,+ 0x7D8903A6, 0x4E800420 ]++ ArchX86 -> pure $+ -- Let the address to jump to be 0xWWXXYYZZ.+ -- Generate movl $0xWWXXYYZZ,%eax ; jmp *%eax+ -- which is+ -- B8 ZZ YY XX WW FF E0++ let w32 = fromIntegral (funPtrToInt a) :: Word32+ insnBytes :: [Word8]+ insnBytes+ = [0xB8, byte0 w32, byte1 w32,+ byte2 w32, byte3 w32,+ 0xFF, 0xE0]+ in+ Left insnBytes++ ArchX86_64 -> pure $+ -- Generates:+ -- jmpq *.L1(%rip)+ -- .align 8+ -- .L1:+ -- .quad <addr>+ --+ -- which looks like:+ -- 8: ff 25 02 00 00 00 jmpq *0x2(%rip) # 10 <f+0x10>+ -- with addr at 10.+ --+ -- We need a full 64-bit pointer (we can't assume the info table is+ -- allocated in low memory). Assuming the info pointer is aligned to+ -- an 8-byte boundary, the addr will also be aligned.++ let w64 = fromIntegral (funPtrToInt a) :: Word64+ insnBytes :: [Word8]+ insnBytes+ = [0xff, 0x25, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,+ byte0 w64, byte1 w64, byte2 w64, byte3 w64,+ byte4 w64, byte5 w64, byte6 w64, byte7 w64]+ in+ Left insnBytes++ ArchAlpha -> pure $+ let w64 = fromIntegral (funPtrToInt a) :: Word64+ in Right [ 0xc3800000 -- br at, .+4+ , 0xa79c000c -- ldq at, 12(at)+ , 0x6bfc0000 -- jmp (at) # with zero hint -- oh well+ , 0x47ff041f -- nop+ , fromIntegral (w64 .&. 0x0000FFFF)+ , fromIntegral ((w64 `shiftR` 32) .&. 0x0000FFFF) ]++ ArchARM {} -> pure $+ -- Generates Arm sequence,+ -- ldr r1, [pc, #0]+ -- bx r1+ --+ -- which looks like:+ -- 00000000 <.addr-0x8>:+ -- 0: 00109fe5 ldr r1, [pc] ; 8 <.addr>+ -- 4: 11ff2fe1 bx r1+ let w32 = fromIntegral (funPtrToInt a) :: Word32+ in Left [ 0x00, 0x10, 0x9f, 0xe5+ , 0x11, 0xff, 0x2f, 0xe1+ , byte0 w32, byte1 w32, byte2 w32, byte3 w32]++ ArchAArch64 {} -> pure $+ -- Generates:+ --+ -- ldr x1, label+ -- br x1+ -- label:+ -- .quad <addr>+ --+ -- which looks like:+ -- 0: 58000041 ldr x1, <label>+ -- 4: d61f0020 br x1+ let w64 = fromIntegral (funPtrToInt a) :: Word64+ in Right [ 0x58000041+ , 0xd61f0020+ , fromIntegral w64+ , fromIntegral (w64 `shiftR` 32) ]++ ArchPPC_64 ELF_V1 -> pure $+ -- We use the compiler's register r12 to read the function+ -- descriptor and the linker's register r11 as a temporary+ -- register to hold the function entry point.+ -- In the medium code model the function descriptor+ -- is located in the first two gigabytes, i.e. the address+ -- of the function pointer is a non-negative 32 bit number.+ -- 0x0EADBEEF stands for the address of the function pointer:+ -- 0: 3d 80 0e ad lis r12,0x0EAD+ -- 4: 61 8c be ef ori r12,r12,0xBEEF+ -- 8: e9 6c 00 00 ld r11,0(r12)+ -- c: e8 4c 00 08 ld r2,8(r12)+ -- 10: 7d 69 03 a6 mtctr r11+ -- 14: e9 6c 00 10 ld r11,16(r12)+ -- 18: 4e 80 04 20 bctr+ let w32 = fromIntegral (funPtrToInt a)+ hi16 x = (x `shiftR` 16) .&. 0xFFFF+ lo16 x = x .&. 0xFFFF+ in Right [ 0x3D800000 .|. hi16 w32,+ 0x618C0000 .|. lo16 w32,+ 0xE96C0000,+ 0xE84C0008,+ 0x7D6903A6,+ 0xE96C0010,+ 0x4E800420]++ ArchPPC_64 ELF_V2 -> pure $+ -- The ABI requires r12 to point to the function's entry point.+ -- We use the medium code model where code resides in the first+ -- two gigabytes, so loading a non-negative32 bit address+ -- with lis followed by ori is fine.+ -- 0x0EADBEEF stands for the address:+ -- 3D800EAD lis r12,0x0EAD+ -- 618CBEEF ori r12,r12,0xBEEF+ -- 7D8903A6 mtctr r12+ -- 4E800420 bctr++ let w32 = fromIntegral (funPtrToInt a)+ hi16 x = (x `shiftR` 16) .&. 0xFFFF+ lo16 x = x .&. 0xFFFF+ in Right [ 0x3D800000 .|. hi16 w32,+ 0x618C0000 .|. lo16 w32,+ 0x7D8903A6, 0x4E800420 ]++ ArchS390X -> pure $+ -- Let 0xAABBCCDDEEFFGGHH be the address to jump to.+ -- The following code loads the address into scratch+ -- register r1 and jumps to it.+ --+ -- 0: C0 1E AA BB CC DD llihf %r1,0xAABBCCDD+ -- 6: C0 19 EE FF GG HH iilf %r1,0xEEFFGGHH+ -- 12: 07 F1 br %r1++ let w64 = fromIntegral (funPtrToInt a) :: Word64+ in Left [ 0xC0, 0x1E, byte7 w64, byte6 w64, byte5 w64, byte4 w64,+ 0xC0, 0x19, byte3 w64, byte2 w64, byte1 w64, byte0 w64,+ 0x07, 0xF1 ]++ ArchRISCV64 -> pure $+ let w64 = fromIntegral (funPtrToInt a) :: Word64+ in Right [ 0x00000297 -- auipc t0,0+ , 0x01053283 -- ld t0,16(t0)+ , 0x00028067 -- jr t0+ , 0x00000013 -- nop+ , fromIntegral w64+ , fromIntegral (w64 `shiftR` 32) ]++ arch ->+ -- The arch isn't supported. You either need to add your architecture as a+ -- distinct case, or use non-TABLES_NEXT_TO_CODE mode.+ fail $ "mkJumpToAddr: arch is not supported with TABLES_NEXT_TO_CODE ("+ ++ show arch ++ ")"++byte0 :: (Integral w) => w -> Word8+byte0 w = fromIntegral w++byte1, byte2, byte3, byte4, byte5, byte6, byte7+ :: (Integral w, Bits w) => w -> Word8+byte1 w = fromIntegral (w `shiftR` 8)+byte2 w = fromIntegral (w `shiftR` 16)+byte3 w = fromIntegral (w `shiftR` 24)+byte4 w = fromIntegral (w `shiftR` 32)+byte5 w = fromIntegral (w `shiftR` 40)+byte6 w = fromIntegral (w `shiftR` 48)+byte7 w = fromIntegral (w `shiftR` 56)+++-- -----------------------------------------------------------------------------+-- read & write intfo tables++-- entry point for direct returns for created constr itbls+foreign import ccall "&stg_interp_constr1_entry" stg_interp_constr1_entry :: EntryFunPtr+foreign import ccall "&stg_interp_constr2_entry" stg_interp_constr2_entry :: EntryFunPtr+foreign import ccall "&stg_interp_constr3_entry" stg_interp_constr3_entry :: EntryFunPtr+foreign import ccall "&stg_interp_constr4_entry" stg_interp_constr4_entry :: EntryFunPtr+foreign import ccall "&stg_interp_constr5_entry" stg_interp_constr5_entry :: EntryFunPtr+foreign import ccall "&stg_interp_constr6_entry" stg_interp_constr6_entry :: EntryFunPtr+foreign import ccall "&stg_interp_constr7_entry" stg_interp_constr7_entry :: EntryFunPtr++interpConstrEntry :: [EntryFunPtr]+interpConstrEntry = [ error "pointer tag 0"+ , stg_interp_constr1_entry+ , stg_interp_constr2_entry+ , stg_interp_constr3_entry+ , stg_interp_constr4_entry+ , stg_interp_constr5_entry+ , stg_interp_constr6_entry+ , stg_interp_constr7_entry ]++data StgConInfoTable = StgConInfoTable {+ conDesc :: Ptr Word8,+ infoTable :: StgInfoTable+}+++pokeConItbl+ :: Bool -> Ptr StgConInfoTable -> Ptr StgConInfoTable -> StgConInfoTable+ -> IO ()+pokeConItbl tables_next_to_code wr_ptr _ex_ptr itbl = do+ if tables_next_to_code+ then do+ -- Write the offset to the con_desc from the end of the standard InfoTable+ -- at the first byte.+ let con_desc_offset = conDesc itbl `minusPtr` (_ex_ptr `plusPtr` conInfoTableSizeB)+ (#poke StgConInfoTable, con_desc) wr_ptr con_desc_offset+ else do+ -- Write the con_desc address after the end of the info table.+ -- Use itblSize because CPP will not pick up PROFILING when calculating+ -- the offset.+ pokeByteOff wr_ptr itblSize (conDesc itbl)+ pokeItbl (wr_ptr `plusPtr` (#offset StgConInfoTable, i)) (infoTable itbl)++sizeOfEntryCode :: MonadFail m => Bool -> m Int+sizeOfEntryCode tables_next_to_code+ | not tables_next_to_code = pure 0+ | otherwise = do+ code' <- mkJumpToAddr undefined+ pure $ case code' of+ Left (xs :: [Word8]) -> sizeOf (undefined :: Word8) * length xs+ Right (xs :: [Word32]) -> sizeOf (undefined :: Word32) * length xs++-- Note: Must return proper pointer for use in a closure+#if MIN_VERSION_rts(1,0,1)+newExecConItbl :: Bool -> StgInfoTable -> ByteString -> IO (FunPtr ())+newExecConItbl tables_next_to_code obj con_desc = do+ sz0 <- sizeOfEntryCode tables_next_to_code+ let lcon_desc = BS.length con_desc + 1{- null terminator -}+ -- SCARY+ -- This size represents the number of bytes in an StgConInfoTable.+ sz = fromIntegral $ conInfoTableSizeB + sz0+ -- Note: we need to allocate the conDesc string next to the info+ -- table, because on a 64-bit platform we reference this string+ -- with a 32-bit offset relative to the info table, so if we+ -- allocated the string separately it might be out of range.++ ex_ptr <- fillExecBuffer (sz + fromIntegral lcon_desc) $ \wr_ptr ex_ptr -> do+ let cinfo = StgConInfoTable { conDesc = ex_ptr `plusPtr` fromIntegral sz+ , infoTable = obj }+ pokeConItbl tables_next_to_code wr_ptr ex_ptr cinfo+ BS.useAsCStringLen con_desc $ \(src, len) ->+ copyBytes (castPtr wr_ptr `plusPtr` fromIntegral sz) src len+ let null_off = fromIntegral sz + fromIntegral (BS.length con_desc)+ poke (castPtr wr_ptr `plusPtr` null_off) (0 :: Word8)++ pure $ if tables_next_to_code+ then castPtrToFunPtr $ ex_ptr `plusPtr` conInfoTableSizeB+ else castPtrToFunPtr ex_ptr+#else+newExecConItbl :: Bool -> StgInfoTable -> ByteString -> IO (FunPtr ())+newExecConItbl tables_next_to_code obj con_desc+ = alloca $ \pcode -> do+ sz0 <- sizeOfEntryCode tables_next_to_code+ let lcon_desc = BS.length con_desc + 1{- null terminator -}+ -- SCARY+ -- This size represents the number of bytes in an StgConInfoTable.+ sz = fromIntegral $ conInfoTableSizeB + sz0+ -- Note: we need to allocate the conDesc string next to the info+ -- table, because on a 64-bit platform we reference this string+ -- with a 32-bit offset relative to the info table, so if we+ -- allocated the string separately it might be out of range.+ wr_ptr <- _allocateExec (sz + fromIntegral lcon_desc) pcode+ ex_ptr <- peek pcode+ let cinfo = StgConInfoTable { conDesc = ex_ptr `plusPtr` fromIntegral sz+ , infoTable = obj }+ pokeConItbl tables_next_to_code wr_ptr ex_ptr cinfo+ BS.useAsCStringLen con_desc $ \(src, len) ->+ copyBytes (castPtr wr_ptr `plusPtr` fromIntegral sz) src len+ let null_off = fromIntegral sz + fromIntegral (BS.length con_desc)+ poke (castPtr wr_ptr `plusPtr` null_off) (0 :: Word8)+ _flushExec sz ex_ptr -- Cache flush (if needed)+ pure $ if tables_next_to_code+ then castPtrToFunPtr $ ex_ptr `plusPtr` conInfoTableSizeB+ else castPtrToFunPtr ex_ptr+#endif++-- | Allocate a buffer of a given size, use the given action to fill it with+-- data, and mark it as executable. The action is given a writable pointer and+-- the executable pointer. Returns a pointer to the executable code.+#if MIN_VERSION_rts(1,0,1)+fillExecBuffer :: CSize -> (Ptr a -> Ptr a -> IO ()) -> IO (Ptr a)+#endif++#if MIN_VERSION_rts(1,0,2)++data ExecPage++foreign import ccall unsafe "allocateExecPage"+ _allocateExecPage :: IO (Ptr ExecPage)++foreign import ccall unsafe "freezeExecPage"+ _freezeExecPage :: Ptr ExecPage -> IO ()++fillExecBuffer sz cont+ -- we can only allocate single pages. This assumes a 4k page size which+ -- isn't strictly correct but is a reasonable conservative lower bound.+ | sz > 4096 = fail "withExecBuffer: Too large"+ | otherwise = do+ pg <- _allocateExecPage+ cont (castPtr pg) (castPtr pg)+ _freezeExecPage pg+ return (castPtr pg)++#elif MIN_VERSION_rts(1,0,1)++foreign import ccall unsafe "allocateExec"+ _allocateExec :: CUInt -> Ptr (Ptr a) -> IO (Ptr a)++foreign import ccall unsafe "flushExec"+ _flushExec :: CUInt -> Ptr a -> IO ()++fillExecBuffer sz cont = alloca $ \pcode -> do+ wr_ptr <- _allocateExec (fromIntegral sz) pcode+ ex_ptr <- peek pcode+ cont wr_ptr ex_ptr+ _flushExec (fromIntegral sz) ex_ptr -- Cache flush (if needed)+ return (ex_ptr)++#else++foreign import ccall unsafe "allocateExec"+ _allocateExec :: CUInt -> Ptr (Ptr a) -> IO (Ptr a)++foreign import ccall unsafe "flushExec"+ _flushExec :: CUInt -> Ptr a -> IO ()+++#endif++-- -----------------------------------------------------------------------------+-- Constants and config++wORD_SIZE :: Int+wORD_SIZE = (#const SIZEOF_HSINT)++conInfoTableSizeB :: Int+conInfoTableSizeB = wORD_SIZE + itblSize
+ libraries/ghci/GHCi/ObjLink.hs view
@@ -0,0 +1,195 @@+{-# LANGUAGE CPP, UnboxedTuples, MagicHash #-}+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}+--+-- (c) The University of Glasgow 2002-2006+--++-- ---------------------------------------------------------------------------+-- The dynamic linker for object code (.o .so .dll files)+-- ---------------------------------------------------------------------------++-- | Primarily, this module consists of an interface to the C-land+-- dynamic linker.+module GHCi.ObjLink+ ( initObjLinker, ShouldRetainCAFs(..)+ , loadDLL+ , loadArchive+ , loadObj+ , unloadObj+ , purgeObj+ , lookupSymbol+ , lookupClosure+ , resolveObjs+ , addLibrarySearchPath+ , removeLibrarySearchPath+ , findSystemLibrary+ ) where++import Prelude -- See note [Why do we import Prelude here?]+import GHCi.RemoteTypes+import Control.Exception (throwIO, ErrorCall(..))+import Control.Monad ( when )+import Foreign.C+import Foreign.Marshal.Alloc ( free )+import Foreign ( nullPtr )+import GHC.Exts+import System.Posix.Internals ( CFilePath, withFilePath, peekFilePath )+import System.FilePath ( dropExtension, normalise )+++++-- ---------------------------------------------------------------------------+-- RTS Linker Interface+-- ---------------------------------------------------------------------------++data ShouldRetainCAFs+ = RetainCAFs+ -- ^ Retain CAFs unconditionally in linked Haskell code.+ -- Note that this prevents any code from being unloaded.+ -- It should not be necessary unless you are GHCi or+ -- hs-plugins, which needs to be able call any function+ -- in the compiled code.+ | DontRetainCAFs+ -- ^ Do not retain CAFs. Everything reachable from foreign+ -- exports will be retained, due to the StablePtrs+ -- created by the module initialisation code. unloadObj+ -- frees these StablePtrs, which will allow the CAFs to+ -- be GC'd and the code to be removed.++initObjLinker :: ShouldRetainCAFs -> IO ()+initObjLinker RetainCAFs = c_initLinker_ 1+initObjLinker _ = c_initLinker_ 0++lookupSymbol :: String -> IO (Maybe (Ptr a))+lookupSymbol str_in = do+ let str = prefixUnderscore str_in+ withCAString str $ \c_str -> do+ addr <- c_lookupSymbol c_str+ if addr == nullPtr+ then return Nothing+ else return (Just addr)++lookupClosure :: String -> IO (Maybe HValueRef)+lookupClosure str = do+ m <- lookupSymbol str+ case m of+ Nothing -> return Nothing+ Just (Ptr addr) -> case addrToAny# addr of+ (# a #) -> Just <$> mkRemoteRef (HValue a)++prefixUnderscore :: String -> String+prefixUnderscore+ | cLeadingUnderscore = ('_':)+ | otherwise = id++-- | loadDLL loads a dynamic library using the OS's native linker+-- (i.e. dlopen() on Unix, LoadLibrary() on Windows). It takes either+-- an absolute pathname to the file, or a relative filename+-- (e.g. "libfoo.so" or "foo.dll"). In the latter case, loadDLL+-- searches the standard locations for the appropriate library.+--+loadDLL :: String -> IO (Maybe String)+-- Nothing => success+-- Just err_msg => failure+loadDLL str0 = do+ let+ -- On Windows, addDLL takes a filename without an extension, because+ -- it tries adding both .dll and .drv. To keep things uniform in the+ -- layers above, loadDLL always takes a filename with an extension, and+ -- we drop it here on Windows only.+ str | isWindowsHost = dropExtension str0+ | otherwise = str0+ --+ maybe_errmsg <- withFilePath (normalise str) $ \dll -> c_addDLL dll+ if maybe_errmsg == nullPtr+ then return Nothing+ else do str <- peekCString maybe_errmsg+ free maybe_errmsg+ return (Just str)++loadArchive :: String -> IO ()+loadArchive str = do+ withFilePath str $ \c_str -> do+ r <- c_loadArchive c_str+ when (r == 0) (throwIO (ErrorCall ("loadArchive " ++ show str ++ ": failed")))++loadObj :: String -> IO ()+loadObj str = do+ withFilePath str $ \c_str -> do+ r <- c_loadObj c_str+ when (r == 0) (throwIO (ErrorCall ("loadObj " ++ show str ++ ": failed")))++-- | @unloadObj@ drops the given dynamic library from the symbol table+-- as well as enables the library to be removed from memory during+-- a future major GC.+unloadObj :: String -> IO ()+unloadObj str =+ withFilePath str $ \c_str -> do+ r <- c_unloadObj c_str+ when (r == 0) (throwIO (ErrorCall ("unloadObj " ++ show str ++ ": failed")))++-- | @purgeObj@ drops the symbols for the dynamic library from the symbol+-- table. Unlike 'unloadObj', the library will not be dropped memory during+-- a future major GC.+purgeObj :: String -> IO ()+purgeObj str =+ withFilePath str $ \c_str -> do+ r <- c_purgeObj c_str+ when (r == 0) (throwIO (ErrorCall ("purgeObj " ++ show str ++ ": failed")))++addLibrarySearchPath :: String -> IO (Ptr ())+addLibrarySearchPath str =+ withFilePath str c_addLibrarySearchPath++removeLibrarySearchPath :: Ptr () -> IO Bool+removeLibrarySearchPath = c_removeLibrarySearchPath++findSystemLibrary :: String -> IO (Maybe String)+findSystemLibrary str = do+ result <- withFilePath str c_findSystemLibrary+ case result == nullPtr of+ True -> return Nothing+ False -> do path <- peekFilePath result+ free result+ return $ Just path++resolveObjs :: IO Bool+resolveObjs = do+ r <- c_resolveObjs+ return (r /= 0)++-- ---------------------------------------------------------------------------+-- Foreign declarations to RTS entry points which does the real work;+-- ---------------------------------------------------------------------------++foreign import ccall unsafe "addDLL" c_addDLL :: CFilePath -> IO CString+foreign import ccall unsafe "initLinker_" c_initLinker_ :: CInt -> IO ()+foreign import ccall unsafe "lookupSymbol" c_lookupSymbol :: CString -> IO (Ptr a)+foreign import ccall unsafe "loadArchive" c_loadArchive :: CFilePath -> IO Int+foreign import ccall unsafe "loadObj" c_loadObj :: CFilePath -> IO Int+foreign import ccall unsafe "purgeObj" c_purgeObj :: CFilePath -> IO Int+foreign import ccall unsafe "unloadObj" c_unloadObj :: CFilePath -> IO Int+foreign import ccall unsafe "resolveObjs" c_resolveObjs :: IO Int+foreign import ccall unsafe "addLibrarySearchPath" c_addLibrarySearchPath :: CFilePath -> IO (Ptr ())+foreign import ccall unsafe "findSystemLibrary" c_findSystemLibrary :: CFilePath -> IO CFilePath+foreign import ccall unsafe "removeLibrarySearchPath" c_removeLibrarySearchPath :: Ptr() -> IO Bool++-- -----------------------------------------------------------------------------+-- Configuration++#include "ghcautoconf.h"++cLeadingUnderscore :: Bool+#if defined(LEADING_UNDERSCORE)+cLeadingUnderscore = True+#else+cLeadingUnderscore = False+#endif++isWindowsHost :: Bool+#if defined(mingw32_HOST_OS)+isWindowsHost = True+#else+isWindowsHost = False+#endif
+ libraries/ghci/GHCi/Run.hs view
@@ -0,0 +1,398 @@+{-# LANGUAGE GADTs, RecordWildCards, MagicHash, ScopedTypeVariables, CPP,+ UnboxedTuples #-}+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}++-- |+-- Execute GHCi messages.+--+-- For details on Remote GHCi, see Note [Remote GHCi] in+-- compiler/GHC/Runtime/Interpreter.hs.+--+module GHCi.Run+ ( run, redirectInterrupts+ ) where++import Prelude -- See note [Why do we import Prelude here?]+import GHCi.CreateBCO+import GHCi.InfoTable+import GHCi.FFI+import GHCi.Message+import GHCi.ObjLink+import GHCi.RemoteTypes+import GHCi.TH+import GHCi.BreakArray+import GHCi.StaticPtrTable++import Control.Concurrent+import Control.DeepSeq+import Control.Exception+import Control.Monad+import Data.Binary+import Data.Binary.Get+import Data.ByteString (ByteString)+import qualified Data.ByteString.Unsafe as B+import GHC.Exts+import qualified GHC.Exts.Heap as Heap+import GHC.Stack+import Foreign hiding (void)+import Foreign.C+import GHC.Conc.Sync+import GHC.IO hiding ( bracket )+import System.Mem.Weak ( deRefWeak )+import Unsafe.Coerce++-- -----------------------------------------------------------------------------+-- Implement messages++foreign import ccall "revertCAFs" rts_revertCAFs :: IO ()+ -- Make it "safe", just in case++run :: Message a -> IO a+run m = case m of+ InitLinker -> initObjLinker RetainCAFs+ RtsRevertCAFs -> rts_revertCAFs+ LookupSymbol str -> fmap toRemotePtr <$> lookupSymbol str+ LookupClosure str -> lookupClosure str+ LoadDLL str -> loadDLL str+ LoadArchive str -> loadArchive str+ LoadObj str -> loadObj str+ UnloadObj str -> unloadObj str+ AddLibrarySearchPath str -> toRemotePtr <$> addLibrarySearchPath str+ RemoveLibrarySearchPath ptr -> removeLibrarySearchPath (fromRemotePtr ptr)+ ResolveObjs -> resolveObjs+ FindSystemLibrary str -> findSystemLibrary str+ CreateBCOs bcos -> createBCOs (concatMap (runGet get) bcos)+ FreeHValueRefs rs -> mapM_ freeRemoteRef rs+ AddSptEntry fpr r -> localRef r >>= sptAddEntry fpr+ EvalStmt opts r -> evalStmt opts r+ ResumeStmt opts r -> resumeStmt opts r+ AbandonStmt r -> abandonStmt r+ EvalString r -> evalString r+ EvalStringToString r s -> evalStringToString r s+ EvalIO r -> evalIO r+ MkCostCentres mod ccs -> mkCostCentres mod ccs+ CostCentreStackInfo ptr -> ccsToStrings (fromRemotePtr ptr)+ NewBreakArray sz -> mkRemoteRef =<< newBreakArray sz+ SetupBreakpoint ref ix cnt -> do+ arr <- localRef ref;+ _ <- setupBreakpoint arr ix cnt+ return ()+ BreakpointStatus ref ix -> do+ arr <- localRef ref; r <- getBreak arr ix+ case r of+ Nothing -> return False+ Just w -> return (w == 0)+ GetBreakpointVar ref ix -> do+ aps <- localRef ref+ mapM mkRemoteRef =<< getIdValFromApStack aps ix+ MallocData bs -> mkString bs+ MallocStrings bss -> mapM mkString0 bss+ PrepFFI conv args res -> toRemotePtr <$> prepForeignCall conv args res+ FreeFFI p -> freeForeignCallInfo (fromRemotePtr p)+ MkConInfoTable tc ptrs nptrs tag ptrtag desc ->+ toRemotePtr <$> mkConInfoTable tc ptrs nptrs tag ptrtag desc+ StartTH -> startTH+ GetClosure ref -> do+ clos <- Heap.getClosureData =<< localRef ref+ mapM (\(Heap.Box x) -> mkRemoteRef (HValue x)) clos+ Seq ref -> doSeq ref+ ResumeSeq ref -> resumeSeq ref+ _other -> error "GHCi.Run.run"++evalStmt :: EvalOpts -> EvalExpr HValueRef -> IO (EvalStatus [HValueRef])+evalStmt opts expr = do+ io <- mkIO expr+ sandboxIO opts $ do+ rs <- unsafeCoerce io :: IO [HValue]+ mapM mkRemoteRef rs+ where+ mkIO (EvalThis href) = localRef href+ mkIO (EvalApp l r) = do+ l' <- mkIO l+ r' <- mkIO r+ return ((unsafeCoerce l' :: HValue -> HValue) r')++evalIO :: HValueRef -> IO (EvalResult ())+evalIO r = do+ io <- localRef r+ tryEval (unsafeCoerce io :: IO ())++evalString :: HValueRef -> IO (EvalResult String)+evalString r = do+ io <- localRef r+ tryEval $ do+ r <- unsafeCoerce io :: IO String+ evaluate (force r)++evalStringToString :: HValueRef -> String -> IO (EvalResult String)+evalStringToString r str = do+ io <- localRef r+ tryEval $ do+ r <- (unsafeCoerce io :: String -> IO String) str+ evaluate (force r)++-- | Process the Seq message to force a value. #2950+-- If during this processing a breakpoint is hit, return+-- an EvalBreak value in the EvalStatus to the UI process,+-- otherwise return an EvalComplete.+-- The UI process has more and therefore also can show more+-- information about the breakpoint than the current iserv+-- process.+doSeq :: RemoteRef a -> IO (EvalStatus ())+doSeq ref = do+ sandboxIO evalOptsSeq $ do+ _ <- (void $ evaluate =<< localRef ref)+ return ()++-- | Process a ResumeSeq message. Continue the :force processing #2950+-- after a breakpoint.+resumeSeq :: RemoteRef (ResumeContext ()) -> IO (EvalStatus ())+resumeSeq hvref = do+ ResumeContext{..} <- localRef hvref+ withBreakAction evalOptsSeq resumeBreakMVar resumeStatusMVar $+ mask_ $ do+ putMVar resumeBreakMVar () -- this awakens the stopped thread...+ redirectInterrupts resumeThreadId $ takeMVar resumeStatusMVar++evalOptsSeq :: EvalOpts+evalOptsSeq = EvalOpts+ { useSandboxThread = True+ , singleStep = False+ , breakOnException = False+ , breakOnError = False+ }++-- When running a computation, we redirect ^C exceptions to the running+-- thread. ToDo: we might want a way to continue even if the target+-- thread doesn't die when it receives the exception... "this thread+-- is not responding".+--+-- Careful here: there may be ^C exceptions flying around, so we start the new+-- thread blocked (forkIO inherits mask from the parent, #1048), and unblock+-- only while we execute the user's code. We can't afford to lose the final+-- putMVar, otherwise deadlock ensues. (#1583, #1922, #1946)++sandboxIO :: EvalOpts -> IO a -> IO (EvalStatus a)+sandboxIO opts io = do+ -- We are running in uninterruptibleMask+ breakMVar <- newEmptyMVar+ statusMVar <- newEmptyMVar+ withBreakAction opts breakMVar statusMVar $ do+ let runIt = measureAlloc $ tryEval $ rethrow opts $ clearCCS io+ if useSandboxThread opts+ then do+ tid <- forkIO $ do unsafeUnmask runIt >>= putMVar statusMVar+ -- empty: can't block+ redirectInterrupts tid $ unsafeUnmask $ takeMVar statusMVar+ else+ -- GLUT on OS X needs to run on the main thread. If you+ -- try to use it from another thread then you just get a+ -- white rectangle rendered. For this, or anything else+ -- with such restrictions, you can turn the GHCi sandbox off+ -- and things will be run in the main thread.+ --+ -- BUT, note that the debugging features (breakpoints,+ -- tracing, etc.) need the expression to be running in a+ -- separate thread, so debugging is only enabled when+ -- using the sandbox.+ runIt++-- We want to turn ^C into a break when -fbreak-on-exception is on,+-- but it's an async exception and we only break for sync exceptions.+-- Idea: if we catch and re-throw it, then the re-throw will trigger+-- a break. Great - but we don't want to re-throw all exceptions, because+-- then we'll get a double break for ordinary sync exceptions (you'd have+-- to :continue twice, which looks strange). So if the exception is+-- not "Interrupted", we unset the exception flag before throwing.+--+rethrow :: EvalOpts -> IO a -> IO a+rethrow EvalOpts{..} io =+ catch io $ \se -> do+ -- If -fbreak-on-error, we break unconditionally,+ -- but with care of not breaking twice+ if breakOnError && not breakOnException+ then poke exceptionFlag 1+ else case fromException se of+ -- If it is a "UserInterrupt" exception, we allow+ -- a possible break by way of -fbreak-on-exception+ Just UserInterrupt -> return ()+ -- In any other case, we don't want to break+ _ -> poke exceptionFlag 0+ throwIO se++--+-- While we're waiting for the sandbox thread to return a result, if+-- the current thread receives an asynchronous exception we re-throw+-- it at the sandbox thread and continue to wait.+--+-- This is for two reasons:+--+-- * So that ^C interrupts runStmt (e.g. in GHCi), allowing the+-- computation to run its exception handlers before returning the+-- exception result to the caller of runStmt.+--+-- * clients of the GHC API can terminate a runStmt in progress+-- without knowing the ThreadId of the sandbox thread (#1381)+--+-- NB. use a weak pointer to the thread, so that the thread can still+-- be considered deadlocked by the RTS and sent a BlockedIndefinitely+-- exception. A symptom of getting this wrong is that conc033(ghci)+-- will hang.+--+redirectInterrupts :: ThreadId -> IO a -> IO a+redirectInterrupts target wait = do+ wtid <- mkWeakThreadId target+ wait `catch` \e -> do+ m <- deRefWeak wtid+ case m of+ Nothing -> wait+ Just target -> do throwTo target (e :: SomeException); wait++measureAlloc :: IO (EvalResult a) -> IO (EvalStatus a)+measureAlloc io = do+ setAllocationCounter 0 -- #16012+ a <- io+ ctr <- getAllocationCounter+ let allocs = negate $ fromIntegral ctr+ return (EvalComplete allocs a)++-- Exceptions can't be marshaled because they're dynamically typed, so+-- everything becomes a String.+tryEval :: IO a -> IO (EvalResult a)+tryEval io = do+ e <- try io+ case e of+ Left ex -> return (EvalException (toSerializableException ex))+ Right a -> return (EvalSuccess a)++-- This function sets up the interpreter for catching breakpoints, and+-- resets everything when the computation has stopped running. This+-- is a not-very-good way to ensure that only the interactive+-- evaluation should generate breakpoints.+withBreakAction :: EvalOpts -> MVar () -> MVar (EvalStatus b) -> IO a -> IO a+withBreakAction opts breakMVar statusMVar act+ = bracket setBreakAction resetBreakAction (\_ -> act)+ where+ setBreakAction = do+ stablePtr <- newStablePtr onBreak+ poke breakPointIOAction stablePtr+ when (breakOnException opts) $ poke exceptionFlag 1+ when (singleStep opts) $ setStepFlag+ return stablePtr+ -- Breaking on exceptions is not enabled by default, since it+ -- might be a bit surprising. The exception flag is turned off+ -- as soon as it is hit, or in resetBreakAction below.++ onBreak :: BreakpointCallback+ onBreak ix# uniq# is_exception apStack = do+ tid <- myThreadId+ let resume = ResumeContext+ { resumeBreakMVar = breakMVar+ , resumeStatusMVar = statusMVar+ , resumeThreadId = tid }+ resume_r <- mkRemoteRef resume+ apStack_r <- mkRemoteRef apStack+ ccs <- toRemotePtr <$> getCCSOf apStack+ putMVar statusMVar $ EvalBreak is_exception apStack_r (I# ix#) (I# uniq#) resume_r ccs+ takeMVar breakMVar++ resetBreakAction stablePtr = do+ poke breakPointIOAction noBreakStablePtr+ poke exceptionFlag 0+ resetStepFlag+ freeStablePtr stablePtr++resumeStmt+ :: EvalOpts -> RemoteRef (ResumeContext [HValueRef])+ -> IO (EvalStatus [HValueRef])+resumeStmt opts hvref = do+ ResumeContext{..} <- localRef hvref+ withBreakAction opts resumeBreakMVar resumeStatusMVar $+ mask_ $ do+ putMVar resumeBreakMVar () -- this awakens the stopped thread...+ redirectInterrupts resumeThreadId $ takeMVar resumeStatusMVar++-- when abandoning a computation we have to+-- (a) kill the thread with an async exception, so that the+-- computation itself is stopped, and+-- (b) fill in the MVar. This step is necessary because any+-- thunks that were under evaluation will now be updated+-- with the partial computation, which still ends in takeMVar,+-- so any attempt to evaluate one of these thunks will block+-- unless we fill in the MVar.+-- (c) wait for the thread to terminate by taking its status MVar. This+-- step is necessary to prevent race conditions with+-- -fbreak-on-exception (see #5975).+-- See test break010.+abandonStmt :: RemoteRef (ResumeContext [HValueRef]) -> IO ()+abandonStmt hvref = do+ ResumeContext{..} <- localRef hvref+ killThread resumeThreadId+ putMVar resumeBreakMVar ()+ _ <- takeMVar resumeStatusMVar+ return ()++foreign import ccall "&rts_stop_next_breakpoint" stepFlag :: Ptr CInt+foreign import ccall "&rts_stop_on_exception" exceptionFlag :: Ptr CInt++setStepFlag :: IO ()+setStepFlag = poke stepFlag 1+resetStepFlag :: IO ()+resetStepFlag = poke stepFlag 0++type BreakpointCallback+ = Int# -- the breakpoint index+ -> Int# -- the module uniq+ -> Bool -- exception?+ -> HValue -- the AP_STACK, or exception+ -> IO ()++foreign import ccall "&rts_breakpoint_io_action"+ breakPointIOAction :: Ptr (StablePtr BreakpointCallback)++noBreakStablePtr :: StablePtr BreakpointCallback+noBreakStablePtr = unsafePerformIO $ newStablePtr noBreakAction++noBreakAction :: BreakpointCallback+noBreakAction _ _ False _ = putStrLn "*** Ignoring breakpoint"+noBreakAction _ _ True _ = return () -- exception: just continue++-- Malloc and copy the bytes. We don't have any way to monitor the+-- lifetime of this memory, so it just leaks.+mkString :: ByteString -> IO (RemotePtr ())+mkString bs = B.unsafeUseAsCStringLen bs $ \(cstr,len) -> do+ ptr <- mallocBytes len+ copyBytes ptr cstr len+ return (castRemotePtr (toRemotePtr ptr))++mkString0 :: ByteString -> IO (RemotePtr ())+mkString0 bs = B.unsafeUseAsCStringLen bs $ \(cstr,len) -> do+ ptr <- mallocBytes (len+1)+ copyBytes ptr cstr len+ pokeElemOff (ptr :: Ptr CChar) len 0+ return (castRemotePtr (toRemotePtr ptr))++mkCostCentres :: String -> [(String,String)] -> IO [RemotePtr CostCentre]+#if defined(PROFILING)+mkCostCentres mod ccs = do+ c_module <- newCString mod+ mapM (mk_one c_module) ccs+ where+ mk_one c_module (decl_path,srcspan) = do+ c_name <- newCString decl_path+ c_srcspan <- newCString srcspan+ toRemotePtr <$> c_mkCostCentre c_name c_module c_srcspan++foreign import ccall unsafe "mkCostCentre"+ c_mkCostCentre :: Ptr CChar -> Ptr CChar -> Ptr CChar -> IO (Ptr CostCentre)+#else+mkCostCentres _ _ = return []+#endif++getIdValFromApStack :: HValue -> Int -> IO (Maybe HValue)+getIdValFromApStack apStack (I# stackDepth) = do+ case getApStackVal# apStack stackDepth of+ (# ok, result #) ->+ case ok of+ 0# -> return Nothing -- AP_STACK not found+ _ -> return (Just (unsafeCoerce# result))
+ libraries/ghci/GHCi/Signals.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE CPP #-}+module GHCi.Signals (installSignalHandlers) where++import Prelude -- See note [Why do we import Prelude here?]+import Control.Concurrent+import Control.Exception+import System.Mem.Weak ( deRefWeak )++#if !defined(mingw32_HOST_OS)+import System.Posix.Signals+#endif++#if defined(mingw32_HOST_OS)+import GHC.ConsoleHandler+#endif++-- | Install standard signal handlers for catching ^C, which just throw an+-- exception in the target thread. The current target thread is the+-- thread at the head of the list in the MVar passed to+-- installSignalHandlers.+installSignalHandlers :: IO ()+installSignalHandlers = do+ main_thread <- myThreadId+ wtid <- mkWeakThreadId main_thread++ let interrupt = do+ r <- deRefWeak wtid+ case r of+ Nothing -> return ()+ Just t -> throwTo t UserInterrupt++#if !defined(mingw32_HOST_OS)+ _ <- installHandler sigQUIT (Catch interrupt) Nothing+ _ <- installHandler sigINT (Catch interrupt) Nothing+#else+ -- GHC 6.3+ has support for console events on Windows+ -- NOTE: running GHCi under a bash shell for some reason requires+ -- you to press Ctrl-Break rather than Ctrl-C to provoke+ -- an interrupt. Ctrl-C is getting blocked somewhere, I don't know+ -- why --SDM 17/12/2004+ let sig_handler ControlC = interrupt+ sig_handler Break = interrupt+ sig_handler _ = return ()++ _ <- installHandler (Catch sig_handler)+#endif+ return ()
+ libraries/ghci/GHCi/StaticPtrTable.hs view
@@ -0,0 +1,25 @@+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE ForeignFunctionInterface #-}++module GHCi.StaticPtrTable ( sptAddEntry ) where++import Prelude -- See note [Why do we import Prelude here?]+import Data.Word+import Foreign+import GHC.Fingerprint+import GHCi.RemoteTypes++-- | Used by GHCi to add an SPT entry for a set of interactive bindings.+sptAddEntry :: Fingerprint -> HValue -> IO ()+sptAddEntry (Fingerprint a b) (HValue x) = do+ -- We own the memory holding the key (fingerprint) which gets inserted into+ -- the static pointer table and can't free it until the SPT entry is removed+ -- (which is currently never).+ fpr_ptr <- newArray [a,b]+ sptr <- newStablePtr x+ ent_ptr <- malloc+ poke ent_ptr (castStablePtrToPtr sptr)+ spt_insert_stableptr fpr_ptr ent_ptr++foreign import ccall "hs_spt_insert_stableptr"+ spt_insert_stableptr :: Ptr Word64 -> Ptr (Ptr ()) -> IO ()
+ libraries/ghci/GHCi/TH.hs view
@@ -0,0 +1,273 @@+{-# LANGUAGE ScopedTypeVariables, StandaloneDeriving, DeriveGeneric,+ TupleSections, RecordWildCards, InstanceSigs, CPP #-}+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}++-- |+-- Running TH splices+--+module GHCi.TH+ ( startTH+ , runModFinalizerRefs+ , runTH+ , GHCiQException(..)+ ) where++{- Note [Remote Template Haskell]+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Here is an overview of how TH works with -fexternal-interpreter.++Initialisation+~~~~~~~~~~~~~~++GHC sends a StartTH message to the server (see GHC.Tc.Gen.Splice.getTHState):++ StartTH :: Message (RemoteRef (IORef QState))++The server creates an initial QState object, makes an IORef to it, and+returns a RemoteRef to this to GHC. (see GHCi.TH.startTH below).++This happens once per module, the first time we need to run a TH+splice. The reference that GHC gets back is kept in+tcg_th_remote_state in the TcGblEnv, and passed to each RunTH call+that follows.+++For each splice+~~~~~~~~~~~~~~~++1. GHC compiles a splice to byte code, and sends it to the server: in+ a CreateBCOs message:++ CreateBCOs :: [LB.ByteString] -> Message [HValueRef]++2. The server creates the real byte-code objects in its heap, and+ returns HValueRefs to GHC. HValueRef is the same as RemoteRef+ HValue.++3. GHC sends a RunTH message to the server:++ RunTH+ :: RemoteRef (IORef QState)+ -- The state returned by StartTH in step1+ -> HValueRef+ -- The HValueRef we got in step 4, points to the code for the splice+ -> THResultType+ -- Tells us what kind of splice this is (decl, expr, type, etc.)+ -> Maybe TH.Loc+ -- Source location+ -> Message (QResult ByteString)+ -- Eventually it will return a QResult back to GHC. The+ -- ByteString here is the (encoded) result of the splice.++4. The server runs the splice code.++5. Each time the splice code calls a method of the Quasi class, such+ as qReify, a message is sent from the server to GHC. These+ messages are defined by the THMessage type. GHC responds with the+ result of the request, e.g. in the case of qReify it would be the+ TH.Info for the requested entity.++6. When the splice has been fully evaluated, the server sends+ RunTHDone back to GHC. This tells GHC that the server has finished+ sending THMessages and will send the QResult next.++8. The server then sends a QResult back to GHC, which is notionally+ the response to the original RunTH message. The QResult indicates+ whether the splice succeeded, failed, or threw an exception.+++After typechecking+~~~~~~~~~~~~~~~~~~++GHC sends a FinishTH message to the server (see GHC.Tc.Gen.Splice.finishTH).+The server runs any finalizers that were added by addModuleFinalizer.+++Other Notes on TH / Remote GHCi++ * Note [Remote GHCi] in compiler/GHC/Runtime/Interpreter.hs+ * Note [External GHCi pointers] in compiler/GHC/Runtime/Interpreter.hs+ * Note [TH recover with -fexternal-interpreter] in GHC.Tc.Gen.Splice+-}++import Prelude -- See note [Why do we import Prelude here?]+import GHCi.Message+import GHCi.RemoteTypes+import GHC.Serialized++import Control.Exception+import Control.Monad.IO.Class (MonadIO (..))+import Data.Binary+import Data.Binary.Put+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as LB+import Data.Data+import Data.Dynamic+import Data.Either+import Data.IORef+import Data.Map (Map)+import qualified Data.Map as M+import Data.Maybe+import GHC.Desugar+import qualified Language.Haskell.TH as TH+import qualified Language.Haskell.TH.Syntax as TH+import Unsafe.Coerce++-- | Create a new instance of 'QState'+initQState :: Pipe -> QState+initQState p = QState M.empty Nothing p++-- | The monad in which we run TH computations on the server+newtype GHCiQ a = GHCiQ { runGHCiQ :: QState -> IO (a, QState) }++-- | The exception thrown by "fail" in the GHCiQ monad+data GHCiQException = GHCiQException QState String+ deriving Show++instance Exception GHCiQException++instance Functor GHCiQ where+ fmap f (GHCiQ s) = GHCiQ $ fmap (\(x,s') -> (f x,s')) . s++instance Applicative GHCiQ where+ f <*> a = GHCiQ $ \s ->+ do (f',s') <- runGHCiQ f s+ (a',s'') <- runGHCiQ a s'+ return (f' a', s'')+ pure x = GHCiQ (\s -> return (x,s))++instance Monad GHCiQ where+ m >>= f = GHCiQ $ \s ->+ do (m', s') <- runGHCiQ m s+ (a, s'') <- runGHCiQ (f m') s'+ return (a, s'')++instance MonadFail GHCiQ where+ fail err = GHCiQ $ \s -> throwIO (GHCiQException s err)++getState :: GHCiQ QState+getState = GHCiQ $ \s -> return (s,s)++noLoc :: TH.Loc+noLoc = TH.Loc "<no file>" "<no package>" "<no module>" (0,0) (0,0)++-- | Send a 'THMessage' to GHC and return the result.+ghcCmd :: Binary a => THMessage (THResult a) -> GHCiQ a+ghcCmd m = GHCiQ $ \s -> do+ r <- remoteTHCall (qsPipe s) m+ case r of+ THException str -> throwIO (GHCiQException s str)+ THComplete res -> return (res, s)++instance MonadIO GHCiQ where+ liftIO m = GHCiQ $ \s -> fmap (,s) m++instance TH.Quasi GHCiQ where+ qNewName str = ghcCmd (NewName str)+ qReport isError msg = ghcCmd (Report isError msg)++ -- See Note [TH recover with -fexternal-interpreter] in GHC.Tc.Gen.Splice+ qRecover (GHCiQ h) a = GHCiQ $ \s -> mask $ \unmask -> do+ remoteTHCall (qsPipe s) StartRecover+ e <- try $ unmask $ runGHCiQ (a <* ghcCmd FailIfErrs) s+ remoteTHCall (qsPipe s) (EndRecover (isLeft e))+ case e of+ Left GHCiQException{} -> h s+ Right r -> return r+ qLookupName isType occ = ghcCmd (LookupName isType occ)+ qReify name = ghcCmd (Reify name)+ qReifyFixity name = ghcCmd (ReifyFixity name)+ qReifyType name = ghcCmd (ReifyType name)+ qReifyInstances name tys = ghcCmd (ReifyInstances name tys)+ qReifyRoles name = ghcCmd (ReifyRoles name)++ -- To reify annotations, we send GHC the AnnLookup and also the+ -- TypeRep of the thing we're looking for, to avoid needing to+ -- serialize irrelevant annotations.+ qReifyAnnotations :: forall a . Data a => TH.AnnLookup -> GHCiQ [a]+ qReifyAnnotations lookup =+ map (deserializeWithData . B.unpack) <$>+ ghcCmd (ReifyAnnotations lookup typerep)+ where typerep = typeOf (undefined :: a)++ qReifyModule m = ghcCmd (ReifyModule m)+ qReifyConStrictness name = ghcCmd (ReifyConStrictness name)+ qLocation = fromMaybe noLoc . qsLocation <$> getState+ qGetPackageRoot = ghcCmd GetPackageRoot+ qAddDependentFile file = ghcCmd (AddDependentFile file)+ qAddTempFile suffix = ghcCmd (AddTempFile suffix)+ qAddTopDecls decls = ghcCmd (AddTopDecls decls)+ qAddForeignFilePath lang fp = ghcCmd (AddForeignFilePath lang fp)+ qAddModFinalizer fin = GHCiQ (\s -> mkRemoteRef fin >>= return . (, s)) >>=+ ghcCmd . AddModFinalizer+ qAddCorePlugin str = ghcCmd (AddCorePlugin str)+ qGetQ = GHCiQ $ \s ->+ let lookup :: forall a. Typeable a => Map TypeRep Dynamic -> Maybe a+ lookup m = fromDynamic =<< M.lookup (typeOf (undefined::a)) m+ in return (lookup (qsMap s), s)+ qPutQ k = GHCiQ $ \s ->+ return ((), s { qsMap = M.insert (typeOf k) (toDyn k) (qsMap s) })+ qIsExtEnabled x = ghcCmd (IsExtEnabled x)+ qExtsEnabled = ghcCmd ExtsEnabled+ qPutDoc l s = ghcCmd (PutDoc l s)+ qGetDoc l = ghcCmd (GetDoc l)++-- | The implementation of the 'StartTH' message: create+-- a new IORef QState, and return a RemoteRef to it.+startTH :: IO (RemoteRef (IORef QState))+startTH = do+ r <- newIORef (initQState (error "startTH: no pipe"))+ mkRemoteRef r++-- | Runs the mod finalizers.+--+-- The references must be created on the caller process.+runModFinalizerRefs :: Pipe -> RemoteRef (IORef QState)+ -> [RemoteRef (TH.Q ())]+ -> IO ()+runModFinalizerRefs pipe rstate qrefs = do+ qs <- mapM localRef qrefs+ qstateref <- localRef rstate+ qstate <- readIORef qstateref+ _ <- runGHCiQ (TH.runQ $ sequence_ qs) qstate { qsPipe = pipe }+ return ()++-- | The implementation of the 'RunTH' message+runTH+ :: Pipe+ -> RemoteRef (IORef QState)+ -- ^ The TH state, created by 'startTH'+ -> HValueRef+ -- ^ The splice to run+ -> THResultType+ -- ^ What kind of splice it is+ -> Maybe TH.Loc+ -- ^ The source location+ -> IO ByteString+ -- ^ Returns an (encoded) result that depends on the THResultType++runTH pipe rstate rhv ty mb_loc = do+ hv <- localRef rhv+ case ty of+ THExp -> runTHQ pipe rstate mb_loc (unsafeCoerce hv :: TH.Q TH.Exp)+ THPat -> runTHQ pipe rstate mb_loc (unsafeCoerce hv :: TH.Q TH.Pat)+ THType -> runTHQ pipe rstate mb_loc (unsafeCoerce hv :: TH.Q TH.Type)+ THDec -> runTHQ pipe rstate mb_loc (unsafeCoerce hv :: TH.Q [TH.Dec])+ THAnnWrapper -> do+ hv <- unsafeCoerce <$> localRef rhv+ case hv :: AnnotationWrapper of+ AnnotationWrapper thing -> return $!+ LB.toStrict (runPut (put (toSerialized serializeWithData thing)))++-- | Run a Q computation.+runTHQ+ :: Binary a => Pipe -> RemoteRef (IORef QState) -> Maybe TH.Loc -> TH.Q a+ -> IO ByteString+runTHQ pipe rstate mb_loc ghciq = do+ qstateref <- localRef rstate+ qstate <- readIORef qstateref+ let st = qstate { qsLocation = mb_loc, qsPipe = pipe }+ (r,new_state) <- runGHCiQ (TH.runQ ghciq) st+ writeIORef qstateref new_state+ return $! LB.toStrict (runPut (put r))
+ libraries/template-haskell/Language/Haskell/TH/CodeDo.hs view
@@ -0,0 +1,22 @@+-- | This module exists to work nicely with the QualifiedDo+-- extension.+--+-- @+-- import qualified Language.Haskell.TH.CodeDo as Code+--+-- myExample :: Monad m => Code m a -> Code m a -> Code m a+-- myExample opt1 opt2 =+-- Code.do+-- x <- someSideEffect -- This one is of type `M Bool`+-- if x then opt1 else opt2+-- @+module Language.Haskell.TH.CodeDo((>>=), (>>)) where++import Language.Haskell.TH.Syntax+import Prelude(Monad)++-- | Module over monad operator for 'Code'+(>>=) :: Monad m => m a -> (a -> Code m b) -> Code m b+(>>=) = bindCode+(>>) :: Monad m => m a -> Code m b -> Code m b+(>>) = bindCode_
+ libraries/template-haskell/Language/Haskell/TH/Quote.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE RankNTypes, ScopedTypeVariables, Safe #-}+{- |+Module : Language.Haskell.TH.Quote+Description : Quasi-quoting support for Template Haskell++Template Haskell supports quasiquoting, which permits users to construct+program fragments by directly writing concrete syntax. A quasiquoter is+essentially a function with takes a string to a Template Haskell AST.+This module defines the 'QuasiQuoter' datatype, which specifies a+quasiquoter @q@ which can be invoked using the syntax+@[q| ... string to parse ... |]@ when the @QuasiQuotes@ language+extension is enabled, and some utility functions for manipulating+quasiquoters. Nota bene: this package does not define any parsers,+that is up to you.+-}+module Language.Haskell.TH.Quote(+ QuasiQuoter(..),+ quoteFile,+ -- * For backwards compatibility+ dataToQa, dataToExpQ, dataToPatQ+ ) where++import Language.Haskell.TH.Syntax+import Prelude++-- | The 'QuasiQuoter' type, a value @q@ of this type can be used+-- in the syntax @[q| ... string to parse ...|]@. In fact, for+-- convenience, a 'QuasiQuoter' actually defines multiple quasiquoters+-- to be used in different splice contexts; if you are only interested+-- in defining a quasiquoter to be used for expressions, you would+-- define a 'QuasiQuoter' with only 'quoteExp', and leave the other+-- fields stubbed out with errors.+data QuasiQuoter = QuasiQuoter {+ -- | Quasi-quoter for expressions, invoked by quotes like @lhs = $[q|...]@+ quoteExp :: String -> Q Exp,+ -- | Quasi-quoter for patterns, invoked by quotes like @f $[q|...] = rhs@+ quotePat :: String -> Q Pat,+ -- | Quasi-quoter for types, invoked by quotes like @f :: $[q|...]@+ quoteType :: String -> Q Type,+ -- | Quasi-quoter for declarations, invoked by top-level quotes+ quoteDec :: String -> Q [Dec]+ }++-- | 'quoteFile' takes a 'QuasiQuoter' and lifts it into one that read+-- the data out of a file. For example, suppose @asmq@ is an+-- assembly-language quoter, so that you can write [asmq| ld r1, r2 |]+-- as an expression. Then if you define @asmq_f = quoteFile asmq@, then+-- the quote [asmq_f|foo.s|] will take input from file @"foo.s"@ instead+-- of the inline text+quoteFile :: QuasiQuoter -> QuasiQuoter+quoteFile (QuasiQuoter { quoteExp = qe, quotePat = qp, quoteType = qt, quoteDec = qd }) + = QuasiQuoter { quoteExp = get qe, quotePat = get qp, quoteType = get qt, quoteDec = get qd }+ where+ get :: (String -> Q a) -> String -> Q a+ get old_quoter file_name = do { file_cts <- runIO (readFile file_name) + ; addDependentFile file_name+ ; old_quoter file_cts }