Warn about implicit kind variables with -Wcompat
authorVladislav Zavialov <vlad.z.4096@gmail.com>
Sun, 17 Jun 2018 03:44:39 +0000 (23:44 -0400)
committerBen Gamari <ben@smart-cactus.org>
Sun, 17 Jun 2018 03:44:57 +0000 (23:44 -0400)
According to an accepted proposal
https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/002
4-no-kind-vars.rst

    With -Wcompat, warn if a kind variable is brought into scope
    implicitly in a type with an explicit forall. This applies to type
    signatures and to other contexts that allow a forall with the
    forall-or-nothing rule in effect (for example, class instances).

Test Plan: Validate

Reviewers: goldfire, hvr, bgamari, RyanGlScott

Reviewed By: goldfire

Subscribers: RyanGlScott, rwbarton, thomie, carter

GHC Trac Issues: #15264

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

compiler/main/DynFlags.hs
compiler/rename/RnTypes.hs
docs/users_guide/8.6.1-notes.rst
docs/users_guide/using-warnings.rst
libraries/base/Data/Typeable/Internal.hs
testsuite/tests/dependent/should_compile/T15264.hs [new file with mode: 0644]
testsuite/tests/dependent/should_compile/T15264.stderr [new file with mode: 0644]
testsuite/tests/dependent/should_compile/all.T

index 437afc3..485eb72 100644 (file)
@@ -807,6 +807,7 @@ data WarningFlag =
    | Opt_WarnMissingExportList
    | Opt_WarnInaccessibleCode
    | Opt_WarnStarIsType                   -- Since 8.6
+   | Opt_WarnImplicitKindVars             -- Since 8.6
    deriving (Eq, Show, Enum)
 
 data Language = Haskell98 | Haskell2010
@@ -3784,6 +3785,7 @@ wWarningFlagsDeps = [
   flagSpec "hi-shadowing"                Opt_WarnHiShadows,
   flagSpec "inaccessible-code"           Opt_WarnInaccessibleCode,
   flagSpec "implicit-prelude"            Opt_WarnImplicitPrelude,
+  flagSpec "implicit-kind-vars"          Opt_WarnImplicitKindVars,
   flagSpec "incomplete-patterns"         Opt_WarnIncompletePatterns,
   flagSpec "incomplete-record-updates"   Opt_WarnIncompletePatternsRecUpd,
   flagSpec "incomplete-uni-patterns"     Opt_WarnIncompleteUniPatterns,
@@ -4593,6 +4595,7 @@ minusWcompatOpts
     = [ Opt_WarnMissingMonadFailInstances
       , Opt_WarnSemigroup
       , Opt_WarnNonCanonicalMonoidInstances
+      , Opt_WarnImplicitKindVars
       ]
 
 enableUnusedBinds :: DynP ()
index 3d60a9f..ca4986f 100644 (file)
@@ -334,6 +334,11 @@ rnImplicitBndrs bind_free_tvs
 
        ; traceRn "rnImplicitBndrs" (vcat [ ppr kvs, ppr tvs, ppr real_tvs ])
 
+       ; whenWOptM Opt_WarnImplicitKindVars $
+         unless (bind_free_tvs || null kvs) $
+         addWarnAt (Reason Opt_WarnImplicitKindVars) (getLoc (head kvs)) $
+         implicit_kind_vars_msg kvs
+
        ; loc <- getSrcSpanM
        ; vars <- mapM (newLocalBndrRn . L loc . unLoc) (kvs ++ real_tvs)
 
@@ -343,6 +348,16 @@ rnImplicitBndrs bind_free_tvs
 
        ; bindLocalNamesFV vars $
          thing_inside vars }
+  where
+    implicit_kind_vars_msg kvs =
+      vcat [ text "An explicit" <+> quotes (text "forall") <+>
+             text "was used, but the following kind variables" <+>
+             text "are not quantified:" <+>
+             hsep (punctuate comma (map (quotes . ppr) kvs))
+           , text "Despite this fact, GHC will introduce them into scope," <+>
+             text "but it will stop doing so in the future."
+           , text "Suggested fix: add" <+>
+             quotes (text "forall" <+> hsep (map ppr kvs) <> char '.') ]
 
 rnLHsInstType :: SDoc -> LHsSigType GhcPs -> RnM (LHsSigType GhcRn, FreeVars)
 -- Rename the type in an instance.
index 4bc01c9..5b13909 100644 (file)
@@ -152,6 +152,12 @@ Compiler
   :ghc-flag:`-fexternal-dynamic-refs`. If you don't know why you might
   need this, you don't need it.
 
+- :ghc-flag:`-Wcompat` now includes :ghc-flag:`-Wimplicit-kind-vars` to
+  provide early detection of breakage that will be caused by implementation of
+  `GHC proposal #24
+  <https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0024-no-kind-vars.rst>`__
+  in a future release.
+
 Plugins
 ~~~~~~~
 
index 87ddcda..7dc4a3b 100644 (file)
@@ -109,6 +109,7 @@ The following flags are simple ways to select standard "packages" of warnings:
         * :ghc-flag:`-Wmissing-monadfail-instances`
         * :ghc-flag:`-Wsemigroup`
         * :ghc-flag:`-Wnoncanonical-monoid-instances`
+        * :ghc-flag:`-Wimplicit-kind-vars`
 
 .. ghc-flag:: -Wno-compat
     :shortdesc: Disables all warnings enabled by :ghc-flag:`-Wcompat`.
@@ -768,6 +769,58 @@ of ``-W(no-)*``.
 
     This warning is off by default.
 
+.. ghc-flag:: -Wimplicit-kind-vars
+    :shortdesc: warn when kind variables are brought into scope implicitly despite
+        the "forall-or-nothing" rule
+    :type: dynamic
+    :reverse: -Wno-implicit-kind-vars
+    :category:
+
+    :since: 8.6
+
+    `GHC proposal #24
+    <https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0024-no-kind-vars.rst>`__
+    prescribes to treat kind variables and type variables identically in
+    ``forall``, removing the legacy distinction between them.
+
+    Consider the following examples: ::
+
+        f :: Proxy a -> Proxy b -> ()
+        g :: forall a b. Proxy a -> Proxy b -> ()
+
+    ``f`` does not use an explicit ``forall``, so type variables ``a`` and ``b``
+    are brought into scope implicitly. ``g`` quantifies both ``a`` and ``b``
+    explicitly. Both ``f`` and ``g`` work today and will continue to work in the
+    future because they adhere to the "forall-or-nothing" rule: either all type
+    variables in a function definition are introduced explicitly or implicitly,
+    there is no middle ground.
+
+    A violation of the "forall-or-nothing" rule looks like this: ::
+
+        m :: forall a. Proxy a -> Proxy b -> ()
+
+    ``m`` does not introduce one of the variables, ``b``, and thus is rejected.
+
+    However, consider the following example: ::
+
+        n :: forall a. Proxy (a :: k) -> ()
+
+    While ``n`` uses ``k`` without introducing it and thus violates the rule, it
+    is currently accepted. This is because ``k`` in ``n`` is considered a kind
+    variable, as it occurs in a kind signature. In reality, the line between
+    type variables and kind variables is blurry, as the following example
+    demonstrates: ::
+
+        kindOf :: forall a. Proxy (a :: k) -> Proxy k
+
+    In ``kindOf``, the ``k`` variable is used both in a kind position and a type
+    position. Currently, ``kindOf`` happens to be accepted as well.
+
+    In a future release of GHC, both ``n`` and ``kindOf`` will be rejected per
+    the "forall-or-nothing" rule. This warning, being part of the
+    :ghc-flag:`-Wcompat` option group, allows to detect this before the actual
+    breaking change takes place.
+
 .. ghc-flag:: -Wincomplete-patterns
     :shortdesc: warn when a pattern match could fail
     :type: dynamic
index 0929048..0d4fc82 100644 (file)
@@ -476,7 +476,7 @@ splitApp (TrTyCon{trTyCon = con, trKindVars = kinds})
       Refl -> IsCon con kinds
 
 -- | Use a 'TypeRep' as 'Typeable' evidence.
-withTypeable :: forall (a :: k) (r :: TYPE rep). ()
+withTypeable :: forall k (a :: k) rep (r :: TYPE rep). ()
              => TypeRep a -> (Typeable a => r) -> r
 withTypeable rep k = unsafeCoerce k' rep
   where k' :: Gift a r
@@ -631,7 +631,7 @@ unkindedTypeRep :: SomeKindedTypeRep k -> SomeTypeRep
 unkindedTypeRep (SomeKindedTypeRep x) = SomeTypeRep x
 
 data SomeKindedTypeRep k where
-    SomeKindedTypeRep :: forall (a :: k). TypeRep a
+    SomeKindedTypeRep :: forall (a :: k). TypeRep a
                       -> SomeKindedTypeRep k
 
 kApp :: SomeKindedTypeRep (k -> k')
@@ -640,7 +640,7 @@ kApp :: SomeKindedTypeRep (k -> k')
 kApp (SomeKindedTypeRep f) (SomeKindedTypeRep a) =
     SomeKindedTypeRep (mkTrApp f a)
 
-kindedTypeRep :: forall (a :: k). Typeable a => SomeKindedTypeRep k
+kindedTypeRep :: forall (a :: k). Typeable a => SomeKindedTypeRep k
 kindedTypeRep = SomeKindedTypeRep (typeRep @a)
 
 buildList :: forall k. Typeable k
@@ -980,7 +980,8 @@ tcNat :: TyCon
 tcNat = typeRepTyCon (typeRep @Nat)
 
 -- | An internal function, to make representations for type literals.
-typeLitTypeRep :: forall (a :: k). (Typeable k) => String -> TyCon -> TypeRep a
+typeLitTypeRep :: forall k (a :: k). (Typeable k) =>
+                  String -> TyCon -> TypeRep a
 typeLitTypeRep nm kind_tycon = mkTrCon (mkTypeLitTyCon nm kind_tycon) []
 
 -- | For compiler use.
diff --git a/testsuite/tests/dependent/should_compile/T15264.hs b/testsuite/tests/dependent/should_compile/T15264.hs
new file mode 100644 (file)
index 0000000..a03cf43
--- /dev/null
@@ -0,0 +1,15 @@
+{-# LANGUAGE ExplicitForAll, PolyKinds #-}
+{-# OPTIONS -Wcompat #-}
+
+module T15264 where
+
+import Data.Proxy
+
+bad1 :: forall (a :: k). Proxy a -> ()
+bad1 _ = ()
+
+bad2 :: forall (a :: k1) (b :: k2). Proxy a -> ()
+bad2 _ = ()
+
+good :: forall k (a :: k). Proxy a -> ()
+good _ = ()
diff --git a/testsuite/tests/dependent/should_compile/T15264.stderr b/testsuite/tests/dependent/should_compile/T15264.stderr
new file mode 100644 (file)
index 0000000..222d686
--- /dev/null
@@ -0,0 +1,10 @@
+
+T15264.hs:8:22: warning: [-Wimplicit-kind-vars (in -Wcompat)]
+    An explicit ‘forall’ was used, but the following kind variables are not quantified: ‘k’
+    Despite this fact, GHC will introduce them into scope, but it will stop doing so in the future.
+    Suggested fix: add ‘forall k.’
+
+T15264.hs:11:22: warning: [-Wimplicit-kind-vars (in -Wcompat)]
+    An explicit ‘forall’ was used, but the following kind variables are not quantified: ‘k1’, ‘k2’
+    Despite this fact, GHC will introduce them into scope, but it will stop doing so in the future.
+    Suggested fix: add ‘forall k1 k2.’
index 40ba211..2865351 100644 (file)
@@ -48,4 +48,5 @@ test('T14720', normal, compile, [''])
 test('T14066a', normal, compile, [''])
 test('T14749', normal, compile, [''])
 test('T14991', normal, compile, [''])
+test('T15264', normal, compile, [''])
 test('DkNameRes', normal, compile, [''])
\ No newline at end of file