I boobed when I decoupled record selectors from their data types.
The most straightforward and robust fix means attaching the TyCon
of a record selector to its IfaceIdInfo, so
you'll need to rebuild all .hi files
That said, the fix is easy.
return (IfaceRec ac)
instance Binary IfaceIdDetails where
return (IfaceRec ac)
instance Binary IfaceIdDetails where
- put_ bh IfVanillaId = putByte bh 0
- put_ bh (IfRecSelId b) = do { putByte bh 1; put_ bh b }
- put_ bh IfDFunId = putByte bh 2
+ put_ bh IfVanillaId = putByte bh 0
+ put_ bh (IfRecSelId a b) = do { putByte bh 1; put_ bh a; put_ bh b }
+ put_ bh IfDFunId = putByte bh 2
get bh = do
h <- getByte bh
case h of
0 -> return IfVanillaId
1 -> do a <- get bh
get bh = do
h <- getByte bh
case h of
0 -> return IfVanillaId
1 -> do a <- get bh
+ b <- get bh
+ return (IfRecSelId a b)
_ -> return IfDFunId
instance Binary IfaceIdInfo where
_ -> return IfDFunId
instance Binary IfaceIdInfo where
data IfaceIdDetails
= IfVanillaId
data IfaceIdDetails
= IfVanillaId
+ | IfRecSelId IfaceTyCon Bool
| IfDFunId
data IfaceIdInfo
| IfDFunId
data IfaceIdInfo
------------------
instance Outputable IfaceIdDetails where
ppr IfVanillaId = empty
------------------
instance Outputable IfaceIdDetails where
ppr IfVanillaId = empty
- ppr (IfRecSelId b) = ptext (sLit "RecSel")
- <> if b then ptext (sLit "<naughty>") else empty
+ ppr (IfRecSelId tc b) = ptext (sLit "RecSel") <+> ppr tc
+ <+> if b then ptext (sLit "<naughty>") else empty
ppr IfDFunId = ptext (sLit "DFunId")
instance Outputable IfaceIdInfo where
ppr IfDFunId = ptext (sLit "DFunId")
instance Outputable IfaceIdInfo where
toIfaceIdDetails :: IdDetails -> IfaceIdDetails
toIfaceIdDetails VanillaId = IfVanillaId
toIfaceIdDetails DFunId = IfVanillaId
toIfaceIdDetails :: IdDetails -> IfaceIdDetails
toIfaceIdDetails VanillaId = IfVanillaId
toIfaceIdDetails DFunId = IfVanillaId
-toIfaceIdDetails (RecSelId { sel_naughty = n }) = IfRecSelId n
+toIfaceIdDetails (RecSelId { sel_naughty = n
+ , sel_tycon = tc }) = IfRecSelId (toIfaceTyCon tc) n
toIfaceIdDetails other = pprTrace "toIfaceIdDetails" (ppr other)
IfVanillaId -- Unexpected
toIfaceIdDetails other = pprTrace "toIfaceIdDetails" (ppr other)
IfVanillaId -- Unexpected
import IfaceEnv
import BuildTyCl
import TcRnMonad
import IfaceEnv
import BuildTyCl
import TcRnMonad
-import TcType ( tcSplitSigmaTy )
import Type
import TypeRep
import HscTypes
import Type
import TypeRep
import HscTypes
ifIdDetails = details, ifIdInfo = info})
= do { name <- lookupIfaceTop occ_name
; ty <- tcIfaceType iface_type
ifIdDetails = details, ifIdInfo = info})
= do { name <- lookupIfaceTop occ_name
; ty <- tcIfaceType iface_type
- ; details <- tcIdDetails ty details
+ ; details <- tcIdDetails details
; info <- tcIdInfo ignore_prags name ty info
; return (AnId (mkGlobalId details name ty info)) }
; info <- tcIdInfo ignore_prags name ty info
; return (AnId (mkGlobalId details name ty info)) }
%************************************************************************
\begin{code}
%************************************************************************
\begin{code}
-tcIdDetails :: Type -> IfaceIdDetails -> IfL IdDetails
-tcIdDetails _ IfVanillaId = return VanillaId
-tcIdDetails _ IfDFunId = return DFunId
-tcIdDetails ty (IfRecSelId naughty)
- = return (RecSelId { sel_tycon = tc, sel_naughty = naughty })
- where
- (_, _, tau) = tcSplitSigmaTy ty
- tc = tyConAppTyCon (funArgTy tau)
- -- A bit fragile. Relies on the selector type looking like
- -- forall abc. (stupid-context) => T a b c -> blah
+tcIdDetails :: IfaceIdDetails -> IfL IdDetails
+tcIdDetails IfVanillaId = return VanillaId
+tcIdDetails IfDFunId = return DFunId
+tcIdDetails (IfRecSelId tc naughty)
+ = do { tc' <- tcIfaceTyCon tc
+ ; return (RecSelId { sel_tycon = tc', sel_naughty = naughty }) }
tcIdInfo :: Bool -> Name -> Type -> IfaceIdInfo -> IfL IdInfo
tcIdInfo ignore_prags name ty info
tcIdInfo :: Bool -> Name -> Type -> IfaceIdInfo -> IfL IdInfo
tcIdInfo ignore_prags name ty info
data T a where { MkT { f1::a, f2::b->b } :: T a }
f :: T a -> b -> T b
f t b = t { f1=b }
data T a where { MkT { f1::a, f2::b->b } :: T a }
f :: T a -> b -> T b
f t b = t { f1=b }
The criterion we use is this:
The types of the updated fields
mention only the universally-quantified type variables
of the data constructor
The criterion we use is this:
The types of the updated fields
mention only the universally-quantified type variables
of the data constructor
+NB: this is not (quite) the same as being a "naughty" record selector
+(See Note [Naughty record selectors]) in TcTyClsDecls), at least
+in the case of GADTs. Consider
+ data T a where { MkT :: { f :: a } :: T [a] }
+Then f is not "naughty" because it has a well-typed record selector.
+But we don't allow updates for 'f'. (One could consider trying to
+allow this, but it makes my head hurt. Badly. And no one has asked
+for it.)
+
In principle one could go further, and allow
g :: T a -> T a
g t = t { f2 = \x -> x }
In principle one could go further, and allow
g :: T a -> T a
g t = t { f2 = \x -> x }
data_tvs = tyVarsOfType data_ty
is_naughty = not (tyVarsOfType field_ty `subVarSet` data_tvs)
(field_tvs, field_theta, field_tau) = tcSplitSigmaTy field_ty
data_tvs = tyVarsOfType data_ty
is_naughty = not (tyVarsOfType field_ty `subVarSet` data_tvs)
(field_tvs, field_theta, field_tau) = tcSplitSigmaTy field_ty
- sel_ty | is_naughty = unitTy
+ sel_ty | is_naughty = unitTy -- See Note [Naughty record selectors]
| otherwise = mkForAllTys (varSetElems data_tvs ++ field_tvs) $
mkPhiTy (dataConStupidTheta con1) $ -- Urgh!
mkPhiTy field_theta $ -- Urgh!
| otherwise = mkForAllTys (varSetElems data_tvs ++ field_tvs) $
mkPhiTy (dataConStupidTheta con1) $ -- Urgh!
mkPhiTy field_theta $ -- Urgh!
helpfully, rather than saying unhelpfully that 'x' is not in scope.
Hence the sel_naughty flag, to identify record selectors that don't really exist.
helpfully, rather than saying unhelpfully that 'x' is not in scope.
Hence the sel_naughty flag, to identify record selectors that don't really exist.
-In general, a field is naughty if its type mentions a type variable that
-isn't in the result type of the constructor.
+In general, a field is "naughty" if its type mentions a type variable that
+isn't in the result type of the constructor. Note that this *allows*
+GADT record selectors (Note [GADT record selectors]) whose types may look
+like sel :: T [a] -> a
+For naughty selectors we make a dummy binding
sel = ()
for naughty selectors, so that the later type-check will add them to the
environment, and they'll be exported. The function is never called, because
sel = ()
for naughty selectors, so that the later type-check will add them to the
environment, and they'll be exported. The function is never called, because