[commit: ghc] master: Fix #15792 by not reifying invisible arguments in AppTys (bfd93f9)

git at git.haskell.org git at git.haskell.org
Wed Oct 24 12:19:58 UTC 2018


Repository : ssh://git@git.haskell.org/ghc

On branch  : master
Link       : http://ghc.haskell.org/trac/ghc/changeset/bfd93f90b6c63edf2790356e95feddf9898ec888/ghc

>---------------------------------------------------------------

commit bfd93f90b6c63edf2790356e95feddf9898ec888
Author: Ryan Scott <ryan.gl.scott at gmail.com>
Date:   Wed Oct 24 07:03:40 2018 -0400

    Fix #15792 by not reifying invisible arguments in AppTys
    
    Summary:
    The `reifyType` function in `TcSplice` is carefully designed
    to avoid reifying visible arguments to `TyConApp`s. However, the same
    care was not given towards the `AppTy` case, which lead to #15792.
    
    This patch changes to the `AppTy` case of `reifyType` so that it
    consults the kind of the function type to determine which of the
    argument types are invisible (and therefore should be dropped) during
    reification. This required crafting a variant of `tyConArgFlags`,
    which I dubbed `appTyArgFlags`, that accept an arbitrary function
    `Type` instead of a `TyCon`.
    
    Test Plan: make test TEST=T15792
    
    Reviewers: goldfire, bgamari, simonpj
    
    Reviewed By: simonpj
    
    Subscribers: simonpj, rwbarton, carter
    
    GHC Trac Issues: #15792
    
    Differential Revision: https://phabricator.haskell.org/D5252


>---------------------------------------------------------------

bfd93f90b6c63edf2790356e95feddf9898ec888
 compiler/typecheck/TcSplice.hs   | 18 +++++++++++++++++-
 compiler/types/Type.hs           | 29 +++++++++++++++++++++++++----
 testsuite/tests/th/T15792.hs     | 16 ++++++++++++++++
 testsuite/tests/th/T15792.stderr |  2 ++
 testsuite/tests/th/all.T         |  1 +
 5 files changed, 61 insertions(+), 5 deletions(-)

diff --git a/compiler/typecheck/TcSplice.hs b/compiler/typecheck/TcSplice.hs
index 8f05225..c5886d3 100644
--- a/compiler/typecheck/TcSplice.hs
+++ b/compiler/typecheck/TcSplice.hs
@@ -1743,7 +1743,23 @@ reifyType ty@(ForAllTy {})  = reify_for_all ty
 reifyType (LitTy t)         = do { r <- reifyTyLit t; return (TH.LitT r) }
 reifyType (TyVarTy tv)      = return (TH.VarT (reifyName tv))
 reifyType (TyConApp tc tys) = reify_tc_app tc tys   -- Do not expand type synonyms here
-reifyType (AppTy t1 t2)     = do { [r1,r2] <- reifyTypes [t1,t2] ; return (r1 `TH.AppT` r2) }
+reifyType ty@(AppTy {})     = do
+  let (ty_head, ty_args) = splitAppTys ty
+  ty_head' <- reifyType ty_head
+  ty_args' <- reifyTypes (filter_out_invisible_args ty_head ty_args)
+  pure $ mkThAppTs ty_head' ty_args'
+  where
+    -- Make sure to filter out any invisible arguments. For instance, if you
+    -- reify the following:
+    --
+    --   newtype T (f :: forall a. a -> Type) = MkT (f Bool)
+    --
+    -- Then you should receive back `f Bool`, not `f Type Bool`, since the
+    -- `Type` argument is invisible (#15792).
+    filter_out_invisible_args :: Type -> [Type] -> [Type]
+    filter_out_invisible_args ty_head ty_args =
+      filterByList (map isVisibleArgFlag $ appTyArgFlags ty_head ty_args)
+                   ty_args
 reifyType ty@(FunTy t1 t2)
   | isPredTy t1 = reify_for_all ty  -- Types like ((?x::Int) => Char -> Char)
   | otherwise   = do { [r1,r2] <- reifyTypes [t1,t2] ; return (TH.ArrowT `TH.AppT` r1 `TH.AppT` r2) }
diff --git a/compiler/types/Type.hs b/compiler/types/Type.hs
index 9012815..1846525 100644
--- a/compiler/types/Type.hs
+++ b/compiler/types/Type.hs
@@ -63,7 +63,8 @@ module Type (
         stripCoercionTy, splitCoercionType_maybe,
 
         splitPiTysInvisible, filterOutInvisibleTypes, filterOutInferredTypes,
-        partitionInvisibleTypes, partitionInvisibles, tyConArgFlags,
+        partitionInvisibleTypes, partitionInvisibles,
+        tyConArgFlags, appTyArgFlags,
         synTyConResKind,
 
         modifyJoinResTy, setJoinResTy,
@@ -1573,8 +1574,9 @@ partitionInvisibles = partitionWith pick_invis
     pick_invis (thing, vis) | isInvisibleArgFlag vis = Left thing
                             | otherwise              = Right thing
 
--- | Given a 'TyCon' and a list of argument types, determine each argument's
--- visibility ('Inferred', 'Specified', or 'Required').
+-- | Given a 'TyCon' and a list of argument types to which the 'TyCon' is
+-- applied, determine each argument's visibility
+-- ('Inferred', 'Specified', or 'Required').
 --
 -- Wrinkle: consider the following scenario:
 --
@@ -1588,7 +1590,26 @@ partitionInvisibles = partitionWith pick_invis
 -- Thus, the first argument is invisible, @S@ is visible, @R@ is invisible again,
 -- and @Q@ is visible.
 tyConArgFlags :: TyCon -> [Type] -> [ArgFlag]
-tyConArgFlags tc = go emptyTCvSubst (tyConKind tc)
+tyConArgFlags tc = fun_kind_arg_flags (tyConKind tc)
+
+-- | Given a 'Type' and a list of argument types to which the 'Type' is
+-- applied, determine each argument's visibility
+-- ('Inferred', 'Specified', or 'Required').
+--
+-- Most of the time, the arguments will be 'Required', but not always. Consider
+-- @f :: forall a. a -> Type at . In @f Type Bool@, the first argument (@Type@) is
+-- 'Specified' and the second argument (@Bool@) is 'Required'. It is precisely
+-- this sort of higher-rank situation in which 'appTyArgFlags' comes in handy,
+-- since @f Type Bool@ would be represented in Core using 'AppTy's.
+-- (See also Trac #15792).
+appTyArgFlags :: Type -> [Type] -> [ArgFlag]
+appTyArgFlags ty = fun_kind_arg_flags (typeKind ty)
+
+-- | Given a function kind and a list of argument types (where each argument's
+-- kind aligns with the corresponding position in the argument kind), determine
+-- each argument's visibility ('Inferred', 'Specified', or 'Required').
+fun_kind_arg_flags :: Kind -> [Type] -> [ArgFlag]
+fun_kind_arg_flags = go emptyTCvSubst
   where
     go _ _ [] = []
     go subst (ForAllTy (Bndr tv argf) res_ki) (arg_ty:arg_tys)
diff --git a/testsuite/tests/th/T15792.hs b/testsuite/tests/th/T15792.hs
new file mode 100644
index 0000000..2567fb5
--- /dev/null
+++ b/testsuite/tests/th/T15792.hs
@@ -0,0 +1,16 @@
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE TemplateHaskell #-}
+module T15792 where
+
+import Data.Kind
+import Language.Haskell.TH hiding (Type)
+import System.IO
+
+newtype T (f :: forall a. a -> Type) = MkT (f Bool)
+
+$(pure [])
+
+$(do info <- reify ''T
+     runIO $ hPutStrLn stderr $ pprint info
+     pure [])
diff --git a/testsuite/tests/th/T15792.stderr b/testsuite/tests/th/T15792.stderr
new file mode 100644
index 0000000..c13f7ba
--- /dev/null
+++ b/testsuite/tests/th/T15792.stderr
@@ -0,0 +1,2 @@
+newtype T15792.T (f_0 :: forall (a_1 :: *) . a_1 -> *)
+  = T15792.MkT (f_0 GHC.Types.Bool)
diff --git a/testsuite/tests/th/all.T b/testsuite/tests/th/all.T
index d10523c..75ec5db 100644
--- a/testsuite/tests/th/all.T
+++ b/testsuite/tests/th/all.T
@@ -441,3 +441,4 @@ test('TH_recover_warns', normal, compile, ['-v0 -ddump-splices -dsuppress-unique
 test('T15738', normal, compile, ['-v0 -ddump-splices -dsuppress-uniques'])
 test('T15783', normal, multimod_compile,
     ['T15783A', '-v0 ' + config.ghc_th_way_flags])
+test('T15792', normal, compile, ['-v0 -dsuppress-uniques'])



More information about the ghc-commits mailing list