Commit 03e8d26f authored by Ryan Scott's avatar Ryan Scott Committed by Ben Gamari

Prevent GND from inferring an instance context for method-less classes

When `GeneralizedNewtypeDeriving` is used with a type class that has no
methods, it will generate a redundant context, and as a result, it can
trigger warnings when compiled with `-Wredundant-constraints`. This is a
simple change in behavior to check beforehand if a class has methods
when deriving it with GND, and if it has no methods, avoid inferring the
redundant context.

Beware that the test for #6088, which used to be expected to fail, now
compiles without issue since it doesn't infer a problematic instance
context.

Thanks to Simon Peyton Jones for doing the necessary refactoring in
f05d685a.

Fixes #12814.

Test Plan: ./validate

Reviewers: goldfire, rwbarton, simonpj, austin, bgamari

Reviewed By: simonpj

Subscribers: thomie

Differential Revision: https://phabricator.haskell.org/D2692

GHC Trac Issues: #12814
parent e8ae4dc8
......@@ -1271,9 +1271,9 @@ mkNewTypeEqn dflags overlap_mode tvs
-- and constraints to coerce each individual method
meth_theta :: [PredOrigin]
meths = classMethods cls
meth_theta = rep_pred_o : coercible_constraints
-- meth_theta | null meths = [] -- No methods => no constraints (Trac #12814)
-- | otherwise = rep_pred_o : coercible_constraints
meth_theta | null meths = [] -- No methods => no constraints
-- (Trac #12814)
| otherwise = rep_pred_o : coercible_constraints
coercible_constraints
= [ mkPredOrigin (DerivOriginCoerce meth t1 t2) TypeLevel
(mkReprPrimEqPred t1 t2)
......@@ -1415,7 +1415,7 @@ However, we must watch out for three things:
with the former definition of C, you'd end up with something like this:
instance C a x => C a (Foo x) where
instance C a (Foo x) where
type T a = T ???
This T family instance doesn't mention the newtype (or its representation
......
......@@ -61,6 +61,28 @@ Compiler
:ref:`GeneralizedNewtypeDeriving and associated type families
<gnd-and-associated-types>`.
- :ghc-flag:`-XGeneralizedNewtypeDeriving` will no longer infer constraints
when deriving a class with no methods. That is, this code: ::
class Throws e
newtype Id a = MkId a
deriving Throws
will now generate this instance: ::
instance Throws (Id a)
instead of this instance: ::
instance Throws a => Throws (Id a)
This change was motivated by the fact that the latter code has a strictly
redundant ``Throws a`` constraint, so it would emit a warning when compiled
with :ghc-flag:`-Wredundant-constraints`. The latter instance could still
be derived if so desired using :ghc-flag:`-XStandaloneDeriving`: ::
deriving instance Throws a => Throws (Id a)
- Add warning flag :ghc-flag:`-Wcpp-undef` which passes ``-Wundef`` to the C
pre-processor causing the pre-processor to warn on uses of the ``#if``
directive on undefined identifiers.
......
......@@ -3971,6 +3971,11 @@ Then the derived instance declaration is of the form ::
instance C t1..tj t => C t1..tj (T v1...vk)
Note that if ``C`` does not contain any class methods, the instance context
is wholly unnecessary, and as such GHC will instead generate: ::
instance C t1..tj (T v1..vk)
As an example which does *not* work, consider ::
newtype NonMonad m s = NonMonad (State s m s) deriving Monad
......@@ -4018,7 +4023,7 @@ associated type families. Here is an example: ::
The derived ``HasRing`` instance would look like ::
instance HasRing a => HasRing (L1Norm a) where
instance HasRing (L1Norm a) where
type Ring (L1Norm a) = Ring a
To be precise, if the class being derived is of the form ::
......@@ -4051,7 +4056,7 @@ then you can derive a ``C c_1 c_2 ... c_(m-1)`` instance for
For the derived ``Bad Int`` instance, GHC would need to generate something
like this: ::
instance Bad Int a => Bad Int (Foo a) where
instance Bad Int (Foo a) where
type B Int = B ???
Now we're stuck, since we have no way to refer to ``a`` on the right-hand
......@@ -4088,6 +4093,10 @@ If both of these conditions are met, GHC will generate this instance: ::
type Tk tk_1 tk_2 ... (N n_1 n_2 ... n_q) ... tk_p
= Tk tk_1 tk_2 ... <rep-type> ... tk_p
Again, if ``C`` contains no class methods, the instance context will be
redundant, so GHC will instead generate
``instance C c_1 c_2 ... c_(m-1) (N n_1 n_2 ... n_q)``.
Beware that in some cases, you may need to enable the
:ghc-flag:`-XUndecidableInstances` extension in order to use this feature.
Here's a pathological case that illustrates why this might happen: ::
......
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeFamilies #-}
module T12814 where
class C a where
type T a
newtype Identity a = Identity a
deriving C
......@@ -79,3 +79,4 @@ test('T12399', normal, compile, [''])
test('T12583', normal, compile, [''])
test('T12616', normal, compile, [''])
test('T12688', normal, compile, [''])
test('T12814', normal, compile, ['-Wredundant-constraints'])
......@@ -3,7 +3,7 @@ module T3621 where
-- This one is ok, even though the deriving clause mentions 'a'
-- which is not a parameter of 'T'
class C a b
class C a b
instance C a S
data S = MkS
......@@ -12,6 +12,7 @@ newtype T = MkT S deriving( C a )
-- But this one fails, and should fail
class (Monad m) => MonadState s m | m -> s where
state :: (s -> (a, s)) -> m a
newtype State s a = State { runState :: s -> (a, s) }
instance Functor (State s) where {}
......
T3621.hs:23:43:
No instance for (MonadState state (State s))
arising from the 'deriving' clause of a data type declaration
Possible fix:
use a standalone 'deriving instance' declaration,
so you can specify the instance context yourself
When deriving the instance for (MonadState state (WrappedState s))
T3621.hs:24:43: error:
• No instance for (MonadState state (State s))
arising from the 'deriving' clause of a data type declaration
Possible fix:
use a standalone 'deriving instance' declaration,
so you can specify the instance context yourself
• When deriving the instance for (MonadState
state (WrappedState s))
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE EmptyDataDecls #-}
module T6088 where
......@@ -15,4 +15,4 @@ instance (Pos n ~ True) => C (A n)
newtype B n = B (A n) deriving (C)
-- This should work, giving
-- instance (Pos n ~ True) => C (B n)
-- instance C (B n)
......@@ -200,6 +200,7 @@ test('Overlap14', normal, compile, [''])
test('T7156', normal, compile, [''])
test('T5591a', normal, compile, [''])
test('T5591b', normal, compile, [''])
test('T6088', normal, compile, [''])
test('T7280', normal, compile, [''])
test('T7474', normal, compile, [''])
test('T7489', normal, compile, [''])
......
T6088.hs:16:33: error:
Couldn't match type ‘Pos n’ with ‘True’
arising from the 'deriving' clause of a data type declaration
When deriving the instance for (C (B n))
......@@ -129,7 +129,6 @@ test('T9580', normal, multimod_compile_fail, ['T9580', ''])
test('T9662', normal, compile_fail, [''])
test('T7862', normal, compile, [''])
test('T9896', normal, compile_fail, [''])
test('T6088', normal, compile_fail, [''])
test('T8550', normal, compile_fail, [''])
test('T9554', normal, compile_fail, [''])
test('T10141', normal, compile_fail, [''])
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment