summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsheaf <sam.derbyshire@gmail.com>2021-11-17 15:23:15 +0100
committerMarge Bot <ben+marge-bot@smart-cactus.org>2021-11-25 05:02:39 -0500
commitf27a63fe12c60e6d6ec52e286dd0a0980eae206d (patch)
tree7605b1f07f678d4d64b4b895a86cf59e3bddaf4e
parentb52a9a3fa3caf6672fa8c04ac3bd1df353af44cf (diff)
downloadhaskell-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.hs10
-rw-r--r--docs/users_guide/separate_compilation.rst28
-rw-r--r--testsuite/tests/typecheck/should_compile/T20588d.hs15
-rw-r--r--testsuite/tests/typecheck/should_compile/T20588d.hs-boot15
-rw-r--r--testsuite/tests/typecheck/should_compile/T20588d_aux.hs4
-rw-r--r--testsuite/tests/typecheck/should_compile/T20661.hs5
-rw-r--r--testsuite/tests/typecheck/should_compile/T20661.hs-boot7
-rw-r--r--testsuite/tests/typecheck/should_compile/T20661_aux.hs7
-rw-r--r--testsuite/tests/typecheck/should_compile/all.T2
-rw-r--r--testsuite/tests/typecheck/should_fail/T20588c.hs10
-rw-r--r--testsuite/tests/typecheck/should_fail/T20588c.hs-boot8
-rw-r--r--testsuite/tests/typecheck/should_fail/T20588c.stderr14
-rw-r--r--testsuite/tests/typecheck/should_fail/T20588c_aux.hs4
-rw-r--r--testsuite/tests/typecheck/should_fail/all.T1
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'])