[Git][ghc/ghc][master] 2 commits: Fix GHC.Internal.Prim haddock
Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC Commits: 12f8b829 by Luite Stegeman at 2026-03-07T05:03:33-05:00 Fix GHC.Internal.Prim haddock Haddock used to parse Haskell source to generate documentation, but switched to using interface files instead. This broke documentation of the GHC.Internal.Prim module, since it's a wired-in interface that didn't provide a document structure. This patch adds the missing document structure and updates genprimopcode to make the section headers and descriptions available. fixes #26954 - - - - - f87e5e57 by Luite Stegeman at 2026-03-07T05:03:33-05:00 Remove obsolete --make-haskell-source from genprimopcode Now that haddock uses the wired-in interface for GHC.Internal.Prim, the generated Haskell source file is no longer needed. Remove the --make-haskell-source code generator from genprimopcode and replace the generated GHC/Internal/Prim.hs with a minimal static source file. - - - - - 9 changed files: - compiler/GHC/Builtin/PrimOps.hs - compiler/GHC/Builtin/Utils.hs - compiler/GHC/Builtin/primops.txt.pp - hadrian/src/Rules/Generate.hs - hadrian/src/Settings/Builders/GenPrimopCode.hs - libraries/ghc-internal/ghc-internal.cabal.in - + libraries/ghc-internal/src/GHC/Internal/Prim.hs - testsuite/tests/pmcheck/should_compile/T11303.hs - utils/genprimopcode/Main.hs Changes: ===================================== compiler/GHC/Builtin/PrimOps.hs ===================================== @@ -17,7 +17,7 @@ module GHC.Builtin.PrimOps ( primOpOutOfLine, primOpCodeSize, primOpOkForSpeculation, primOpOkToDiscard, - primOpIsWorkFree, primOpIsCheap, primOpFixity, primOpDocs, primOpDeprecations, + primOpIsWorkFree, primOpIsCheap, primOpFixity, primOpDocs, PrimOpDoc(..), primOpDeprecations, primOpIsDiv, primOpIsReallyInline, PrimOpEffect(..), primOpEffect, @@ -166,7 +166,13 @@ primOpFixity :: PrimOp -> Maybe Fixity See Note [GHC.Prim Docs] in GHC.Builtin.Utils -} -primOpDocs :: [(FastString, String)] +data PrimOpDoc + = -- | Section header with title and description + PrimOpSection String String + | -- | Documentation for a named declaration + PrimOpDecl FastString String + +primOpDocs :: [PrimOpDoc] #include "primop-docs.hs-incl" primOpDeprecations :: [(OccName, FastString)] ===================================== compiler/GHC/Builtin/Utils.hs ===================================== @@ -246,12 +246,39 @@ ghcPrimExports | tc <- exposedPrimTyCons, let n = tyConName tc ] ghcPrimDeclDocs :: Docs -ghcPrimDeclDocs = emptyDocs { docs_decls = listToUniqMap $ mapMaybe findName primOpDocs } +ghcPrimDeclDocs = emptyDocs + { docs_decls = listToUniqMap $ mapMaybe declDoc primOpDocs + , docs_structure = buildStructure primOpDocs + } where - findName (nameStr, doc) - | Just name <- lookupFsEnv ghcPrimNames nameStr - = Just (name, [WithHsDocIdentifiers (mkGeneratedHsDocString doc) []]) - | otherwise = Nothing + declDoc (PrimOpDecl fs doc) + | not (null doc) + , Just name <- lookupFsEnv ghcPrimNames fs + = Just (name, [mkHsDoc doc]) + declDoc _ = Nothing + + buildStructure [] = [] + buildStructure (PrimOpSection title desc : rest) = + DsiSectionHeading 1 (mkHsDoc title) + : [DsiDocChunk (mkHsDoc desc) | not (null desc)] + ++ buildStructure rest + buildStructure items = + let (decls, rest) = span isDecl items + avails = mapMaybe declAvail decls + in [DsiExports (DefinitelyDeterministicAvails avails) | not (null avails)] + ++ buildStructure rest + + isDecl (PrimOpDecl {}) = True + isDecl _ = False + + declAvail (PrimOpDecl fs _) + | Just name <- lookupFsEnv ghcPrimNames fs + = Just $ if isTyConName name + then AvailTC name [name] + else Avail name + declAvail _ = Nothing + + mkHsDoc s = WithHsDocIdentifiers (mkGeneratedHsDocString s) [] ghcPrimNames :: FastStringEnv Name ghcPrimNames @@ -287,18 +314,13 @@ ghcPrimFixities = fixities {- Note [GHC.Prim Docs] ~~~~~~~~~~~~~~~~~~~~ -For haddocks of GHC.Prim we generate a dummy haskell file (gen_hs_source) that -contains the type signatures and the comments (but no implementations) -specifically for consumption by haddock. - -GHCi's :doc command reads directly from ModIface's though, and GHC.Prim has a -wired-in iface that has nothing to do with the above haskell file. The code -below converts primops.txt into an intermediate form that would later be turned -into a proper DeclDocMap. - -We output the docs as a list of pairs (name, docs). We use stringy names here -because mapping names to "Name"s is difficult for things like primtypes and -pseudoops. +GHCi's :doc command and Haddock read from ModIface's. GHC.Prim has a wired-in +iface whose docs are populated from primops.txt. + +genprimopcode --wired-in-docs generates the primOpDocs list (included as +primop-docs.hs-incl), which contains section headers (PrimOpSection) and +per-declaration documentation (PrimOpDecl). We use stringy names because +mapping names to "Name"s is difficult for things like primtypes and pseudoops. Note [GHC.Prim Deprecations] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ===================================== compiler/GHC/Builtin/primops.txt.pp ===================================== @@ -46,7 +46,7 @@ -- * The primitives are listed in this file, primops.txt.pp. -- It goes through CPP, which creates primops.txt. -- It is then consumed by the utility program genprimopcode, which produces --- the following three types of files. +-- the following types of files. -- -- 1. The files with extension .hs-incl. -- They can be found by grepping for hs-incl. @@ -71,12 +71,7 @@ -- Additionally, we pattern match on PrimOp when generating Cmm in -- GHC/StgToCmm/Prim.hs. -- --- 2. The dummy Prim.hs file, which is used for Haddock and --- contains descriptions taken from primops.txt.pp. --- All definitions are replaced by placeholders. --- See Note [GHC.Prim Docs] in GHC.Builtin.Utils. --- --- 3. The module PrimopWrappers.hs, which wraps every call for GHCi; +-- 2. The module PrimopWrappers.hs, which wraps every call for GHCi; -- see Note [Primop wrappers] in GHC.Builtin.Primops for details. -- -- * This file does not list internal-only equality types ===================================== hadrian/src/Rules/Generate.hs ===================================== @@ -49,7 +49,7 @@ ghcInternalDependencies :: Expr [FilePath] ghcInternalDependencies = do stage <- getStage path <- expr $ buildPath (vanillaContext stage ghcInternal) - return [path -/- "GHC/Internal/Prim.hs", path -/- "GHC/Internal/PrimopWrappers.hs"] + return [path -/- "GHC/Internal/PrimopWrappers.hs"] rtsDependencies :: Expr [FilePath] rtsDependencies = do @@ -141,7 +141,6 @@ generatePackageCode context@(Context stage pkg _ _) = do root -/- "**" -/- dir -/- "GHC/Settings/Config.hs" %> go generateConfigHs root -/- "**" -/- dir -/- "*.hs-incl" %> genPrimopCode context when (pkg == ghcInternal) $ do - root -/- "**" -/- dir -/- "GHC/Internal/Prim.hs" %> genPrimopCode context root -/- "**" -/- dir -/- "GHC/Internal/PrimopWrappers.hs" %> genPrimopCode context when (pkg == ghcBoot) $ do root -/- "**" -/- dir -/- "GHC/Version.hs" %> go generateVersionHs ===================================== hadrian/src/Settings/Builders/GenPrimopCode.hs ===================================== @@ -5,7 +5,6 @@ import Settings.Builders.Common genPrimopCodeBuilderArgs :: Args genPrimopCodeBuilderArgs = builder GenPrimopCode ? mconcat [ output "//PrimopWrappers.hs" ? arg "--make-haskell-wrappers" - , output "//Prim.hs" ? arg "--make-haskell-source" , output "//primop-data-decl.hs-incl" ? arg "--data-decl" , output "//primop-tag.hs-incl" ? arg "--primop-tag" , output "//primop-list.hs-incl" ? arg "--primop-list" ===================================== libraries/ghc-internal/ghc-internal.cabal.in ===================================== @@ -357,7 +357,6 @@ Library GHC.Internal.Types autogen-modules: - GHC.Internal.Prim GHC.Internal.PrimopWrappers other-modules: ===================================== libraries/ghc-internal/src/GHC/Internal/Prim.hs ===================================== @@ -0,0 +1,11 @@ +{-# LANGUAGE Unsafe #-} +{-# LANGUAGE NoImplicitPrelude #-} + +-- | GHC's primitive types and operations. +-- Use GHC.Exts from the base package instead of importing this +-- module directly. +-- +-- This module has no source-level definitions. All primitive types +-- and operations are wired-in to GHC. See Note [GHC.Prim] in +-- primops.txt.pp. +module GHC.Internal.Prim () where ===================================== testsuite/tests/pmcheck/should_compile/T11303.hs ===================================== @@ -24,6 +24,5 @@ main = do "--primop-vector-maps" -> "hmmm" "--primop-vector-paper" -> "hmmm" "--make-haskell-wrappers" -> "123512" - "--make-haskell-source" -> "as;dg" "--make-latex-doc" -> "adghiw" _ -> error "Should not happen, known_args out of sync?" ===================================== utils/genprimopcode/Main.hs ===================================== @@ -204,9 +204,6 @@ main = getArgs >>= \args -> "--make-haskell-wrappers" -> putStr (gen_wrappers p_o_specs) - "--make-haskell-source" - -> putStr (gen_hs_source p_o_specs) - "--wired-in-docs" -> putStr (gen_wired_in_docs p_o_specs) @@ -238,7 +235,6 @@ known_args "--primop-vector-tys-exports", "--primop-vector-tycons", "--make-haskell-wrappers", - "--make-haskell-source", "--make-latex-doc", "--wired-in-docs", "--wired-in-deprecations", @@ -249,149 +245,6 @@ known_args -- Code generators ----------------------------------------------- ------------------------------------------------------------------ -gen_hs_source :: Info -> String -gen_hs_source (Info defaults entries) = - "{-\n" - ++ "This is a generated file (generated by genprimopcode).\n" - ++ "It is not code to actually be used. Its only purpose is to be\n" - ++ "consumed by haddock.\n" - ++ "-}\n" - ++ "\n" - ++ (replicate 77 '-' ++ "\n") -- For 80-col cleanliness - ++ "-- |\n" - ++ "-- Module : GHC.Internal.Prim\n" - ++ "-- \n" - ++ "-- Maintainer : ghc-devs@haskell.org\n" - ++ "-- Stability : internal\n" - ++ "-- Portability : non-portable (GHC extensions)\n" - ++ "--\n" - ++ "-- GHC\'s primitive types and operations.\n" - ++ "-- Use GHC.Exts from the base package instead of importing this\n" - ++ "-- module directly.\n" - ++ "--\n" - ++ (replicate 77 '-' ++ "\n") -- For 80-col cleanliness - ++ "{-# LANGUAGE Unsafe #-}\n" - ++ "{-# LANGUAGE MagicHash #-}\n" - ++ "{-# LANGUAGE MultiParamTypeClasses #-}\n" - ++ "{-# LANGUAGE NoImplicitPrelude #-}\n" - ++ "{-# LANGUAGE UnboxedTuples #-}\n" - ++ "{-# LANGUAGE NegativeLiterals #-}\n" - - ++ "{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}\n" - -- We generate a binding for coerce, like - -- coerce :: Coercible a b => a -> b - -- coerce = let x = x in x - -- and we don't want a complaint that the constraint is redundant - -- Remember, this silly file is only for Haddock's consumption - - ++ "{-# OPTIONS_HADDOCK print-explicit-runtime-reps #-}\n" - ++ "module GHC.Internal.Prim (\n" - ++ unlines (map ((" " ++) . hdr) entries') - ++ ") where\n" - ++ "\n" - ++ "{-\n" - ++ unlines (map opt defaults) - ++ "-}\n" - -- this import introduces a loop between GHC.Internal.Types and - -- GHC.Internal.Prim. - -- ++ "import GHC.Internal.Types (Coercible)\n" - - ++ "default ()" -- If we don't say this then the default type include Integer - -- so that runs off and loads modules that are not part of - -- package ghc-prim at all. And that in turn somehow ends up - -- with Declaration for $fEqMaybe: - -- attempting to use module ‘GHC.Classes’ - -- (libraries/ghc-prim/./GHC/Classes.hs) which is not loaded - -- coming from GHC.Iface.Load.homeModError - -- I'm not sure precisely why; but I *am* sure that we don't need - -- any type-class defaulting; and it's clearly wrong to need - -- the base package when haddocking ghc-prim - - -- Now the main payload - ++ "\n" ++ unlines (concatMap ent entries') ++ "\n\n\n" - - where entries' = concatMap desugarVectorSpec entries - - opt (OptionFalse n) = n ++ " = False" - opt (OptionTrue n) = n ++ " = True" - opt (OptionString n v) = n ++ " = { " ++ v ++ "}" - opt (OptionInteger n v) = n ++ " = " ++ show v - opt (OptionVector _) = "" - opt (OptionFixity mf) = "fixity = " ++ show mf - opt (OptionEffect eff) = "effect = " ++ show eff - opt (OptionDefinedBits bc) = "defined_bits = " ++ show bc - opt (OptionCanFailWarnFlag wf) = "can_fail_warning = " ++ show wf - - hdr s@(Section {}) = sec s - hdr (PrimOpSpec { name = n }) = wrapOp n ++ "," - hdr (PrimVecOpSpec { name = n }) = wrapOp n ++ "," - hdr (PseudoOpSpec { name = n }) = wrapOp n ++ "," - hdr (PrimTypeSpec { ty = TyApp (TyCon n) _ }) = wrapOp n ++ "," - hdr (PrimTypeSpec {}) = error $ "Illegal type spec" - hdr (PrimVecTypeSpec { ty = TyApp (VecTyCon n _) _ }) = wrapOp n ++ "," - hdr (PrimVecTypeSpec {}) = error $ "Illegal type spec" - - sec s = "\n{- * " ++ title s ++ "-}\n{-|" ++ desc s ++ "-}" - - - ent (Section {}) = [] - ent o@(PrimOpSpec {}) = spec o - ent o@(PrimVecOpSpec {}) = spec o - ent o@(PrimTypeSpec {}) = spec o - ent o@(PrimVecTypeSpec {}) = spec o - ent o@(PseudoOpSpec {}) = spec o - - spec o = ([ "" ] ++) . concat $ - -- Doc comments - [ case desc o ++ extra (opts o) of - "" -> [] - cmmt -> lines ("{-|" ++ cmmt ++ "-}") - - -- Deprecations - , [ d | Just n <- [getName o], d <- prim_deprecated (opts o) n ] - - -- Fixity - , [ f | Just n <- [getName o], f <- prim_fixity (opts o) n ] - - -- Declarations (see Note [Placeholder declarations]) - , case o of - PrimOpSpec { name = n, ty = t } -> prim_func n t - PrimVecOpSpec { name = n, ty = t } -> prim_func n t - PseudoOpSpec { name = n, ty = t } -> prim_func n t - PrimTypeSpec { ty = t } -> prim_data t - PrimVecTypeSpec { ty = t } -> prim_data t - Section { } -> error "Section is not an entity" - ] - - extra options = case can_fail options of - [m] -> "\n\n__/Warning:/__ this " ++ m ++ "." - _ -> "" - - can_fail options - = [ "can fail with an unchecked exception" - | Just (OptionEffect eff) <- [lookup_attrib "effect" options] - , Just (OptionCanFailWarnFlag wflag) <- [lookup_attrib "can_fail_warning" options] - , wflag /= DoNotWarnCanFail - , wflag == YesWarnCanFail || eff == CanFail ] - - prim_deprecated options n - = [ "{-# DEPRECATED " ++ wrapOp n ++ " \"" ++ msg ++ "\" #-}" - | Just (OptionString _ msg) - <- [lookup_attrib "deprecated_msg" options] ] - - prim_fixity options n - = [ pprFixityDir d ++ " " ++ show i ++ " " ++ asInfix n - | OptionFixity (Just (Fixity i d)) <- options ] - - prim_func n t = [ wrapOp n ++ " :: " ++ pprTy t, - wrapOp n ++ " = " ++ funcRhs n ] - - funcRhs "tagToEnum#" = "let x = x in x" - funcRhs nm = wrapOp nm - -- Special case for tagToEnum#: see Note [Placeholder declarations] - - prim_data t = [ "data " ++ pprTy t ] - -- | Extract a string representation of the name getName :: Entry -> Maybe String getName PrimOpSpec{ name = n } = Just n @@ -401,25 +254,6 @@ getName PrimTypeSpec{ ty = TyApp tc _ } = Just (show tc) getName PrimVecTypeSpec{ ty = TyApp tc _ } = Just (show tc) getName _ = Nothing -{- Note [Placeholder declarations] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -We are generating fake declarations for things in GHC.Internal.Prim, just to -keep GHC's renamer and typechecker happy enough for what Haddock -needs. Our main plan is to say - foo :: <type> - foo = foo - -That works for all the primitive functions except tagToEnum#. -If we generate the binding - tagToEnum# = tagToEnum# -GHC will complain about "tagToEnum# must appear applied to one argument". -We could hack GHC to silence this complaint when compiling GHC.Internal.Prim, -but it seems easier to generate - tagToEnum# = let x = x in x -We don't do this for *all* bindings because for ones with an unboxed -RHS we would get other complaints (e.g.can't unify "*" with "#"). --} - -- | "Pretty"-print a type pprTy :: Ty -> String pprTy = pty @@ -442,11 +276,6 @@ wrapOp :: String -> String wrapOp nm | isAlpha (head nm) = nm | otherwise = "(" ++ nm ++ ")" --- | Turn an identifier or operator into its infix form -asInfix :: String -> String -asInfix nm | isAlpha (head nm) = "`" ++ nm ++ "`" - | otherwise = nm - {- Note [OPTIONS_GHC in GHC.PrimopWrappers] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -666,11 +495,30 @@ gen_switch_from_attribs attrib_name fn_name (Info defaults entries) -- See Note [GHC.Prim Docs] in GHC.Builtin.Utils gen_wired_in_docs :: Info -> String gen_wired_in_docs (Info _ entries) - = "primOpDocs =\n [ " ++ intercalate "\n , " (catMaybes $ map mkDoc $ concatMap desugarVectorSpec entries) ++ "\n ]\n" + = "primOpDocs =\n [ " + ++ intercalate "\n , " (concatMap mkEntry desugared) + ++ "\n ]\n" where - mkDoc po | Just poName <- getName po - , not $ null $ desc po = Just $ "(fsLit " ++ show poName ++ "," ++ show (desc po) ++ ")" - | otherwise = Nothing + desugared = concatMap desugarVectorSpec entries + + mkEntry :: Entry -> [String] + mkEntry (Section{title = t, desc = d}) = + ["PrimOpSection " ++ show t ++ " " ++ show d] + mkEntry po + | Just poName <- getName po + = ["PrimOpDecl (fsLit " ++ show poName ++ ") " ++ show (desc po ++ extra (opts po))] + | otherwise = [] + + extra options = case canFail options of + [m] -> "\n\n__/Warning:/__ this " ++ m ++ "." + _ -> "" + + canFail options + = [ "can fail with an unchecked exception" + | Just (OptionEffect eff) <- [lookup_attrib "effect" options] + , Just (OptionCanFailWarnFlag wflag) <- [lookup_attrib "can_fail_warning" options] + , wflag /= DoNotWarnCanFail + , wflag == YesWarnCanFail || eff == CanFail ] -- See Note [GHC.Prim Deprecations] in GHC.Builtin.Utils gen_wired_in_deprecations :: Info -> String View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/eca445e7c002077afdb5bdd100daaa1... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/eca445e7c002077afdb5bdd100daaa1... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
Marge Bot (@marge-bot)