summaryrefslogtreecommitdiff
path: root/compiler/hsSyn/HsUtils.hs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/hsSyn/HsUtils.hs')
-rw-r--r--compiler/hsSyn/HsUtils.hs74
1 files changed, 73 insertions, 1 deletions
diff --git a/compiler/hsSyn/HsUtils.hs b/compiler/hsSyn/HsUtils.hs
index b49cd98f25..58948cc862 100644
--- a/compiler/hsSyn/HsUtils.hs
+++ b/compiler/hsSyn/HsUtils.hs
@@ -28,7 +28,8 @@ module HsUtils(
mkHsOpApp, mkHsDo, mkHsComp, mkHsWrapPat, mkHsWrapPatCo,
mkLHsPar, mkHsCmdWrap, mkLHsCmdWrap,
- nlHsTyApp, nlHsTyApps, nlHsVar, nlHsLit, nlHsApp, nlHsApps, nlHsSyntaxApps,
+ nlHsTyApp, nlHsTyApps, nlHsVar, nlHsDataCon,
+ nlHsLit, nlHsApp, nlHsApps, nlHsSyntaxApps,
nlHsIntLit, nlHsVarApps,
nlHsDo, nlHsOpApp, nlHsLam, nlHsPar, nlHsIf, nlHsCase, nlList,
mkLHsTupleExpr, mkLHsVarTuple, missingTupArg,
@@ -71,6 +72,8 @@ module HsUtils(
noRebindableInfo,
-- Collecting binders
+ isUnliftedHsBind,
+
collectLocalBinders, collectHsValBinders, collectHsBindListBinders,
collectHsIdBinders,
collectHsBindsBinders, collectHsBindBinders, collectMethodBinders,
@@ -105,6 +108,8 @@ import Type ( filterOutInvisibleTypes )
import TysWiredIn ( unitTy )
import TcType
import DataCon
+import ConLike
+import Id
import Name
import NameSet
import NameEnv
@@ -365,6 +370,10 @@ userHsTyVarBndrs loc bndrs = [ L loc (UserTyVar (L loc v)) | v <- bndrs ]
nlHsVar :: id -> LHsExpr id
nlHsVar n = noLoc (HsVar (noLoc n))
+-- NB: Only for LHsExpr **Id**
+nlHsDataCon :: DataCon -> LHsExpr Id
+nlHsDataCon con = noLoc (HsConLikeOut (RealDataCon con))
+
nlHsLit :: HsLit -> LHsExpr id
nlHsLit n = noLoc (HsLit n)
@@ -772,9 +781,72 @@ These functions should only be used on HsSyn *after* the renamer,
to return a [Name] or [Id]. Before renaming the record punning
and wild-card mechanism makes it hard to know what is bound.
So these functions should not be applied to (HsSyn RdrName)
+
+Note [Unlifted id check in isHsUnliftedBind]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Suppose there is a binding with the type (Num a => (# a, a #)). Is this a
+strict binding that should be disallowed at the top level? At first glance,
+no, because it's a function. But consider how this is desugared via
+AbsBinds:
+
+ -- x :: Num a => (# a, a #)
+ x = (# 3, 4 #)
+
+becomes
+
+ x = \ $dictNum ->
+ let x_mono = (# fromInteger $dictNum 3, fromInteger $dictNum 4 #) in
+ x_mono
+
+Note that the inner let is strict. And thus if we have a bunch of mutually
+recursive bindings of this form, we could end up in trouble. This was shown
+up in #9140.
+
+But if there is a type signature on x, everything changes because of the
+desugaring used by AbsBindsSig:
+
+ x :: Num a => (# a, a #)
+ x = (# 3, 4 #)
+
+becomes
+
+ x = \ $dictNum -> (# fromInteger $dictNum 3, fromInteger $dictNum 4 #)
+
+No strictness anymore! The bottom line here is that, for inferred types, we
+care about the strictness of the type after the =>. For checked types
+(AbsBindsSig), we care about the overall strictness.
+
+This matters. If we don't separate out the AbsBindsSig case, then GHC runs into
+a problem when compiling
+
+ undefined :: forall (r :: RuntimeRep) (a :: TYPE r). HasCallStack => a
+
+Looking only after the =>, we cannot tell if this is strict or not. (GHC panics
+if you try.) Looking at the whole type, on the other hand, tells you that this
+is a lifted function type, with no trouble at all.
+
-}
----------------- Bindings --------------------------
+
+-- | Should we treat this as an unlifted bind? This will be true for any
+-- bind that binds an unlifted variable, but we must be careful around
+-- AbsBinds. See Note [Unlifted id check in isUnliftedHsBind]. For usage
+-- information, see Note [Strict binds check] is DsBinds.
+isUnliftedHsBind :: HsBind Id -> Bool -- works only over typechecked binds
+isUnliftedHsBind (AbsBindsSig { abs_sig_export = id })
+ = isUnliftedType (idType id)
+isUnliftedHsBind bind
+ = any is_unlifted_id (collectHsBindBinders bind)
+ where
+ is_unlifted_id id
+ = case tcSplitSigmaTy (idType id) of
+ (_, _, tau) -> isUnliftedType tau
+ -- For the is_unlifted check, we need to look inside polymorphism
+ -- and overloading. E.g. x = (# 1, True #)
+ -- would get type forall a. Num a => (# a, Bool #)
+ -- and we want to reject that. See Trac #9140
+
collectLocalBinders :: HsLocalBindsLR idL idR -> [idL]
collectLocalBinders (HsValBinds binds) = collectHsIdBinders binds
-- No pattern synonyms here