diff options
author | Ian Lynagh <igloo@earth.li> | 2011-09-30 21:57:53 +0100 |
---|---|---|
committer | Ian Lynagh <igloo@earth.li> | 2011-10-01 01:45:41 +0100 |
commit | 5b988961338f73af5790bfd365ca79c858249cea (patch) | |
tree | 44b28b8dd216e9a150676abc0da82da9da4b1277 /compiler | |
parent | 53191d55079529dd3682a66e86f2ab9f6479f1bb (diff) | |
download | haskell-5b988961338f73af5790bfd365ca79c858249cea.tar.gz |
Handle newtypes and type functions correctly in FFI types; fixes #3008
You can now use type functions in FFI types.
Newtypes are now only looked through if the constructor is in scope.
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/deSugar/DsCCall.lhs | 4 | ||||
-rw-r--r-- | compiler/deSugar/DsForeign.lhs | 82 | ||||
-rw-r--r-- | compiler/deSugar/DsMeta.hs | 2 | ||||
-rw-r--r-- | compiler/hsSyn/Convert.lhs | 4 | ||||
-rw-r--r-- | compiler/hsSyn/HsDecls.lhs | 32 | ||||
-rw-r--r-- | compiler/hsSyn/HsUtils.lhs | 2 | ||||
-rw-r--r-- | compiler/parser/RdrHsSyn.lhs | 6 | ||||
-rw-r--r-- | compiler/rename/RnNames.lhs | 2 | ||||
-rw-r--r-- | compiler/rename/RnSource.lhs | 8 | ||||
-rw-r--r-- | compiler/typecheck/TcForeign.lhs | 160 | ||||
-rw-r--r-- | compiler/typecheck/TcHsSyn.lhs | 4 | ||||
-rw-r--r-- | compiler/typecheck/TcType.lhs | 50 | ||||
-rw-r--r-- | compiler/types/FamInstEnv.lhs | 2 |
13 files changed, 213 insertions, 145 deletions
diff --git a/compiler/deSugar/DsCCall.lhs b/compiler/deSugar/DsCCall.lhs index a40d454852..f926f53a08 100644 --- a/compiler/deSugar/DsCCall.lhs +++ b/compiler/deSugar/DsCCall.lhs @@ -209,7 +209,7 @@ boxResult :: Type -- State# RealWorld -> (# State# RealWorld #) boxResult result_ty - | Just (io_tycon, io_res_ty, co) <- tcSplitIOType_maybe result_ty + | Just (io_tycon, io_res_ty) <- tcSplitIOType_maybe result_ty -- isIOType_maybe handles the case where the type is a -- simple wrapping of IO. E.g. -- newtype Wrap a = W (IO a) @@ -236,7 +236,7 @@ boxResult result_ty ; let io_data_con = head (tyConDataCons io_tycon) toIOCon = dataConWrapId io_data_con - wrap the_call = mkCoerce (mkSymCo co) $ + wrap the_call = mkApps (Var toIOCon) [ Type io_res_ty, Lam state_id $ diff --git a/compiler/deSugar/DsForeign.lhs b/compiler/deSugar/DsForeign.lhs index ea07ee7e90..22a4a7bdde 100644 --- a/compiler/deSugar/DsForeign.lhs +++ b/compiler/deSugar/DsForeign.lhs @@ -18,7 +18,6 @@ import DsMonad import HsSyn import DataCon -import CoreUtils import CoreUnfold import Id import Literal @@ -45,6 +44,7 @@ import Platform import Config import Constants import OrdList +import Pair import Data.Maybe import Data.List \end{code} @@ -84,14 +84,14 @@ dsForeigns fos = do where do_ldecl (L loc decl) = putSrcSpanDs loc (do_decl decl) - do_decl (ForeignImport id _ spec) = do + do_decl (ForeignImport id _ co spec) = do traceIf (text "fi start" <+> ppr id) - (bs, h, c) <- dsFImport (unLoc id) spec + (bs, h, c) <- dsFImport (unLoc id) co spec traceIf (text "fi end" <+> ppr id) return (h, c, [], bs) - do_decl (ForeignExport (L _ id) _ (CExport (CExportStatic ext_nm cconv))) = do - (h, c, _, _) <- dsFExport id (idType id) ext_nm cconv False + do_decl (ForeignExport (L _ id) _ co (CExport (CExportStatic ext_nm cconv))) = do + (h, c, _, _) <- dsFExport id co ext_nm cconv False return (h, c, [id], []) \end{code} @@ -122,20 +122,22 @@ because it exposes the boxing to the call site. \begin{code} dsFImport :: Id + -> Coercion -> ForeignImport -> DsM ([Binding], SDoc, SDoc) -dsFImport id (CImport cconv safety _ spec) = do - (ids, h, c) <- dsCImport id spec cconv safety +dsFImport id co (CImport cconv safety _ spec) = do + (ids, h, c) <- dsCImport id co spec cconv safety return (ids, h, c) dsCImport :: Id + -> Coercion -> CImportSpec -> CCallConv -> Safety -> DsM ([Binding], SDoc, SDoc) -dsCImport id (CLabel cid) cconv _ = do - let ty = idType id - fod = case tyConAppTyCon_maybe (repType ty) of +dsCImport id co (CLabel cid) cconv _ = do + let ty = pFst $ coercionKind co + fod = case tyConAppTyCon_maybe ty of Just tycon | tyConUnique tycon == funPtrTyConKey -> IsFunction @@ -144,23 +146,24 @@ dsCImport id (CLabel cid) cconv _ = do ASSERT(fromJust resTy `eqType` addrPrimTy) -- typechecker ensures this let rhs = foRhs (Lit (MachLabel cid stdcall_info fod)) + rhs' = Cast rhs co stdcall_info = fun_type_arg_stdcall_info cconv ty in - return ([(id, rhs)], empty, empty) + return ([(id, rhs')], empty, empty) -dsCImport id (CFunction target) cconv@PrimCallConv safety - = dsPrimCall id (CCall (CCallSpec target cconv safety)) -dsCImport id (CFunction target) cconv safety - = dsFCall id (CCall (CCallSpec target cconv safety)) -dsCImport id CWrapper cconv _ - = dsFExportDynamic id cconv +dsCImport id co (CFunction target) cconv@PrimCallConv safety + = dsPrimCall id co (CCall (CCallSpec target cconv safety)) +dsCImport id co (CFunction target) cconv safety + = dsFCall id co (CCall (CCallSpec target cconv safety)) +dsCImport id co CWrapper cconv _ + = dsFExportDynamic id co cconv -- For stdcall labels, if the type was a FunPtr or newtype thereof, -- then we need to calculate the size of the arguments in order to add -- the @n suffix to the label. fun_type_arg_stdcall_info :: CCallConv -> Type -> Maybe Int fun_type_arg_stdcall_info StdCallConv ty - | Just (tc,[arg_ty]) <- splitTyConApp_maybe (repType ty), + | Just (tc,[arg_ty]) <- splitTyConApp_maybe ty, tyConUnique tc == funPtrTyConKey = let (_tvs,sans_foralls) = tcSplitForAllTys arg_ty @@ -178,10 +181,10 @@ fun_type_arg_stdcall_info _other_conv _ %************************************************************************ \begin{code} -dsFCall :: Id -> ForeignCall -> DsM ([(Id, Expr TyVar)], SDoc, SDoc) -dsFCall fn_id fcall = do +dsFCall :: Id -> Coercion -> ForeignCall -> DsM ([(Id, Expr TyVar)], SDoc, SDoc) +dsFCall fn_id co fcall = do let - ty = idType fn_id + ty = pFst $ coercionKind co (tvs, fun_ty) = tcSplitForAllTys ty (arg_tys, io_res_ty) = tcSplitFunTys fun_ty -- Must use tcSplit* functions because we want to @@ -208,9 +211,10 @@ dsFCall fn_id fcall = do work_app = mkApps (mkVarApps (Var work_id) tvs) val_args wrapper_body = foldr ($) (res_wrapper work_app) arg_wrappers wrap_rhs = mkLams (tvs ++ args) wrapper_body - fn_id_w_inl = fn_id `setIdUnfolding` mkInlineUnfolding (Just (length args)) wrap_rhs + wrap_rhs' = Cast wrap_rhs co + fn_id_w_inl = fn_id `setIdUnfolding` mkInlineUnfolding (Just (length args)) wrap_rhs' - return ([(work_id, work_rhs), (fn_id_w_inl, wrap_rhs)], empty, empty) + return ([(work_id, work_rhs), (fn_id_w_inl, wrap_rhs')], empty, empty) \end{code} @@ -228,10 +232,11 @@ kind of Id, or perhaps to bundle them with PrimOps since semantically and for calling convention they are really prim ops. \begin{code} -dsPrimCall :: Id -> ForeignCall -> DsM ([(Id, Expr TyVar)], SDoc, SDoc) -dsPrimCall fn_id fcall = do +dsPrimCall :: Id -> Coercion -> ForeignCall + -> DsM ([(Id, Expr TyVar)], SDoc, SDoc) +dsPrimCall fn_id co fcall = do let - ty = idType fn_id + ty = pFst $ coercionKind co (tvs, fun_ty) = tcSplitForAllTys ty (arg_tys, io_res_ty) = tcSplitFunTys fun_ty -- Must use tcSplit* functions because we want to @@ -243,7 +248,8 @@ dsPrimCall fn_id fcall = do let call_app = mkFCall ccall_uniq fcall (map Var args) io_res_ty rhs = mkLams tvs (mkLams args call_app) - return ([(fn_id, rhs)], empty, empty) + rhs' = Cast rhs co + return ([(fn_id, rhs')], empty, empty) \end{code} @@ -267,7 +273,8 @@ the user-written Haskell function `@M.foo@'. \begin{code} dsFExport :: Id -- Either the exported Id, -- or the foreign-export-dynamic constructor - -> Type -- The type of the thing callable from C + -> Coercion -- Coercion between the Haskell type callable + -- from C, and its representation type -> CLabelString -- The name to export to C land -> CCallConv -> Bool -- True => foreign export dynamic @@ -279,8 +286,9 @@ dsFExport :: Id -- Either the exported Id, , Int -- size of args to stub function ) -dsFExport fn_id ty ext_name cconv isDyn= do +dsFExport fn_id co ext_name cconv isDyn = do let + ty = pSnd $ coercionKind co (_tvs,sans_foralls) = tcSplitForAllTys ty (fe_arg_tys', orig_res_ty) = tcSplitFunTys sans_foralls -- We must use tcSplits here, because we want to see @@ -294,9 +302,8 @@ dsFExport fn_id ty ext_name cconv isDyn= do (res_ty, -- t is_IO_res_ty) <- -- Bool case tcSplitIOType_maybe orig_res_ty of - Just (_ioTyCon, res_ty, _co) -> return (res_ty, True) + Just (_ioTyCon, res_ty) -> return (res_ty, True) -- The function already returns IO t - -- ToDo: what about the coercion? Nothing -> return (orig_res_ty, False) -- The function returns t @@ -339,9 +346,10 @@ f_helper(StablePtr s, HsBool b, HsInt i) \begin{code} dsFExportDynamic :: Id + -> Coercion -> CCallConv -> DsM ([Binding], SDoc, SDoc) -dsFExportDynamic id cconv = do +dsFExportDynamic id co0 cconv = do fe_id <- newSysLocalDs ty mod <- getModuleDs let @@ -356,7 +364,7 @@ dsFExportDynamic id cconv = do export_ty = mkFunTy stable_ptr_ty arg_ty bindIOId <- dsLookupGlobalId bindIOName stbl_value <- newSysLocalDs stable_ptr_ty - (h_code, c_code, typestring, args_size) <- dsFExport id export_ty fe_nm cconv True + (h_code, c_code, typestring, args_size) <- dsFExport id (Refl export_ty) fe_nm cconv True let {- The arguments to the external function which will @@ -386,7 +394,6 @@ dsFExportDynamic id cconv = do let io_app = mkLams tvs $ Lam cback $ - mkCoerce (mkSymCo co) $ mkApps (Var bindIOId) [ Type stable_ptr_ty , Type res_ty @@ -394,19 +401,18 @@ dsFExportDynamic id cconv = do , Lam stbl_value ccall_adj ] - fed = (id `setInlineActivation` NeverActive, io_app) + fed = (id `setInlineActivation` NeverActive, Cast io_app co0) -- Never inline the f.e.d. function, because the litlit -- might not be in scope in other modules. return ([fed], h_code, c_code) where - ty = idType id + ty = pFst (coercionKind co0) (tvs,sans_foralls) = tcSplitForAllTys ty ([arg_ty], fn_res_ty) = tcSplitFunTys sans_foralls - Just (io_tc, res_ty, co) = tcSplitIOType_maybe fn_res_ty + Just (io_tc, res_ty) = tcSplitIOType_maybe fn_res_ty -- Must have an IO type; hence Just - -- co : fn_res_ty ~ IO res_ty toCName :: Id -> String toCName i = showSDoc (pprCode CStyle (ppr (idName i))) diff --git a/compiler/deSugar/DsMeta.hs b/compiler/deSugar/DsMeta.hs index 0551a1a2db..ff104c3f4f 100644 --- a/compiler/deSugar/DsMeta.hs +++ b/compiler/deSugar/DsMeta.hs @@ -324,7 +324,7 @@ repInstD' (L loc (InstDecl ty binds _ ats)) -- Ignore user pragmas for now Just (tvs, cxt, cls, tys) = splitHsInstDeclTy_maybe (unLoc ty) repForD :: Located (ForeignDecl Name) -> DsM (SrcSpan, Core TH.DecQ) -repForD (L loc (ForeignImport name typ (CImport cc s ch cis))) +repForD (L loc (ForeignImport name typ _ (CImport cc s ch cis))) = do MkC name' <- lookupLOcc name MkC typ' <- repLTy typ MkC cc' <- repCCallConv cc diff --git a/compiler/hsSyn/Convert.lhs b/compiler/hsSyn/Convert.lhs index b1fd047421..5ece574e25 100644 --- a/compiler/hsSyn/Convert.lhs +++ b/compiler/hsSyn/Convert.lhs @@ -370,7 +370,7 @@ cvtForD (ImportF callconv safety from nm ty) (mkFastString (TH.nameBase nm)) from = do { nm' <- vNameL nm ; ty' <- cvtType ty - ; return (ForeignImport nm' ty' impspec) + ; return (ForeignImport nm' ty' noForeignImportCoercionYet impspec) } | otherwise = failWith $ text (show from) <+> ptext (sLit "is not a valid ccall impent") @@ -384,7 +384,7 @@ cvtForD (ExportF callconv as nm ty) = do { nm' <- vNameL nm ; ty' <- cvtType ty ; let e = CExport (CExportStatic (mkFastString as) (cvt_conv callconv)) - ; return $ ForeignExport nm' ty' e } + ; return $ ForeignExport nm' ty' noForeignExportCoercionYet e } cvt_conv :: TH.Callconv -> CCallConv cvt_conv TH.CCall = CCallConv diff --git a/compiler/hsSyn/HsDecls.lhs b/compiler/hsSyn/HsDecls.lhs index deb72edc96..6686ef1033 100644 --- a/compiler/hsSyn/HsDecls.lhs +++ b/compiler/hsSyn/HsDecls.lhs @@ -35,6 +35,7 @@ module HsDecls ( SpliceDecl(..), -- ** Foreign function interface declarations ForeignDecl(..), LForeignDecl, ForeignImport(..), ForeignExport(..), + noForeignImportCoercionYet, noForeignExportCoercionYet, CImportSpec(..), -- ** Data-constructor declarations ConDecl(..), LConDecl, ResType(..), @@ -64,6 +65,7 @@ import NameSet import Name import {- Kind parts of -} Type import BasicTypes +import Coercion import ForeignCall -- others: @@ -911,9 +913,31 @@ instance (OutputableBndr name) type LForeignDecl name = Located (ForeignDecl name) data ForeignDecl name - = ForeignImport (Located name) (LHsType name) ForeignImport -- defines name - | ForeignExport (Located name) (LHsType name) ForeignExport -- uses name + = ForeignImport (Located name) -- defines this name + (LHsType name) -- sig_ty + Coercion -- rep_ty ~ sig_ty + ForeignImport + | ForeignExport (Located name) -- uses this name + (LHsType name) -- sig_ty + Coercion -- sig_ty ~ rep_ty + ForeignExport deriving (Data, Typeable) +{- + In both ForeignImport and ForeignExport: + sig_ty is the type given in the Haskell code + rep_ty is the representation for this type, i.e. with newtypes + coerced away and type functions evaluated. + Thus if the declaration is valid, then rep_ty will only use types + such as Int and IO that we know how to make foreign calls with. +-} + +noForeignImportCoercionYet :: Coercion +noForeignImportCoercionYet + = panic "ForeignImport coercion evaluated before typechecking" + +noForeignExportCoercionYet :: Coercion +noForeignExportCoercionYet + = panic "ForeignExport coercion evaluated before typechecking" -- Specification Of an imported external entity in dependence on the calling -- convention @@ -956,10 +980,10 @@ data ForeignExport = CExport CExportSpec -- contains the calling convention -- instance OutputableBndr name => Outputable (ForeignDecl name) where - ppr (ForeignImport n ty fimport) = + ppr (ForeignImport n ty _ fimport) = hang (ptext (sLit "foreign import") <+> ppr fimport <+> ppr n) 2 (dcolon <+> ppr ty) - ppr (ForeignExport n ty fexport) = + ppr (ForeignExport n ty _ fexport) = hang (ptext (sLit "foreign export") <+> ppr fexport <+> ppr n) 2 (dcolon <+> ppr ty) diff --git a/compiler/hsSyn/HsUtils.lhs b/compiler/hsSyn/HsUtils.lhs index 3451e4ce6c..3b520c0c9e 100644 --- a/compiler/hsSyn/HsUtils.lhs +++ b/compiler/hsSyn/HsUtils.lhs @@ -610,7 +610,7 @@ hsGroupBinders (HsGroup { hs_valds = val_decls, hs_tyclds = tycl_decls, hsForeignDeclsBinders :: [LForeignDecl Name] -> [Name] hsForeignDeclsBinders foreign_decls - = [n | L _ (ForeignImport (L _ n) _ _) <- foreign_decls] + = [n | L _ (ForeignImport (L _ n) _ _ _) <- foreign_decls] hsTyClDeclsBinders :: [[LTyClDecl Name]] -> [Located (InstDecl Name)] -> [Name] hsTyClDeclsBinders tycl_decls inst_decls diff --git a/compiler/parser/RdrHsSyn.lhs b/compiler/parser/RdrHsSyn.lhs index 25ed3c2888..2521eec564 100644 --- a/compiler/parser/RdrHsSyn.lhs +++ b/compiler/parser/RdrHsSyn.lhs @@ -920,12 +920,12 @@ mkImport cconv safety (L loc entity, v, ty) | cconv == PrimCallConv = do let funcTarget = CFunction (StaticTarget entity Nothing) importSpec = CImport PrimCallConv safety nilFS funcTarget - return (ForD (ForeignImport v ty importSpec)) + return (ForD (ForeignImport v ty noForeignImportCoercionYet importSpec)) | otherwise = do case parseCImport cconv safety (mkExtName (unLoc v)) (unpackFS entity) of Nothing -> parseErrorSDoc loc (text "Malformed entity string") - Just importSpec -> return (ForD (ForeignImport v ty importSpec)) + Just importSpec -> return (ForD (ForeignImport v ty noForeignImportCoercionYet importSpec)) -- the string "foo" is ambigous: either a header or a C identifier. The -- C identifier case comes first in the alternatives below, so we pick @@ -970,7 +970,7 @@ mkExport :: CCallConv -> (Located FastString, Located RdrName, LHsType RdrName) -> P (HsDecl RdrName) mkExport cconv (L _ entity, v, ty) = return $ - ForD (ForeignExport v ty (CExport (CExportStatic entity' cconv))) + ForD (ForeignExport v ty noForeignExportCoercionYet (CExport (CExportStatic entity' cconv))) where entity' | nullFS entity = mkExtName (unLoc v) | otherwise = entity diff --git a/compiler/rename/RnNames.lhs b/compiler/rename/RnNames.lhs index fa8a993ec0..eeaae149a3 100644 --- a/compiler/rename/RnNames.lhs +++ b/compiler/rename/RnNames.lhs @@ -521,7 +521,7 @@ getLocalNonValBinders fixity_env ; return (envs, new_bndrs) } } where for_hs_bndrs :: [Located RdrName] - for_hs_bndrs = [nm | L _ (ForeignImport nm _ _) <- foreign_decls] + for_hs_bndrs = [nm | L _ (ForeignImport nm _ _ _) <- foreign_decls] -- In a hs-boot file, the value binders come from the -- *signatures*, and there should be no foreign binders diff --git a/compiler/rename/RnSource.lhs b/compiler/rename/RnSource.lhs index fc74b25cc2..79876caaf4 100644 --- a/compiler/rename/RnSource.lhs +++ b/compiler/rename/RnSource.lhs @@ -363,7 +363,7 @@ rnDefaultDecl (DefaultDecl tys) \begin{code} rnHsForeignDecl :: ForeignDecl RdrName -> RnM (ForeignDecl Name, FreeVars) -rnHsForeignDecl (ForeignImport name ty spec) +rnHsForeignDecl (ForeignImport name ty _ spec) = do { topEnv :: HscEnv <- getTopEnv ; name' <- lookupLocatedTopBndrRn name ; (ty', fvs) <- rnHsTypeFVs (fo_decl_msg name) ty @@ -372,12 +372,12 @@ rnHsForeignDecl (ForeignImport name ty spec) ; let packageId = thisPackage $ hsc_dflags topEnv spec' = patchForeignImport packageId spec - ; return (ForeignImport name' ty' spec', fvs) } + ; return (ForeignImport name' ty' noForeignImportCoercionYet spec', fvs) } -rnHsForeignDecl (ForeignExport name ty spec) +rnHsForeignDecl (ForeignExport name ty _ spec) = do { name' <- lookupLocatedOccRn name ; (ty', fvs) <- rnHsTypeFVs (fo_decl_msg name) ty - ; return (ForeignExport name' ty' spec, fvs `addOneFV` unLoc name') } + ; return (ForeignExport name' ty' noForeignExportCoercionYet spec, fvs `addOneFV` unLoc name') } -- NB: a foreign export is an *occurrence site* for name, so -- we add it to the free-variable list. It might, for example, -- be imported from another module diff --git a/compiler/typecheck/TcForeign.lhs b/compiler/typecheck/TcForeign.lhs index d691eec6f0..0f713f390a 100644 --- a/compiler/typecheck/TcForeign.lhs +++ b/compiler/typecheck/TcForeign.lhs @@ -27,6 +27,10 @@ import TcHsType import TcExpr import TcEnv +import FamInst +import FamInstEnv +import Type +import TypeRep import ForeignCall import ErrUtils import Id @@ -48,13 +52,94 @@ import Util \begin{code} -- Defines a binding isForeignImport :: LForeignDecl name -> Bool -isForeignImport (L _ (ForeignImport _ _ _)) = True -isForeignImport _ = False +isForeignImport (L _ (ForeignImport _ _ _ _)) = True +isForeignImport _ = False -- Exports a binding isForeignExport :: LForeignDecl name -> Bool -isForeignExport (L _ (ForeignExport _ _ _)) = True -isForeignExport _ = False +isForeignExport (L _ (ForeignExport _ _ _ _)) = True +isForeignExport _ = False +\end{code} + +\begin{code} +-- normaliseFfiType takes the type from an FFI declaration, and +-- evaluates any type synonyms, type functions, and newtypes. However, +-- we are only allowed to look through newtypes if the constructor is +-- in scope. +normaliseFfiType :: Type -> TcM (Coercion, Type) +normaliseFfiType ty + = do fam_envs <- tcGetFamInstEnvs + normaliseFfiType' fam_envs ty + +normaliseFfiType' :: FamInstEnvs -> Type -> TcM (Coercion, Type) +normaliseFfiType' env ty0 = go [] ty0 + where + go :: [TyCon] -> Type -> TcM (Coercion, Type) + go rec_nts ty | Just ty' <- coreView ty -- Expand synonyms + = go rec_nts ty' + + go rec_nts ty@(TyConApp tc tys) + -- We don't want to look through the IO newtype, even if it is + -- in scope, so we have a special case for it: + | tc `hasKey` ioTyConKey + = children_only + | isNewTyCon tc -- Expand newtypes + -- We can't just use isRecursiveTyCon here, as we need to allow + -- some recursive types as described below + = if tc `elem` rec_nts -- See Note [Expanding newtypes] in Type.lhs + then -- If this is a recursive newtype then it will normally + -- be rejected later as not being a valid FFI type. + -- Sometimes recursion is OK though, e.g. with + -- newtype T = T (Ptr T) + -- we don't reject the type for being recursive. + return (Refl ty, ty) + else do newtypeOK <- do env <- getGblEnv + case tyConSingleDataCon_maybe tc of + Just dataCon -> + return $ notNull $ lookupGRE_Name (tcg_rdr_env env) $ dataConName dataCon + _ -> + return False + let newtypeForeign = nameModule_maybe (tyConName tc) `elem` + [Just (mkBaseModule (fsLit "Foreign.C.Types")), + Just (mkBaseModule (fsLit "System.Posix.Types"))] + if newtypeOK || newtypeForeign + then do let nt_co = mkAxInstCo (newTyConCo tc) tys + add_co nt_co rec_nts' nt_rhs + else children_only + | isFamilyTyCon tc -- Expand open tycons + , (co, ty) <- normaliseTcApp env tc tys + , not (isReflCo co) + = add_co co rec_nts ty + | otherwise + = children_only + where + children_only = do xs <- mapM (go rec_nts) tys + let (cos, tys') = unzip xs + return (mkTyConAppCo tc cos, mkTyConApp tc tys') + nt_rhs = newTyConInstRhs tc tys + rec_nts' | isRecursiveTyCon tc = tc:rec_nts + | otherwise = rec_nts + + go rec_nts (AppTy ty1 ty2) + = do (coi1, nty1) <- go rec_nts ty1 + (coi2, nty2) <- go rec_nts ty2 + return (mkAppCo coi1 coi2, mkAppTy nty1 nty2) + + go rec_nts (FunTy ty1 ty2) + = do (coi1,nty1) <- go rec_nts ty1 + (coi2,nty2) <- go rec_nts ty2 + return (mkFunCo coi1 coi2, mkFunTy nty1 nty2) + + go rec_nts (ForAllTy tyvar ty1) + = do (coi,nty1) <- go rec_nts ty1 + return (mkForAllCo tyvar coi, ForAllTy tyvar nty1) + + go _ ty@(TyVarTy _) + = return (Refl ty, ty) + + add_co co rec_nts ty + = do (co', ty') <- go rec_nts ty + return (mkTransCo co co', ty') \end{code} %************************************************************************ @@ -69,13 +154,14 @@ tcForeignImports decls = mapAndUnzipM (wrapLocSndM tcFImport) (filter isForeignImport decls) tcFImport :: ForeignDecl Name -> TcM (Id, ForeignDecl Id) -tcFImport fo@(ForeignImport (L loc nm) hs_ty imp_decl) +tcFImport fo@(ForeignImport (L loc nm) hs_ty _ imp_decl) = addErrCtxt (foreignDeclCtxt fo) $ do { sig_ty <- tcHsSigType (ForSigCtxt nm) hs_ty + ; (norm_co, norm_sig_ty) <- normaliseFfiType sig_ty ; let -- Drop the foralls before inspecting the -- structure of the foreign type. - (_, t_ty) = tcSplitForAllTys sig_ty + (_, t_ty) = tcSplitForAllTys norm_sig_ty (arg_tys, res_ty) = tcSplitFunTys t_ty id = mkLocalId nm sig_ty -- Use a LocalId to obey the invariant that locally-defined @@ -85,7 +171,7 @@ tcFImport fo@(ForeignImport (L loc nm) hs_ty imp_decl) ; imp_decl' <- tcCheckFIType sig_ty arg_tys res_ty imp_decl -- Can't use sig_ty here because sig_ty :: Type and -- we need HsType Id hence the undefined - ; return (id, ForeignImport (L loc id) undefined imp_decl') } + ; return (id, ForeignImport (L loc id) undefined (mkSymCo norm_co) imp_decl') } tcFImport d = pprPanic "tcFImport" (ppr d) \end{code} @@ -198,13 +284,15 @@ tcForeignExports decls return (b `consBag` binds, f:fs) tcFExport :: ForeignDecl Name -> TcM (LHsBind Id, ForeignDecl Id) -tcFExport fo@(ForeignExport (L loc nm) hs_ty spec) +tcFExport fo@(ForeignExport (L loc nm) hs_ty _ spec) = addErrCtxt (foreignDeclCtxt fo) $ do sig_ty <- tcHsSigType (ForSigCtxt nm) hs_ty rhs <- tcPolyExpr (nlHsVar nm) sig_ty - tcCheckFEType sig_ty spec + (norm_co, norm_sig_ty) <- normaliseFfiType sig_ty + + tcCheckFEType norm_sig_ty spec -- we're exporting a function, but at a type possibly more -- constrained than its declared/inferred type. Hence the need @@ -216,7 +304,7 @@ tcFExport fo@(ForeignExport (L loc nm) hs_ty spec) -- is *stable* (i.e. the compiler won't change it later), -- because this name will be referred to by the C code stub. id <- mkStableIdFromName nm sig_ty loc mkForeignExportOcc - return (mkVarBind id rhs, ForeignExport (L loc id) undefined spec) + return (mkVarBind id rhs, ForeignExport (L loc id) undefined norm_co spec) tcFExport d = pprPanic "tcFExport" (ppr d) \end{code} @@ -264,49 +352,15 @@ nonIOok = True mustBeIO = False checkForeignRes non_io_result_ok safehs_check pred_res_ty ty - -- (IO t) is ok, and so is any newtype wrapping thereof - = do m <- tcSplitVisibleIOType_maybe ty - case m of - Just (_, res_ty, _) - | pred_res_ty res_ty -> - return () - _ -> - check (non_io_result_ok && pred_res_ty ty) - (illegalForeignTyErr result ty $+$ safeHsErr safehs_check) - --- This is mostly a copy of TcType.tcSplitIOType_maybe, except it checks --- that it doesn't look through any newtypes for which the constructor --- is not exported. -tcSplitVisibleIOType_maybe :: Type -> TcM (Maybe (TyCon, Type, Coercion)) -tcSplitVisibleIOType_maybe ty - = case tcSplitTyConApp_maybe ty of - -- This split absolutely has to be a tcSplit, because we must - -- see the IO type; and it's a newtype which is transparent to - -- splitTyConApp. - - Just (io_tycon, [io_res_ty]) - | io_tycon `hasKey` ioTyConKey - -> return $ Just (io_tycon, io_res_ty, mkReflCo ty) - - Just (tc, tys) - | not (isRecursiveTyCon tc) - , Just (ty, co1) <- instNewTyCon_maybe tc tys - -- Newtypes that require a coercion are ok - -> do newtypeOK <- do env <- getGblEnv - case tyConSingleDataCon_maybe tc of - Just dataCon -> - return $ notNull $ lookupGRE_Name (tcg_rdr_env env) $ dataConName dataCon - Nothing -> - return False - if newtypeOK - then do m <- tcSplitVisibleIOType_maybe ty - return $ case m of - Nothing -> Nothing - Just (tc, ty', co2) -> Just (tc, ty', co1 `mkTransCo` co2) - else return Nothing - - _ -> return Nothing - + -- We need an (IO t) result. Any newtype wrappers of type functions + -- have already been dealt with by normaliseFfiType. + = case tcSplitIOType_maybe ty of + Just (_, res_ty) + | pred_res_ty res_ty -> + return () + _ -> + check (non_io_result_ok && pred_res_ty ty) + (illegalForeignTyErr result ty $+$ safeHsErr safehs_check) \end{code} \begin{code} diff --git a/compiler/typecheck/TcHsSyn.lhs b/compiler/typecheck/TcHsSyn.lhs index e35dafb1b2..fa97c9753d 100644 --- a/compiler/typecheck/TcHsSyn.lhs +++ b/compiler/typecheck/TcHsSyn.lhs @@ -964,8 +964,8 @@ zonkForeignExports :: ZonkEnv -> [LForeignDecl TcId] -> TcM [LForeignDecl Id] zonkForeignExports env ls = mappM (wrapLocM (zonkForeignExport env)) ls zonkForeignExport :: ZonkEnv -> ForeignDecl TcId -> TcM (ForeignDecl Id) -zonkForeignExport env (ForeignExport i _hs_ty spec) = - returnM (ForeignExport (fmap (zonkIdOcc env) i) undefined spec) +zonkForeignExport env (ForeignExport i _hs_ty co spec) = + returnM (ForeignExport (fmap (zonkIdOcc env) i) undefined co spec) zonkForeignExport _ for_imp = returnM for_imp -- Foreign imports don't need zonking \end{code} diff --git a/compiler/typecheck/TcType.lhs b/compiler/typecheck/TcType.lhs index cc6eac0d36..fcfaf882d7 100644 --- a/compiler/typecheck/TcType.lhs +++ b/compiler/typecheck/TcType.lhs @@ -1230,28 +1230,17 @@ restricted set of types as arguments and results (the restricting factor being the ) \begin{code} -tcSplitIOType_maybe :: Type -> Maybe (TyCon, Type, Coercion) --- (isIOType t) returns Just (IO,t',co) --- if co : t ~ IO t' --- returns Nothing otherwise -tcSplitIOType_maybe ty +tcSplitIOType_maybe :: Type -> Maybe (TyCon, Type) +-- (tcSplitIOType_maybe t) returns Just (IO,t',co) +-- if co : t ~ IO t' +-- returns Nothing otherwise +tcSplitIOType_maybe ty = case tcSplitTyConApp_maybe ty of - -- This split absolutely has to be a tcSplit, because we must - -- see the IO type; and it's a newtype which is transparent to splitTyConApp. - - Just (io_tycon, [io_res_ty]) - | io_tycon `hasKey` ioTyConKey - -> Just (io_tycon, io_res_ty, mkReflCo ty) - - Just (tc, tys) - | not (isRecursiveTyCon tc) - , Just (ty, co1) <- instNewTyCon_maybe tc tys - -- Newtypes that require a coercion are ok - -> case tcSplitIOType_maybe ty of - Nothing -> Nothing - Just (tc, ty', co2) -> Just (tc, ty', co1 `mkTransCo` co2) - - _ -> Nothing + Just (io_tycon, [io_res_ty]) + | io_tycon `hasKey` ioTyConKey -> + Just (io_tycon, io_res_ty) + _ -> + Nothing isFFITy :: Type -> Bool -- True for any TyCon that can possibly be an arg or result of an FFI call @@ -1318,20 +1307,15 @@ isFFIDotnetObjTy ty isFunPtrTy :: Type -> Bool isFunPtrTy = checkRepTyConKey [funPtrTyConKey] +-- normaliseFfiType gets run before checkRepTyCon, so we don't +-- need to worry about looking through newtypes or type functions +-- here; that's already been taken care of. checkRepTyCon :: (TyCon -> Bool) -> Type -> Bool --- Look through newtypes, but *not* foralls --- Should work even for recursive newtypes --- eg Manuel had: newtype T = MkT (Ptr T) checkRepTyCon check_tc ty - = go emptyNameSet ty - where - go rec_nts ty - | Just (tc,tys) <- splitTyConApp_maybe ty - = case carefullySplitNewType_maybe rec_nts tc tys of - Just (rec_nts', ty') -> go rec_nts' ty' - Nothing -> check_tc tc - | otherwise - = False + | Just (tc, _) <- splitTyConApp_maybe ty + = check_tc tc + | otherwise + = False checkRepTyConKey :: [Unique] -> Type -> Bool -- Like checkRepTyCon, but just looks at the TyCon key diff --git a/compiler/types/FamInstEnv.lhs b/compiler/types/FamInstEnv.lhs index ab99e9f8e5..07a15dd644 100644 --- a/compiler/types/FamInstEnv.lhs +++ b/compiler/types/FamInstEnv.lhs @@ -17,7 +17,7 @@ module FamInstEnv ( lookupFamInstEnv, lookupFamInstEnvConflicts, lookupFamInstEnvConflicts', -- Normalisation - topNormaliseType, normaliseType + topNormaliseType, normaliseType, normaliseTcApp ) where #include "HsVersions.h" |