Skip to content

Commit

Permalink
Error on duplicate type class or instance declarations (#3126)
Browse files Browse the repository at this point in the history
  • Loading branch information
LiamGoodacre committed Oct 21, 2017
1 parent 3280923 commit c9b58e1
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 6 deletions.
6 changes: 6 additions & 0 deletions examples/failing/DuplicateInstance.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-- @shouldFailWith DuplicateInstance
module Main where
class X
class Y
instance i :: X
instance i :: Y
4 changes: 4 additions & 0 deletions examples/failing/DuplicateTypeClass.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- @shouldFailWith DuplicateTypeClass
module Main where
class C
class C
2 changes: 2 additions & 0 deletions src/Language/PureScript/AST/Declarations.hs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ data SimpleErrorMessage
| DeclConflict Name Name
| ExportConflict (Qualified Name) (Qualified Name)
| DuplicateModule ModuleName [SourceSpan]
| DuplicateTypeClass (ProperName 'ClassName) SourceSpan
| DuplicateInstance Ident SourceSpan
| DuplicateTypeArgument Text
| InvalidDoBind
| InvalidDoLet
Expand Down
10 changes: 10 additions & 0 deletions src/Language/PureScript/Errors.hs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ errorCode em = case unwrapErrorMessage em of
DeclConflict{} -> "DeclConflict"
ExportConflict{} -> "ExportConflict"
DuplicateModule{} -> "DuplicateModule"
DuplicateTypeClass{} -> "DuplicateTypeClass"
DuplicateInstance{} -> "DuplicateInstance"
DuplicateTypeArgument{} -> "DuplicateTypeArgument"
InvalidDoBind -> "InvalidDoBind"
InvalidDoLet -> "InvalidDoLet"
Expand Down Expand Up @@ -536,6 +538,14 @@ prettyPrintSingleError (PPEOptions codeColor full level showDocs relPath) e = fl
paras [ line ("Module " <> markCode (runModuleName mn) <> " has been defined multiple times:")
, indent . paras $ map (line . displaySourceSpan relPath) ss
]
renderSimpleErrorMessage (DuplicateTypeClass pn ss) =
paras [ line ("Type class " <> markCode (runProperName pn) <> " has been defined multiple times:")
, indent $ line $ displaySourceSpan relPath ss
]
renderSimpleErrorMessage (DuplicateInstance pn ss) =
paras [ line ("Instance " <> markCode (showIdent pn) <> " has been defined multiple times:")
, indent $ line $ displaySourceSpan relPath ss
]
renderSimpleErrorMessage (CycleInDeclaration nm) =
line $ "The value of " <> markCode (showIdent nm) <> " is undefined here, so this reference is not allowed."
renderSimpleErrorMessage (CycleInModules mns) =
Expand Down
19 changes: 13 additions & 6 deletions src/Language/PureScript/TypeChecker.hs
Original file line number Diff line number Diff line change
Expand Up @@ -116,17 +116,16 @@ addValue moduleName name ty nameKind = do
addTypeClass
:: forall m
. (MonadState CheckState m, MonadError MultipleErrors m)
=> ModuleName
-> ProperName 'ClassName
=> Qualified (ProperName 'ClassName)
-> [(Text, Maybe Kind)]
-> [Constraint]
-> [FunctionalDependency]
-> [Declaration]
-> m ()
addTypeClass moduleName pn args implies dependencies ds = do
addTypeClass qualifiedClassName args implies dependencies ds = do
env <- getEnv
traverse_ (checkMemberIsUsable (typeSynonyms env)) classMembers
modify $ \st -> st { checkEnv = (checkEnv st) { typeClasses = M.insert (Qualified (Just moduleName) pn) newClass (typeClasses . checkEnv $ st) } }
modify $ \st -> st { checkEnv = (checkEnv st) { typeClasses = M.insert qualifiedClassName newClass (typeClasses . checkEnv $ st) } }
where
classMembers :: [(Ident, Type)]
classMembers = map toPair ds
Expand Down Expand Up @@ -318,11 +317,19 @@ typeCheckAll moduleName _ = traverse go
go d@ImportDeclaration{} = return d
go d@(TypeClassDeclaration (ss, _) pn args implies deps tys) = do
warnAndRethrow (addHint (ErrorInTypeClassDeclaration pn) . addHint (PositionedError ss)) $ do
addTypeClass moduleName pn args implies deps tys
env <- getEnv
let qualifiedClassName = Qualified (Just moduleName) pn
guardWith (errorMessage (DuplicateTypeClass pn ss)) $
not (M.member qualifiedClassName (typeClasses env))
addTypeClass qualifiedClassName args implies deps tys
return d
go (d@(TypeInstanceDeclaration (ss, _) ch idx dictName deps className tys body)) =
rethrow (addHint (ErrorInInstance className tys) . addHint (PositionedError ss)) $ do
env <- getEnv
let qualifiedDictName = Qualified (Just moduleName) dictName
flip (traverse_ . traverse_) (typeClassDictionaries env) $ \dictionaries ->
guardWith (errorMessage (DuplicateInstance dictName ss)) $
not (M.member qualifiedDictName dictionaries)
case M.lookup className (typeClasses env) of
Nothing -> internalError "typeCheckAll: Encountered unknown type class in instance declaration"
Just typeClass -> do
Expand All @@ -331,7 +338,7 @@ typeCheckAll moduleName _ = traverse go
checkOrphanInstance dictName className typeClass tys
_ <- traverseTypeInstanceBody checkInstanceMembers body
deps' <- (traverse . overConstraintArgs . traverse) replaceAllTypeSynonyms deps
let dict = TypeClassDictionaryInScope (Qualified (Just moduleName) <$> ch) idx (Qualified (Just moduleName) dictName) [] className tys (Just deps')
let dict = TypeClassDictionaryInScope (Qualified (Just moduleName) <$> ch) idx qualifiedDictName [] className tys (Just deps')
addTypeClassDictionaries (Just moduleName) . M.singleton className $ M.singleton (tcdValue dict) dict
return d

Expand Down

0 comments on commit c9b58e1

Please sign in to comment.