module RtClosureInspect(
- cvObtainTerm, -- :: HscEnv -> Bool -> Maybe Type -> HValue -> IO Term
+ cvObtainTerm, -- :: HscEnv -> Int -> Bool -> Maybe Type -> HValue -> IO Term
Term(..),
pprTerm,
mapTermType,
termTyVars,
-- unsafeDeepSeq,
- cvReconstructType
+ cvReconstructType,
+ computeRTTIsubst,
+ sigmaType
) where
#include "HsVersions.h"
import ByteCodeItbls ( StgInfoTable )
import qualified ByteCodeItbls as BCI( StgInfoTable(..) )
-import ByteCodeLink ( HValue )
import HscTypes ( HscEnv )
+import Linker
import DataCon
import Type
-import TcRnMonad ( TcM, initTcPrintErrors, ioToTcRn, recoverM
- , writeMutVar )
+import TcRnMonad ( TcM, initTc, initTcPrintErrors, ioToTcRn,
+ tryTcErrs)
import TcType
import TcMType
import TcUnify
import TcGadt
+import TcEnv
+import DriverPhases
import TyCon
-import Var
import Name
import VarEnv
-import OccName
import Util
import VarSet
-import {-#SOURCE#-} TcRnDriver ( tcRnRecoverDataCon )
import TysPrim
import PrelNames
import Outputable
import Maybes
import Panic
-import FiniteMap
import GHC.Arr ( Array(..) )
-import GHC.Ptr ( Ptr(..), castPtr )
import GHC.Exts
import Control.Monad
import Data.Maybe
import Data.Array.Base
-import Data.List ( partition, nub )
+import Data.List ( partition )
+import qualified Data.Sequence as Seq
import Foreign
import System.IO.Unsafe
-}
data Term = Term { ty :: Type
- , dc :: DataCon -- The heap datacon. If ty is a newtype,
- -- this is NOT the newtype datacon
+ , dc :: Either String DataCon
+ -- The heap datacon. If ty is a newtype,
+ -- this is NOT the newtype datacon.
+ -- Empty if the datacon aint exported by the .hi
+ -- (private constructors in -O0 libraries)
, val :: HValue
, subTerms :: [Term] }
, bound_to :: Maybe Name -- Useful for printing
}
+isTerm, isSuspension, isPrim :: Term -> Bool
isTerm Term{} = True
isTerm _ = False
isSuspension Suspension{} = True
isPrim Prim{} = True
isPrim _ = False
+termType :: Term -> Maybe Type
termType t@(Suspension {}) = mb_ty t
termType t = Just$ ty t
(# iptr, ptrs, nptrs #) -> do
itbl <- peek (Ptr iptr)
let tipe = readCType (BCI.tipe itbl)
- elems = BCI.ptrs itbl
- ptrsList = Array 0 (fromIntegral$ elems) ptrs
+ elems = fromIntegral (BCI.ptrs itbl)
+ ptrsList = Array 0 (elems - 1) elems ptrs
nptrs_data = [W# (indexWordArray# nptrs i)
| I# i <- [0.. fromIntegral (BCI.nptrs itbl)] ]
+ ASSERT(fromIntegral elems >= 0) return ()
ptrsList `seq`
return (Closure tipe (Ptr iptr) itbl ptrsList nptrs_data)
| fromIntegral i == pAP_CODE = PAP
| otherwise = Other (fromIntegral i)
-isConstr, isIndirection :: ClosureType -> Bool
+isConstr, isIndirection, isThunk :: ClosureType -> Bool
isConstr Constr = True
isConstr _ = False
otherwise -> return False
where amapM f = sequence . amap' f
-amap' f (Array i0 i arr#) = map (\(I# i#) -> case indexArray# arr# i# of
- (# e #) -> f e)
- [0 .. i - i0]
+amap' f (Array i0 i _ arr#) = map g [0 .. i - i0]
+ where g (I# i#) = case indexArray# arr# i# of
+ (# e #) -> f e
-- TODO: Fix it. Probably the otherwise case is failing, trace/debug it
{-
| otherwise = pprPanic "Expected a TcTyCon" (ppr t)
go [] _ = []
go (t:tt) xx
- | (x, rest) <- splitAt (sizeofType t `div` wORD_SIZE) xx
+ | (x, rest) <- splitAt ((sizeofType t + wORD_SIZE - 1) `div` wORD_SIZE) xx
= x : go tt rest
sizeofTyCon = sizeofPrimRep . tyConPrimRep
-- * Traversals for Terms
-----------------------------------
-data TermFold a = TermFold { fTerm :: Type -> DataCon -> HValue -> [a] -> a
+data TermFold a = TermFold { fTerm :: Type -> Either String DataCon -> HValue -> [a] -> a
, fPrim :: Type -> [Word] -> a
, fSuspension :: ClosureType -> Maybe Type -> HValue
-> Maybe Name -> a
fSuspension = (((return.).).). Suspension
}
+mapTermType :: (Type -> Type) -> Term -> Term
mapTermType f = foldTerm idTermFold {
fTerm = \ty dc hval tt -> Term (f ty) dc hval tt,
fSuspension = \ct mb_ty hval n ->
Suspension ct (fmap f mb_ty) hval n }
+termTyVars :: Term -> TyVarSet
termTyVars = foldTerm TermFold {
fTerm = \ty _ _ tt ->
tyVarsOfType ty `plusVarEnv` concatVarEnv tt,
pprTerm y p t | Just doc <- pprTermM y p t = doc
pprTermM :: Monad m => (Int -> Term -> m SDoc) -> Int -> Term -> m SDoc
-pprTermM y p t@Term{dc=dc, subTerms=tt, ty=ty}
+pprTermM y p t@Term{dc=Left dc_tag, subTerms=tt, ty=ty} = do
+ tt_docs <- mapM (y app_prec) tt
+ return$ cparen (not(null tt) && p >= app_prec) (text dc_tag <+> sep tt_docs)
+
+pprTermM y p t@Term{dc=Right dc, subTerms=tt, ty=ty}
{- | dataConIsInfix dc, (t1:t2:tt') <- tt --TODO fixity
= parens (pprTerm1 True t1 <+> ppr dc <+> pprTerm1 True ppr t2)
<+> hsep (map (pprTerm1 True) tt)
--}
+-} -- TODO Printing infix constructors properly
| null tt = return$ ppr dc
| Just (tc,_) <- splitNewTyConApp_maybe ty
, isNewTyCon tc
return$ cparen (p >= app_prec) (ppr dc <+> sep tt_docs)
pprTermM y _ t = pprTermM1 y t
-
-pprTermM1 _ Prim{value=words, ty=ty} = return$ text$ repPrim (tyConAppTyCon ty)
- words
+pprTermM1 _ Prim{value=words, ty=ty} =
+ return$ text$ repPrim (tyConAppTyCon ty) words
pprTermM1 y t@Term{} = panic "pprTermM1 - unreachable"
pprTermM1 _ Suspension{bound_to=Nothing} = return$ char '_'
pprTermM1 _ Suspension{mb_ty=Just ty, bound_to=Just n}
cPprTerm :: forall m. Monad m =>
((Int->Term->m SDoc)->[Int->Term->m (Maybe SDoc)]) -> Term -> m SDoc
cPprTerm custom = go 0 where
- go prec t@Term{subTerms=tt, dc=dc} = do
+ go prec t@Term{} = do
let default_ prec t = Just `liftM` pprTermM go prec t
mb_customDocs = [pp prec t | pp <- custom go ++ [default_]]
Just doc <- firstJustM mb_customDocs
, ifTerm (isTyCon doubleTyCon) (coerceShow$ \(a::Double)->a)
, ifTerm isIntegerTy (coerceShow$ \(a::Integer)->a)
]
- where ifTerm pred f p t@Term{} | pred t = liftM Just (f p t)
- | otherwise = return Nothing
- isIntegerTy Term{ty=ty} | Just (tc,_) <- splitTyConApp_maybe ty
- = tyConName tc == integerTyConName
- isTupleTy Term{ty=ty} | Just (tc,_) <- splitTyConApp_maybe ty
- = tc `elem` (fst.unzip.elems) boxedTupleArr
- isTyCon a_tc Term{ty=ty} | Just (tc,_) <- splitTyConApp_maybe ty
- = a_tc == tc
+ where ifTerm pred f p t@Term{} | pred t = liftM Just (f p t)
+ ifTerm _ _ _ _ = return Nothing
+ isIntegerTy Term{ty=ty} = fromMaybe False $ do
+ (tc,_) <- splitTyConApp_maybe ty
+ return (tyConName tc == integerTyConName)
+ isTupleTy Term{ty=ty} = fromMaybe False $ do
+ (tc,_) <- splitTyConApp_maybe ty
+ return (tc `elem` (fst.unzip.elems) boxedTupleArr)
+ isTyCon a_tc Term{ty=ty} = fromMaybe False $ do
+ (tc,_) <- splitTyConApp_maybe ty
+ return (a_tc == tc)
coerceShow f _ = return . text . show . f . unsafeCoerce# . val
--TODO pprinting of list terms is not lazy
doList p h t = do
runTR :: HscEnv -> TR a -> IO a
runTR hsc_env c = do
- mb_term <- initTcPrintErrors hsc_env iNTERACTIVE c
+ mb_term <- runTR_maybe hsc_env c
case mb_term of
Nothing -> panic "Can't unify"
- Just x -> return x
+ Just x -> return x
+
+runTR_maybe :: HscEnv -> TR a -> IO (Maybe a)
+runTR_maybe hsc_env = fmap snd . initTc hsc_env HsSrcFile False iNTERACTIVE
trIO :: IO a -> TR a
trIO = liftTcM . ioToTcRn
+liftTcM :: TcM a -> TR a
liftTcM = id
-newVar :: Kind -> TR TcTyVar
-newVar = liftTcM . newFlexiTyVar
+newVar :: Kind -> TR TcType
+newVar = liftTcM . fmap mkTyVarTy . newFlexiTyVar
-- | Returns the instantiated type scheme ty', and the substitution sigma
-- such that sigma(ty') = ty
-- do its magic.
addConstraint :: TcType -> TcType -> TR ()
addConstraint t1 t2 = congruenceNewtypes t1 t2 >>= uncurry unifyType
-
-
+ >> return () -- TOMDO: what about the coercion?
+ -- we should consider family instances
-- Type & Term reconstruction
-cvObtainTerm :: HscEnv -> Bool -> Maybe Type -> HValue -> IO Term
-cvObtainTerm hsc_env force mb_ty hval = runTR hsc_env $ do
- tv <- liftM mkTyVarTy (newVar argTypeKind)
+cvObtainTerm :: HscEnv -> Int -> Bool -> Maybe Type -> HValue -> IO Term
+cvObtainTerm hsc_env bound force mb_ty hval = runTR hsc_env $ do
+ tv <- newVar argTypeKind
case mb_ty of
- Nothing -> go tv tv hval >>= zonkTerm
- Just ty | isMonomorphic ty -> go ty ty hval >>= zonkTerm
+ Nothing -> go bound tv tv hval >>= zonkTerm
+ Just ty | isMonomorphic ty -> go bound ty ty hval >>= zonkTerm
Just ty -> do
(ty',rev_subst) <- instScheme (sigmaType ty)
addConstraint tv ty'
- term <- go tv tv hval >>= zonkTerm
+ term <- go bound tv tv hval >>= zonkTerm
--restore original Tyvars
return$ mapTermType (substTy rev_subst) term
where
- go tv ty a = do
+ go bound _ _ _ | seq bound False = undefined
+ go 0 tv ty a = do
+ clos <- trIO $ getClosureData a
+ return (Suspension (tipe clos) (Just tv) a Nothing)
+ go bound tv ty a = do
let monomorphic = not(isTyVarTy tv)
-- This ^^^ is a convention. The ancestor tests for
-- monomorphism and passes a type instead of a tv
-- NB. this won't attempt to force a BLACKHOLE. Even with :force, we never
-- force blackholes, because it would almost certainly result in deadlock,
-- and showing the '_' is more useful.
- t | isThunk t && force -> seq a $ go tv ty a
+ t | isThunk t && force -> seq a $ go (pred bound) tv ty a
-- We always follow indirections
- Indirection _ -> go tv ty $! (ptrs clos ! 0)
+ Indirection _ -> go (pred bound) tv ty $! (ptrs clos ! 0)
-- The interesting case
Constr -> do
- m_dc <- trIO$ tcRnRecoverDataCon hsc_env (infoPtr clos)
- case m_dc of
- Nothing -> panic "Can't find the DataCon for a term"
+ Right dcname <- dataConInfoPtrToName (infoPtr clos)
+ (_,mb_dc) <- tryTcErrs (tcLookupDataCon dcname)
+ case mb_dc of
+ Nothing -> do -- This can happen for private constructors compiled -O0
+ -- where the .hi descriptor does not export them
+ -- In such case, we return a best approximation:
+ -- ignore the unpointed args, and recover the pointeds
+ -- This preserves laziness, and should be safe.
+ let tag = showSDoc (ppr dcname)
+ vars <- replicateM (length$ elems$ ptrs clos)
+ (newVar (liftedTypeKind))
+ subTerms <- sequence [appArr (go (pred bound) tv tv) (ptrs clos) i
+ | (i, tv) <- zip [0..] vars]
+ return (Term tv (Left ('<' : tag ++ ">")) a subTerms)
Just dc -> do
let extra_args = length(dataConRepArgTys dc) -
length(dataConOrigArgTys dc)
(subTtypesP, subTtypesNP) = partition isPointed subTtypes
subTermTvs <- sequence
[ if isMonomorphic t then return t
- else (mkTyVarTy `fmap` newVar k)
+ else (newVar k)
| (t,k) <- zip subTtypesP (map typeKind subTtypesP)]
-- It is vital for newtype reconstruction that the unification step
-- is done right here, _before_ the subterms are RTTI reconstructed
addConstraint myType signatureType
subTermsP <- sequence $ drop extra_args
-- ^^^ all extra arguments are pointed
- [ appArr (go tv t) (ptrs clos) i
+ [ appArr (go (pred bound) tv t) (ptrs clos) i
| (i,tv,t) <- zip3 [0..] subTermTvs subTtypesP]
let unboxeds = extractUnboxed subTtypesNP clos
subTermsNP = map (uncurry Prim) (zip subTtypesNP unboxeds)
subTerms = reOrderTerms subTermsP subTermsNP
(drop extra_args subTtypes)
- return (Term tv dc a subTerms)
+ return (Term tv (Right dc) a subTerms)
-- The otherwise case: can be a Thunk,AP,PAP,etc.
- otherwise ->
- return (Suspension (tipe clos) (Just tv) a Nothing)
+ tipe_clos ->
+ return (Suspension tipe_clos (Just tv) a Nothing)
+-- matchSubTypes dc ty | pprTrace "matchSubtypes" (ppr dc <+> ppr ty) False = undefined
matchSubTypes dc ty
| Just (_,ty_args) <- splitTyConApp_maybe (repType ty)
+-- assumption: ^^^ looks through newtypes
, isVanillaDataCon dc --TODO non-vanilla case
= dataConInstArgTys dc ty_args
--- assumes that newtypes are looked ^^^ through
| otherwise = dataConRepArgTys dc
-- This is used to put together pointed and nonpointed subterms in the
-- Fast, breadth-first Type reconstruction
max_depth = 10 :: Int
-cvReconstructType :: HscEnv -> Bool -> Maybe Type -> HValue -> IO Type
-cvReconstructType hsc_env force mb_ty hval = runTR hsc_env $ do
- tv <- liftM mkTyVarTy (newVar argTypeKind)
+cvReconstructType :: HscEnv -> Bool -> Maybe Type -> HValue -> IO (Maybe Type)
+cvReconstructType hsc_env force mb_ty hval = runTR_maybe hsc_env $ do
+ tv <- newVar argTypeKind
case mb_ty of
Nothing -> do search (isMonomorphic `fmap` zonkTcType tv)
(uncurry go)
zonkTcType tv -- TODO untested!
Just ty | isMonomorphic ty -> return ty
Just ty -> do
- (ty',rev_subst) <- instScheme (sigmaType ty)
+ (ty',rev_subst) <- instScheme (sigmaType ty)
addConstraint tv ty'
search (isMonomorphic `fmap` zonkTcType tv)
- (uncurry go)
+ (\(ty,a) -> go ty a)
[(tv, hval)]
max_depth
substTy rev_subst `fmap` zonkTcType tv
search stop expand [] depth = return ()
search stop expand x 0 = fail$ "Failed to reconstruct a type after " ++
show max_depth ++ " steps"
- search stop expand (x:xx) d = do
+ search stop expand (x:xx) d = unlessM stop $ do
new <- expand x
- unlessM stop $ search stop expand (xx ++ new) $! (pred d)
+ search stop expand (xx ++ new) $! (pred d)
-- returns unification tasks,since we are going to want a breadth-first search
go :: Type -> HValue -> TR [(Type, HValue)]
case tipe clos of
Indirection _ -> go tv $! (ptrs clos ! 0)
Constr -> do
- m_dc <- trIO$ tcRnRecoverDataCon hsc_env (infoPtr clos)
- case m_dc of
- Nothing -> panic "Can't find the DataCon for a term"
+ Right dcname <- dataConInfoPtrToName (infoPtr clos)
+ (_,mb_dc) <- tryTcErrs (tcLookupDataCon dcname)
+ case mb_dc of
+ Nothing-> do
+ -- TODO: Check this case
+ vars <- replicateM (length$ elems$ ptrs clos)
+ (newVar (liftedTypeKind))
+ subTerms <- sequence [ appArr (go tv) (ptrs clos) i
+ | (i, tv) <- zip [0..] vars]
+ forM [0..length (elems $ ptrs clos)] $ \i -> do
+ tv <- newVar liftedTypeKind
+ return$ appArr (\e->(tv,e)) (ptrs clos) i
+
Just dc -> do
let extra_args = length(dataConRepArgTys dc) -
length(dataConOrigArgTys dc)
subTtypes <- mapMif (not . isMonomorphic)
- (\t -> mkTyVarTy `fmap` newVar (typeKind t))
+ (\t -> newVar (typeKind t))
(dataConRepArgTys dc)
-- It is vital for newtype reconstruction that the unification step
-- is done right here, _before_ the subterms are RTTI reconstructed
| (i,t) <- drop extra_args $ zip [0..] subTtypes]
otherwise -> return []
+ -- This helper computes the difference between a base type t and the
+ -- improved rtti_t computed by RTTI
+ -- The main difference between RTTI types and their normal counterparts
+ -- is that the former are _not_ polymorphic, thus polymorphism must
+ -- be stripped. Syntactically, forall's must be stripped
+computeRTTIsubst ty rtti_ty =
+ -- In addition, we strip newtypes too, since the reconstructed type might
+ -- not have recovered them all
+ tcUnifyTys (const BindMe)
+ [repType' $ dropForAlls$ ty]
+ [repType' $ rtti_ty]
+-- TODO stripping newtypes shouldn't be necessary, test
+
-- Dealing with newtypes
{-
congruenceNewtypes lhs rhs
-- TyVar lhs inductive case
| Just tv <- getTyVar_maybe lhs
- = recoverM (return (lhs,rhs)) $ do
+ = recoverTc (return (lhs,rhs)) $ do
Indirect ty_v <- readMetaTyVar tv
(lhs1, rhs1) <- congruenceNewtypes ty_v rhs
return (lhs, rhs1)
--------------------------------------------------------------------------------
+-- Semantically different to recoverM in TcRnMonad
+-- recoverM retains the errors in the first action,
+-- whereas recoverTc here does not
+recoverTc recover thing = do
+ (_,mb_res) <- tryTcErrs thing
+ case mb_res of
+ Nothing -> recover
+ Just res -> return res
isMonomorphic ty | (tvs, ty') <- splitForAllTys ty
= null tvs && (isEmptyVarSet . tyVarsOfType) ty'
unlessM condM acc = condM >>= \c -> unless c acc
-- Strict application of f at index i
-appArr f (Array _ _ ptrs#) (I# i#) = case indexArray# ptrs# i# of
- (# e #) -> f e
+appArr f a@(Array _ _ _ ptrs#) i@(I# i#)
+ = ASSERT (i < length(elems a))
+ case indexArray# ptrs# i# of
+ (# e #) -> f e
zonkTerm :: Term -> TcM Term
zonkTerm = foldTerm idTermFoldM {