Skip to content

Commit 129a378

Browse files
LiamGoodacrepaf31
authored andcommitted
Error on duplicate type class or instance declarations (purescript#3126)
1 parent b515a80 commit 129a378

5 files changed

Lines changed: 35 additions & 6 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-- @shouldFailWith DuplicateInstance
2+
module Main where
3+
class X
4+
class Y
5+
instance i :: X
6+
instance i :: Y
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- @shouldFailWith DuplicateTypeClass
2+
module Main where
3+
class C
4+
class C

src/Language/PureScript/AST/Declarations.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ data SimpleErrorMessage
9494
| DeclConflict Name Name
9595
| ExportConflict (Qualified Name) (Qualified Name)
9696
| DuplicateModule ModuleName [SourceSpan]
97+
| DuplicateTypeClass (ProperName 'ClassName) SourceSpan
98+
| DuplicateInstance Ident SourceSpan
9799
| DuplicateTypeArgument Text
98100
| InvalidDoBind
99101
| InvalidDoLet

src/Language/PureScript/Errors.hs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ errorCode em = case unwrapErrorMessage em of
103103
DeclConflict{} -> "DeclConflict"
104104
ExportConflict{} -> "ExportConflict"
105105
DuplicateModule{} -> "DuplicateModule"
106+
DuplicateTypeClass{} -> "DuplicateTypeClass"
107+
DuplicateInstance{} -> "DuplicateInstance"
106108
DuplicateTypeArgument{} -> "DuplicateTypeArgument"
107109
InvalidDoBind -> "InvalidDoBind"
108110
InvalidDoLet -> "InvalidDoLet"
@@ -536,6 +538,14 @@ prettyPrintSingleError (PPEOptions codeColor full level showDocs relPath) e = fl
536538
paras [ line ("Module " <> markCode (runModuleName mn) <> " has been defined multiple times:")
537539
, indent . paras $ map (line . displaySourceSpan relPath) ss
538540
]
541+
renderSimpleErrorMessage (DuplicateTypeClass pn ss) =
542+
paras [ line ("Type class " <> markCode (runProperName pn) <> " has been defined multiple times:")
543+
, indent $ line $ displaySourceSpan relPath ss
544+
]
545+
renderSimpleErrorMessage (DuplicateInstance pn ss) =
546+
paras [ line ("Instance " <> markCode (showIdent pn) <> " has been defined multiple times:")
547+
, indent $ line $ displaySourceSpan relPath ss
548+
]
539549
renderSimpleErrorMessage (CycleInDeclaration nm) =
540550
line $ "The value of " <> markCode (showIdent nm) <> " is undefined here, so this reference is not allowed."
541551
renderSimpleErrorMessage (CycleInModules mns) =

src/Language/PureScript/TypeChecker.hs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,17 +116,16 @@ addValue moduleName name ty nameKind = do
116116
addTypeClass
117117
:: forall m
118118
. (MonadState CheckState m, MonadError MultipleErrors m)
119-
=> ModuleName
120-
-> ProperName 'ClassName
119+
=> Qualified (ProperName 'ClassName)
121120
-> [(Text, Maybe Kind)]
122121
-> [Constraint]
123122
-> [FunctionalDependency]
124123
-> [Declaration]
125124
-> m ()
126-
addTypeClass moduleName pn args implies dependencies ds = do
125+
addTypeClass qualifiedClassName args implies dependencies ds = do
127126
env <- getEnv
128127
traverse_ (checkMemberIsUsable (typeSynonyms env)) classMembers
129-
modify $ \st -> st { checkEnv = (checkEnv st) { typeClasses = M.insert (Qualified (Just moduleName) pn) newClass (typeClasses . checkEnv $ st) } }
128+
modify $ \st -> st { checkEnv = (checkEnv st) { typeClasses = M.insert qualifiedClassName newClass (typeClasses . checkEnv $ st) } }
130129
where
131130
classMembers :: [(Ident, Type)]
132131
classMembers = map toPair ds
@@ -317,11 +316,19 @@ typeCheckAll moduleName _ = traverse go
317316
go d@ImportDeclaration{} = return d
318317
go d@(TypeClassDeclaration (ss, _) pn args implies deps tys) = do
319318
warnAndRethrow (addHint (ErrorInTypeClassDeclaration pn) . addHint (PositionedError ss)) $ do
320-
addTypeClass moduleName pn args implies deps tys
319+
env <- getEnv
320+
let qualifiedClassName = Qualified (Just moduleName) pn
321+
guardWith (errorMessage (DuplicateTypeClass pn ss)) $
322+
not (M.member qualifiedClassName (typeClasses env))
323+
addTypeClass qualifiedClassName args implies deps tys
321324
return d
322325
go (d@(TypeInstanceDeclaration (ss, _) dictName deps className tys body)) =
323326
rethrow (addHint (ErrorInInstance className tys) . addHint (PositionedError ss)) $ do
324327
env <- getEnv
328+
let qualifiedDictName = Qualified (Just moduleName) dictName
329+
flip (traverse_ . traverse_) (typeClassDictionaries env) $ \dictionaries ->
330+
guardWith (errorMessage (DuplicateInstance dictName ss)) $
331+
not (M.member qualifiedDictName dictionaries)
325332
case M.lookup className (typeClasses env) of
326333
Nothing -> internalError "typeCheckAll: Encountered unknown type class in instance declaration"
327334
Just typeClass -> do
@@ -330,7 +337,7 @@ typeCheckAll moduleName _ = traverse go
330337
checkOrphanInstance dictName className typeClass tys
331338
_ <- traverseTypeInstanceBody checkInstanceMembers body
332339
deps' <- (traverse . overConstraintArgs . traverse) replaceAllTypeSynonyms deps
333-
let dict = TypeClassDictionaryInScope (Qualified (Just moduleName) dictName) [] className tys (Just deps')
340+
let dict = TypeClassDictionaryInScope qualifiedDictName [] className tys (Just deps')
334341
addTypeClassDictionaries (Just moduleName) . M.singleton className $ M.singleton (tcdValue dict) dict
335342
return d
336343

0 commit comments

Comments
 (0)