@@ -14,7 +14,7 @@ import Protolude (ordNub)
1414
1515import Control.Monad (when , unless , void , forM )
1616import Control.Monad.Error.Class (MonadError (.. ))
17- import Control.Monad.State.Class (MonadState (.. ), modify )
17+ import Control.Monad.State.Class (MonadState (.. ), modify , gets )
1818import Control.Monad.Supply.Class (MonadSupply )
1919import Control.Monad.Writer.Class (MonadWriter (.. ))
2020import Control.Lens ((^..) , _1 , _2 )
@@ -432,31 +432,87 @@ typeCheckModule (Module ss coms mn decls (Just exps)) =
432432 warnAndRethrow (addHint (ErrorInModule mn)) $ do
433433 modify (\ s -> s { checkCurrentModule = Just mn })
434434 decls' <- typeCheckAll mn exps decls
435+ checkSuperClassesAreExported <- getSuperClassExportCheck
435436 for_ exps $ \ e -> do
436437 checkTypesAreExported e
437438 checkClassMembersAreExported e
438439 checkClassesAreExported e
440+ checkSuperClassesAreExported e
439441 return $ Module ss coms mn decls' (Just exps)
440442 where
443+ qualify' :: a -> Qualified a
444+ qualify' = Qualified (Just mn)
445+
446+ getSuperClassExportCheck = do
447+ classesToSuperClasses <- gets
448+ ( M. map
449+ ( S. fromList
450+ . filter (\ (Qualified mn' _) -> mn' == Just mn)
451+ . fmap constraintClass
452+ . typeClassSuperclasses
453+ )
454+ . typeClasses
455+ . checkEnv
456+ )
457+ let
458+ -- A function that, given a class name, returns the set of
459+ -- transitive class dependencies that are defined in this
460+ -- module.
461+ transitiveSuperClassesFor
462+ :: Qualified (ProperName 'ClassName)
463+ -> S. Set (Qualified (ProperName 'ClassName))
464+ transitiveSuperClassesFor qname =
465+ untilSame
466+ (\ s -> s <> foldMap (\ n -> fromMaybe S. empty (M. lookup n classesToSuperClasses)) s)
467+ (fromMaybe S. empty (M. lookup qname classesToSuperClasses))
468+
469+ superClassesFor qname =
470+ fromMaybe S. empty (M. lookup qname classesToSuperClasses)
471+
472+ pure $ checkSuperClassExport superClassesFor transitiveSuperClassesFor
473+ moduleClassExports :: S. Set (Qualified (ProperName 'ClassName))
474+ moduleClassExports = S. fromList $ mapMaybe (\ x -> case x of
475+ TypeClassRef _ name -> Just (qualify' name)
476+ _ -> Nothing ) exps
477+
478+ untilSame :: Eq a => (a -> a ) -> a -> a
479+ untilSame f a = let a' = f a in if a == a' then a else untilSame f a'
441480
442481 checkMemberExport :: (Type -> [DeclarationRef ]) -> DeclarationRef -> m ()
443482 checkMemberExport extract dr@ (TypeRef _ name dctors) = do
444483 env <- getEnv
445- case M. lookup (Qualified (Just mn) name) (typeSynonyms env) of
446- Nothing -> return ()
447- Just (_, ty) -> checkExport dr extract ty
448- case dctors of
449- Nothing -> return ()
450- Just dctors' -> for_ dctors' $ \ dctor ->
451- case M. lookup (Qualified (Just mn) dctor) (dataConstructors env) of
452- Nothing -> return ()
453- Just (_, _, ty, _) -> checkExport dr extract ty
454- return ()
484+ for_ (M. lookup (qualify' name) (typeSynonyms env)) $ \ (_, ty) ->
485+ checkExport dr extract ty
486+ for_ dctors $ \ dctors' ->
487+ for_ dctors' $ \ dctor ->
488+ for_ (M. lookup (qualify' dctor) (dataConstructors env)) $ \ (_, _, ty, _) ->
489+ checkExport dr extract ty
455490 checkMemberExport extract dr@ (ValueRef _ name) = do
456- ty <- lookupVariable (Qualified ( Just mn) name)
491+ ty <- lookupVariable (qualify' name)
457492 checkExport dr extract ty
458493 checkMemberExport _ _ = return ()
459494
495+ checkSuperClassExport
496+ :: (Qualified (ProperName 'ClassName) -> S. Set (Qualified (ProperName 'ClassName)))
497+ -> (Qualified (ProperName 'ClassName) -> S. Set (Qualified (ProperName 'ClassName)))
498+ -> DeclarationRef
499+ -> m ()
500+ checkSuperClassExport superClassesFor transitiveSuperClassesFor dr@ (TypeClassRef drss className) = do
501+ let superClasses = superClassesFor (qualify' className)
502+ -- thanks to laziness, the computation of the transitive
503+ -- superclasses defined in-module will only occur if we actually
504+ -- throw the error. Constructing the full set of transitive
505+ -- superclasses is likely to be costly for every single term.
506+ transitiveSuperClasses = transitiveSuperClassesFor (qualify' className)
507+ unexported = S. difference superClasses moduleClassExports
508+ unless (null unexported)
509+ . throwError . errorMessage' drss
510+ . TransitiveExportError dr
511+ . map (TypeClassRef drss . disqualify)
512+ $ toList transitiveSuperClasses
513+ checkSuperClassExport _ _ _ =
514+ return ()
515+
460516 checkExport :: DeclarationRef -> (Type -> [DeclarationRef ]) -> Type -> m ()
461517 checkExport dr extract ty = case filter (not . exported) (extract ty) of
462518 [] -> return ()
@@ -512,3 +568,4 @@ typeCheckModule (Module ss coms mn decls (Just exps)) =
512568 extractMemberName (TypeDeclaration td) = tydeclIdent td
513569 extractMemberName _ = internalError " Unexpected declaration in typeclass member list"
514570 checkClassMembersAreExported _ = return ()
571+
0 commit comments