module GHC.Runtime.Heap.Inspect(
cvObtainTerm,
cvReconstructType,
improveRTTIType,
Term(..),
isFullyEvaluatedTerm,
termType, mapTermType, termTyCoVars,
foldTerm, TermFold(..),
cPprTerm, cPprTermBase,
constrClosToName
) where
#include "HsVersions.h"
import GHC.Prelude
import GHC.Platform
import GHC.Runtime.Interpreter as GHCi
import GHCi.RemoteTypes
import GHC.Driver.Env
import GHCi.Message ( fromSerializableException )
import GHC.Core.DataCon
import GHC.Core.Type
import GHC.Types.RepType
import GHC.Core.Multiplicity
import qualified GHC.Core.Unify as U
import GHC.Types.Var
import GHC.Tc.Utils.Monad
import GHC.Tc.Utils.TcType
import GHC.Tc.Utils.TcMType
import GHC.Tc.Utils.Zonk ( zonkTcTypeToTypeX, mkEmptyZonkEnv, ZonkFlexi( RuntimeUnkFlexi ) )
import GHC.Tc.Utils.Unify
import GHC.Tc.Utils.Env
import GHC.Core.TyCon
import GHC.Types.Name
import GHC.Types.Name.Occurrence as OccName
import GHC.Unit.Module
import GHC.Iface.Env
import GHC.Utils.Misc
import GHC.Types.Var.Set
import GHC.Types.Basic ( Boxity(..) )
import GHC.Builtin.Types.Prim
import GHC.Builtin.Types
import GHC.Driver.Session
import GHC.Driver.Ppr
import GHC.Utils.Outputable as Ppr
import GHC.Utils.Panic
import GHC.Char
import GHC.Exts.Heap
import GHC.Runtime.Heap.Layout ( roundUpTo )
import GHC.IO (throwIO)
import Control.Monad
import Data.Maybe
import Data.List ((\\))
import GHC.Exts
import qualified Data.Sequence as Seq
import Data.Sequence (viewl, ViewL(..))
import Foreign
import System.IO.Unsafe
data Term = Term { ty :: RttiType
, dc :: Either String DataCon
, val :: ForeignHValue
, subTerms :: [Term] }
| Prim { ty :: RttiType
, valRaw :: [Word] }
| Suspension { ctype :: ClosureType
, ty :: RttiType
, val :: ForeignHValue
, bound_to :: Maybe Name
}
| NewtypeWrap{
ty :: RttiType
, dc :: Either String DataCon
, wrapped_term :: Term }
| RefWrap {
ty :: RttiType
, wrapped_term :: Term }
termType :: Term -> RttiType
termType t = ty t
isFullyEvaluatedTerm :: Term -> Bool
isFullyEvaluatedTerm Term {subTerms=tt} = all isFullyEvaluatedTerm tt
isFullyEvaluatedTerm Prim {} = True
isFullyEvaluatedTerm NewtypeWrap{wrapped_term=t} = isFullyEvaluatedTerm t
isFullyEvaluatedTerm RefWrap{wrapped_term=t} = isFullyEvaluatedTerm t
isFullyEvaluatedTerm _ = False
instance Outputable (Term) where
ppr t | Just doc <- cPprTerm cPprTermBase t = doc
| otherwise = panic "Outputable Term instance"
isThunk :: GenClosure a -> Bool
isThunk ThunkClosure{} = True
isThunk APClosure{} = True
isThunk APStackClosure{} = True
isThunk _ = False
constrClosToName :: HscEnv -> GenClosure a -> IO (Either String Name)
constrClosToName hsc_env ConstrClosure{pkg=pkg,modl=mod,name=occ} = do
let occName = mkOccName OccName.dataName occ
modName = mkModule (stringToUnit pkg) (mkModuleName mod)
Right `fmap` lookupOrigIO hsc_env modName occName
constrClosToName _hsc_env clos =
return (Left ("conClosToName: Expected ConstrClosure, got " ++ show (fmap (const ()) clos)))
type TermProcessor a b = RttiType -> Either String DataCon -> ForeignHValue -> [a] -> b
data TermFold a = TermFold { fTerm :: TermProcessor a a
, fPrim :: RttiType -> [Word] -> a
, fSuspension :: ClosureType -> RttiType -> ForeignHValue
-> Maybe Name -> a
, fNewtypeWrap :: RttiType -> Either String DataCon
-> a -> a
, fRefWrap :: RttiType -> a -> a
}
data TermFoldM m a =
TermFoldM {fTermM :: TermProcessor a (m a)
, fPrimM :: RttiType -> [Word] -> m a
, fSuspensionM :: ClosureType -> RttiType -> ForeignHValue
-> Maybe Name -> m a
, fNewtypeWrapM :: RttiType -> Either String DataCon
-> a -> m a
, fRefWrapM :: RttiType -> a -> m a
}
foldTerm :: TermFold a -> Term -> a
foldTerm tf (Term ty dc v tt) = fTerm tf ty dc v (map (foldTerm tf) tt)
foldTerm tf (Prim ty v ) = fPrim tf ty v
foldTerm tf (Suspension ct ty v b) = fSuspension tf ct ty v b
foldTerm tf (NewtypeWrap ty dc t) = fNewtypeWrap tf ty dc (foldTerm tf t)
foldTerm tf (RefWrap ty t) = fRefWrap tf ty (foldTerm tf t)
foldTermM :: Monad m => TermFoldM m a -> Term -> m a
foldTermM tf (Term ty dc v tt) = mapM (foldTermM tf) tt >>= fTermM tf ty dc v
foldTermM tf (Prim ty v ) = fPrimM tf ty v
foldTermM tf (Suspension ct ty v b) = fSuspensionM tf ct ty v b
foldTermM tf (NewtypeWrap ty dc t) = foldTermM tf t >>= fNewtypeWrapM tf ty dc
foldTermM tf (RefWrap ty t) = foldTermM tf t >>= fRefWrapM tf ty
idTermFold :: TermFold Term
idTermFold = TermFold {
fTerm = Term,
fPrim = Prim,
fSuspension = Suspension,
fNewtypeWrap = NewtypeWrap,
fRefWrap = RefWrap
}
mapTermType :: (RttiType -> Type) -> Term -> Term
mapTermType f = foldTerm idTermFold {
fTerm = \ty dc hval tt -> Term (f ty) dc hval tt,
fSuspension = \ct ty hval n ->
Suspension ct (f ty) hval n,
fNewtypeWrap= \ty dc t -> NewtypeWrap (f ty) dc t,
fRefWrap = \ty t -> RefWrap (f ty) t}
mapTermTypeM :: Monad m => (RttiType -> m Type) -> Term -> m Term
mapTermTypeM f = foldTermM TermFoldM {
fTermM = \ty dc hval tt -> f ty >>= \ty' -> return $ Term ty' dc hval tt,
fPrimM = (return.) . Prim,
fSuspensionM = \ct ty hval n ->
f ty >>= \ty' -> return $ Suspension ct ty' hval n,
fNewtypeWrapM= \ty dc t -> f ty >>= \ty' -> return $ NewtypeWrap ty' dc t,
fRefWrapM = \ty t -> f ty >>= \ty' -> return $ RefWrap ty' t}
termTyCoVars :: Term -> TyCoVarSet
termTyCoVars = foldTerm TermFold {
fTerm = \ty _ _ tt ->
tyCoVarsOfType ty `unionVarSet` concatVarEnv tt,
fSuspension = \_ ty _ _ -> tyCoVarsOfType ty,
fPrim = \ _ _ -> emptyVarSet,
fNewtypeWrap= \ty _ t -> tyCoVarsOfType ty `unionVarSet` t,
fRefWrap = \ty t -> tyCoVarsOfType ty `unionVarSet` t}
where concatVarEnv = foldr unionVarSet emptyVarSet
type Precedence = Int
type TermPrinterM m = Precedence -> Term -> m SDoc
app_prec,cons_prec, max_prec ::Int
max_prec = 10
app_prec = max_prec
cons_prec = 5
pprTermM, ppr_termM, pprNewtypeWrap :: Monad m => TermPrinterM m -> TermPrinterM m
pprTermM y p t = pprDeeper `liftM` ppr_termM y p t
ppr_termM y p Term{dc=Left dc_tag, subTerms=tt} = do
tt_docs <- mapM (y app_prec) tt
return $ cparen (not (null tt) && p >= app_prec)
(text dc_tag <+> pprDeeperList fsep tt_docs)
ppr_termM y p Term{dc=Right dc, subTerms=tt}
= do { tt_docs' <- mapM (y app_prec) tt
; return $ ifPprDebug (show_tm tt_docs')
(show_tm (dropList (dataConTheta dc) tt_docs'))
}
where
show_tm tt_docs
| null tt_docs = ppr dc
| otherwise = cparen (p >= app_prec) $
sep [ppr dc, nest 2 (pprDeeperList fsep tt_docs)]
ppr_termM y p t@NewtypeWrap{} = pprNewtypeWrap y p t
ppr_termM y p RefWrap{wrapped_term=t} = do
contents <- y app_prec t
return$ cparen (p >= app_prec) (text "GHC.Prim.MutVar#" <+> contents)
ppr_termM _ _ t = ppr_termM1 t
ppr_termM1 :: Monad m => Term -> m SDoc
ppr_termM1 Prim{valRaw=words, ty=ty} =
return $ repPrim (tyConAppTyCon ty) words
ppr_termM1 Suspension{ty=ty, bound_to=Nothing} =
return (char '_' <+> whenPprDebug (text "::" <> ppr ty))
ppr_termM1 Suspension{ty=ty, bound_to=Just n}
| otherwise = return$ parens$ ppr n <> text "::" <> ppr ty
ppr_termM1 Term{} = panic "ppr_termM1 - Term"
ppr_termM1 RefWrap{} = panic "ppr_termM1 - RefWrap"
ppr_termM1 NewtypeWrap{} = panic "ppr_termM1 - NewtypeWrap"
pprNewtypeWrap y p NewtypeWrap{ty=ty, wrapped_term=t}
| Just (tc,_) <- tcSplitTyConApp_maybe ty
, ASSERT(isNewTyCon tc) True
, Just new_dc <- tyConSingleDataCon_maybe tc = do
real_term <- y max_prec t
return $ cparen (p >= app_prec) (ppr new_dc <+> real_term)
pprNewtypeWrap _ _ _ = panic "pprNewtypeWrap"
type CustomTermPrinter m = TermPrinterM m
-> [Precedence -> Term -> (m (Maybe SDoc))]
cPprTerm :: Monad m => CustomTermPrinter m -> Term -> m SDoc
cPprTerm printers_ = go 0 where
printers = printers_ go
go prec t = do
let default_ = Just `liftM` pprTermM go prec t
mb_customDocs = [pp prec t | pp <- printers] ++ [default_]
mdoc <- firstJustM mb_customDocs
case mdoc of
Nothing -> panic "cPprTerm"
Just doc -> return $ cparen (prec>app_prec+1) doc
firstJustM (mb:mbs) = mb >>= maybe (firstJustM mbs) (return . Just)
firstJustM [] = return Nothing
cPprTermBase :: forall m. Monad m => CustomTermPrinter m
cPprTermBase y =
[ ifTerm (isTupleTy.ty) (\_p -> liftM (parens . hcat . punctuate comma)
. mapM (y (1))
. subTerms)
, ifTerm (\t -> isTyCon listTyCon (ty t) && subTerms t `lengthIs` 2)
ppr_list
, ifTerm' (isTyCon intTyCon . ty) ppr_int
, ifTerm' (isTyCon charTyCon . ty) ppr_char
, ifTerm' (isTyCon floatTyCon . ty) ppr_float
, ifTerm' (isTyCon doubleTyCon . ty) ppr_double
, ifTerm' (isTyCon integerTyCon . ty) ppr_integer
, ifTerm' (isTyCon naturalTyCon . ty) ppr_natural
]
where
ifTerm :: (Term -> Bool)
-> (Precedence -> Term -> m SDoc)
-> Precedence -> Term -> m (Maybe SDoc)
ifTerm pred f = ifTerm' pred (\prec t -> Just <$> f prec t)
ifTerm' :: (Term -> Bool)
-> (Precedence -> Term -> m (Maybe SDoc))
-> Precedence -> Term -> m (Maybe SDoc)
ifTerm' pred f prec t@Term{}
| pred t = f prec t
ifTerm' _ _ _ _ = return Nothing
isTupleTy ty = fromMaybe False $ do
(tc,_) <- tcSplitTyConApp_maybe ty
return (isBoxedTupleTyCon tc)
isTyCon a_tc ty = fromMaybe False $ do
(tc,_) <- tcSplitTyConApp_maybe ty
return (a_tc == tc)
ppr_int, ppr_char, ppr_float, ppr_double
:: Precedence -> Term -> m (Maybe SDoc)
ppr_int _ Term{subTerms=[Prim{valRaw=[w]}]} =
return (Just (Ppr.int (fromIntegral w)))
ppr_int _ _ = return Nothing
ppr_char _ Term{subTerms=[Prim{valRaw=[w]}]} =
return (Just (Ppr.pprHsChar (chr (fromIntegral w))))
ppr_char _ _ = return Nothing
ppr_float _ Term{subTerms=[Prim{valRaw=[w]}]} = do
let f = unsafeDupablePerformIO $
alloca $ \p -> poke p w >> peek (castPtr p)
return (Just (Ppr.float f))
ppr_float _ _ = return Nothing
ppr_double _ Term{subTerms=[Prim{valRaw=[w]}]} = do
let f = unsafeDupablePerformIO $
alloca $ \p -> poke p w >> peek (castPtr p)
return (Just (Ppr.double f))
ppr_double _ Term{subTerms=[Prim{valRaw=[w1,w2]}]} = do
let f = unsafeDupablePerformIO $
alloca $ \p -> do
poke p (fromIntegral w1 :: Word32)
poke (p `plusPtr` 4) (fromIntegral w2 :: Word32)
peek (castPtr p)
return (Just (Ppr.double f))
ppr_double _ _ = return Nothing
ppr_bignat :: Bool -> Precedence -> [Word] -> m (Maybe SDoc)
ppr_bignat sign _ ws = do
let
wordSize = finiteBitSize (0 :: Word)
makeInteger n _ [] = n
makeInteger n s (x:xs) = makeInteger (n + (fromIntegral x `shiftL` s)) (s + wordSize) xs
signf = case sign of
False -> 1
True -> 1
return $ Just $ Ppr.integer $ signf * (makeInteger 0 0 ws)
ppr_integer :: Precedence -> Term -> m (Maybe SDoc)
ppr_integer _ Term{dc=Right con, subTerms=[Prim{valRaw=ws}]}
| con == integerISDataCon
, [W# w] <- ws
= return (Just (Ppr.integer (fromIntegral (I# (word2Int# w)))))
ppr_integer p Term{dc=Right con, subTerms=[Term{subTerms=[Prim{valRaw=ws}]}]}
| con == integerIPDataCon = ppr_bignat False p ws
| con == integerINDataCon = ppr_bignat True p ws
| otherwise = panic "Unexpected Integer constructor"
ppr_integer _ _ = return Nothing
ppr_natural :: Precedence -> Term -> m (Maybe SDoc)
ppr_natural _ Term{dc=Right con, subTerms=[Prim{valRaw=ws}]}
| con == naturalNSDataCon
, [w] <- ws
= return (Just (Ppr.integer (fromIntegral w)))
ppr_natural p Term{dc=Right con, subTerms=[Term{subTerms=[Prim{valRaw=ws}]}]}
| con == naturalNBDataCon = ppr_bignat False p ws
| otherwise = panic "Unexpected Natural constructor"
ppr_natural _ _ = return Nothing
ppr_list :: Precedence -> Term -> m SDoc
ppr_list p (Term{subTerms=[h,t]}) = do
let elems = h : getListTerms t
isConsLast = not (termType (last elems) `eqType` termType h)
is_string = all (isCharTy . ty) elems
chars = [ chr (fromIntegral w)
| Term{subTerms=[Prim{valRaw=[w]}]} <- elems ]
print_elems <- mapM (y cons_prec) elems
if is_string
then return (Ppr.doubleQuotes (Ppr.text chars))
else if isConsLast
then return $ cparen (p >= cons_prec)
$ pprDeeperList fsep
$ punctuate (space<>colon) print_elems
else return $ brackets
$ pprDeeperList fcat
$ punctuate comma print_elems
where getListTerms Term{subTerms=[h,t]} = h : getListTerms t
getListTerms Term{subTerms=[]} = []
getListTerms t@Suspension{} = [t]
getListTerms t = pprPanic "getListTerms" (ppr t)
ppr_list _ _ = panic "doList"
repPrim :: TyCon -> [Word] -> SDoc
repPrim t = rep where
rep x
| t == charPrimTyCon = text $ show (chr (build x :: Int))
| t == intPrimTyCon = text $ show (build x :: Int)
| t == wordPrimTyCon = text $ show (build x :: Word)
| t == floatPrimTyCon = text $ show (build x :: Float)
| t == doublePrimTyCon = text $ show (build x :: Double)
| t == int8PrimTyCon = text $ show (build x :: Int8)
| t == word8PrimTyCon = text $ show (build x :: Word8)
| t == int16PrimTyCon = text $ show (build x :: Int16)
| t == word16PrimTyCon = text $ show (build x :: Word16)
| t == int32PrimTyCon = text $ show (build x :: Int32)
| t == word32PrimTyCon = text $ show (build x :: Word32)
| t == int64PrimTyCon = text $ show (build x :: Int64)
| t == word64PrimTyCon = text $ show (build x :: Word64)
| t == addrPrimTyCon = text $ show (nullPtr `plusPtr` build x)
| t == stablePtrPrimTyCon = text "<stablePtr>"
| t == stableNamePrimTyCon = text "<stableName>"
| t == statePrimTyCon = text "<statethread>"
| t == proxyPrimTyCon = text "<proxy>"
| t == realWorldTyCon = text "<realworld>"
| t == threadIdPrimTyCon = text "<ThreadId>"
| t == weakPrimTyCon = text "<Weak>"
| t == arrayPrimTyCon = text "<array>"
| t == smallArrayPrimTyCon = text "<smallArray>"
| t == byteArrayPrimTyCon = text "<bytearray>"
| t == mutableArrayPrimTyCon = text "<mutableArray>"
| t == smallMutableArrayPrimTyCon = text "<smallMutableArray>"
| t == mutableByteArrayPrimTyCon = text "<mutableByteArray>"
| t == mutVarPrimTyCon = text "<mutVar>"
| t == mVarPrimTyCon = text "<mVar>"
| t == tVarPrimTyCon = text "<tVar>"
| otherwise = char '<' <> ppr t <> char '>'
where build ww = unsafePerformIO $ withArray ww (peek . castPtr)
type RttiType = Type
type GhciType = Type
type TR a = TcM a
runTR :: HscEnv -> TR a -> IO a
runTR hsc_env thing = do
mb_val <- runTR_maybe hsc_env thing
case mb_val of
Nothing -> error "unable to :print the term"
Just x -> return x
runTR_maybe :: HscEnv -> TR a -> IO (Maybe a)
runTR_maybe hsc_env thing_inside
= do { (_errs, res) <- initTcInteractive hsc_env thing_inside
; return res }
traceTR :: SDoc -> TR ()
traceTR = liftTcM . traceOptTcRn Opt_D_dump_rtti
recoverTR :: TR a -> TR a -> TR a
recoverTR = tryTcDiscardingErrs
trIO :: IO a -> TR a
trIO = liftTcM . liftIO
liftTcM :: TcM a -> TR a
liftTcM = id
newVar :: Kind -> TR TcType
newVar kind = liftTcM (do { tv <- newAnonMetaTyVar RuntimeUnkTv kind
; return (mkTyVarTy tv) })
newOpenVar :: TR TcType
newOpenVar = liftTcM (do { kind <- newOpenTypeKind
; newVar kind })
instTyVars :: [TyVar] -> TR (TCvSubst, [TcTyVar])
instTyVars tvs
= liftTcM $ fst <$> captureConstraints (newMetaTyVars tvs)
type RttiInstantiation = [(TcTyVar, TyVar)]
instScheme :: QuantifiedType -> TR (TcType, RttiInstantiation)
instScheme (tvs, ty)
= do { (subst, tvs') <- instTyVars tvs
; let rtti_inst = [(tv',tv) | (tv',tv) <- tvs' `zip` tvs]
; traceTR (text "instScheme" <+> (ppr tvs $$ ppr ty $$ ppr tvs'))
; return (substTy subst ty, rtti_inst) }
applyRevSubst :: RttiInstantiation -> TR ()
applyRevSubst pairs = liftTcM (mapM_ do_pair pairs)
where
do_pair (tc_tv, rtti_tv)
= do { tc_ty <- zonkTcTyVar tc_tv
; case tcGetTyVar_maybe tc_ty of
Just tv | isMetaTyVar tv -> writeMetaTyVar tv (mkTyVarTy rtti_tv)
_ -> return () }
addConstraint :: TcType -> TcType -> TR ()
addConstraint actual expected = do
traceTR (text "add constraint:" <+> fsep [ppr actual, equals, ppr expected])
recoverTR (traceTR $ fsep [text "Failed to unify", ppr actual,
text "with", ppr expected]) $
discardResult $
captureConstraints $
do { (ty1, ty2) <- congruenceNewtypes actual expected
; unifyType Nothing ty1 ty2 }
cvObtainTerm
:: HscEnv
-> Int
-> Bool
-> RttiType
-> ForeignHValue
-> IO Term
cvObtainTerm hsc_env max_depth force old_ty hval = runTR hsc_env $ do
let quant_old_ty@(old_tvs, old_tau) = quantifyType old_ty
sigma_old_ty = mkInfForAllTys old_tvs old_tau
traceTR (text "Term reconstruction started with initial type " <> ppr old_ty)
term <-
if null old_tvs
then do
term <- go max_depth sigma_old_ty sigma_old_ty hval
term' <- zonkTerm term
return $ fixFunDictionaries $ expandNewtypes term'
else do
(old_ty', rev_subst) <- instScheme quant_old_ty
my_ty <- newOpenVar
when (check1 quant_old_ty) (traceTR (text "check1 passed") >>
addConstraint my_ty old_ty')
term <- go max_depth my_ty sigma_old_ty hval
new_ty <- zonkTcType (termType term)
if isMonomorphic new_ty || check2 (quantifyType new_ty) quant_old_ty
then do
traceTR (text "check2 passed")
addConstraint new_ty old_ty'
applyRevSubst rev_subst
zterm' <- zonkTerm term
return ((fixFunDictionaries . expandNewtypes) zterm')
else do
traceTR (text "check2 failed" <+> parens
(ppr term <+> text "::" <+> ppr new_ty))
zterm' <- mapTermTypeM
(\ty -> case tcSplitTyConApp_maybe ty of
Just (tc, _:_) | tc /= funTyCon
-> newOpenVar
_ -> return ty)
term
zonkTerm zterm'
traceTR (text "Term reconstruction completed." $$
text "Term obtained: " <> ppr term $$
text "Type obtained: " <> ppr (termType term))
return term
where
interp = hscInterp hsc_env
go :: Int -> Type -> Type -> ForeignHValue -> TcM Term
go 0 my_ty _old_ty a = do
traceTR (text "Gave up reconstructing a term after" <>
int max_depth <> text " steps")
clos <- trIO $ GHCi.getClosure interp a
return (Suspension (tipe (info clos)) my_ty a Nothing)
go !max_depth my_ty old_ty a = do
let monomorphic = not(isTyVarTy my_ty)
clos <- trIO $ GHCi.getClosure interp a
case clos of
t | isThunk t && force -> do
traceTR (text "Forcing a " <> text (show (fmap (const ()) t)))
evalRslt <- liftIO $ GHCi.seqHValue interp hsc_env a
case evalRslt of
EvalSuccess _ -> go (pred max_depth) my_ty old_ty a
EvalException ex -> do
traceTR $ text "Exception occured:" <+> text (show ex)
liftIO $ throwIO $ fromSerializableException ex
BlackholeClosure{indirectee=ind} -> do
traceTR (text "Following a BLACKHOLE")
ind_clos <- trIO (GHCi.getClosure interp ind)
let return_bh_value = return (Suspension BLACKHOLE my_ty a Nothing)
case ind_clos of
BlockingQueueClosure{} -> return_bh_value
OtherClosure info _ _
| tipe info == TSO -> return_bh_value
UnsupportedClosure info
| tipe info == TSO -> return_bh_value
_ -> go max_depth my_ty old_ty ind
IndClosure{indirectee=ind} -> do
traceTR (text "Following an indirection" )
go max_depth my_ty old_ty ind
MutVarClosure{var=contents}
| Just (tycon,[world,contents_ty]) <- tcSplitTyConApp_maybe old_ty
-> do
traceTR (text "Following a MutVar")
contents_tv <- newVar liftedTypeKind
MASSERT(isUnliftedType my_ty)
(mutvar_ty,_) <- instScheme $ quantifyType $ mkVisFunTyMany
contents_ty (mkTyConApp tycon [world,contents_ty])
addConstraint (mkVisFunTyMany contents_tv my_ty) mutvar_ty
x <- go (pred max_depth) contents_tv contents_ty contents
return (RefWrap my_ty x)
ConstrClosure{ptrArgs=pArgs,dataArgs=dArgs} -> do
traceTR (text "entering a constructor " <> ppr dArgs <+>
if monomorphic
then parens (text "already monomorphic: " <> ppr my_ty)
else Ppr.empty)
Right dcname <- liftIO $ constrClosToName hsc_env clos
(mb_dc, _) <- tryTc (tcLookupDataCon dcname)
case mb_dc of
Nothing -> do
traceTR (text "Not constructor" <+> ppr dcname)
let dflags = hsc_dflags hsc_env
tag = showPpr dflags dcname
vars <- replicateM (length pArgs)
(newVar liftedTypeKind)
subTerms <- sequence $ zipWith (\x tv ->
go (pred max_depth) tv tv x) pArgs vars
return (Term my_ty (Left ('<' : tag ++ ">")) a subTerms)
Just dc -> do
traceTR (text "Is constructor" <+> (ppr dc $$ ppr my_ty))
subTtypes <- getDataConArgTys dc my_ty
subTerms <- extractSubTerms (\ty -> go (pred max_depth) ty ty) clos subTtypes
return (Term my_ty (Right dc) a subTerms)
ArrWordsClosure{bytes=b, arrWords=ws} -> do
traceTR (text "ByteArray# closure, size " <> ppr b)
return (Term my_ty (Left "ByteArray#") a [Prim my_ty ws])
_ -> do
traceTR (text "Unknown closure:" <+>
text (show (fmap (const ()) clos)))
return (Suspension (tipe (info clos)) my_ty a Nothing)
expandNewtypes = foldTerm idTermFold { fTerm = worker } where
worker ty dc hval tt
| Just (tc, args) <- tcSplitTyConApp_maybe ty
, isNewTyCon tc
, wrapped_type <- newTyConInstRhs tc args
, Just dc' <- tyConSingleDataCon_maybe tc
, t' <- worker wrapped_type dc hval tt
= NewtypeWrap ty (Right dc') t'
| otherwise = Term ty dc hval tt
fixFunDictionaries = foldTerm idTermFold {fSuspension = worker} where
worker ct ty hval n | isFunTy ty = Suspension ct (dictsView ty) hval n
| otherwise = Suspension ct ty hval n
extractSubTerms :: (Type -> ForeignHValue -> TcM Term)
-> GenClosure ForeignHValue -> [Type] -> TcM [Term]
extractSubTerms recurse clos = liftM thdOf3 . go 0 0
where
array = dataArgs clos
go ptr_i arr_i [] = return (ptr_i, arr_i, [])
go ptr_i arr_i (ty:tys)
| Just (tc, elem_tys) <- tcSplitTyConApp_maybe ty
, isUnboxedTupleTyCon tc
= do (ptr_i, arr_i, terms0) <-
go ptr_i arr_i (dropRuntimeRepArgs elem_tys)
(ptr_i, arr_i, terms1) <- go ptr_i arr_i tys
return (ptr_i, arr_i, unboxedTupleTerm ty terms0 : terms1)
| otherwise
= case typePrimRepArgs ty of
[rep_ty] -> do
(ptr_i, arr_i, term0) <- go_rep ptr_i arr_i ty rep_ty
(ptr_i, arr_i, terms1) <- go ptr_i arr_i tys
return (ptr_i, arr_i, term0 : terms1)
rep_tys -> do
(ptr_i, arr_i, terms0) <- go_unary_types ptr_i arr_i rep_tys
(ptr_i, arr_i, terms1) <- go ptr_i arr_i tys
return (ptr_i, arr_i, unboxedTupleTerm ty terms0 : terms1)
go_unary_types ptr_i arr_i [] = return (ptr_i, arr_i, [])
go_unary_types ptr_i arr_i (rep_ty:rep_tys) = do
tv <- newVar liftedTypeKind
(ptr_i, arr_i, term0) <- go_rep ptr_i arr_i tv rep_ty
(ptr_i, arr_i, terms1) <- go_unary_types ptr_i arr_i rep_tys
return (ptr_i, arr_i, term0 : terms1)
go_rep ptr_i arr_i ty rep
| isGcPtrRep rep = do
t <- recurse ty $ (ptrArgs clos)!!ptr_i
return (ptr_i + 1, arr_i, t)
| otherwise = do
platform <- getPlatform
let word_size = platformWordSizeInBytes platform
endian = platformByteOrder platform
size_b = primRepSizeB platform rep
!aligned_idx = roundUpTo arr_i (min word_size size_b)
!new_arr_i = aligned_idx + size_b
ws | size_b < word_size =
[index size_b aligned_idx word_size endian]
| otherwise =
let (q, r) = size_b `quotRem` word_size
in ASSERT( r == 0 )
[ array!!i
| o <- [0.. q 1]
, let i = (aligned_idx `quot` word_size) + o
]
return (ptr_i, new_arr_i, Prim ty ws)
unboxedTupleTerm ty terms
= Term ty (Right (tupleDataCon Unboxed (length terms)))
(error "unboxedTupleTerm: no HValue for unboxed tuple") terms
index size_b aligned_idx word_size endian = case endian of
BigEndian -> (word `shiftL` moveBits) `shiftR` zeroOutBits `shiftL` zeroOutBits
LittleEndian -> (word `shiftR` moveBits) `shiftL` zeroOutBits `shiftR` zeroOutBits
where
(q, r) = aligned_idx `quotRem` word_size
word = array!!q
moveBits = r * 8
zeroOutBits = (word_size size_b) * 8
cvReconstructType
:: HscEnv
-> Int
-> GhciType
-> ForeignHValue
-> IO (Maybe Type)
cvReconstructType hsc_env max_depth old_ty hval = runTR_maybe hsc_env $ do
traceTR (text "RTTI started with initial type " <> ppr old_ty)
let sigma_old_ty@(old_tvs, _) = quantifyType old_ty
new_ty <-
if null old_tvs
then return old_ty
else do
(old_ty', rev_subst) <- instScheme sigma_old_ty
my_ty <- newOpenVar
when (check1 sigma_old_ty) (traceTR (text "check1 passed") >>
addConstraint my_ty old_ty')
search (isMonomorphic `fmap` zonkTcType my_ty)
(\(ty,a) -> go ty a)
(Seq.singleton (my_ty, hval))
max_depth
new_ty <- zonkTcType my_ty
if isMonomorphic new_ty || check2 (quantifyType new_ty) sigma_old_ty
then do
traceTR (text "check2 passed" <+> ppr old_ty $$ ppr new_ty)
addConstraint my_ty old_ty'
applyRevSubst rev_subst
zonkRttiType new_ty
else traceTR (text "check2 failed" <+> parens (ppr new_ty)) >>
return old_ty
traceTR (text "RTTI completed. Type obtained:" <+> ppr new_ty)
return new_ty
where
interp = hscInterp hsc_env
search _ _ _ 0 = traceTR (text "Failed to reconstruct a type after " <>
int max_depth <> text " steps")
search stop expand l d =
case viewl l of
EmptyL -> return ()
x :< xx -> unlessM stop $ do
new <- expand x
search stop expand (xx `mappend` Seq.fromList new) $! (pred d)
go :: Type -> ForeignHValue -> TR [(Type, ForeignHValue)]
go my_ty a = do
traceTR (text "go" <+> ppr my_ty)
clos <- trIO $ GHCi.getClosure interp a
case clos of
BlackholeClosure{indirectee=ind} -> go my_ty ind
IndClosure{indirectee=ind} -> go my_ty ind
MutVarClosure{var=contents} -> do
tv' <- newVar liftedTypeKind
world <- newVar liftedTypeKind
addConstraint my_ty (mkTyConApp mutVarPrimTyCon [world,tv'])
return [(tv', contents)]
ConstrClosure{ptrArgs=pArgs} -> do
Right dcname <- liftIO $ constrClosToName hsc_env clos
traceTR (text "Constr1" <+> ppr dcname)
(mb_dc, _) <- tryTc (tcLookupDataCon dcname)
case mb_dc of
Nothing->
forM pArgs $ \x -> do
tv <- newVar liftedTypeKind
return (tv, x)
Just dc -> do
arg_tys <- getDataConArgTys dc my_ty
(_, itys) <- findPtrTyss 0 arg_tys
traceTR (text "Constr2" <+> ppr dcname <+> ppr arg_tys)
return $ zipWith (\(_,ty) x -> (ty, x)) itys pArgs
_ -> return []
findPtrTys :: Int
-> Type
-> TR (Int, [(Int, Type)])
findPtrTys i ty
| Just (tc, elem_tys) <- tcSplitTyConApp_maybe ty
, isUnboxedTupleTyCon tc
= findPtrTyss i elem_tys
| otherwise
= case typePrimRep ty of
[rep] | isGcPtrRep rep -> return (i + 1, [(i, ty)])
| otherwise -> return (i, [])
prim_reps ->
foldM (\(i, extras) prim_rep ->
if isGcPtrRep prim_rep
then newVar liftedTypeKind >>= \tv -> return (i + 1, extras ++ [(i, tv)])
else return (i, extras))
(i, []) prim_reps
findPtrTyss :: Int
-> [Type]
-> TR (Int, [(Int, Type)])
findPtrTyss i tys = foldM step (i, []) tys
where step (i, discovered) elem_ty = do
(i, extras) <- findPtrTys i elem_ty
return (i, discovered ++ extras)
improveRTTIType :: HscEnv -> RttiType -> RttiType -> Maybe TCvSubst
improveRTTIType _ base_ty new_ty = U.tcUnifyTyKi base_ty new_ty
getDataConArgTys :: DataCon -> Type -> TR [Type]
getDataConArgTys dc con_app_ty
= do { let rep_con_app_ty = unwrapType con_app_ty
; traceTR (text "getDataConArgTys 1" <+> (ppr con_app_ty $$ ppr rep_con_app_ty
$$ ppr (tcSplitTyConApp_maybe rep_con_app_ty)))
; ASSERT( all isTyVar ex_tvs ) return ()
; (subst, _) <- instTyVars (univ_tvs ++ ex_tvs)
; addConstraint rep_con_app_ty (substTy subst (dataConOrigResTy dc))
; let con_arg_tys = substTys subst (map scaledThing $ dataConRepArgTys dc)
; traceTR (text "getDataConArgTys 2" <+> (ppr rep_con_app_ty $$ ppr con_arg_tys $$ ppr subst))
; return con_arg_tys }
where
univ_tvs = dataConUnivTyVars dc
ex_tvs = dataConExTyCoVars dc
check1 :: QuantifiedType -> Bool
check1 (tvs, _) = not $ any isHigherKind (map tyVarKind tvs)
where
isHigherKind = not . null . fst . splitPiTys
check2 :: QuantifiedType -> QuantifiedType -> Bool
check2 (_, rtti_ty) (_, old_ty)
| Just (_, rttis) <- tcSplitTyConApp_maybe rtti_ty
= case () of
_ | Just (_,olds) <- tcSplitTyConApp_maybe old_ty
-> and$ zipWith check2 (map quantifyType rttis) (map quantifyType olds)
_ | Just _ <- splitAppTy_maybe old_ty
-> isMonomorphicOnNonPhantomArgs rtti_ty
_ -> True
| otherwise = True
congruenceNewtypes :: TcType -> TcType -> TR (TcType,TcType)
congruenceNewtypes lhs rhs = go lhs rhs >>= \rhs' -> return (lhs,rhs')
where
go l r
| Just tv <- getTyVar_maybe l
, isTcTyVar tv
, isMetaTyVar tv
= recoverTR (return r) $ do
Indirect ty_v <- readMetaTyVar tv
traceTR $ fsep [text "(congruence) Following indirect tyvar:",
ppr tv, equals, ppr ty_v]
go ty_v r
| Just (w1,l1,l2) <- splitFunTy_maybe l
, Just (w2,r1,r2) <- splitFunTy_maybe r
, w1 `eqType` w2
= do r2' <- go l2 r2
r1' <- go l1 r1
return (mkVisFunTy w1 r1' r2')
| Just (tycon_l, _) <- tcSplitTyConApp_maybe lhs
, Just (tycon_r, _) <- tcSplitTyConApp_maybe rhs
, tycon_l /= tycon_r
= upgrade tycon_l r
| otherwise = return r
where upgrade :: TyCon -> Type -> TR Type
upgrade new_tycon ty
| not (isNewTyCon new_tycon) = do
traceTR (text "(Upgrade) Not matching newtype evidence: " <>
ppr new_tycon <> text " for " <> ppr ty)
return ty
| otherwise = do
traceTR (text "(Upgrade) upgraded " <> ppr ty <>
text " in presence of newtype evidence " <> ppr new_tycon)
(_, vars) <- instTyVars (tyConTyVars new_tycon)
let ty' = mkTyConApp new_tycon (mkTyVarTys vars)
rep_ty = unwrapType ty'
_ <- liftTcM (unifyType Nothing ty rep_ty)
return ty'
zonkTerm :: Term -> TcM Term
zonkTerm = foldTermM (TermFoldM
{ fTermM = \ty dc v tt -> zonkRttiType ty >>= \ty' ->
return (Term ty' dc v tt)
, fSuspensionM = \ct ty v b -> zonkRttiType ty >>= \ty ->
return (Suspension ct ty v b)
, fNewtypeWrapM = \ty dc t -> zonkRttiType ty >>= \ty' ->
return$ NewtypeWrap ty' dc t
, fRefWrapM = \ty t -> return RefWrap `ap`
zonkRttiType ty `ap` return t
, fPrimM = (return.) . Prim })
zonkRttiType :: TcType -> TcM Type
zonkRttiType ty= do { ze <- mkEmptyZonkEnv RuntimeUnkFlexi
; zonkTcTypeToTypeX ze ty }
dictsView :: Type -> Type
dictsView ty = ty
isMonomorphic :: RttiType -> Bool
isMonomorphic ty = noExistentials && noUniversals
where (tvs, _, ty') = tcSplitSigmaTy ty
noExistentials = noFreeVarsOfType ty'
noUniversals = null tvs
isMonomorphicOnNonPhantomArgs :: RttiType -> Bool
isMonomorphicOnNonPhantomArgs ty
| Just (tc, all_args) <- tcSplitTyConApp_maybe (unwrapType ty)
, phantom_vars <- tyConPhantomTyVars tc
, concrete_args <- [ arg | (tyv,arg) <- tyConTyVars tc `zip` all_args
, tyv `notElem` phantom_vars]
= all isMonomorphicOnNonPhantomArgs concrete_args
| Just (_, ty1, ty2) <- splitFunTy_maybe ty
= all isMonomorphicOnNonPhantomArgs [ty1,ty2]
| otherwise = isMonomorphic ty
tyConPhantomTyVars :: TyCon -> [TyVar]
tyConPhantomTyVars tc
| isAlgTyCon tc
, Just dcs <- tyConDataCons_maybe tc
, dc_vars <- concatMap dataConUnivTyVars dcs
= tyConTyVars tc \\ dc_vars
tyConPhantomTyVars _ = []
type QuantifiedType = ([TyVar], Type)
quantifyType :: Type -> QuantifiedType
quantifyType ty = ( filter isTyVar $
tyCoVarsOfTypeWellScoped rho
, rho)
where
(_tvs, rho) = tcSplitForAllInvisTyVars ty