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
- 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
- return (IfRecSelId a)
+ b <- get bh
+ return (IfRecSelId a b)
_ -> return IfDFunId
instance Binary IfaceIdInfo where
data IfaceIdDetails
= IfVanillaId
- | IfRecSelId Bool
+ | IfRecSelId IfaceTyCon Bool
| IfDFunId
data IfaceIdInfo
------------------
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
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
import IfaceEnv
import BuildTyCl
import TcRnMonad
-import TcType ( tcSplitSigmaTy )
import Type
import TypeRep
import HscTypes
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)) }
%************************************************************************
\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
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
+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 }
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!
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
-We make a dummy binding
+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