[Git][ghc/ghc][master] Reject dodgy scoping in associated family instance RHSes

Marge Bot gitlab at gitlab.haskell.org
Thu Dec 17 18:56:44 UTC 2020



 Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC


Commits:
b1178cbc by Ryan Scott at 2020-12-17T13:56:35-05:00
Reject dodgy scoping in associated family instance RHSes

Commit e63518f5d6a93be111f9108c0990a1162f88d615 tried to push all of the logic
of detecting out-of-scope type variables on the RHSes of associated type family
instances to `GHC.Tc.Validity` by deleting a similar check in the renamer.
Unfortunately, this commit went a little too far, as there are some corner
cases that `GHC.Tc.Validity` doesn't detect. Consider this example:

```hs
class C a where
  data D a

instance forall a. C Int where
  data instance D Int = MkD a
```

If this program isn't rejected by the time it reaches the typechecker, then
GHC will believe the `a` in `MkD a` is existentially quantified and accept it.
This is almost surely not what the user wants! The simplest way to reject
programs like this is to restore the old validity check in the renamer
(search for `improperly_scoped` in `rnFamEqn`).

Note that this is technically a breaking change, since the program in the
`polykinds/T9574` test case (which previously compiled) will now be rejected:

```hs
instance Funct ('KProxy :: KProxy o) where
    type Codomain 'KProxy = NatTr (Proxy :: o -> *)
```

This is because the `o` on the RHS will now be rejected for being out of scope.
Luckily, this is simple to repair:

```hs
instance Funct ('KProxy :: KProxy o) where
    type Codomain ('KProxy @o) = NatTr (Proxy :: o -> *)
```

All of the discussion is now a part of the revamped
`Note [Renaming associated types]` in `GHC.Rename.Module`.

A different design would be to make associated type family instances have
completely separate scoping from the parent instance declaration, much like
how associated type family default declarations work today. See the discussion
beginning at https://gitlab.haskell.org/ghc/ghc/-/issues/18021#note_265729 for
more on this point. This, however, would break even more programs that are
accepted today and likely warrants a GHC proposal before going forward. In the
meantime, this patch fixes the issue described in #18021 in the least invasive
way possible. There are programs that are accepted today that will no longer
be accepted after this patch, but they are arguably pathological programs, and
they are simple to repair.

Fixes #18021.

- - - - -


8 changed files:

- compiler/GHC/Rename/Module.hs
- docs/users_guide/9.2.1-notes.rst
- testsuite/tests/indexed-types/should_fail/T5515.stderr
- + testsuite/tests/polykinds/T9574.stderr
- testsuite/tests/polykinds/all.T
- + testsuite/tests/rename/should_fail/T18021.hs
- + testsuite/tests/rename/should_fail/T18021.stderr
- testsuite/tests/rename/should_fail/all.T


Changes:

=====================================
compiler/GHC/Rename/Module.hs
=====================================
@@ -661,12 +661,13 @@ rnClsInstDecl (ClsInstDecl { cid_poly_ty = inst_ty, cid_binds = mbinds
 rnFamEqn :: HsDocContext
          -> AssocTyFamInfo
          -> FreeKiTyVars
-         -- ^ Kind variables from the equation's RHS to be implicitly bound
-         -- if no explicit forall.
+         -- ^ Additional kind variables to implicitly bind if there is no
+         --   explicit forall. (See the comments on @all_imp_vars@ below for a
+         --   more detailed explanation.)
          -> FamEqn GhcPs rhs
          -> (HsDocContext -> rhs -> RnM (rhs', FreeVars))
          -> RnM (FamEqn GhcRn rhs', FreeVars)
-rnFamEqn doc atfi rhs_kvars
+rnFamEqn doc atfi extra_kvars
     (FamEqn { feqn_tycon  = tycon
             , feqn_bndrs  = outer_bndrs
             , feqn_pats   = pats
@@ -679,15 +680,19 @@ rnFamEqn doc atfi rhs_kvars
          -- Note [forall-or-nothing rule] in GHC.Hs.Type), which means
          -- ignoring:
          --
-         -- - pat_kity_vars_with_dups, the variables mentioned in the LHS of
-         --   the equation, and
-         -- - rhs_kvars, the kind variables mentioned in an outermost kind
-         --   signature on the RHS of the equation. (See
-         --   Note [Implicit quantification in type synonyms] in
-         --   GHC.Rename.HsType for why these are implicitly quantified in the
-         --   absence of an explicit forall).
+         -- - pat_kity_vars, the free variables mentioned in the type patterns
+         --   on the LHS of the equation, and
+         -- - extra_kvars, which is one of the following:
+         --   * For type family instances, extra_kvars are the free kind
+         --     variables mentioned in an outermost kind signature on the RHS
+         --     of the equation.
+         --     (See Note [Implicit quantification in type synonyms] in
+         --     GHC.Rename.HsType.)
+         --   * For data family instances, extra_kvars are the free kind
+         --     variables mentioned in the explicit return kind, if one is
+         --     provided. (e.g., the `k` in `data instance T :: k -> Type`).
          --
-         -- For example:
+         -- Some examples:
          --
          -- @
          -- type family F a b
@@ -695,8 +700,20 @@ rnFamEqn doc atfi rhs_kvars
          --   -- all_imp_vars = []
          -- type instance F [(a, b)] c = a -> b -> c
          --   -- all_imp_vars = [a, b, c]
+         --
+         -- type family G :: Maybe a
+         -- type instance forall a. G = (Nothing :: Maybe a)
+         --   -- all_imp_vars = []
+         -- type instance G = (Nothing :: Maybe a)
+         --   -- all_imp_vars = [a]
+         --
+         -- data family H :: k -> Type
+         -- data instance forall k. H :: k -> Type where ...
+         --   -- all_imp_vars = []
+         -- data instance H :: k -> Type where ...
+         --   -- all_imp_vars = [k]
          -- @
-       ; let all_imp_vars = pat_kity_vars_with_dups ++ rhs_kvars
+       ; let all_imp_vars = pat_kity_vars ++ extra_kvars
 
        ; bindHsOuterTyVarBndrs doc mb_cls all_imp_vars outer_bndrs $ \rn_outer_bndrs ->
     do { (pats', pat_fvs) <- rnLHsTypeArgs (FamPatCtx tycon) pats
@@ -714,8 +731,7 @@ rnFamEqn doc atfi rhs_kvars
                                                   rn_outer_bndrs
 
              groups :: [NonEmpty (Located RdrName)]
-             groups = equivClasses cmpLocated $
-                      pat_kity_vars_with_dups
+             groups = equivClasses cmpLocated pat_kity_vars
        ; nms_dups <- mapM (lookupOccRn . unLoc) $
                         [ tv | (tv :| (_:_)) <- groups ]
              -- Add to the used variables
@@ -725,10 +741,24 @@ rnFamEqn doc atfi rhs_kvars
              --     of the instance decl.  See
              --     Note [Unused type variables in family instances]
        ; let nms_used = extendNameSetList rhs_fvs $
-                           inst_tvs ++ nms_dups
+                           nms_dups {- (a) -} ++ inst_head_tvs {- (b) -}
              all_nms = hsOuterTyVarNames rn_outer_bndrs'
        ; warnUnusedTypePatterns all_nms nms_used
 
+         -- For associated family instances, if a type variable from the
+         -- parent instance declaration is mentioned on the RHS of the
+         -- associated family instance but not bound on the LHS, then reject
+         -- that type variable as being out of scope.
+         -- See Note [Renaming associated types]
+       ; let lhs_bound_vars = extendNameSetList pat_fvs all_nms
+             improperly_scoped cls_tkv =
+                  cls_tkv `elemNameSet` rhs_fvs
+                    -- Mentioned on the RHS...
+               && not (cls_tkv `elemNameSet` lhs_bound_vars)
+                    -- ...but not bound on the LHS.
+             bad_tvs = filter improperly_scoped inst_head_tvs
+       ; unless (null bad_tvs) (badAssocRhs bad_tvs)
+
        ; let eqn_fvs = rhs_fvs `plusFV` pat_fvs
              -- See Note [Type family equations and occurrences]
              all_fvs = case atfi of
@@ -754,12 +784,12 @@ rnFamEqn doc atfi rhs_kvars
 
     -- The type variables from the instance head, if we are dealing with an
     -- associated type family instance.
-    inst_tvs = case atfi of
-      NonAssocTyFamEqn _        -> []
-      AssocTyFamDeflt _         -> []
-      AssocTyFamInst _ inst_tvs -> inst_tvs
+    inst_head_tvs = case atfi of
+      NonAssocTyFamEqn _             -> []
+      AssocTyFamDeflt _              -> []
+      AssocTyFamInst _ inst_head_tvs -> inst_head_tvs
 
-    pat_kity_vars_with_dups = extractHsTyArgRdrKiTyVars pats
+    pat_kity_vars = extractHsTyArgRdrKiTyVars pats
              -- It is crucial that extractHsTyArgRdrKiTyVars return
              -- duplicate occurrences, since they're needed to help
              -- determine unused binders on the LHS.
@@ -769,11 +799,18 @@ rnFamEqn doc atfi rhs_kvars
     --
     --   type instance F a b c = Either a b
     --                   ^^^^^
-    lhs_loc = case map lhsTypeArgSrcSpan pats ++ map getLoc rhs_kvars of
+    lhs_loc = case map lhsTypeArgSrcSpan pats ++ map getLoc extra_kvars of
       []         -> panic "rnFamEqn.lhs_loc"
       [loc]      -> loc
       (loc:locs) -> loc `combineSrcSpans` last locs
 
+    badAssocRhs :: [Name] -> RnM ()
+    badAssocRhs ns
+      = addErr (hang (text "The RHS of an associated type declaration mentions"
+                      <+> text "out-of-scope variable" <> plural ns
+                      <+> pprWithCommas (quotes . ppr) ns)
+                   2 (text "All such variables must be bound on the LHS"))
+
 rnTyFamInstDecl :: AssocTyFamInfo
                 -> TyFamInstDecl GhcPs
                 -> RnM (TyFamInstDecl GhcRn, FreeVars)
@@ -829,9 +866,9 @@ rnTyFamInstEqn :: AssocTyFamInfo
                -> TyFamInstEqn GhcPs
                -> RnM (TyFamInstEqn GhcRn, FreeVars)
 rnTyFamInstEqn atfi eqn@(FamEqn { feqn_tycon = tycon, feqn_rhs = rhs })
-  = rnFamEqn (TySynCtx tycon) atfi rhs_kvs eqn rnTySyn
+  = rnFamEqn (TySynCtx tycon) atfi extra_kvs eqn rnTySyn
   where
-    rhs_kvs = extractHsTyRdrTyVarsKindVars rhs
+    extra_kvs = extractHsTyRdrTyVarsKindVars rhs
 
 rnTyFamDefltDecl :: Name
                  -> TyFamDefltDecl GhcPs
@@ -844,9 +881,9 @@ rnDataFamInstDecl :: AssocTyFamInfo
 rnDataFamInstDecl atfi (DataFamInstDecl { dfid_eqn =
                     eqn@(FamEqn { feqn_tycon = tycon
                                 , feqn_rhs   = rhs })})
-  = do { let rhs_kvs = extractDataDefnKindVars rhs
+  = do { let extra_kvs = extractDataDefnKindVars rhs
        ; (eqn', fvs) <-
-           rnFamEqn (TyDataCtx tycon) atfi rhs_kvs eqn rnDataDefn
+           rnFamEqn (TyDataCtx tycon) atfi extra_kvs eqn rnDataDefn
        ; return (DataFamInstDecl { dfid_eqn = eqn' }, fvs) }
 
 -- Renaming of the associated types in instances.
@@ -927,58 +964,131 @@ Relevant tickets: #3699, #10586, #10982 and #11451.
 
 Note [Renaming associated types]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Check that the RHS of the decl mentions only type variables that are explicitly
-bound on the LHS.  For example, this is not ok
-   class C a b where
-      type F a x :: *
-   instance C (p,q) r where
-      type F (p,q) x = (x, r)   -- BAD: mentions 'r'
-c.f. #5515
-
-Kind variables, on the other hand, are allowed to be implicitly or explicitly
-bound. As examples, this (#9574) is acceptable:
-   class Funct f where
-      type Codomain f :: *
-   instance Funct ('KProxy :: KProxy o) where
-      -- o is implicitly bound by the kind signature
-      -- of the LHS type pattern ('KProxy)
-      type Codomain 'KProxy = NatTr (Proxy :: o -> *)
-And this (#14131) is also acceptable:
-    data family Nat :: k -> k -> *
-    -- k is implicitly bound by an invisible kind pattern
-    newtype instance Nat :: (k -> *) -> (k -> *) -> * where
-      Nat :: (forall xx. f xx -> g xx) -> Nat f g
-We could choose to disallow this, but then associated type families would not
-be able to be as expressive as top-level type synonyms. For example, this type
-synonym definition is allowed:
-    type T = (Nothing :: Maybe a)
-So for parity with type synonyms, we also allow:
-    type family   T :: Maybe a
-    type instance T = (Nothing :: Maybe a)
-
-All this applies only for *instance* declarations.  In *class*
-declarations there is no RHS to worry about, and the class variables
-can all be in scope (#5862):
+When renaming a type/data family instance, be it top-level or associated with
+a class, we must check that all of the type variables mentioned on the RHS are
+properly scoped. Specifically, the rule is this:
+
+  Every variable mentioned on the RHS of a type instance declaration
+  (whether associated or not) must be either
+  * Mentioned on the LHS, or
+  * Mentioned in an outermost kind signature on the RHS
+    (see Note [Implicit quantification in type synonyms])
+
+Here is a simple example of something we should reject:
+
+  class C a b where
+    type F a x
+  instance C Int Bool where
+    type F Int x = z
+
+Here, `z` is mentioned on the RHS of the associated instance without being
+mentioned on the LHS, nor is `z` mentioned in an outermost kind signature. The
+renamer will reject `z` as being out of scope without much fuss.
+
+Things get slightly trickier when the instance header itself binds type
+variables. Consider this example (adapted from #5515):
+
+   instance C (p,q) z where
+      type F (p,q) x = (x, z)
+
+According to the rule above, this instance is improperly scoped. However, due
+to the way GHC's renamer works, `z` is /technically/ in scope, as GHC will
+always bring type variables from an instance header into scope over the
+associated type family instances. As a result, the renamer won't simply reject
+the `z` as being out of scope (like it would for the `type F Int x = z`
+example) unless further action is taken. It is important to reject this sort of
+thing in the renamer, because if it is allowed to make it through to the
+typechecker, unexpected shenanigans can occur (see #18021 for examples).
+
+To prevent these sorts of shenanigans, we reject programs like the one above
+with an extra validity check in rnFamEqn. For each type variable bound in the
+parent instance head, we check if it is mentioned on the RHS of the associated
+family instance but not bound on the LHS. If any of the instance-head-bound
+variables meet these criteria, we throw an error.
+(See rnFamEqn.improperly_scoped for how this is implemented.)
+
+Some additional wrinkles:
+
+* This Note only applies to *instance* declarations.  In *class* declarations
+  there is no RHS to worry about, and the class variables can all be in scope
+  (#5862):
+
     class Category (x :: k -> k -> *) where
       type Ob x :: k -> Constraint
       id :: Ob x a => x a a
       (.) :: (Ob x a, Ob x b, Ob x c) => x b c -> x a b -> x a c
-Here 'k' is in scope in the kind signature, just like 'x'.
 
-Although type family equations can bind type variables with explicit foralls,
-it need not be the case that all variables that appear on the RHS must be bound
-by a forall. For instance, the following is acceptable:
+  Here 'k' is in scope in the kind signature, just like 'x'.
+
+* Although type family equations can bind type variables with explicit foralls,
+  it need not be the case that all variables that appear on the RHS must be
+  bound by a forall. For instance, the following is acceptable:
+
+    class C4 a where
+      type T4 a b
+    instance C4 (Maybe a) where
+      type forall b. T4 (Maybe a) b = Either a b
+
+  Even though `a` is not bound by the forall, this is still accepted because `a`
+  was previously bound by the `instance C4 (Maybe a)` part. (see #16116).
+
+* In addition to the validity check in rnFamEqn.improperly_scoped, there is an
+  additional check in GHC.Tc.Validity.checkFamPatBinders that checks each family
+  instance equation for type variables used on the RHS but not bound on the
+  LHS. This is not made redundant by rmFamEqn.improperly_scoped, as there are
+  programs that each check will reject that the other check will not catch:
+
+  - checkValidFamPats is used on all forms of family instances, whereas
+    rmFamEqn.improperly_scoped only checks associated family instances. Since
+    checkFamPatBinders occurs after typechecking, it can catch programs that
+    introduce dodgy scoping by way of type synonyms (see #7536), which is
+    impractical to accomplish in the renamer.
+  - rnFamEqn.improperly_scoped catches some programs that, if allowed to escape
+    the renamer, would accidentally be accepted by the typechecker. Here is one
+    such program (#18021):
+
+      class C5 a where
+        data family D a
+
+      instance forall a. C5 Int where
+        data instance D Int = MkD a
+
+    If this is not rejected in the renamer, the typechecker would treat this
+    program as though the `a` were existentially quantified, like so:
+
+      data instance D Int = forall a. MkD a
+
+    This is likely not what the user intended!
+
+    Here is another such program (#9574):
+
+      class Funct f where
+        type Codomain f
+      instance Funct ('KProxy :: KProxy o) where
+        type Codomain 'KProxy = NatTr (Proxy :: o -> Type)
+
+    Where:
+
+      data Proxy (a :: k) = Proxy
+      data KProxy (t :: Type) = KProxy
+      data NatTr (c :: o -> Type)
 
-   class C a where
-     type T a b
-   instance C (Maybe a) where
-     type forall b. T (Maybe a) b = Either a b
+    Note that the `o` in the `Codomain 'KProxy` instance should be considered
+    improperly scoped. It does not meet the criteria for being explicitly
+    quantified, as it is not mentioned by name on the LHS, nor does it meet the
+    criteria for being implicitly quantified, as it is used in a RHS kind
+    signature that is not outermost (see Note [Implicit quantification in type
+    synonyms]). However, `o` /is/ bound by the instance header, so if this
+    program is not rejected by the renamer, the typechecker would treat it as
+    though you had written this:
 
-Even though `a` is not bound by the forall, this is still accepted because `a`
-was previously bound by the `instance C (Maybe a)` part. (see #16116).
+      instance Funct ('KProxy :: KProxy o) where
+        type Codomain ('KProxy @o) = NatTr (Proxy :: o -> Type)
 
-In each case, the function which detects improperly bound variables on the RHS
-is GHC.Tc.Validity.checkValidFamPats.
+    Although this is a valid program, it's probably a stretch too far to turn
+    `type Codomain 'KProxy = ...` into `type Codomain ('KProxy @o) = ...` here.
+    If the user really wants the latter, it is simple enough to communicate
+    their intent by mentioning `o` on the LHS by name.
 
 Note [Type family equations and occurrences]
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


=====================================
docs/users_guide/9.2.1-notes.rst
=====================================
@@ -18,6 +18,34 @@ Language
   more restrictive than before.  See the user manual :ref:`kind-inference-data-family-instances`.
   This is a breaking change, albeit a fairly obscure one that corrects a specification bug.
 
+* GHC is stricter about checking for out-of-scope type variables on the
+  right-hand sides of associated type family instances that are not bound on
+  the left-hand side. As a result, some programs that were accidentally
+  accepted in previous versions of GHC will now be rejected, such as this
+  example: ::
+
+      class Funct f where
+        type Codomain f
+      instance Funct ('KProxy :: KProxy o) where
+        type Codomain 'KProxy = NatTr (Proxy :: o -> Type)
+
+  Where: ::
+
+      data Proxy (a :: k) = Proxy
+      data KProxy (t :: Type) = KProxy
+      data NatTr (c :: o -> Type)
+
+  GHC will now reject the ``o`` on the right-hand side of the ``Codomain``
+  instance as being out of scope, as it does not meet the requirements for
+  being explicitly bound (as it is not mentioned on the left-hand side) nor
+  implicitly bound (as it is not mentioned in an *outermost* kind signature,
+  as required by :ref:`scoping-class-params`). This program can be repaired in
+  a backwards-compatible way by mentioning ``o`` on the left-hand side: ::
+
+      instance Funct ('KProxy :: KProxy o) where
+        type Codomain ('KProxy @o) = NatTr (Proxy :: o -> Type)
+        -- Alternatively,
+        -- type Codomain ('KProxy :: KProxy o) = NatTr (Proxy :: o -> Type)
 
 Compiler
 ~~~~~~~~
@@ -52,7 +80,7 @@ Compiler
 - There is a significant refactoring in the solver; any type-checker plugins
   will have to be updated, as GHC no longer uses flattening skolems or
   flattening metavariables.
-  
+
 ``ghc-prim`` library
 ~~~~~~~~~~~~~~~~~~~~
 


=====================================
testsuite/tests/indexed-types/should_fail/T5515.stderr
=====================================
@@ -1,24 +1,8 @@
 
-T5515.hs:6:16: error:
-    • Expecting one more argument to ‘ctx’
-      Expected a type, but ‘ctx’ has kind ‘* -> Constraint’
-    • In the first argument of ‘Arg’, namely ‘ctx’
-      In the first argument of ‘ctx’, namely ‘(Arg ctx)’
-      In the class declaration for ‘Bome’
+T5515.hs:9:3: error:
+    The RHS of an associated type declaration mentions out-of-scope variable ‘a’
+      All such variables must be bound on the LHS
 
-T5515.hs:14:1: error:
-    • Type variable ‘a’ is mentioned in the RHS,
-        but not bound on the LHS of the family instance
-    • In the type instance declaration for ‘Arg’
-      In the instance declaration for ‘Some f’
-
-T5515.hs:14:10: error:
-    • Could not deduce (C f a0)
-      from the context: C f a
-        bound by an instance declaration:
-                   forall f a. C f a => Some f
-        at T5515.hs:14:10-24
-      The type variable ‘a0’ is ambiguous
-    • In the ambiguity check for an instance declaration
-      To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
-      In the instance declaration for ‘Some f’
+T5515.hs:15:3: error:
+    The RHS of an associated type declaration mentions out-of-scope variable ‘a’
+      All such variables must be bound on the LHS


=====================================
testsuite/tests/polykinds/T9574.stderr
=====================================
@@ -0,0 +1,4 @@
+
+T9574.hs:13:5: error:
+    The RHS of an associated type declaration mentions out-of-scope variable ‘o’
+      All such variables must be bound on the LHS


=====================================
testsuite/tests/polykinds/all.T
=====================================
@@ -107,7 +107,7 @@ test('T9725', normal, compile, [''])
 test('T9750', normal, compile, [''])
 test('T9569', normal, compile, [''])
 test('T9838', normal, multimod_compile, ['T9838.hs','-v0'])
-test('T9574', normal, compile, [''])
+test('T9574', normal, compile_fail, [''])
 test('T9833', normal, compile, [''])
 test('T7908', normal, compile, [''])
 test('PolyInstances', normal, compile, [''])


=====================================
testsuite/tests/rename/should_fail/T18021.hs
=====================================
@@ -0,0 +1,18 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE MultiParamTypeClasses #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeFamilies #-}
+module T18021 where
+
+class C a where
+  data D a
+
+instance forall a. C Int where
+  data instance D Int = MkD1 a
+
+class X a b
+
+instance forall a. C Bool where
+  data instance D Bool = MkD2
+    deriving (X a)


=====================================
testsuite/tests/rename/should_fail/T18021.stderr
=====================================
@@ -0,0 +1,8 @@
+
+T18021.hs:12:3: error:
+    The RHS of an associated type declaration mentions out-of-scope variable ‘a’
+      All such variables must be bound on the LHS
+
+T18021.hs:17:3: error:
+    The RHS of an associated type declaration mentions out-of-scope variable ‘a’
+      All such variables must be bound on the LHS


=====================================
testsuite/tests/rename/should_fail/all.T
=====================================
@@ -156,6 +156,7 @@ test('T16504', normal, compile_fail, [''])
 test('T14548', normal, compile_fail, [''])
 test('T16610', normal, compile_fail, [''])
 test('T17593', normal, compile_fail, [''])
+test('T18021', normal, compile_fail, [''])
 test('T18145', normal, compile_fail, [''])
 test('T18240a', normal, compile_fail, [''])
 test('T18240b', normal, compile_fail, [''])



View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/b1178cbc87feb1ec9c2bf98e0ad347f99dd3f20e

-- 
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/commit/b1178cbc87feb1ec9c2bf98e0ad347f99dd3f20e
You're receiving this email because of your account on gitlab.haskell.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20201217/b0cf9ec3/attachment-0001.html>


More information about the ghc-commits mailing list