diff options
author | Ryan Scott <ryan.gl.scott@gmail.com> | 2019-07-12 10:47:05 -0400 |
---|---|---|
committer | Marge Bot <ben+marge-bot@smart-cactus.org> | 2019-07-26 00:57:02 -0400 |
commit | 30b6f391801d58e364f79df5da2cf9f02be2ba5f (patch) | |
tree | f11e81851c126fa689c60f157ec768bebe1fe35b /compiler/hsSyn | |
parent | b9c99df1a4cdd23bcd26db7ae6ee7ee6464d654e (diff) | |
download | haskell-30b6f391801d58e364f79df5da2cf9f02be2ba5f.tar.gz |
Banish reportFloatingViaTvs to the shadow realm (#15831, #16181)
GHC used to reject programs of this form:
```
newtype Age = MkAge Int
deriving Eq via Const Int a
```
That's because an earlier implementation of `DerivingVia` would
generate the following instance:
```
instance Eq Age where
(==) = coerce @(Const Int a -> Const Int a -> Bool)
@(Age -> Age -> Bool)
(==)
```
Note that the `a` in `Const Int a` is not bound anywhere, which
causes all sorts of issues. I figured that no one would ever want to
write code like this anyway, so I simply banned "floating" `via` type
variables like `a`, checking for their presence in the aptly named
`reportFloatingViaTvs` function.
`reportFloatingViaTvs` ended up being implemented in a subtly
incorrect way, as #15831 demonstrates. Following counsel with the
sage of gold fire, I decided to abandon `reportFloatingViaTvs`
entirely and opt for a different approach that would _accept_
the instance above. This is because GHC now generates this instance
instead:
```
instance forall a. Eq Age where
(==) = coerce @(Const Int a -> Const Int a -> Bool)
@(Age -> Age -> Bool)
(==)
```
Notice that we now explicitly quantify the `a` in
`instance forall a. Eq Age`, so everything is peachy scoping-wise.
See `Note [Floating `via` type variables]` in `TcDeriv` for the full
scoop.
A pleasant benefit of this refactoring is that it made it much easier
to catch the problem observed in #16181, so this patch fixes that
issue too.
Fixes #15831. Fixes #16181.
Diffstat (limited to 'compiler/hsSyn')
-rw-r--r-- | compiler/hsSyn/HsDecls.hs | 18 |
1 files changed, 17 insertions, 1 deletions
diff --git a/compiler/hsSyn/HsDecls.hs b/compiler/hsSyn/HsDecls.hs index bd0bcb527d..3cac82ed2f 100644 --- a/compiler/hsSyn/HsDecls.hs +++ b/compiler/hsSyn/HsDecls.hs @@ -47,7 +47,8 @@ module HsDecls ( -- ** Standalone deriving declarations DerivDecl(..), LDerivDecl, -- ** Deriving strategies - DerivStrategy(..), LDerivStrategy, derivStrategyName, + DerivStrategy(..), LDerivStrategy, + derivStrategyName, foldDerivStrategy, mapDerivStrategy, -- ** @RULE@ declarations LRuleDecls,RuleDecls(..),RuleDecl(..),LRuleDecl,HsRuleRn(..), RuleBndr(..),LRuleBndr, @@ -1936,6 +1937,21 @@ derivStrategyName = text . go go NewtypeStrategy = "newtype" go (ViaStrategy {}) = "via" +-- | Eliminate a 'DerivStrategy'. +foldDerivStrategy :: (p ~ GhcPass pass) + => r -> (XViaStrategy p -> r) -> DerivStrategy p -> r +foldDerivStrategy other _ StockStrategy = other +foldDerivStrategy other _ AnyclassStrategy = other +foldDerivStrategy other _ NewtypeStrategy = other +foldDerivStrategy _ via (ViaStrategy t) = via t + +-- | Map over the @via@ type if dealing with 'ViaStrategy'. Otherwise, +-- return the 'DerivStrategy' unchanged. +mapDerivStrategy :: (p ~ GhcPass pass) + => (XViaStrategy p -> XViaStrategy p) + -> DerivStrategy p -> DerivStrategy p +mapDerivStrategy f ds = foldDerivStrategy ds (ViaStrategy . f) ds + {- ************************************************************************ * * |