GHC issueshttps://gitlab.haskell.org/ghc/ghc/-/issues2023-09-01T09:47:55Zhttps://gitlab.haskell.org/ghc/ghc/-/issues/23832Extend defaulting plugin API to allow defaulting on multi-parameter type classes2023-09-01T09:47:55ZGergő ÉrdiExtend defaulting plugin API to allow defaulting on multi-parameter type classesCurrently, the defaulting plugin API matches exactly the signature of the built-in defaulting mechanism, where each type variable occuring in the `Wanteds` can be proposed to be defaulted to a priority list of types, one by one, with som...Currently, the defaulting plugin API matches exactly the signature of the built-in defaulting mechanism, where each type variable occuring in the `Wanteds` can be proposed to be defaulted to a priority list of types, one by one, with some extra constraints that need to hold after defaulting.
Since each new constraint is associated with a single defaultable type variable, this makes it impossible to propose defaults for multi-parameter typeclasses `C a b`, or even constraints with multiple as-yet-unsolved type variables`C (T a b)`.
However, it would not take much to extend the plugin API to support this use case. I have a prototype implementation at https://gitlab.haskell.org/cactus/ghc/-/commit/b73d407b20c2ba8c0792855b41d1b5dd5dc1aee5 that changes the API to this:
```
data DefaultingProposal
= DefaultingProposal
{ deProposalTyVars :: [TcTyVar]
-- ^ The type variable to default.
, deProposalCandidates :: [[Type]]
-- ^ Candidate types to default the type variable to.
, deProposalCts :: [Ct]
-- ^ The constraints against which defaults are checked.
}
```
The types could be a bit tighter, because basically we want a rectangle of proposal candidates (`all (equalLength deProposalTyVars) deProposalCandidates`), but I don't know how that's usually done in the GHC codebase so for the prototype this shall do.https://gitlab.haskell.org/ghc/ghc/-/issues/19940tcLookup in plugins seems broken on HEAD2023-06-23T07:57:57Zsheafsam.derbyshire@gmail.comtcLookup in plugins seems broken on HEADThe operation of looking up names in a typechecker plugin seems to have been broken sometime recently. For instance:
```haskell
-- MyModule
type family MyFam a b where
-- MyPlugin
tcPluginInit :: TcPluginM ()
tcPluginInit = do
myMod...The operation of looking up names in a typechecker plugin seems to have been broken sometime recently. For instance:
```haskell
-- MyModule
type family MyFam a b where
-- MyPlugin
tcPluginInit :: TcPluginM ()
tcPluginInit = do
myModule <- findModule myPackage myModuleName
lookupResult <- tcLookupGlobal =<< lookupOrig myModule ( mkTcOcc "MyFam" )
pprPanic "Result was:" ( ppr lookupResult )
findModule :: String -> String -> TcPluginM Module
findModule pkg modName = do
findResult <- findImportedModule ( mkModuleName modName ) ( Just $ fsLit pkg )
case findResult of
Found _ res -> pure res
FoundMultiple _ -> error $ "MyPlugin: found multiple modules named " <> modName <> "."
_ -> error $ "MyPlugin: could not find any module named " <> modName <> "."
```
On GHC 9.2.1 (alpha1), I get the expected result:
```
GHC version 9.2.0.20210331:
Result was:
Type constructor `MyFam'
```
On GHC HEAD, when running in ghci I get:
```
GHC version 9.3.20210604:
Result was:
Identifier `disableBuffering'
```
and when compiling normally, I get:
```
GHC version 9.3.20210604:
Result was:
Identifier `plugin'
```
I have only checked 7a05185a and 79d12d34 and they both present the same issue. Note that this is on Windows 10 x64
(10.0.19041).
See here for full reproducer:
[tclookup.zip](/uploads/d1c32487021ec8e1c19d6b97b56f32ec/tclookup.zip)9.4.1https://gitlab.haskell.org/ghc/ghc/-/issues/21427Irreducible givens hidden for TcPlugin2022-07-21T11:33:12ZPavol VargovčíkIrreducible givens hidden for TcPlugin## Motivation
Irreducible constraints could be often solved by TcPlugin, but irreducible givens are not accessible there.
## Proposal
Expose irreducible givens to TcPluginSolver.
~"typechecker plugins"## Motivation
Irreducible constraints could be often solved by TcPlugin, but irreducible givens are not accessible there.
## Proposal
Expose irreducible givens to TcPluginSolver.
~"typechecker plugins"https://gitlab.haskell.org/ghc/ghc/-/issues/19981Test suite does not include any nontrivial type-checking plugins2022-02-24T14:43:46Zsheafsam.derbyshire@gmail.comTest suite does not include any nontrivial type-checking pluginsThe GHC test suite currently does not include any tests involving non-trivial type-checking plugins. All type-checking plugins tests use a simple stub plugin:
```haskell
thePlugin :: [CommandLineOption] -> TcPlugin
thePlugin opts = TcPl...The GHC test suite currently does not include any tests involving non-trivial type-checking plugins. All type-checking plugins tests use a simple stub plugin:
```haskell
thePlugin :: [CommandLineOption] -> TcPlugin
thePlugin opts = TcPlugin
{ tcPluginInit = return ()
, tcPluginSolve = \_ _ _ _ -> return $ TcPluginOk [] []
, tcPluginStop = \_ -> return ()
}
```
On top of the obvious issue that we're not testing the solver part of the plugins, this also shields GHC developers from the consequences their changes might have on plugin authors. Having to update the plugins from the test suite to accommodate for changes in the internals of GHC would help GHC developers spell out possible migration strategies, much like head-hackage does today for other user-facing changes to GHC.sheafsam.derbyshire@gmail.comsheafsam.derbyshire@gmail.comhttps://gitlab.haskell.org/ghc/ghc/-/issues/20895TcPlugins: newWanted discards the source location2022-01-12T13:16:27Zsheafsam.derbyshire@gmail.comTcPlugins: newWanted discards the source locationWhen a plugin spins off a new Wanted constraint, using `newWanted`, based on a constraint it can simplify, the source location for the constraint is discarded.
This happens because we have:
```haskell
-- GHC.Tc.Plugin
newWanted :: CtLo...When a plugin spins off a new Wanted constraint, using `newWanted`, based on a constraint it can simplify, the source location for the constraint is discarded.
This happens because we have:
```haskell
-- GHC.Tc.Plugin
newWanted :: CtLoc -> PredType -> TcPluginM CtEvidence
newWanted loc pty
= unsafeTcPluginTcM (TcM.newWanted (ctLocOrigin loc) Nothing pty)
-- GHC.Tc.Utils.TcMType
newWanted :: CtOrigin -> Maybe TypeOrKind -> PredType -> TcM CtEvidence
newWanted orig t_or_k pty
= do loc <- getCtLocM orig t_or_k
d <- if isEqPrimPred pty then HoleDest <$> newCoercionHole pty
else EvVarDest <$> newEvVar pty
return $ CtWanted { ctev_dest = d
, ctev_pred = pty
, ctev_nosh = WDeriv
, ctev_loc = loc }
-- GHC.Tc.Utils.Monad
getCtLocM :: CtOrigin -> Maybe TypeOrKind -> TcM CtLoc
getCtLocM origin t_or_k
= do { env <- getLclEnv
; return (CtLoc { ctl_origin = origin
, ctl_env = env
, ctl_t_or_k = t_or_k
, ctl_depth = initialSubGoalDepth }) }
```
That is, starting with the `CtLoc` (which has the source span information we want), we keep only the `CtOrigin`, and discard the rest. The typechecker environment is then obtained from the monadic environment with `getLclEnv` in `getCtLocM`.
In practice, in a typechecking plugin, when one wants the emitted Wanted to have the same source location, one must wrap the `newWanted` call in `setCtLocM`. This sets the correct typechecker environment that to be retrieved by `getLcLEnv`.
If `GHC.Tc.Plugin.newWanted` is going to discard the local typechecking environment, then it should clearly indicate so in its type signature by taking a `CtOrigin` instead of a `CtLoc`.
@rae @nfrisby Do either of you have opinions on how to design the API to avoid this problem? Also tagging @edsko as he has been running into this issue.sheafsam.derbyshire@gmail.comsheafsam.derbyshire@gmail.comhttps://gitlab.haskell.org/ghc/ghc/-/issues/12780Calling "do nothing" type checker plugin affects type checking when it shouldn't2020-10-13T17:53:57ZclintonCalling "do nothing" type checker plugin affects type checking when it shouldn'tThe following has been compiled against ghc version 8.1.20161022, my apologies if this bug has already been fixed.
The following program compiles fine:
```hs
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
...The following has been compiled against ghc version 8.1.20161022, my apologies if this bug has already been fixed.
The following program compiles fine:
```hs
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
main :: IO ()
main = return ()
type family F p = t | t -> p
type IsF p t = (C p, t ~ F p)
class C p where
f :: (IsF p t) => t
f = undefined
g :: (IsF p t) => t
g = f
```
But if we define a "do nothing" type checker plugin like so:
```hs
{-# OPTIONS_GHC -dynamic-too #-}
module MyPlugin (plugin) where
import Plugins (
Plugin(tcPlugin),
defaultPlugin, tcPlugin
)
import TcRnTypes (
TcPlugin(TcPlugin), tcPluginInit, tcPluginSolve, tcPluginStop,
TcPluginSolver,
TcPluginResult(TcPluginContradiction, TcPluginOk)
)
plugin :: Plugin
plugin = defaultPlugin { tcPlugin = const (Just myPlugin) }
myPlugin :: TcPlugin
myPlugin =
TcPlugin
{
tcPluginInit = return (),
tcPluginSolve = const solver,
tcPluginStop = const (return ())
}
solver :: TcPluginSolver
solver _ _ _ = return $ TcPluginOk [] []
```
And call it from our original source file by adding:
```hs
{-# OPTIONS_GHC -fplugin MyPlugin #-}
```
We get the following error:
```
pluginbug.hs:18:5: error:
• Could not deduce (C p0, t ~ F p0) arising from a use of ‘f’
from the context: IsF p t
bound by the type signature for:
g :: IsF p t => t
at pluginbug.hs:17:1-19
The type variable ‘p0’ is ambiguous
Relevant bindings include g :: t (bound at pluginbug.hs:18:1)
• In the expression: f
In an equation for ‘g’: g = f
```
Note that not just calling my "do nothing" type checking plugin does this, to be sure, instead I called `ghc-typelits-natnormalise` like so:
```hs
{-# OPTIONS_GHC -fplugin GHC.TypeLits.Normalise #-}
```
And the same error results.
One solution is to remove `(C p)` from the `IsF` type declaration, and instead adding it to `g`s definition, like so:
```hs
--type IsF p t = (C p, t ~ F p)
type IsF p t = (t ~ F p)
--g :: (IsF p t) => t
g :: (C p, IsF p t) => t
```
In addition, either of the following two solutions will get the code to compile:
1. Replacing the reference to `IsF p t` in the class signature with `t ~ F p` like so:
```hs
-- f :: (IsF p t) => t
f :: (t ~ F p) => t
```
1. Alternatively, expanding out `IsF p t` in the signature of `g` as follows:
```hs
--g :: (IsF p t) => t
g :: (C p, t ~ F p) => t
```
Note that is we remove the class `C` entirely (making `f` an ordinary function) the bug disappears, but if instead replace the class with a constraint `C` like so:
```hs
type family C t :: Constraint
```
the bug still appears.
So it seems this bug needs four things to all occur for it to appear:
1. A type constraint synonym in the calling code.
1. A type constraint synonym in the called code.
1. The type constraint synonym to contain a class constraint, or at least a non-equality one.
1. A type checking plugin to be called.
Note that the error requires a type checking plugin to be called, if one does not override `defaultPlugin` like so:
```hs
plugin = defaultPlugin { tcPlugin = const (Just myPlugin) }
```
and instead just does:
```hs
plugin = defaultPlugin
```
then the issue will not occur.
So in summary, it seems that calling a type checking plugin somehow affects ordinary type checking in particular circumstances. I don't think this should happen.https://gitlab.haskell.org/ghc/ghc/-/issues/9840Permit empty closed type families2020-06-17T11:38:04ZAdam GundryPermit empty closed type familiesAt the moment, closed type families without any equations fail with a parse error. In addition, they cannot be created by TH (see #8028). Would it be possible to permit these instead?
My use case is in my typechecker plugin for units of...At the moment, closed type families without any equations fail with a parse error. In addition, they cannot be created by TH (see #8028). Would it be possible to permit these instead?
My use case is in my typechecker plugin for units of measure, where I want to add new type-level operators without any equational theory (because it will be supplied by the plugin) and without users having the ability to introduce their own type family instances.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ----------------------- |
| Version | 7.8.3 |
| Type | FeatureRequest |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler (Type checker) |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | goldfire |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Permit empty closed type families","status":"New","operating_system":"","component":"Compiler (Type checker)","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"7.8.3","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":["goldfire"],"type":"FeatureRequest","description":"At the moment, closed type families without any equations fail with a parse error. In addition, they cannot be created by TH (see #8028). Would it be possible to permit these instead?\r\n\r\nMy use case is in my typechecker plugin for units of measure, where I want to add new type-level operators without any equational theory (because it will be supplied by the plugin) and without users having the ability to introduce their own type family instances.","type_of_failure":"OtherFailure","blocking":[]} -->8.0.1https://gitlab.haskell.org/ghc/ghc/-/issues/10078tcPluginStop of a type checker plugin is not called if an error occurs2019-07-07T18:37:39ZjbrackertcPluginStop of a type checker plugin is not called if an error occursWhen a module using a type checker plugin produces a compiler error the clean up function `tcPluginStop` of the plugin is not called.
I am not sure if this is intended, but according to the description of the wiki page (Plugins/TypeChec...When a module using a type checker plugin produces a compiler error the clean up function `tcPluginStop` of the plugin is not called.
I am not sure if this is intended, but according to the description of the wiki page (Plugins/TypeChecker) this should always be called.
### Test plugin
`MyPlugin.hs`:
```hs
module MyPlugin
( plugin ) where
import Plugins
import TcRnTypes
import TcPluginM
plugin :: Plugin
plugin = defaultPlugin
{ tcPlugin = \clos -> Just $ TcPlugin
{ tcPluginInit = tcPluginIO $ putStrLn ">>> Plugin Init"
, tcPluginSolve = \_ _ _ _ -> do
tcPluginIO $ putStrLn ">>> Plugin Solve"
return $ TcPluginOk [] []
, tcPluginStop = \_ -> tcPluginIO $ putStrLn ">>> Plugin Stop"
}
}
```
### Minimal example (with type error)
`Main.hs`:
```hs
{-# OPTIONS_GHC -fplugin MyPlugin #-}
module Main where
main :: (Monad m) => m ()
main = do
return 1
```
Compiling this will lead to the following output:
```
$ ~/ghc/inplace/bin/ghc-stage2 --make -package ghc-7.11.20150209 -dynamic Main.hs
[2 of 2] Compiling Main ( Main.hs, Main.o )
>>> Plugin Init
>>> Plugin Solve
>>> Plugin Solve
>>> Plugin Solve
Main.hs:6:10:
Could not deduce (Num ()) arising from the literal ‘1’
from the context: Monad m
bound by the type signature for: main :: Monad m => m ()
at Main.hs:4:9-25
In the first argument of ‘return’, namely ‘1’
In a stmt of a 'do' block: return 1
In the expression: do { return 1 }
```
Which means `tcPluginStop` was _not_ called.
### Minimal example (without type error)
`Main.hs`:
```hs
{-# OPTIONS_GHC -fplugin MyPlugin #-}
module Main where
main :: (Monad m) => m ()
main = do
return ()
```
Compiling this will lead to the following output:
```
$ ~/ghc/inplace/bin/ghc-stage2 --make -package ghc-7.11.20150209 -dynamic Main.hs
[2 of 2] Compiling Main ( Main.hs, Main.o ) [MyPlugin changed]
>>> Plugin Init
>>> Plugin Solve
>>> Plugin Solve
>>> Plugin Stop
Linking Main ...
```
Which means `tcPluginStop` _was_ called.
### Possible solution
As far as I can see, the solution to this should be to change the plugin code at the bottom of `typechecker/TcRnDriver.hs` from
```hs
withTcPlugins :: HscEnv -> TcM a -> TcM a
withTcPlugins hsc_env m =
do plugins <- liftIO (loadTcPlugins hsc_env)
case plugins of
[] -> m -- Common fast case
_ -> do (solvers,stops) <- unzip `fmap` mapM startPlugin plugins
res <- updGblEnv (\e -> e { tcg_tc_plugins = solvers }) m
mapM_ runTcPluginM stops
return res
where
startPlugin (TcPlugin start solve stop) =
do s <- runTcPluginM start
return (solve s, stop s)
```
to
```hs
withTcPlugins :: HscEnv -> TcM a -> TcM a
withTcPlugins hsc_env m =
do plugins <- liftIO (loadTcPlugins hsc_env)
case plugins of
[] -> m -- Common fast case
_ -> do (solvers,stops) <- unzip `fmap` mapM startPlugin plugins
eitherRes <- tryM $ do updGblEnv (\e -> e { tcg_tc_plugins = solvers }) m
mapM_ runTcPluginM stops
case eitherRes of
Left e -> failM
Right res -> return res
where
startPlugin (TcPlugin start solve stop) =
do s <- runTcPluginM start
return (solve s, stop s)
```
.
I have tried this. It compiles and my minimal example delivers the correct result.
Are there any arguments against this change? If not, I would try to commit a patch for this problem sometime this weekend.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ----------------------- |
| Version | 7.11 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler (Type checker) |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | adamgundry |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"tcPluginStop of a type checker plugin is not called if an error occurs","status":"New","operating_system":"","component":"Compiler (Type checker)","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"7.11","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":["adamgundry"],"type":"Bug","description":"When a module using a type checker plugin produces a compiler error the clean up function `tcPluginStop` of the plugin is not called.\r\n\r\nI am not sure if this is intended, but according to the description of the wiki page (Plugins/TypeChecker) this should always be called.\r\n\r\n=== Test plugin\r\n\r\n`MyPlugin.hs`:\r\n{{{#!hs\r\nmodule MyPlugin\r\n ( plugin ) where\r\n\r\nimport Plugins\r\nimport TcRnTypes\r\nimport TcPluginM\r\n\r\nplugin :: Plugin\r\nplugin = defaultPlugin \r\n { tcPlugin = \\clos -> Just $ TcPlugin \r\n { tcPluginInit = tcPluginIO $ putStrLn \">>> Plugin Init\"\r\n , tcPluginSolve = \\_ _ _ _ -> do\r\n tcPluginIO $ putStrLn \">>> Plugin Solve\"\r\n return $ TcPluginOk [] []\r\n , tcPluginStop = \\_ -> tcPluginIO $ putStrLn \">>> Plugin Stop\"\r\n }\r\n }\r\n}}}\r\n\r\n=== Minimal example (with type error)\r\n\r\n`Main.hs`:\r\n{{{#!hs\r\n{-# OPTIONS_GHC -fplugin MyPlugin #-}\r\nmodule Main where\r\n\r\nmain :: (Monad m) => m ()\r\nmain = do\r\n return 1\r\n}}}\r\n\r\nCompiling this will lead to the following output:\r\n{{{\r\n$ ~/ghc/inplace/bin/ghc-stage2 --make -package ghc-7.11.20150209 -dynamic Main.hs\r\n[2 of 2] Compiling Main ( Main.hs, Main.o )\r\n>>> Plugin Init\r\n>>> Plugin Solve\r\n>>> Plugin Solve\r\n>>> Plugin Solve\r\n\r\nMain.hs:6:10:\r\n Could not deduce (Num ()) arising from the literal ‘1’\r\n from the context: Monad m\r\n bound by the type signature for: main :: Monad m => m ()\r\n at Main.hs:4:9-25\r\n In the first argument of ‘return’, namely ‘1’\r\n In a stmt of a 'do' block: return 1\r\n In the expression: do { return 1 }\r\n}}}\r\nWhich means `tcPluginStop` was _not_ called.\r\n\r\n=== Minimal example (without type error)\r\n\r\n`Main.hs`:\r\n{{{#!hs\r\n{-# OPTIONS_GHC -fplugin MyPlugin #-}\r\nmodule Main where\r\n\r\nmain :: (Monad m) => m ()\r\nmain = do\r\n return ()\r\n}}}\r\n\r\nCompiling this will lead to the following output:\r\n{{{\r\n$ ~/ghc/inplace/bin/ghc-stage2 --make -package ghc-7.11.20150209 -dynamic Main.hs\r\n[2 of 2] Compiling Main ( Main.hs, Main.o ) [MyPlugin changed]\r\n>>> Plugin Init\r\n>>> Plugin Solve\r\n>>> Plugin Solve\r\n>>> Plugin Stop\r\nLinking Main ...\r\n}}}\r\nWhich means `tcPluginStop` _was_ called.\r\n\r\n=== Possible solution\r\n\r\nAs far as I can see, the solution to this should be to change the plugin code at the bottom of `typechecker/TcRnDriver.hs` from\r\n{{{#!hs\r\nwithTcPlugins :: HscEnv -> TcM a -> TcM a\r\nwithTcPlugins hsc_env m =\r\n do plugins <- liftIO (loadTcPlugins hsc_env)\r\n case plugins of\r\n [] -> m -- Common fast case\r\n _ -> do (solvers,stops) <- unzip `fmap` mapM startPlugin plugins\r\n res <- updGblEnv (\\e -> e { tcg_tc_plugins = solvers }) m\r\n mapM_ runTcPluginM stops\r\n return res\r\n where\r\n startPlugin (TcPlugin start solve stop) =\r\n do s <- runTcPluginM start\r\n return (solve s, stop s)\r\n}}}\r\nto\r\n{{{#!hs\r\nwithTcPlugins :: HscEnv -> TcM a -> TcM a\r\nwithTcPlugins hsc_env m =\r\n do plugins <- liftIO (loadTcPlugins hsc_env)\r\n case plugins of\r\n [] -> m -- Common fast case\r\n _ -> do (solvers,stops) <- unzip `fmap` mapM startPlugin plugins\r\n eitherRes <- tryM $ do updGblEnv (\\e -> e { tcg_tc_plugins = solvers }) m\r\n mapM_ runTcPluginM stops\r\n case eitherRes of\r\n Left e -> failM\r\n Right res -> return res\r\n where\r\n startPlugin (TcPlugin start solve stop) =\r\n do s <- runTcPluginM start\r\n return (solve s, stop s)\r\n}}}\r\n.\r\n\r\nI have tried this. It compiles and my minimal example delivers the correct result.\r\n\r\nAre there any arguments against this change? If not, I would try to commit a patch for this problem sometime this weekend.","type_of_failure":"OtherFailure","blocking":[]} -->7.10.1jbrackerjbrackerhttps://gitlab.haskell.org/ghc/ghc/-/issues/11462Use of typechecker plugin erroneously triggers "unbound implicit parameter" e...2019-07-07T18:30:19ZkwfUse of typechecker plugin erroneously triggers "unbound implicit parameter" errorTo document this bug, we're going to need a typechecker plugin to test it with. I've built a dummy plugin for this purpose, so we can be sure it is not interference from a particular plugin.
`dummy-plugin/dummy-plugin.cabal`
```
name: ...To document this bug, we're going to need a typechecker plugin to test it with. I've built a dummy plugin for this purpose, so we can be sure it is not interference from a particular plugin.
`dummy-plugin/dummy-plugin.cabal`
```
name: dummy-plugin
version: 0.1.0.0
category: Development
build-type: Simple
cabal-version: >=1.10
library
hs-source-dirs: .
exposed-modules: DummyPlugin
build-depends: base, ghc
default-language: Haskell2010
GHC-options: -Wall -O2
```
`dummy-plugin/DummyPlugin.hs`
```hs
module DummyPlugin(plugin) where
import TcRnMonad ( TcPlugin(..), TcPluginResult(..) )
import Plugins ( defaultPlugin, Plugin(..), CommandLineOption )
plugin :: Plugin
plugin = defaultPlugin { tcPlugin = Just . thePlugin }
thePlugin :: [CommandLineOption] -> TcPlugin
thePlugin opts = TcPlugin
{ tcPluginInit = return ()
, tcPluginSolve = \_ _ _ _ -> return $ TcPluginOk [] []
, tcPluginStop = \_ -> return ()
}
```
`Bug.hs`
```hs
{-# OPTIONS_GHC -fplugin=DummyPlugin #-}
module Bug where
impossible :: a
impossible = undefined
```
First, compile the dummy plugin. From its directory, run `cabal install` to install the plugin.
Then, from the main directory, run `ghc Bug.hs`.
Expected result: the file compiles.
Actual result:
```
Bug.hs:6:14: error:
• Unbound implicit parameter ?callStack::GHC.Stack.Types.CallStack
arising from a use of implicit parameter ‘?callStack’
• In the expression: undefined
In an equation for ‘impossible’: impossible = undefined
```
Further, observe that commenting out the line which invokes the type-checker plugin (the pragma on line 1) causes the file to compile correctly.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ----------------------- |
| Version | 7.10.3 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | high |
| Resolution | Unresolved |
| Component | Compiler (Type checker) |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Use of typechecker plugin erroneously triggers \"unbound implicit parameter\" error","status":"New","operating_system":"","component":"Compiler (Type checker)","related":[],"milestone":"8.0.1","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"7.10.3","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"To document this bug, we're going to need a typechecker plugin to test it with. I've built a dummy plugin for this purpose, so we can be sure it is not interference from a particular plugin.\r\n\r\n{{{dummy-plugin/dummy-plugin.cabal}}}\r\n{{{\r\nname: dummy-plugin\r\nversion: 0.1.0.0\r\ncategory: Development\r\nbuild-type: Simple\r\ncabal-version: >=1.10\r\n\r\nlibrary\r\n hs-source-dirs: .\r\n exposed-modules: DummyPlugin\r\n build-depends: base, ghc\r\n default-language: Haskell2010\r\n GHC-options: -Wall -O2\r\n}}}\r\n\r\n{{{dummy-plugin/DummyPlugin.hs}}}\r\n{{{#!hs\r\nmodule DummyPlugin(plugin) where\r\n\r\nimport TcRnMonad ( TcPlugin(..), TcPluginResult(..) )\r\nimport Plugins ( defaultPlugin, Plugin(..), CommandLineOption )\r\n\r\nplugin :: Plugin\r\nplugin = defaultPlugin { tcPlugin = Just . thePlugin }\r\n\r\nthePlugin :: [CommandLineOption] -> TcPlugin\r\nthePlugin opts = TcPlugin\r\n { tcPluginInit = return ()\r\n , tcPluginSolve = \\_ _ _ _ -> return $ TcPluginOk [] []\r\n , tcPluginStop = \\_ -> return ()\r\n }\r\n}}}\r\n\r\n{{{Bug.hs}}}\r\n{{{#!hs\r\n{-# OPTIONS_GHC -fplugin=DummyPlugin #-}\r\n\r\nmodule Bug where\r\n\r\nimpossible :: a\r\nimpossible = undefined\r\n}}}\r\n\r\nFirst, compile the dummy plugin. From its directory, run {{{cabal install}}} to install the plugin.\r\n\r\nThen, from the main directory, run {{{ghc Bug.hs}}}.\r\n\r\nExpected result: the file compiles.\r\nActual result:\r\n{{{\r\nBug.hs:6:14: error:\r\n • Unbound implicit parameter ?callStack::GHC.Stack.Types.CallStack\r\n arising from a use of implicit parameter ‘?callStack’\r\n • In the expression: undefined\r\n In an equation for ‘impossible’: impossible = undefined\r\n}}}\r\n\r\nFurther, observe that commenting out the line which invokes the type-checker plugin (the pragma on line 1) causes the file to compile correctly.","type_of_failure":"OtherFailure","blocking":[]} -->8.0.1Eric SeidelEric Seidelhttps://gitlab.haskell.org/ghc/ghc/-/issues/11525Using a dummy typechecker plugin causes an ambiguity check error2019-07-07T18:30:02ZjmeUsing a dummy typechecker plugin causes an ambiguity check errorThe following variation on
\[\[GhcFile(testsuite/tests/typecheck/should_compile/T10009.hs)\]\]
compiles cleanly:
```hs
{-# LANGUAGE TypeFamilies, ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
module T10009W...The following variation on
\[\[GhcFile(testsuite/tests/typecheck/should_compile/T10009.hs)\]\]
compiles cleanly:
```hs
{-# LANGUAGE TypeFamilies, ScopedTypeVariables #-}
{-# OPTIONS_GHC -fno-warn-redundant-constraints #-}
module T10009WithClass where
type family F a
type family UnF a
class (UnF (F b) ~ b) => C b where
f :: F b -> ()
g :: forall a. (C a) => a -> ()
g _ = f (undefined :: F a)
```
But compiling it with the dummy typechecker plugin
\[\[GhcFile(testsuite/tests/typecheck/should_compile/T11462_Plugin.hs)\]\]
yields
```
$ ghc -dynamic -c T11462_Plugin.hs
$ ghc -fplugin=T11462_Plugin -dynamic -c T10009WithClass.hs
T10009WithClass.hs:10:5: error:
- Could not deduce: F b0 ~ F b
from the context: C b
bound by the type signature for:
f :: C b => F b -> ()
at T10009WithClass.hs:10:5-18
NB: 'F' is a type function, and may not be injective
The type variable 'b0' is ambiguous
Expected type: F b -> ()
Actual type: F b0 -> ()
- In the ambiguity check for 'f'
To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
When checking the class method: f :: forall b. C b => F b -> ()
In the class declaration for 'C'
```
Superficially, the problem is that the
presence of the plugin causes `runTcPluginsWanted` to zonk
the pending superclass constraint, changing it to a `CNonCanonical`. This in
turn prevents `solveWanteds` from making any further progress (which
ultimately leads to the error):
```
getUnsolvedInerts
tv eqs = {[W] hole{aIO} :: s_aIL[fuv:2]
GHC.Prim.~# fsk_aIC[fsk] (CTyEqCan)}
fun eqs = {[W] hole{aIM} :: F b_aIu[tau:3]
GHC.Prim.~# s_aIL[fuv:2] (CFunEqCan)}
insols = {}
others = {[W] $dC_aIv :: C b_aIu[tau:3] (CDictCan(psc))} <===== BEFORE
implics = {}
Unflattening
{Funeqs = [W] hole{aIM} :: F b_aIu[tau:3]
GHC.Prim.~# s_aIL[fuv:2] (CFunEqCan)
Tv eqs = [W] hole{aIO} :: s_aIL[fuv:2]
GHC.Prim.~# fsk_aIC[fsk] (CTyEqCan)}
Filling coercion hole aIM := <F b_aIu[tau:3]>_N
unflattenFmv s_aIL[fuv:2] := F b_aIu[tau:3]
writeMetaTyVar s_aIL[fuv:2] :: * := F b_aIu[tau:3]
Unflattening 1 {}
Unflattening 2
{[W] hole{aIO} :: s_aIL[fuv:2] GHC.Prim.~# fsk_aIC[fsk] (CTyEqCan)}
Unflattening 3 {}
Unflattening done
{[W] hole{aIO} :: s_aIL[fuv:2]
GHC.Prim.~# fsk_aIC[fsk] (CNonCanonical)}
zonkSimples done:
{[W] hole{aIO} :: F b_aIu[tau:3]
GHC.Prim.~# F b_aIs[sk] (CNonCanonical)}
zonkSimples done:
{[W] $dC_aIv :: C b_aIu[tau:3] (CNonCanonical), <===== AFTER
[W] hole{aIO} :: F b_aIu[tau:3]
GHC.Prim.~# F b_aIs[sk] (CNonCanonical)}
solveSimples end }
iterations = 1
residual = WC {wc_simple =
[W] $dC_aIv :: C b_aIu[tau:3] (CNonCanonical)
[W] hole{aIO} :: F b_aIu[tau:3]
GHC.Prim.~# F b_aIs[sk] (CNonCanonical)}
solveWanteds }
```8.2.1darchondarchonhttps://gitlab.haskell.org/ghc/ghc/-/issues/14691Replace EvTerm with CoreExpr2019-07-07T18:15:59ZJoachim Breitnermail@joachim-breitner.deReplace EvTerm with CoreExprI asked
> I had some funky idea where a type checker plugin would have to \| synthesize code for a custom-solved instances on the fly. But it seems that does not work because EvTerm is less expressive than Core (especially, no lambdas)
...I asked
> I had some funky idea where a type checker plugin would have to \| synthesize code for a custom-solved instances on the fly. But it seems that does not work because EvTerm is less expressive than Core (especially, no lambdas)
>
> What would break if we had
>
> ```
> | EvExpr CoreExpr
> ```
>
> as an additional constructor there?
And Simon said
> This has come up before. I think that'd be a solid win.
>
> In fact, eliminate all the existing evidence constructors with "smart constructors" that produce an EvExpr. That'd mean moving stuff from the desugarer into these smart constructors, but that's ok.
>
> I /think/ I didn't do that initially only because there were very few forms and it mean that there was no CoreExpr stuff in the type checker. But as we add more forms that decision looks and less good.
>
> You'd need to add `zonkCoreExpr` in place of `zonkEvTerm`.
>
> `evVarsOfTerm` is called quite a bit; you might want to cache the result in the `EvExpr` constructor.
This ticket tracks it. Not sure if i get to it right away, but I am happy to advise, review, and play around with the result.
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ----------------------- |
| Version | 8.3 |
| Type | Task |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler (Type checker) |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Replace EvTerm with CoreExpr","status":"New","operating_system":"","component":"Compiler (Type checker)","related":[],"milestone":"","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.3","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Task","description":"I asked\r\n\r\n> I had some funky idea where a type checker plugin would have to | synthesize code for a custom-solved instances on the fly. But it seems that does not work because EvTerm is less expressive than Core (especially, no lambdas)\r\n>\r\n> What would break if we had\r\n> {{{\r\n> | EvExpr CoreExpr\r\n> }}}\r\n> as an additional constructor there?\r\n\r\nAnd Simon said\r\n\r\n> This has come up before. I think that'd be a solid win. \r\n> \r\n> In fact, eliminate all the existing evidence constructors with \"smart constructors\" that produce an EvExpr. That'd mean moving stuff from the desugarer into these smart constructors, but that's ok.\r\n> \r\n> I /think/ I didn't do that initially only because there were very few forms and it mean that there was no CoreExpr stuff in the type checker. But as we add more forms that decision looks and less good.\r\n>\r\n> You'd need to add `zonkCoreExpr` in place of `zonkEvTerm`.\r\n>\r\n> `evVarsOfTerm` is called quite a bit; you might want to cache the result in the `EvExpr` constructor.\r\n\r\nThis ticket tracks it. Not sure if i get to it right away, but I am happy to advise, review, and play around with the result.\r\n","type_of_failure":"OtherFailure","blocking":[]} -->https://gitlab.haskell.org/ghc/ghc/-/issues/15633Type-checker plugins aren't loaded in GHCi 8.6.12019-07-07T18:03:39ZOleg GrenrusType-checker plugins aren't loaded in GHCi 8.6.1Type-Checker plugins seem to work in GHCi-8.4.3 https://gist.github.com/phadej/f2040eba327a88d3652cda021403f97f
However with GHC-8.6.1
The Glorious Glasgow Haskell Compilation System, version 8.6.0.20180907
76a233143f1ec940f342ce3ce3af...Type-Checker plugins seem to work in GHCi-8.4.3 https://gist.github.com/phadej/f2040eba327a88d3652cda021403f97f
However with GHC-8.6.1
The Glorious Glasgow Haskell Compilation System, version 8.6.0.20180907
76a233143f1ec940f342ce3ce3afaf306923b392 (which seems to be the last commit in 8.6 branch atm)
the plugins aren't loaded.
```
% ghci-8.6.1 -fplugin=ThereIsNoPlugin
GHCi, version 8.6.0.20180907: http://www.haskell.org/ghc/ :? for help
Loaded GHCi configuration from /home/ogre/.ghci
λ>
```
starts a session without a warning. 8.4.3 however fails:
```
% ghci-8.4.3 -fplugin=ThereIsNoPlugin
GHCi, version 8.4.3: http://www.haskell.org/ghc/ :? for help
<command line>: Could not find module ‘ThereIsNoPlugin’
```
<details><summary>Trac metadata</summary>
| Trac field | Value |
| ---------------------- | ------------ |
| Version | 8.6.1-beta1 |
| Type | Bug |
| TypeOfFailure | OtherFailure |
| Priority | normal |
| Resolution | Unresolved |
| Component | Compiler |
| Test case | |
| Differential revisions | |
| BlockedBy | |
| Related | |
| Blocking | |
| CC | |
| Operating system | |
| Architecture | |
</details>
<!-- {"blocked_by":[],"summary":"Type-checker plugins aren't loaded in 8.6.1","status":"New","operating_system":"","component":"Compiler","related":[],"milestone":"8.6.1","resolution":"Unresolved","owner":{"tag":"Unowned"},"version":"8.6.1-beta1","keywords":[],"differentials":[],"test_case":"","architecture":"","cc":[""],"type":"Bug","description":"Type-Checker plugins seem to work in GHCi-8.4.3 https://gist.github.com/phadej/f2040eba327a88d3652cda021403f97f\r\n\r\nHowever with GHC-8.6.1\r\n\r\nThe Glorious Glasgow Haskell Compilation System, version 8.6.0.20180907\r\n76a233143f1ec940f342ce3ce3afaf306923b392 (which seems to be the last commit in 8.6 branch atm)\r\n\r\nthe plugins aren't loaded.\r\n\r\n{{{\r\n% ghci-8.6.1 -fplugin=ThereIsNoPlugin\r\nGHCi, version 8.6.0.20180907: http://www.haskell.org/ghc/ :? for help\r\nLoaded GHCi configuration from /home/ogre/.ghci\r\nλ> \r\n}}}\r\n\r\nstarts a session without a warning. 8.4.3 however fails:\r\n\r\n{{{\r\n% ghci-8.4.3 -fplugin=ThereIsNoPlugin\r\nGHCi, version 8.4.3: http://www.haskell.org/ghc/ :? for help\r\n<command line>: Could not find module ‘ThereIsNoPlugin’\r\n}}}","type_of_failure":"OtherFailure","blocking":[]} -->8.6.3