diff options
author | sheaf <sam.derbyshire@gmail.com> | 2021-11-17 15:23:15 +0100 |
---|---|---|
committer | Marge Bot <ben+marge-bot@smart-cactus.org> | 2021-11-25 05:02:39 -0500 |
commit | f27a63fe12c60e6d6ec52e286dd0a0980eae206d (patch) | |
tree | 7605b1f07f678d4d64b4b895a86cf59e3bddaf4e | |
parent | b52a9a3fa3caf6672fa8c04ac3bd1df353af44cf (diff) | |
download | haskell-f27a63fe12c60e6d6ec52e286dd0a0980eae206d.tar.gz |
Allow boring class declarations in hs-boot files
There are two different ways of declaring a class in an hs-boot file:
- a full declaration, where everything is written as it is
in the .hs file,
- an abstract declaration, where class methods and superclasses
are left out.
However, a declaration with no methods and a trivial superclass,
such as:
class () => C a
was erroneously considered to be an abstract declaration, because
the superclass is trivial.
This is remedied by a one line fix in GHC.Tc.TyCl.tcClassDecl1.
This patch also further clarifies the documentation around
class declarations in hs-boot files.
Fixes #20661, #20588.
-rw-r--r-- | compiler/GHC/Tc/TyCl.hs | 10 | ||||
-rw-r--r-- | docs/users_guide/separate_compilation.rst | 28 | ||||
-rw-r--r-- | testsuite/tests/typecheck/should_compile/T20588d.hs | 15 | ||||
-rw-r--r-- | testsuite/tests/typecheck/should_compile/T20588d.hs-boot | 15 | ||||
-rw-r--r-- | testsuite/tests/typecheck/should_compile/T20588d_aux.hs | 4 | ||||
-rw-r--r-- | testsuite/tests/typecheck/should_compile/T20661.hs | 5 | ||||
-rw-r--r-- | testsuite/tests/typecheck/should_compile/T20661.hs-boot | 7 | ||||
-rw-r--r-- | testsuite/tests/typecheck/should_compile/T20661_aux.hs | 7 | ||||
-rw-r--r-- | testsuite/tests/typecheck/should_compile/all.T | 2 | ||||
-rw-r--r-- | testsuite/tests/typecheck/should_fail/T20588c.hs | 10 | ||||
-rw-r--r-- | testsuite/tests/typecheck/should_fail/T20588c.hs-boot | 8 | ||||
-rw-r--r-- | testsuite/tests/typecheck/should_fail/T20588c.stderr | 14 | ||||
-rw-r--r-- | testsuite/tests/typecheck/should_fail/T20588c_aux.hs | 4 | ||||
-rw-r--r-- | testsuite/tests/typecheck/should_fail/all.T | 1 |
14 files changed, 126 insertions, 4 deletions
diff --git a/compiler/GHC/Tc/TyCl.hs b/compiler/GHC/Tc/TyCl.hs index deeb1dd16b..798da08ec5 100644 --- a/compiler/GHC/Tc/TyCl.hs +++ b/compiler/GHC/Tc/TyCl.hs @@ -2466,7 +2466,15 @@ tcClassDecl1 roles_info class_name hs_ctxt meths fundeps sigs ats at_defs ; mindef <- tcClassMinimalDef class_name sigs sig_stuff ; is_boot <- tcIsHsBootOrSig - ; let body | is_boot, null ctxt, null at_stuff, null sig_stuff + ; let body | is_boot, isNothing hs_ctxt, null at_stuff, null sig_stuff + -- We use @isNothing hs_ctxt@ rather than @null ctxt@, + -- so that a declaration in an hs-boot file such as: + -- + -- class () => C a b | a -> b + -- + -- is not considered abstract; it's sometimes useful + -- to be able to declare such empty classes in hs-boot files. + -- See #20661. = Nothing | otherwise = Just (ctxt, at_stuff, sig_stuff, mindef) diff --git a/docs/users_guide/separate_compilation.rst b/docs/users_guide/separate_compilation.rst index b580c9ab18..ec33cddcd4 100644 --- a/docs/users_guide/separate_compilation.rst +++ b/docs/users_guide/separate_compilation.rst @@ -867,9 +867,31 @@ A hs-boot file is written in a subset of Haskell: - Class declarations can either be given in full, exactly as in Haskell, or they can be given abstractly by omitting everything other than the instance head: no superclasses, no class methods, no associated types. - If the class declaration is given in full, the default delarations - must also match; this applies to both default methods and default - declarations for associated types. + However, if the class has any ::extension::`FunctionalDependencies`, + those given in the hs-boot file must be the same. + + If the class declaration is given in full, the entire class declaration + must be identical, up to a renaming of the type variables bound by the + class head. This means: + + - The class head must be the same. + - The class context must be the same, up to simplification of constraints. + - If there are any ::extension::`FunctionalDependencies`, these must + be the same. + - The order, names, and types of the class methods must be the same. + - The arity and kinds of any associated types must be the same. + - Default methods as well as default signatures (see ::extension::`DefaultSignatures`) + must be provided for the same methods, and must be the same. + - Default declarations for associated types must be provided for the + same types, and must be the same. + + To declare a class with no methods in an hs-boot file, it must have a superclass. + If the class has no superclass constraints, add an empty one, e.g. :: + + class () => C a + + This is a full class declaration, not an abstract declaration in which + the methods were omitted. - You can include instance declarations just as in Haskell; but omit the "where" part. diff --git a/testsuite/tests/typecheck/should_compile/T20588d.hs b/testsuite/tests/typecheck/should_compile/T20588d.hs new file mode 100644 index 0000000000..fd2ab2a3c8 --- /dev/null +++ b/testsuite/tests/typecheck/should_compile/T20588d.hs @@ -0,0 +1,15 @@ + +{-# LANGUAGE TypeFamilies #-} + +module T20588d where + +import Data.Kind + +class C (a :: Type) where + {-# MINIMAL meth #-} + meth :: a -> a + meth = id + +class D (a :: Type) where + type family T a :: Type + type instance T a = Int diff --git a/testsuite/tests/typecheck/should_compile/T20588d.hs-boot b/testsuite/tests/typecheck/should_compile/T20588d.hs-boot new file mode 100644 index 0000000000..fd2ab2a3c8 --- /dev/null +++ b/testsuite/tests/typecheck/should_compile/T20588d.hs-boot @@ -0,0 +1,15 @@ + +{-# LANGUAGE TypeFamilies #-} + +module T20588d where + +import Data.Kind + +class C (a :: Type) where + {-# MINIMAL meth #-} + meth :: a -> a + meth = id + +class D (a :: Type) where + type family T a :: Type + type instance T a = Int diff --git a/testsuite/tests/typecheck/should_compile/T20588d_aux.hs b/testsuite/tests/typecheck/should_compile/T20588d_aux.hs new file mode 100644 index 0000000000..8065767a55 --- /dev/null +++ b/testsuite/tests/typecheck/should_compile/T20588d_aux.hs @@ -0,0 +1,4 @@ +module T20588d_aux where + +import {-# SOURCE #-} T20588d + diff --git a/testsuite/tests/typecheck/should_compile/T20661.hs b/testsuite/tests/typecheck/should_compile/T20661.hs new file mode 100644 index 0000000000..32be5ae52f --- /dev/null +++ b/testsuite/tests/typecheck/should_compile/T20661.hs @@ -0,0 +1,5 @@ +{-# LANGUAGE FunctionalDependencies #-} + +module T20661 where + +class C a b | a -> b diff --git a/testsuite/tests/typecheck/should_compile/T20661.hs-boot b/testsuite/tests/typecheck/should_compile/T20661.hs-boot new file mode 100644 index 0000000000..906d11fc08 --- /dev/null +++ b/testsuite/tests/typecheck/should_compile/T20661.hs-boot @@ -0,0 +1,7 @@ +{-# LANGUAGE FunctionalDependencies #-} + +module T20661 where + +-- Use an empty context to signify that this is not +-- an abstract class, but a class with no methods. +class () => C a b | a -> b diff --git a/testsuite/tests/typecheck/should_compile/T20661_aux.hs b/testsuite/tests/typecheck/should_compile/T20661_aux.hs new file mode 100644 index 0000000000..c50a5bdb49 --- /dev/null +++ b/testsuite/tests/typecheck/should_compile/T20661_aux.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE FunctionalDependencies #-} + +module T20661_aux where + +import {-# SOURCE #-} T20661 + +instance C Int Bool diff --git a/testsuite/tests/typecheck/should_compile/all.T b/testsuite/tests/typecheck/should_compile/all.T index d5faf615b4..74fa4f0da8 100644 --- a/testsuite/tests/typecheck/should_compile/all.T +++ b/testsuite/tests/typecheck/should_compile/all.T @@ -805,3 +805,5 @@ test('T20356', normal, compile, ['']) test('T20584', normal, compile, ['']) test('T20584b', normal, compile, ['']) test('T20588b', [extra_files(['T20588b.hs', 'T20588b.hs-boot', 'T20588b_aux.hs'])], multimod_compile, ['T20588b_aux.hs', '-v0']) +test('T20588d', [extra_files(['T20588d.hs', 'T20588d.hs-boot', 'T20588d_aux.hs'])], multimod_compile, ['T20588d_aux.hs', '-v0']) +test('T20661', [extra_files(['T20661.hs', 'T20661.hs-boot', 'T20661_aux.hs'])], multimod_compile, ['T20661_aux.hs', '-v0']) diff --git a/testsuite/tests/typecheck/should_fail/T20588c.hs b/testsuite/tests/typecheck/should_fail/T20588c.hs new file mode 100644 index 0000000000..1ebf815de2 --- /dev/null +++ b/testsuite/tests/typecheck/should_fail/T20588c.hs @@ -0,0 +1,10 @@ +{-# LANGUAGE DefaultSignatures #-} + +module T20588c where + +import Data.Kind + +class C (a :: Type) where + meth :: a + default meth :: Monoid a => a + meth = mempty diff --git a/testsuite/tests/typecheck/should_fail/T20588c.hs-boot b/testsuite/tests/typecheck/should_fail/T20588c.hs-boot new file mode 100644 index 0000000000..43c1625da1 --- /dev/null +++ b/testsuite/tests/typecheck/should_fail/T20588c.hs-boot @@ -0,0 +1,8 @@ +{-# LANGUAGE DefaultSignatures #-} + +module T20588c where + +import Data.Kind + +class C (a :: Type) where + meth :: a diff --git a/testsuite/tests/typecheck/should_fail/T20588c.stderr b/testsuite/tests/typecheck/should_fail/T20588c.stderr new file mode 100644 index 0000000000..d15573a30f --- /dev/null +++ b/testsuite/tests/typecheck/should_fail/T20588c.stderr @@ -0,0 +1,14 @@ + +T20588c.hs-boot:7:1: error: + Class ‘C’ has conflicting definitions in the module + and its hs-boot file + Main module: type C :: * -> Constraint + class C a where + meth :: a + default meth :: Monoid a => a + Boot file: type C :: * -> Constraint + class C a where + meth :: a + {-# MINIMAL meth #-} + The methods do not match: + The default methods associated with ‘meth’ are different diff --git a/testsuite/tests/typecheck/should_fail/T20588c_aux.hs b/testsuite/tests/typecheck/should_fail/T20588c_aux.hs new file mode 100644 index 0000000000..f1c01164a7 --- /dev/null +++ b/testsuite/tests/typecheck/should_fail/T20588c_aux.hs @@ -0,0 +1,4 @@ +module T20588c_aux where + +import {-# SOURCE #-} T20588c + diff --git a/testsuite/tests/typecheck/should_fail/all.T b/testsuite/tests/typecheck/should_fail/all.T index bba77ee675..18b07edc87 100644 --- a/testsuite/tests/typecheck/should_fail/all.T +++ b/testsuite/tests/typecheck/should_fail/all.T @@ -626,3 +626,4 @@ test('T20260', normal, compile_fail, ['']) test('OrdErr', normal, compile_fail, ['']) test('T20542', normal, compile_fail, ['']) test('T20588', [extra_files(['T20588.hs', 'T20588.hs-boot', 'T20588_aux.hs'])], multimod_compile_fail, ['T20588_aux.hs', '-v0']) +test('T20588c', [extra_files(['T20588c.hs', 'T20588c.hs-boot', 'T20588c_aux.hs'])], multimod_compile_fail, ['T20588c_aux.hs', '-v0']) |