curry-frontend-2.0.0: src/Checks/DeriveCheck.hs
{- |
Module : $Header$
Description : Checks deriving clauses
Copyright : (c) 2016 Finn Teegen
License : BSD-3-clause
Maintainer : bjp@informatik.uni-kiel.de
Stability : experimental
Portability : portable
Before entering derived instances into the instance environment, the
compiler has to ensure that it is not asked for other instances than
those of supported type classes.
-}
module Checks.DeriveCheck (deriveCheck) where
import Curry.Base.Ident
import Curry.Base.Pretty
import Curry.Base.SpanInfo (HasSpanInfo)
import Curry.Syntax
import Base.Messages (Message, spanInfoMessage)
import Env.TypeConstructor
deriveCheck :: TCEnv -> Module a -> [Message]
deriveCheck tcEnv (Module _ _ _ m _ _ ds) = concatMap (checkDecl m tcEnv) ds
-- No instances can be derived for abstract data types as well as for
-- existential data types.
checkDecl :: ModuleIdent -> TCEnv -> Decl a -> [Message]
checkDecl m tcEnv (DataDecl _ tc _ cs clss)
| null clss = []
| null cs = [errNoAbstractDerive tc]
| otherwise = concatMap (checkDerivable m tcEnv cs) clss
checkDecl m tcEnv (NewtypeDecl _ _ _ nc clss) =
concatMap (checkDerivable m tcEnv [toConstrDecl nc]) clss
checkDecl _ _ _ = []
checkDerivable :: ModuleIdent -> TCEnv -> [ConstrDecl] -> QualIdent -> [Message]
checkDerivable m tcEnv cs cls
| ocls == qEnumId && not (isEnum cs) = [errNotEnum cls]
| ocls == qBoundedId && not (isBounded cs) = [errNotBounded cls]
| ocls `notElem` derivableClasses = [errNotDerivable cls]
| ocls == qDataId = [errNoDataDerive cls]
| otherwise = []
where ocls = getOrigName m cls tcEnv
derivableClasses :: [QualIdent]
derivableClasses = [qEqId, qOrdId, qEnumId, qBoundedId, qReadId, qShowId, qDataId]
-- Instances of 'Enum' can be derived only for enumeration types, i.e., types
-- where all data constructors are constants.
isEnum :: [ConstrDecl] -> Bool
isEnum = all ((0 ==) . constrArity)
-- Instances of 'Bounded' can be derived only for enumerations and for single
-- constructor types.
isBounded :: [ConstrDecl] -> Bool
isBounded cs = length cs == 1 || isEnum cs
-- ---------------------------------------------------------------------------
-- Auxiliary functions
-- ---------------------------------------------------------------------------
toConstrDecl :: NewConstrDecl -> ConstrDecl
toConstrDecl (NewConstrDecl p c ty) = ConstrDecl p c [ty]
toConstrDecl (NewRecordDecl p c (l, ty)) =
RecordDecl p c [FieldDecl p [l] ty]
constrArity :: ConstrDecl -> Int
constrArity (ConstrDecl _ _ tys) = length tys
constrArity (ConOpDecl _ _ _ _) = 2
constrArity c@(RecordDecl _ _ _) = length $ recordLabels c
-- ---------------------------------------------------------------------------
-- Error messages
-- ---------------------------------------------------------------------------
errNoAbstractDerive :: HasSpanInfo a => a -> Message
errNoAbstractDerive s = spanInfoMessage s $
text "Instances can only be derived for data types with" <+>
text "at least one constructor"
errNotDerivable :: QualIdent -> Message
errNotDerivable cls = spanInfoMessage cls $ hsep $ map text
["Instances of type class", escQualName cls, "cannot be derived"]
errNoDataDerive :: QualIdent -> Message
errNoDataDerive qcls = spanInfoMessage qcls $ hsep $ map text
[ "Instances of type class"
, escQualName qcls
, "are automatically derived if possible"
]
errNotEnum :: QualIdent -> Message
errNotEnum qcls = spanInfoMessage qcls $ hsep $ map text
[ "Instances of type class"
, escQualName qcls
, "can be derived only for enumeration types"
]
errNotBounded :: QualIdent -> Message
errNotBounded qcls = spanInfoMessage qcls $ hsep $ map text
[ "Instances of type class"
, escQualName qcls
, "can be derived only for enumeration and single constructor types"
]