`-fno-omit-interface-pragmas` causes an unsilenceable warning about optimizations in GHCi
Summary
Calling ghci with -fno-omit-interface-pragmas causes GHCi to issue a warning.
If -Werror is also enabled, then ghci exits entirely without booting.
It is possible to work around this issue by providing -O0 explicitly, but the call to -O0 must be after - calling it before has no effect.
Steps to reproduce
$ ghci -fno-omit-interface-pragmas
when making flags consistent: warning:
Optimization flags conflict with --interactive; optimization flags ignored.
GHCi, version 9.4.2: https://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /home/matt/.dotfiles/haskell/ghci.symlink
λ>
To work around, you must provide -O0 explicitly.
$ ghci -fno-omit-interface-pragmas -O0
GHCi, version 9.4.2: https://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /home/matt/.dotfiles/haskell/ghci.symlink
λ>
Calling with -Werror also causes a problem:
$ ghci -fno-omit-interface-pragmas -Werror
when making flags consistent: error: [-Werror]
Optimization flags conflict with --interactive; optimization flags ignored.
$
The work around does not work if -O0 is provided before -fno-omit-interface-pragmas:
$ ghci -O0 -fno-omit-interface-pragmas -Werror
when making flags consistent: error: [-Werror]
Optimization flags conflict with --interactive; optimization flags ignored.
$
Expected behavior
I'd expect the order of providing -O0 to not matter. I'd prefer a clearer error message, as well - calling cabal repl --verbose and seeing a command like ghc --interactive -O0 (lots of stuff here ...) and then it complains about "Optimization flags conflict with --interactive" is really hard to diagnose.
There are a few upstream fixes that would be nice here:
-
Give the warning a name so I can write
-Wno-optimizer-flags-conflictor-Wwarn=optimizer-flags-conflictand prevent it from breaking in the presence of -Werror -
The warning appears to come from this part of the code:
-- compiler/GHC/Driver/Session.hs
| backendForcesOptimization0 (backend dflags)
, let (dflags', changed) = updOptLevelChanged 0 dflags
, changed
= loop dflags' ("Optimization flags are incompatible with the " ++
backendDescription (backend dflags) ++
"; optimization flags ignored.")
updOptLevelChanged is here:
updOptLevelChanged :: Int -> DynFlags -> (DynFlags, Bool)
-- ^ Sets the 'DynFlags' to be appropriate to the optimisation level and signals if any changes took place
updOptLevelChanged n dfs
= (dfs3, changed1 || changed2 || changed3)
where
final_n = max 0 (min 2 n) -- Clamp to 0 <= n <= 2
(dfs1, changed1) = foldr unset (dfs , False) remove_gopts
(dfs2, changed2) = foldr set (dfs1, False) extra_gopts
(dfs3, changed3) = setLlvmOptLevel dfs2
extra_gopts = [ f | (ns,f) <- optLevelFlags, final_n `elem` ns ]
remove_gopts = [ f | (ns,f) <- optLevelFlags, final_n `notElem` ns ]
set f (dfs, changed)
| gopt f dfs = (dfs, changed)
| otherwise = (gopt_set dfs f, True)
unset f (dfs, changed)
| not (gopt f dfs) = (dfs, changed)
| otherwise = (gopt_unset dfs f, True)
setLlvmOptLevel dfs
| llvmOptLevel dfs /= final_n = (dfs{ llvmOptLevel = final_n }, True)
| otherwise = (dfs, False)
optLevelFlags has this entry:
, ([0], Opt_OmitInterfacePragmas)
Opt_OmitInterfacePragmas is included in the default [GeneralFlag] since it has a 0 - O0 implies the flag.
defaultFlags :: Settings -> [GeneralFlag]
defaultFlags settings
{- snip -}
++ [f | (ns,f) <- optLevelFlags, 0 `elem` ns]
-- The default -O0 options
{- snip -}
So, -fno-omit-interface-pragmas causes GHC to create the EnumSet containing the Opt_OmitInterfacePragmas. Then, extra_gopts looks at optLevelFlags and pulls all the flags with elem 0 ns - this gets Opt_OmitInterfacePragmas. When folding over the options, set is called, which checks if gopt Opt_OmitInterfacePragmas dfs is set. -fno-omit-interface-pragmas was passed, so this returns False, and set flips the Changed switch to True and does a gopt_set dfs Opt_OmitInterfacePragmas.
Condensing:
- GHC has
-fomit-interface-pragmason by default - Calling
-fno-omit-interface-pragmasremoves that setting from theDynFlags - When checking for compatibility with
Interactive(or forcing optimization 0 inHEAD), it forces-O0and sees if anything changes. - Because forcing to
-O0re-enables the flag, this counts as a change, and GHC issues a warning.
Reasoning
We use -fno-omit-interface-pragmas so that we can get build warnings on irrelevant pragmas. The specific case that came up was:
data X = X {-# UNBOX #-} !Int
which passed CI fine (building with -O0) but failed when deploying (due to -Werror -O2). So we have -fno-omit-interface-pragmas even when building locally with -O0.
However, for the developers that have done cabal configure --ghc-options="-Werror", they can't get into a cabal repl or ghcid.
Environment
- GHC version used: 9.4.2, 9.4.3
Optional:
- Operating System:
- System Architecture: