summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoland Senn <rsx@bluewin.ch>2020-02-12 12:42:25 +0100
committerMarge Bot <ben+marge-bot@smart-cactus.org>2020-03-02 17:13:55 -0500
commit7c0c76fb1a11532076a0342655a9c889a2a12768 (patch)
tree427ba3dbf68ceec39f4fbaf95375ee66739d1d4a
parentdbea7e9d4d4a8812f3dbeed720fb6aa97e16f896 (diff)
downloadhaskell-7c0c76fb1a11532076a0342655a9c889a2a12768.tar.gz
Set `ImpredicativeTypes` during :print command. (#14828)
If ImpredicativeTypes is not enabled, then `:print <term>` will fail if the type of <term> has nested `forall`s or `=>`s. This is because the GHCi debugger's internals will attempt to unify a metavariable with the type of <term> and then display the result, but if the type has nested `forall`s or `=>`s, then unification will fail. As a result, `:print` will bail out and the unhelpful result will be `<term> = (_t1::t1)` (where `t1` is a metavariable). Beware: <term> can have nested `forall`s even if its definition doesn't use RankNTypes! Here is an example from #14828: class Functor f where fmap :: (a -> b) -> f a -> f b Somewhat surprisingly, `:print fmap` considers the type of fmap to have nested foralls. This is because the GHCi debugger sees the type `fmap :: forall f. Functor f => forall a b. (a -> b) -> f a -> f b`. We could envision deeply instantiating this type to get the type `forall f a b. Functor f => (a -> b) -> f a -> f b`, but this trick wouldn't work for higher-rank types. Instead, we adopt a simpler fix: enable `ImpredicativeTypes` when using `:print` and friends in the GHCi debugger. This is allows metavariables to unify with types that have nested (or higher-rank) `forall`s/`=>`s, which makes `:print fmap` display as `fmap = (_t1::forall a b. Functor f => (a -> b) -> f a -> f b)`, as expected. Although ImpredicativeTypes is a somewhat unpredictable from a type inference perspective, there is no danger in using it in the GHCi debugger, since all of the terms that the GHCi debugger deals with have already been typechecked.
-rw-r--r--compiler/GHC/Runtime/Eval.hs42
-rw-r--r--testsuite/tests/ghci.debugger/scripts/print027.stdout12
-rw-r--r--testsuite/tests/ghci/scripts/T14828.stdout20
3 files changed, 59 insertions, 15 deletions
diff --git a/compiler/GHC/Runtime/Eval.hs b/compiler/GHC/Runtime/Eval.hs
index 51acffec11..c069c9fa8e 100644
--- a/compiler/GHC/Runtime/Eval.hs
+++ b/compiler/GHC/Runtime/Eval.hs
@@ -79,6 +79,7 @@ import VarEnv
import GHC.ByteCode.Types
import GHC.Runtime.Linker as Linker
import GHC.Driver.Session
+import GHC.LanguageExtensions
import Unique
import UniqSupply
import MonadUtils
@@ -1256,7 +1257,10 @@ obtainTermFromVal hsc_env _bound _force _ty _x = withInterp hsc_env $ \case
obtainTermFromId :: HscEnv -> Int -> Bool -> Id -> IO Term
obtainTermFromId hsc_env bound force id = do
hv <- Linker.getHValue hsc_env (varName id)
- cvObtainTerm hsc_env bound force (idType id) hv
+ cvObtainTerm (updEnv hsc_env) bound force (idType id) hv
+ where updEnv env = env {hsc_dflags = -- #14828
+ xopt_set (hsc_dflags env) ImpredicativeTypes}
+ -- See Note [Setting ImpredicativeTypes for :print command]
-- Uses RTTI to reconstruct the type of an Id, making it less polymorphic
reconstructType :: HscEnv -> Int -> Id -> IO (Maybe Type)
@@ -1266,3 +1270,39 @@ reconstructType hsc_env bound id = do
mkRuntimeUnkTyVar :: Name -> Kind -> TyVar
mkRuntimeUnkTyVar name kind = mkTcTyVar name kind RuntimeUnk
+
+
+{-
+Note [Setting ImpredicativeTypes for :print command]
+
+If ImpredicativeTypes is not enabled, then `:print <term>` will fail if the
+type of <term> has nested `forall`s or `=>`s.
+This is because the GHCi debugger's internals will attempt to unify a
+metavariable with the type of <term> and then display the result, but if the
+type has nested `forall`s or `=>`s, then unification will fail.
+As a result, `:print` will bail out and the unhelpful result will be
+`<term> = (_t1::t1)` (where `t1` is a metavariable).
+
+Beware: <term> can have nested `forall`s even if its definition doesn't use
+RankNTypes! Here is an example from #14828:
+
+ class Functor f where
+ fmap :: (a -> b) -> f a -> f b
+
+Somewhat surprisingly, `:print fmap` considers the type of fmap to have
+nested foralls. This is because the GHCi debugger sees the type
+`fmap :: forall f. Functor f => forall a b. (a -> b) -> f a -> f b`.
+We could envision deeply instantiating this type to get the type
+`forall f a b. Functor f => (a -> b) -> f a -> f b`,
+but this trick wouldn't work for higher-rank types.
+
+Instead, we adopt a simpler fix: enable `ImpredicativeTypes` when using
+`:print` and friends in the GHCi debugger. This allows metavariables
+to unify with types that have nested (or higher-rank) `forall`s/`=>`s,
+which makes `:print fmap` display as
+`fmap = (_t1::forall a b. Functor f => (a -> b) -> f a -> f b)`, as expected.
+
+Although ImpredicativeTypes is a somewhat unpredictable from a type inference
+perspective, there is no danger in using it in the GHCi debugger, since all
+of the terms that the GHCi debugger deals with have already been typechecked.
+-}
diff --git a/testsuite/tests/ghci.debugger/scripts/print027.stdout b/testsuite/tests/ghci.debugger/scripts/print027.stdout
index eb5b363693..3117eace87 100644
--- a/testsuite/tests/ghci.debugger/scripts/print027.stdout
+++ b/testsuite/tests/ghci.debugger/scripts/print027.stdout
@@ -1,6 +1,6 @@
-+ = (_t1::t1)
-print = (_t2::t1)
-log = (_t3::t1)
-head = (_t4::[a] -> a)
-tail = (_t5::[a1] -> [a1])
-fst = (_t6::(a2, b) -> a2)
++ = (_t1::Num a => a -> a -> a)
+print = (_t2::Show a1 => a1 -> IO ())
+log = (_t3::Floating a2 => a2 -> a2)
+head = (_t4::[a4] -> a4)
+tail = (_t5::[a7] -> [a7])
+fst = (_t6::(a11, b) -> a11)
diff --git a/testsuite/tests/ghci/scripts/T14828.stdout b/testsuite/tests/ghci/scripts/T14828.stdout
index 9ccea0ceec..501f8004d5 100644
--- a/testsuite/tests/ghci/scripts/T14828.stdout
+++ b/testsuite/tests/ghci/scripts/T14828.stdout
@@ -1,12 +1,16 @@
foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b
-foldl = (_t1::t1)
+foldl = (_t1::forall b a.
+ Foldable t =>
+ (b -> a -> b) -> b -> t a -> b)
fmap :: Functor f => (a -> b) -> f a -> f b
-fmap = (_t2::t1)
+fmap = (_t2::forall a b. Functor f => (a -> b) -> f a -> f b)
return :: Monad m => a -> m a
-return = (_t3::t1)
+return = (_t3::forall a. Monad m => a -> m a)
pure :: Applicative f => a -> f a
-pure = (_t4::t1)
-mempty = (_t5::t1)
-mappend = (_t6::t1)
-foldl' = (_t7::t1)
-f = (_t8::t1)
+pure = (_t4::forall a. Applicative f1 => a -> f1 a)
+mempty = (_t5::Monoid a => a)
+mappend = (_t6::Monoid a => a -> a -> a)
+foldl' = (_t7::forall b a.
+ Foldable t =>
+ (b -> a -> b) -> b -> t a -> b)
+f = (_t8::(forall a. a -> a) -> b -> b)