futhark-0.21.7: src/Language/Futhark/TypeChecker.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
-- | The type checker checks whether the program is type-consistent
-- and adds type annotations and various other elaborations. The
-- program does not need to have any particular properties for the
-- type checker to function; in particular it does not need unique
-- names.
module Language.Futhark.TypeChecker
( checkProg,
checkExp,
checkDec,
checkModExp,
TypeError,
Warnings,
initialEnv,
envWithImports,
)
where
import Control.Monad.Except
import Control.Monad.Writer hiding (Sum)
import Data.Bifunctor (first, second)
import Data.Char (isAlpha, isAlphaNum)
import Data.Either
import Data.List (isPrefixOf)
import qualified Data.Map.Strict as M
import Data.Maybe
import Data.Ord
import qualified Data.Set as S
import Futhark.FreshNames hiding (newName)
import Futhark.Util.Pretty hiding (space)
import Language.Futhark
import Language.Futhark.Semantic
import Language.Futhark.TypeChecker.Modules
import Language.Futhark.TypeChecker.Monad
import Language.Futhark.TypeChecker.Terms
import Language.Futhark.TypeChecker.Types
import Prelude hiding (abs, mod)
--- The main checker
-- | Type check a program containing no type information, yielding
-- either a type error or a program with complete type information.
-- Accepts a mapping from file names (excluding extension) to
-- previously type checked results. The 'ImportName' is used to resolve
-- relative @import@s.
checkProg ::
Imports ->
VNameSource ->
ImportName ->
UncheckedProg ->
(Warnings, Either TypeError (FileModule, VNameSource))
checkProg files src name prog =
runTypeM initialEnv files' name src $ checkProgM prog
where
files' = M.map fileEnv $ M.fromList files
-- | Type check a single expression containing no type information,
-- yielding either a type error or the same expression annotated with
-- type information. Also returns a list of type parameters, which
-- will be nonempty if the expression is polymorphic. See also
-- 'checkProg'.
checkExp ::
Imports ->
VNameSource ->
Env ->
UncheckedExp ->
(Warnings, Either TypeError ([TypeParam], Exp))
checkExp files src env e =
second (fmap fst) $ runTypeM env files' (mkInitialImport "") src $ checkOneExp e
where
files' = M.map fileEnv $ M.fromList files
-- | Type check a single declaration containing no type information,
-- yielding either a type error or the same declaration annotated with
-- type information along the Env produced by that declaration. See
-- also 'checkProg'.
checkDec ::
Imports ->
VNameSource ->
Env ->
ImportName ->
UncheckedDec ->
(Warnings, Either TypeError (Env, Dec, VNameSource))
checkDec files src env name d =
second (fmap massage) $
runTypeM env files' name src $ do
(_, env', d') <- checkOneDec d
return (env' <> env, d')
where
massage ((env', d'), src') =
(env', d', src')
files' = M.map fileEnv $ M.fromList files
-- | Type check a single module expression containing no type information,
-- yielding either a type error or the same expression annotated with
-- type information along the Env produced by that declaration. See
-- also 'checkProg'.
checkModExp ::
Imports ->
VNameSource ->
Env ->
ModExpBase NoInfo Name ->
(Warnings, Either TypeError (MTy, ModExpBase Info VName))
checkModExp files src env me =
second (fmap fst) . runTypeM env files' (mkInitialImport "") src $ do
(_abs, mty, me') <- checkOneModExp me
pure (mty, me')
where
files' = M.map fileEnv $ M.fromList files
-- | An initial environment for the type checker, containing
-- intrinsics and such.
initialEnv :: Env
initialEnv =
intrinsicsModule
{ envModTable = initialModTable,
envNameMap =
M.insert
(Term, nameFromString "intrinsics")
(qualName intrinsics_v)
topLevelNameMap
}
where
initialTypeTable = M.fromList $ mapMaybe addIntrinsicT $ M.toList intrinsics
initialModTable = M.singleton intrinsics_v (ModEnv intrinsicsModule)
intrinsics_v = VName (nameFromString "intrinsics") 0
intrinsicsModule = Env mempty initialTypeTable mempty mempty intrinsicsNameMap
addIntrinsicT (name, IntrinsicType l ps t) =
Just (name, TypeAbbr l ps $ RetType [] t)
addIntrinsicT _ =
Nothing
-- | Produce an environment, based on the one passed in, where all of
-- the provided imports have been @open@ened in order. This could in principle
-- also be done with 'checkDec', but this is more precise.
envWithImports :: Imports -> Env -> Env
envWithImports imports env =
mconcat (map (fileEnv . snd) (reverse imports)) <> env
checkProgM :: UncheckedProg -> TypeM FileModule
checkProgM (Prog doc decs) = do
checkForDuplicateDecs decs
(abs, env, decs') <- checkDecs decs
return (FileModule abs env $ Prog doc decs')
dupDefinitionError ::
MonadTypeChecker m =>
Namespace ->
Name ->
SrcLoc ->
SrcLoc ->
m a
dupDefinitionError space name loc1 loc2 =
typeError loc1 mempty $
"Duplicate definition of" <+> ppr space
<+> pprName name <> ". Previously defined at"
<+> text (locStr loc2) <> "."
checkForDuplicateDecs :: [DecBase NoInfo Name] -> TypeM ()
checkForDuplicateDecs =
foldM_ (flip f) mempty
where
check namespace name loc known =
case M.lookup (namespace, name) known of
Just loc' ->
dupDefinitionError namespace name loc loc'
_ -> return $ M.insert (namespace, name) loc known
f (ValDec vb) =
check Term (valBindName vb) (srclocOf vb)
f (TypeDec (TypeBind name _ _ _ _ _ loc)) =
check Type name loc
f (SigDec (SigBind name _ _ loc)) =
check Signature name loc
f (ModDec (ModBind name _ _ _ _ loc)) =
check Term name loc
f OpenDec {} = return
f LocalDec {} = return
f ImportDec {} = return
bindingTypeParams :: [TypeParam] -> TypeM a -> TypeM a
bindingTypeParams tparams = localEnv env
where
env = mconcat $ map typeParamEnv tparams
typeParamEnv (TypeParamDim v _) =
mempty
{ envVtable =
M.singleton v $ BoundV [] (Scalar $ Prim $ Signed Int64)
}
typeParamEnv (TypeParamType l v _) =
mempty
{ envTypeTable =
M.singleton v $
TypeAbbr l [] . RetType [] . Scalar $
TypeVar () Nonunique (typeName v) []
}
checkTypeDecl ::
TypeDeclBase NoInfo Name ->
TypeM ([VName], TypeDeclBase Info VName, Liftedness)
checkTypeDecl (TypeDecl te NoInfo) = do
(te', svars, RetType dims st, l) <- checkTypeExp te
pure (svars ++ dims, TypeDecl te' $ Info st, l)
-- In this function, after the recursion, we add the Env of the
-- current Spec *after* the one that is returned from the recursive
-- call. This implements the behaviour that specs later in a module
-- type can override those earlier (it rarely matters, but it affects
-- the specific structure of substitutions in case some module type is
-- redundantly imported multiple times).
checkSpecs :: [SpecBase NoInfo Name] -> TypeM (TySet, Env, [SpecBase Info VName])
checkSpecs [] = return (mempty, mempty, [])
checkSpecs (ValSpec name tparams vtype doc loc : specs) =
bindSpaced [(Term, name)] $ do
name' <- checkName Term name loc
(tparams', vtype') <-
checkTypeParams tparams $ \tparams' -> bindingTypeParams tparams' $ do
(ext, vtype', _) <- checkTypeDecl vtype
unless (null ext) $
typeError loc mempty $
"All function parameters must have non-anonymous sizes."
</> "Hint: add size parameters to" <+> pquote (pprName name') <> "."
return (tparams', vtype')
let binding = BoundV tparams' $ unInfo $ expandedType vtype'
valenv =
mempty
{ envVtable = M.singleton name' binding,
envNameMap = M.singleton (Term, name) $ qualName name'
}
(abstypes, env, specs') <- localEnv valenv $ checkSpecs specs
return
( abstypes,
env <> valenv,
ValSpec name' tparams' vtype' doc loc : specs'
)
checkSpecs (TypeAbbrSpec tdec : specs) =
bindSpaced [(Type, typeAlias tdec)] $ do
(tenv, tdec') <- checkTypeBind tdec
(abstypes, env, specs') <- localEnv tenv $ checkSpecs specs
return
( abstypes,
env <> tenv,
TypeAbbrSpec tdec' : specs'
)
checkSpecs (TypeSpec l name ps doc loc : specs) =
checkTypeParams ps $ \ps' ->
bindSpaced [(Type, name)] $ do
name' <- checkName Type name loc
let tenv =
mempty
{ envNameMap =
M.singleton (Type, name) $ qualName name',
envTypeTable =
M.singleton name' $
TypeAbbr l ps' . RetType [] . Scalar $
TypeVar () Nonunique (typeName name') $
map typeParamToArg ps'
}
(abstypes, env, specs') <- localEnv tenv $ checkSpecs specs
return
( M.insert (qualName name') l abstypes,
env <> tenv,
TypeSpec l name' ps' doc loc : specs'
)
checkSpecs (ModSpec name sig doc loc : specs) =
bindSpaced [(Term, name)] $ do
name' <- checkName Term name loc
(_sig_abs, mty, sig') <- checkSigExp sig
let senv =
mempty
{ envNameMap = M.singleton (Term, name) $ qualName name',
envModTable = M.singleton name' $ mtyMod mty
}
(abstypes, env, specs') <- localEnv senv $ checkSpecs specs
return
( M.mapKeys (qualify name') (mtyAbs mty) <> abstypes,
env <> senv,
ModSpec name' sig' doc loc : specs'
)
checkSpecs (IncludeSpec e loc : specs) = do
(e_abs, env_abs, e_env, e') <- checkSigExpToEnv e
mapM_ (warnIfShadowing . fmap baseName) $ M.keys env_abs
(abstypes, env, specs') <- localEnv e_env $ checkSpecs specs
return
( e_abs <> env_abs <> abstypes,
env <> e_env,
IncludeSpec e' loc : specs'
)
where
warnIfShadowing qn =
(lookupType loc qn >> warnAbout qn)
`catchError` \_ -> return ()
warnAbout qn =
warn loc $ "Inclusion shadows type" <+> pquote (ppr qn) <+> "."
checkSigExp :: SigExpBase NoInfo Name -> TypeM (TySet, MTy, SigExpBase Info VName)
checkSigExp (SigParens e loc) = do
(abs, mty, e') <- checkSigExp e
return (abs, mty, SigParens e' loc)
checkSigExp (SigVar name NoInfo loc) = do
(name', mty) <- lookupMTy loc name
(mty', substs) <- newNamesForMTy mty
return (mtyAbs mty', mty', SigVar name' (Info substs) loc)
checkSigExp (SigSpecs specs loc) = do
checkForDuplicateSpecs specs
(abstypes, env, specs') <- checkSpecs specs
return (abstypes, MTy abstypes $ ModEnv env, SigSpecs specs' loc)
checkSigExp (SigWith s (TypeRef tname ps td trloc) loc) = do
(abs, s_abs, s_env, s') <- checkSigExpToEnv s
checkTypeParams ps $ \ps' -> do
(ext, td', _) <- bindingTypeParams ps' $ checkTypeDecl td
unless (null ext) $
typeError td' mempty "Anonymous dimensions are not allowed here."
(tname', s_abs', s_env') <- refineEnv loc s_abs s_env tname ps' $ unInfo $ expandedType td'
return (abs, MTy s_abs' $ ModEnv s_env', SigWith s' (TypeRef tname' ps' td' trloc) loc)
checkSigExp (SigArrow maybe_pname e1 e2 loc) = do
(e1_abs, MTy s_abs e1_mod, e1') <- checkSigExp e1
(env_for_e2, maybe_pname') <-
case maybe_pname of
Just pname -> bindSpaced [(Term, pname)] $ do
pname' <- checkName Term pname loc
return
( mempty
{ envNameMap = M.singleton (Term, pname) $ qualName pname',
envModTable = M.singleton pname' e1_mod
},
Just pname'
)
Nothing ->
return (mempty, Nothing)
(e2_abs, e2_mod, e2') <- localEnv env_for_e2 $ checkSigExp e2
return
( e1_abs <> e2_abs,
MTy mempty $ ModFun $ FunSig s_abs e1_mod e2_mod,
SigArrow maybe_pname' e1' e2' loc
)
checkSigExpToEnv ::
SigExpBase NoInfo Name ->
TypeM (TySet, TySet, Env, SigExpBase Info VName)
checkSigExpToEnv e = do
(abs, MTy mod_abs mod, e') <- checkSigExp e
case mod of
ModEnv env -> return (abs, mod_abs, env, e')
ModFun {} -> unappliedFunctor $ srclocOf e
checkSigBind :: SigBindBase NoInfo Name -> TypeM (TySet, Env, SigBindBase Info VName)
checkSigBind (SigBind name e doc loc) = do
(abs, env, e') <- checkSigExp e
bindSpaced [(Signature, name)] $ do
name' <- checkName Signature name loc
return
( abs,
mempty
{ envSigTable = M.singleton name' env,
envNameMap = M.singleton (Signature, name) (qualName name')
},
SigBind name' e' doc loc
)
checkOneModExp ::
ModExpBase NoInfo Name ->
TypeM (TySet, MTy, ModExpBase Info VName)
checkOneModExp (ModParens e loc) = do
(abs, mty, e') <- checkOneModExp e
return (abs, mty, ModParens e' loc)
checkOneModExp (ModDecs decs loc) = do
checkForDuplicateDecs decs
(abstypes, env, decs') <- checkDecs decs
return
( abstypes,
MTy abstypes $ ModEnv env,
ModDecs decs' loc
)
checkOneModExp (ModVar v loc) = do
(v', env) <- lookupMod loc v
when
( baseName (qualLeaf v') == nameFromString "intrinsics"
&& baseTag (qualLeaf v') <= maxIntrinsicTag
)
$ typeError loc mempty "The 'intrinsics' module may not be used in module expressions."
return (mempty, MTy mempty env, ModVar v' loc)
checkOneModExp (ModImport name NoInfo loc) = do
(name', env) <- lookupImport loc name
return
( mempty,
MTy mempty $ ModEnv env,
ModImport name (Info name') loc
)
checkOneModExp (ModApply f e NoInfo NoInfo loc) = do
(f_abs, f_mty, f') <- checkOneModExp f
case mtyMod f_mty of
ModFun functor -> do
(e_abs, e_mty, e') <- checkOneModExp e
(mty, psubsts, rsubsts) <- applyFunctor loc functor e_mty
return
( mtyAbs mty <> f_abs <> e_abs,
mty,
ModApply f' e' (Info psubsts) (Info rsubsts) loc
)
_ ->
typeError loc mempty "Cannot apply non-parametric module."
checkOneModExp (ModAscript me se NoInfo loc) = do
(me_abs, me_mod, me') <- checkOneModExp me
(se_abs, se_mty, se') <- checkSigExp se
match_subst <- badOnLeft $ matchMTys me_mod se_mty loc
return (se_abs <> me_abs, se_mty, ModAscript me' se' (Info match_subst) loc)
checkOneModExp (ModLambda param maybe_fsig_e body_e loc) =
withModParam param $ \param' param_abs param_mod -> do
(abs, maybe_fsig_e', body_e', mty) <-
checkModBody (fst <$> maybe_fsig_e) body_e loc
return
( abs,
MTy mempty $ ModFun $ FunSig param_abs param_mod mty,
ModLambda param' maybe_fsig_e' body_e' loc
)
checkOneModExpToEnv :: ModExpBase NoInfo Name -> TypeM (TySet, Env, ModExpBase Info VName)
checkOneModExpToEnv e = do
(e_abs, MTy abs mod, e') <- checkOneModExp e
case mod of
ModEnv env -> pure (e_abs <> abs, env, e')
ModFun {} -> unappliedFunctor $ srclocOf e
withModParam ::
ModParamBase NoInfo Name ->
(ModParamBase Info VName -> TySet -> Mod -> TypeM a) ->
TypeM a
withModParam (ModParam pname psig_e NoInfo loc) m = do
(_abs, MTy p_abs p_mod, psig_e') <- checkSigExp psig_e
bindSpaced [(Term, pname)] $ do
pname' <- checkName Term pname loc
let in_body_env = mempty {envModTable = M.singleton pname' p_mod}
localEnv in_body_env $
m (ModParam pname' psig_e' (Info $ map qualLeaf $ M.keys p_abs) loc) p_abs p_mod
withModParams ::
[ModParamBase NoInfo Name] ->
([(ModParamBase Info VName, TySet, Mod)] -> TypeM a) ->
TypeM a
withModParams [] m = m []
withModParams (p : ps) m =
withModParam p $ \p' pabs pmod ->
withModParams ps $ \ps' -> m $ (p', pabs, pmod) : ps'
checkModBody ::
Maybe (SigExpBase NoInfo Name) ->
ModExpBase NoInfo Name ->
SrcLoc ->
TypeM
( TySet,
Maybe (SigExp, Info (M.Map VName VName)),
ModExp,
MTy
)
checkModBody maybe_fsig_e body_e loc = enteringModule $ do
(body_e_abs, body_mty, body_e') <- checkOneModExp body_e
case maybe_fsig_e of
Nothing ->
return
( mtyAbs body_mty <> body_e_abs,
Nothing,
body_e',
body_mty
)
Just fsig_e -> do
(fsig_abs, fsig_mty, fsig_e') <- checkSigExp fsig_e
fsig_subst <- badOnLeft $ matchMTys body_mty fsig_mty loc
return
( fsig_abs <> body_e_abs,
Just (fsig_e', Info fsig_subst),
body_e',
fsig_mty
)
checkModBind :: ModBindBase NoInfo Name -> TypeM (TySet, Env, ModBindBase Info VName)
checkModBind (ModBind name [] maybe_fsig_e e doc loc) = do
(e_abs, maybe_fsig_e', e', mty) <- checkModBody (fst <$> maybe_fsig_e) e loc
bindSpaced [(Term, name)] $ do
name' <- checkName Term name loc
return
( e_abs,
mempty
{ envModTable = M.singleton name' $ mtyMod mty,
envNameMap = M.singleton (Term, name) $ qualName name'
},
ModBind name' [] maybe_fsig_e' e' doc loc
)
checkModBind (ModBind name (p : ps) maybe_fsig_e body_e doc loc) = do
(abs, params', maybe_fsig_e', body_e', funsig) <-
withModParam p $ \p' p_abs p_mod ->
withModParams ps $ \params_stuff -> do
let (ps', ps_abs, ps_mod) = unzip3 params_stuff
(abs, maybe_fsig_e', body_e', mty) <- checkModBody (fst <$> maybe_fsig_e) body_e loc
let addParam (x, y) mty' = MTy mempty $ ModFun $ FunSig x y mty'
return
( abs,
p' : ps',
maybe_fsig_e',
body_e',
FunSig p_abs p_mod $ foldr addParam mty $ zip ps_abs ps_mod
)
bindSpaced [(Term, name)] $ do
name' <- checkName Term name loc
pure
( abs,
mempty
{ envModTable =
M.singleton name' $ ModFun funsig,
envNameMap =
M.singleton (Term, name) $ qualName name'
},
ModBind name' params' maybe_fsig_e' body_e' doc loc
)
checkForDuplicateSpecs :: [SpecBase NoInfo Name] -> TypeM ()
checkForDuplicateSpecs =
foldM_ (flip f) mempty
where
check namespace name loc known =
case M.lookup (namespace, name) known of
Just loc' ->
dupDefinitionError namespace name loc loc'
_ -> return $ M.insert (namespace, name) loc known
f (ValSpec name _ _ _ loc) =
check Term name loc
f (TypeAbbrSpec (TypeBind name _ _ _ _ _ loc)) =
check Type name loc
f (TypeSpec _ name _ _ loc) =
check Type name loc
f (ModSpec name _ _ loc) =
check Term name loc
f IncludeSpec {} =
return
checkTypeBind ::
TypeBindBase NoInfo Name ->
TypeM (Env, TypeBindBase Info VName)
checkTypeBind (TypeBind name l tps te NoInfo doc loc) =
checkTypeParams tps $ \tps' -> do
(te', svars, RetType dims t, l') <- bindingTypeParams tps' $ checkTypeExp te
let elab_t = RetType (svars ++ dims) t
let used_dims = typeDimNames t
case filter ((`S.notMember` used_dims) . typeParamName) $
filter isSizeParam tps' of
[] -> return ()
tp : _ ->
typeError loc mempty $
"Size parameter" <+> pquote (ppr tp) <+> "unused."
case (l, l') of
(_, Lifted)
| l < Lifted ->
typeError loc mempty $
"Non-lifted type abbreviations may not contain functions."
</> "Hint: consider using 'type^'."
(_, SizeLifted)
| l < SizeLifted ->
typeError loc mempty $
"Non-size-lifted type abbreviations may not contain size-lifted types."
</> "Hint: consider using 'type~'."
(Unlifted, _)
| not $ null $ svars ++ dims ->
typeError loc mempty $
"Non-lifted type abbreviations may not use existential sizes in their definition."
</> "Hint: use 'type~' or add size parameters to"
<+> pquote (pprName name) <> "."
_ -> return ()
bindSpaced [(Type, name)] $ do
name' <- checkName Type name loc
return
( mempty
{ envTypeTable =
M.singleton name' $ TypeAbbr l tps' elab_t,
envNameMap =
M.singleton (Type, name) $ qualName name'
},
TypeBind name' l tps' te' (Info elab_t) doc loc
)
entryPoint :: [Pat] -> Maybe (TypeExp VName) -> StructRetType -> EntryPoint
entryPoint params orig_ret_te (RetType ret orig_ret) =
EntryPoint (map patternEntry params ++ more_params) rettype'
where
(more_params, rettype') = onRetType orig_ret_te $ first extToAny orig_ret
-- Since the entry point type is not a RetType but just a plain
-- StructType, we have to remove any existentially bound sizes.
extToAny (NamedDim v) | qualLeaf v `elem` ret = AnyDim Nothing
extToAny d = d
patternEntry (PatParens p _) =
patternEntry p
patternEntry (PatAscription p tdecl _) =
EntryParam (patternName p) $
EntryType (unInfo (expandedType tdecl)) (Just (declaredType tdecl))
patternEntry p =
EntryParam (patternName p) $
EntryType (patternStructType p) Nothing
patternName (Id x _ _) = baseName x
patternName (PatParens p _) = patternName p
patternName _ = "_"
pname (Named v) = baseName v
pname Unnamed = "_"
onRetType (Just (TEArrow p t1_te t2_te _)) (Scalar (Arrow _ _ t1 (RetType _ t2))) =
let (xs, y) = onRetType (Just t2_te) t2
in (EntryParam (maybe "_" baseName p) (EntryType t1 (Just t1_te)) : xs, y)
onRetType _ (Scalar (Arrow _ p t1 (RetType _ t2))) =
let (xs, y) = onRetType Nothing t2
in (EntryParam (pname p) (EntryType t1 Nothing) : xs, y)
onRetType te t =
([], EntryType t te)
entryPointNameIsAcceptable :: Name -> Bool
entryPointNameIsAcceptable = check . nameToString
where
check [] = True -- academic
check (c : cs) = isAlpha c && all constituent cs
constituent c = isAlphaNum c || c == '_'
checkValBind :: ValBindBase NoInfo Name -> TypeM (Env, ValBind)
checkValBind (ValBind entry fname maybe_tdecl NoInfo tparams params body doc attrs loc) = do
top_level <- atTopLevel
when (not top_level && isJust entry) $
typeError loc mempty $
withIndexLink "nested-entry" "Entry points may not be declared inside modules."
(fname', tparams', params', maybe_tdecl', rettype@(RetType _ rettype_t), body') <-
checkFunDef (fname, maybe_tdecl, tparams, params, body, loc)
let (rettype_params, rettype') = unfoldFunType rettype_t
entry' = Info (entryPoint params' maybe_tdecl' rettype) <$ entry
case entry' of
Just _
| not $ entryPointNameIsAcceptable fname ->
typeError loc mempty "Entry point names must start with a letter and contain only letters, digits, and underscores."
| any isTypeParam tparams' ->
typeError loc mempty "Entry point functions may not be polymorphic."
| not (all patternOrderZero params')
|| not (all orderZero rettype_params)
|| not (orderZero rettype') ->
typeError loc mempty "Entry point functions may not be higher-order."
| sizes_only_in_ret <-
S.fromList (map typeParamName tparams')
`S.intersection` typeDimNames rettype'
`S.difference` foldMap typeDimNames (map patternStructType params' ++ rettype_params),
not $ S.null sizes_only_in_ret ->
typeError loc mempty "Entry point functions must not be size-polymorphic in their return type."
| p : _ <- filter nastyParameter params' ->
warn loc $
"Entry point parameter\n"
</> indent 2 (ppr p)
</> "\nwill have an opaque type, so the entry point will likely not be callable."
| nastyReturnType maybe_tdecl' rettype_t ->
warn loc $
"Entry point return type\n"
</> indent 2 (ppr rettype)
</> "\nwill have an opaque type, so the result will likely not be usable."
_ -> return ()
attrs' <- mapM checkAttr attrs
let vb = ValBind entry' fname' maybe_tdecl' (Info rettype) tparams' params' body' doc attrs' loc
return
( mempty
{ envVtable =
M.singleton fname' $ uncurry BoundV $ valBindTypeScheme vb,
envNameMap =
M.singleton (Term, fname) $ qualName fname'
},
vb
)
nastyType :: Monoid als => TypeBase dim als -> Bool
nastyType (Scalar Prim {}) = False
nastyType t@Array {} = nastyType $ stripArray 1 t
nastyType _ = True
nastyReturnType :: Monoid als => Maybe (TypeExp VName) -> TypeBase dim als -> Bool
nastyReturnType Nothing (Scalar (Arrow _ _ t1 (RetType _ t2))) =
nastyType t1 || nastyReturnType Nothing t2
nastyReturnType (Just (TEArrow _ te1 te2 _)) (Scalar (Arrow _ _ t1 (RetType _ t2))) =
(not (niceTypeExp te1) && nastyType t1)
|| nastyReturnType (Just te2) t2
nastyReturnType (Just te) _
| niceTypeExp te = False
nastyReturnType te t
| Just ts <- isTupleRecord t =
case te of
Just (TETuple tes _) -> or $ zipWith nastyType' (map Just tes) ts
_ -> any nastyType ts
| otherwise = nastyType' te t
where
nastyType' (Just te') _ | niceTypeExp te' = False
nastyType' _ t' = nastyType t'
nastyParameter :: Pat -> Bool
nastyParameter p = nastyType (patternType p) && not (ascripted p)
where
ascripted (PatAscription _ (TypeDecl te _) _) = niceTypeExp te
ascripted (PatParens p' _) = ascripted p'
ascripted _ = False
niceTypeExp :: TypeExp VName -> Bool
niceTypeExp (TEVar (QualName [] _) _) = True
niceTypeExp (TEApply te TypeArgExpDim {} _) = niceTypeExp te
niceTypeExp (TEArray te _ _) = niceTypeExp te
niceTypeExp (TEUnique te _) = niceTypeExp te
niceTypeExp _ = False
checkOneDec :: DecBase NoInfo Name -> TypeM (TySet, Env, DecBase Info VName)
checkOneDec (ModDec struct) = do
(abs, modenv, struct') <- checkModBind struct
return (abs, modenv, ModDec struct')
checkOneDec (SigDec sig) = do
(abs, sigenv, sig') <- checkSigBind sig
return (abs, sigenv, SigDec sig')
checkOneDec (TypeDec tdec) = do
(tenv, tdec') <- checkTypeBind tdec
return (mempty, tenv, TypeDec tdec')
checkOneDec (OpenDec x loc) = do
(x_abs, x_env, x') <- checkOneModExpToEnv x
return (x_abs, x_env, OpenDec x' loc)
checkOneDec (LocalDec d loc) = do
(abstypes, env, d') <- checkOneDec d
return (abstypes, env, LocalDec d' loc)
checkOneDec (ImportDec name NoInfo loc) = do
(name', env) <- lookupImport loc name
when ("/prelude" `isPrefixOf` name) $
typeError loc mempty $ ppr name <+> "may not be explicitly imported."
return (mempty, env, ImportDec name (Info name') loc)
checkOneDec (ValDec vb) = do
(env, vb') <- checkValBind vb
return (mempty, env, ValDec vb')
checkDecs :: [DecBase NoInfo Name] -> TypeM (TySet, Env, [DecBase Info VName])
checkDecs (d : ds) = do
(d_abstypes, d_env, d') <- checkOneDec d
(ds_abstypes, ds_env, ds') <- localEnv d_env $ checkDecs ds
return
( d_abstypes <> ds_abstypes,
case d' of
LocalDec {} -> ds_env
ImportDec {} -> ds_env
_ -> ds_env <> d_env,
d' : ds'
)
checkDecs [] =
return (mempty, mempty, [])