Plugin name lookup behavior change from GHC 8.4 series - haskell

[Update: Turns out this was a GHC bug and it is now fixed, slated for the 8.6.4 release: https://ghc.haskell.org/trac/ghc/ticket/16104#comment:8 ]
I'm trying to port a core plugin to GHC 8.6.3, which was last working fine with GHC 8.4 series. Unfortunately, I'm running into issues. Wondering if pluging programming requirements have changed, or is this a regression in GHC itself. I boiled it down to the following example and would like some guidance on how to make this work:
I have the following in file TestPlugin.hs:
{-# LANGUAGE TemplateHaskell #-}
module TestPlugin (plugin) where
import GhcPlugins
import Data.Bits
plugin :: Plugin
plugin = defaultPlugin {installCoreToDos = install}
where install _ todos = return (test : todos)
test = CoreDoPluginPass "Test" check
check :: ModGuts -> CoreM ModGuts
check m = do mbN <- thNameToGhcName 'complement
case mbN of
Just _ -> liftIO $ putStrLn "Found complement!"
Nothing -> error "Failed to locate complement"
return m
And I have a very simple Test.hs file:
{-# OPTIONS_GHC -fplugin TestPlugin #-}
main :: IO ()
main = return ()
With GHC-8.4.2, I have:
$ ghc-8.4.2 --make -package ghc -c TestPlugin.hs
[1 of 1] Compiling TestPlugin ( TestPlugin.hs, TestPlugin.o )
$ ghc-8.4.2 -package ghc -c Test.hs
Found complement!
But with GHC 8.6.3, I get:
$ ghc-8.6.3 --make -package ghc -c TestPlugin.hs
[1 of 1] Compiling TestPlugin ( TestPlugin.hs, TestPlugin.o )
$ ghc-8.6.3 -package ghc -c Test.hs
ghc: panic! (the 'impossible' happened)
(GHC version 8.6.3 for x86_64-apple-darwin):
Failed to locate complement
The problem goes away if I change Test.hs to:
{-# OPTIONS_GHC -fplugin TestPlugin #-}
import Data.Bits -- Should not be required in the client code!
main :: IO ()
main = return ()
That is, if I explicitly import Data.Bits. But tis is quite undesirable, since Test.hs is client code and the users of the plugin have no reason to import all bunch of modules the plugin might need for its own purposes. (In practice, this would require clients to import a whole bunch of irrelevant modules; quite unworkable and not maintainable.)
I've found the following stack-overflow ticket, which seems to suffer from a similar problem: How to replicate the behaviour of 'name in a TH splice However, the answer suggested there is just not OK in this case (and perhaps wasn't really OK there either) since it would require unnecessary changes to client code in my case that is just not reasonable to expect. (Perhaps #JoachimBretner has an idea?) I've also filed this as a GHC ticket (https://ghc.haskell.org/trac/ghc/ticket/16104#ticket), but feedback from the stack-overflow community is greatly appreciated.
Should I be coding my plugin differently? Or is this a GHC regression?

Not a direct answer, but when I need to “hard-code” a name in a GHC plugin, I don’t use TH. Instead, I use findImportedModule and lookupOrig to look it up, e.g. as in
lookupJDITyCon :: TcPluginM Class
lookupJDITyCon = do
Found _ md <- findImportedModule jdiModule Nothing
jdiTcNm <- lookupOrig md (mkTcOcc "JustDoIt")
tcLookupClass jdiTcNm
where
jdiModule = mkModuleName "GHC.JustDoIt"
from the code of my ghc-justdoit plugin.
I use Template Haskell names when the user needs to mention names, e.g. in splices or annotations, that I want to pick up in the plugin. This is what I do in inspection-testing. I discuss this a bit in the appendix of the Inspection Testing paper.

Related

Code that compiles with ghc but not with cabal new-run

I have this program Main.hs:
main :: IO ()
main = do
if False then undefined
else do
let x = 5
print x
When I compile it with ghc Main.hs it compils well generating a Main executable, but when (after initializing cabal with cabal init) I try to make a cabal new-run it gives an error:
$ cabal new-run
Build profile: -w ghc-8.6.5 -O1
In order, the following will be built (use -v for more details):
- ghc-vs-cabal-0.1.0.0 (exe:ghc-vs-cabal) (file Main.hs changed)
Preprocessing executable 'ghc-vs-cabal' for ghc-vs-cabal-0.1.0.0..
Building executable 'ghc-vs-cabal' for ghc-vs-cabal-0.1.0.0..
[1 of 1] Compiling Main ( Main.hs, /home/ivan/ghc_vs_cabal/dist-newstyle/build/x86_64-linux/ghc-8.6.5/ghc-vs-cabal-0.1.0.0/x/ghc-vs-cabal/build/ghc-vs-cabal/ghc-vs-cabal-tmp/Main.o )
Main.hs:4:10: error: Empty 'do' block
|
4 | else do
| ^^
With cabal run it also gives error.
I have cabal version 2.4.0.0:
$ cabal --version
cabal-install version 2.4.0.0
compiled using version 2.4.0.1 of the Cabal library
And ghc version 8.6.5:
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 8.6.5
Does somebody know what is happening?
I know that I can fix it if I add indentation:
main :: IO ()
main = do
if False then undefined
else do
let x = 5
print x
But I want to know why it compiles with ghc but not with cabal new-run
Your code requires a language extension to parse: NondecreasingIndentation. This extension exists to avoid the awkward runaway effect of nested dos
main :: IO ()
main = do
if False then undefined
else do -- next block should be indented in standard Haskell
let x = 5
print x -- ...but this can easily get out of hand if you do it multiple times
NondecreasingIndentation allows a nested do block to register as nested as long as it's indented as much as the containing block, instead of more than the container.
According to the GHC manual, NondecreasingIndentation is on by default but disabled in Haskell2010 mode (unless explicitly enabled again). I can't find the corresponding cabal documentation, but we can probably guess it defaults to specifying Haskell2010.
You can specify extensions in a source file to be enabled or disabled regardless of external options by adding a pragma like
{-# LANGUAGE NondecreasingIndentation #-}
to the very top of the file.

How to profile TemplateHaskell built with Cabal?

Full project at https://github.com/ysangkok/cabal-profiling-issue
The project contains scaffolding generated by cabal init. I'll paste the most interesting source snippets now.
In Main.hs I have:
newtype Wrapper = Wrapper Int
deriveConvertible ''Wrapper ''Int
in TH.hs I have:
import Data.Convertible
deriveConvertible :: TH.Name -> TH.Name -> TH.Q [TH.Dec]
deriveConvertible newType otherType = do
Just newCon <- TH.lookupValueName (TH.nameBase newType)
v <- TH.newName "v"
[d|
instance Convertible $(TH.conT newType) $(TH.conT otherType) where
safeConvert $(TH.conP newCon [TH.varP v]) = Right $(TH.varE v)
instance Convertible $(TH.conT otherType) $(TH.conT newType) where
safeConvert source = Right ($(TH.conE newCon) source)
|]
But if I put profiling: true in cabal.project and run cabal build with GHC 8.6.5 and Cabal 3.4.0.0 (installed using ghcup), I get:
Failed to load interface for 'Data.Convertible.Base'
Perhaps you haven't installed the profiling libraries for package 'convertible-1.1.1.0'?
What is wrong with the code, and why does it compile without profiling, but fails when enabled?
It is a known problem about compile multi-module program that contains TH code for profiling, see related sections in the documentation:
This causes difficulties if you have a multi-module program containing
Template Haskell code and you need to compile it for profiling,
because GHC cannot load the profiled object code and use it when
executing the splices.
As a workaround, just put TemplateHaskell into the other-modules in your test.cabal,
other-extensions: TemplateHaskell
Then building with profiling (i.e., with the cabal build --enable-library-profiling) every thing would be ok.
For more details about why we need TemplateHaskell in other-modules section, see https://github.com/haskell/cabal/issues/5961.

Why i can't compile with GHC if code contain module definition?

I'am trying to compile a very small haskell code with ghc:
module Comma where
import System.IO
main = do
contents <- getContents
putStr (comma contents)
comma input =
let allLines = lines input
addcomma [x] = x
addcomma (x:xs) = x ++ "," ++ (addcomma xs)
result = addcomma allLines
in result
The command i'm using to compile is :
ghc --make Comma.hs
And i'm getting this answer:
[1 of 1] Compiling Comma ( Comma.hs, Comma.o )
No file is generated, and there is no warning or errors messages.
If i comment the "module Comma where" line from code it compiles correctly:
[1 of 1] Compiling Main ( Comma.hs, Comma.o )
Linking Comma ...
I don't understand what is happening.
I'm using ghc 7,4,1
(Glasgow Haskell Compiler, Version 7.4.1, stage 2 booted by GHC version 7.4.1)
and ubuntu linux.
I appreciate if anyone could tell why doesn't compile with the module definition
GHC compiles the function Main.main to be the entry point of an executable. When you omit the module declaration, Module Main where is implicitly inserted for you.
But when you explicitly name it something other than Main ghc doesn't find an entry point.
My usual workflow is to use ghci (or ghci + emacs) instead for these snippets which let's you bypass this issue entirely. Alternatively, you could compile with -main-is Comma to explicitly tell ghc to use the Comma module.
No file is generated
Are you sure? I would expect that at least Comma.o and Comma.hi are generated. The former contains the compiled code ready to be linked into an executable, and the latter contains interface information that ghc uses to typecheck modules that import the module Comma.
However, ghc will only link the compiled modules into an executable if there is a main function. By default, that means a function named main in a module named Main. If you don't put an explicit module name, the name Main is assumed, and that's why your test works when you delete the module Comma where line.
To compile and link the Comma.hs file you can either use module Main where instead of module Comma where, or you can use the -main-is flag to tell ghc that Comma.main is to be the main function:
ghc --make -main-is Comma Comma.hs
Or:
ghc --make -main-is Comma.main Comma.hs
If you have a main definition in your file and you want to compile it to an executable you need can only have module Main where.

cabal FFI dependency

I am making a small Haskell game in Windows, where I would like to respond each time the user presses a key. Because getChar behaves strangely on Windows, I use FFI to get access to getch in conio.h, as described here. The relevant code is:
foreign import ccall unsafe "conio.h getch" c_getch :: IO CInt
This works fine, when I run it in ghci, or compile it with ghc. I also want to try making a cabal package out of it, so extending from this question, I include the following in my cabal file:
...
executable noughts
Includes: conio.h
Extra-libraries conio
...
But when I run cabal configure, it tells me:
cabal: Missing dependency on a foreign library:
* Missing C library: conio
It makes sense, because in my haskell platform directory, under ...\Haskell Platform\2012.4.0.0\mingw there is a conio.h file under the include directory, but no other conio file to provide the object code.
Am I doing this the right way, and if so, how can I find out which library to include in my cabal file?
First off, there is not always a one-to-one mapping between C header files and libraries. In this case, the functions declared in conio.h can be found in various runtime libraries, such as crtdll (deprecated) or msvcrt (preferred, I guess).
With the Haskell Platform on Windows, Cabal will look for these libraries in .\mingw\lib (under your Haskell Platform directory): if you ask for msvcrt, it will look for .\mingw\lib\libmsvcrt.a. This specific library should already be shipped with your Haskell Platform. (If you want to point to other directories with lib*.a files, you can use Cabal's --extra-lib-dirs option.)
A tiny example of this would be as follows; this is Main.hs:
{-# LANGUAGE ForeignFunctionInterface #-}
import Foreign.C.Types
foreign import ccall unsafe "conio.h _putch" c_putch :: CInt -> IO ()
main :: IO ()
main = do
c_putch . toEnum . fromEnum $ '!'
c_putch . toEnum . fromEnum $ '\n'
And this would be something-awesome.cabal:
name: something-awesome
version: 0.1.0.0
build-type: Simple
cabal-version: >=1.8
executable yay
main-is: Main.hs
build-depends: base ==4.5.*
includes: conio.h
extra-libraries: msvcrt
This should work fine:
c:\tmp\something-awesome> dir /B
Main.hs
something-awesome.cabal
c:\tmp\something-awesome> cabal configure
Resolving dependencies...
Configuring something-awesome-0.1.0.0...
c:\tmp\something-awesome> cabal build
Building something-awesome-0.1.0.0...
Preprocessing executable 'yay' for something-awesome-0.1.0.0...
[1 of 1] Compiling Main ( Main.hs, dist\build\yay\yay-tmp\Main.o )
Linking dist\build\yay\yay.exe ...
c:\tmp\something-awesome> dist\build\yay\yay.exe
!

Haddock not obeying ignore-exports correctly

Take this example:
{-# OPTIONS_HADDOCK ignore-exports #-}
module HaddockTest (e1) where
-- * Exported
-- | This is exported.
e1 :: Int
e1 = 1
-- * Private
-- | This is not exported.
p1 :: Int
p1 = 1
Strangely, the doc-string for the non-exported declaration is ignored, even though we get the structure otherwise correctly:
Does anyone have a clue? For the record, invoking via cabal haddock --internal doesn't help, nor does haddock --ignore-all-exports.
It's a bug in haddock-2.10, reported in March and fixed soon after, but the haddock shipped with GHC-7.4.1 was affected. Presumably the haddock shipped with 7.4.2 includes the fix (haven't built 7.4.2 yet).

Resources