Reject oversaturated VKAs in type family equations
authorRyan Scott <ryan.gl.scott@gmail.com>
Sat, 2 Feb 2019 00:10:55 +0000 (19:10 -0500)
committerMarge Bot <ben+marge-bot@smart-cactus.org>
Sat, 2 Feb 2019 00:10:55 +0000 (19:10 -0500)
compiler/typecheck/TcTyClsDecls.hs
compiler/typecheck/TcValidity.hs
testsuite/tests/typecheck/should_compile/all.T
testsuite/tests/typecheck/should_fail/T15793.hs [moved from testsuite/tests/typecheck/should_compile/T15793.hs with 100% similarity]
testsuite/tests/typecheck/should_fail/T15793.stderr [new file with mode: 0644]
testsuite/tests/typecheck/should_fail/T16255.hs [new file with mode: 0644]
testsuite/tests/typecheck/should_fail/T16255.stderr [new file with mode: 0644]
testsuite/tests/typecheck/should_fail/all.T

index a3b7975..7bf5e20 100644 (file)
@@ -3806,6 +3806,9 @@ wrongKindOfFamily family
                  | isDataFamilyTyCon family = text "data family"
                  | otherwise = pprPanic "wrongKindOfFamily" (ppr family)
 
+-- | Produce an error for oversaturated type family equations with too many
+-- required arguments.
+-- See Note [Oversaturated type family equations] in TcValidity.
 wrongNumberOfParmsErr :: Arity -> SDoc
 wrongNumberOfParmsErr max_args
   = text "Number of parameters must match family declaration; expected"
index 3b88fe1..ca58877 100644 (file)
@@ -2056,7 +2056,17 @@ checkFamPatBinders fam_tc qtvs pats rhs
          --    data instance forall a.  T Int = MkT Int
          -- See Note [Unused explicitly bound variables in a family pattern]
        ; check_tvs bad_qtvs (text "bound by a forall")
-                            (text "used in") }
+                            (text "used in")
+
+         -- Check for oversaturated visible kind arguments in a type family
+         -- equation.
+         -- See Note [Oversaturated type family equations]
+       ; when (isTypeFamilyTyCon fam_tc) $
+           case drop (tyConArity fam_tc) pats of
+             [] -> pure ()
+             spec_arg:_ ->
+               addErr $ text "Illegal oversaturated visible kind argument:"
+                    <+> quotes (char '@' <> pprParendType spec_arg) }
   where
     pat_tvs       = tyCoVarsOfTypes pats
     exact_pat_tvs = exactTyCoVarsOfTypes pats
@@ -2401,6 +2411,62 @@ type application, like @x \@Bool 1@. (Of course it does nothing, but it is
 permissible.) In the type family case, the only sensible explanation is that
 the user has made a mistake -- thus we throw an error.
 
+Note [Oversaturated type family equations]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Type family tycons have very rigid arities. We want to reject something like
+this:
+
+  type family Foo :: Type -> Type where
+    Foo x = ...
+
+Because Foo has arity zero (i.e., it doesn't bind anything to the left of the
+double colon), we want to disallow any equation for Foo that has more than zero
+arguments, such as `Foo x = ...`. The algorithm here is pretty simple: if an
+equation has more arguments than the arity of the type family, reject.
+
+Things get trickier when visible kind application enters the picture. Consider
+the following example:
+
+  type family Bar (x :: j) :: forall k. Either j k where
+    Bar 5 @Symbol = ...
+
+The arity of Bar is two, since it binds two variables, `j` and `x`. But even
+though Bar's equation has two arguments, it's still invalid. Imagine the same
+equation in Core:
+
+    Bar Nat 5 Symbol = ...
+
+Here, it becomes apparent that Bar is actually taking /three/ arguments! So
+we can't just rely on a simple counting argument to reject
+`Bar 5 @Symbol = ...`, since it only has two user-written arguments.
+Moreover, there's one explicit argument (5) and one visible kind argument
+(@Symbol), which matches up perfectly with the fact that Bar has one required
+binder (x) and one specified binder (j), so that's not a valid way to detect
+oversaturation either.
+
+To solve this problem in a robust way, we do the following:
+
+1. When kind-checking, we count the number of user-written *required*
+   arguments and check if there is an equal number of required tycon binders.
+   If not, reject. (See `wrongNumberOfParmsErr` in TcTyClsDecls.)
+
+   We perform this step during kind-checking, not during validity checking,
+   since we can give better error messages if we catch it early.
+2. When validity checking, take all of the (Core) type patterns from on
+   equation, drop the first n of them (where n is the arity of the type family
+   tycon), and check if there are any types leftover. If so, reject.
+
+   Why does this work? We know that after dropping the first n type patterns,
+   none of the leftover types can be required arguments, since step (1) would
+   have already caught that. Moreover, the only places where visible kind
+   applications should be allowed are in the first n types, since those are the
+   only arguments that can correspond to binding forms. Therefore, the
+   remaining arguments must correspond to oversaturated uses of visible kind
+   applications, which are precisely what we want to reject.
+
+Note that we only perform this check for type families, and not for data
+families. This is because it is perfectly acceptable to oversaturate data
+family instance equations: see Note [Arity of data families] in FamInstEnv.
 
 ************************************************************************
 *                                                                      *
index 5146dbc..7a08e21 100644 (file)
@@ -629,7 +629,6 @@ test('T14735', normal, compile, [''])
 test('T15180', normal, compile, [''])
 test('T15232', normal, compile, [''])
 test('T15788', normal, compile, [''])
-test('T15793', normal, compile, [''])
 test('T15807a', normal, compile, [''])
 test('T13833', normal, compile, [''])
 test('T14185', expect_broken(14185), compile, [''])
diff --git a/testsuite/tests/typecheck/should_fail/T15793.stderr b/testsuite/tests/typecheck/should_fail/T15793.stderr
new file mode 100644 (file)
index 0000000..d160947
--- /dev/null
@@ -0,0 +1,5 @@
+
+T15793.hs:18:3: error:
+    • Illegal oversaturated visible kind argument: ‘@a’
+    • In the equations for closed type family ‘F2’
+      In the type family declaration for ‘F2’
diff --git a/testsuite/tests/typecheck/should_fail/T16255.hs b/testsuite/tests/typecheck/should_fail/T16255.hs
new file mode 100644 (file)
index 0000000..ef2f0a9
--- /dev/null
@@ -0,0 +1,21 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE TypeFamilies #-}
+module T16255 where
+
+import Data.Kind
+import GHC.TypeLits
+
+data SBool :: Bool -> Type
+type family F1 :: forall k. k -> Type where
+  F1 @Bool = SBool
+
+data family D1 :: forall k. k -> Type
+-- Note that this similar-looking data family instance is OK, since data family
+-- instances permit oversaturation in their equations.
+data instance D1 @Bool :: Bool -> Type
+
+type family F2 (x :: j) :: forall k. Either j k where
+  F2 5 @Symbol = Left 4
diff --git a/testsuite/tests/typecheck/should_fail/T16255.stderr b/testsuite/tests/typecheck/should_fail/T16255.stderr
new file mode 100644 (file)
index 0000000..74379a3
--- /dev/null
@@ -0,0 +1,10 @@
+
+T16255.hs:13:3: error:
+    • Illegal oversaturated visible kind argument: ‘@Bool’
+    • In the equations for closed type family ‘F1’
+      In the type family declaration for ‘F1’
+
+T16255.hs:21:3: error:
+    • Illegal oversaturated visible kind argument: ‘@Symbol’
+    • In the equations for closed type family ‘F2’
+      In the type family declaration for ‘F2’
index 2b85619..a87c846 100644 (file)
@@ -492,6 +492,7 @@ test('T15592a', normal, compile_fail, [''])
 test('T15629', normal, compile_fail, [''])
 test('T15767', normal, compile_fail, [''])
 test('T15648', [extra_files(['T15648a.hs'])], multimod_compile_fail, ['T15648', '-v0 -fprint-equality-relations'])
+test('T15793', normal, compile_fail, [''])
 test('T15796', normal, compile_fail, [''])
 test('T15807', normal, compile_fail, [''])
 test('T15954', normal, compile_fail, [''])
@@ -508,3 +509,4 @@ test('T16059d', [extra_files(['T16059b.hs'])], multimod_compile_fail,
     ['T16059d', '-v0'])
 test('T16059e', [extra_files(['T16059b.hs'])], multimod_compile_fail,
     ['T16059e', '-v0'])
+test('T16255', normal, compile_fail, [''])