diff options
| author | Ryan Scott <ryan.gl.scott@gmail.com> | 2018-04-19 08:30:53 -0400 | 
|---|---|---|
| committer | Ryan Scott <ryan.gl.scott@gmail.com> | 2018-04-19 08:30:54 -0400 | 
| commit | 447d1264abcd52bdd98267d109bfc1ff1c96ad27 (patch) | |
| tree | 6f1be25d242ad28f03b7dd1f6a5f3535aa35fd11 /compiler | |
| parent | 5d76846405240c051b00cddcda9d8d02c880968e (diff) | |
| download | haskell-447d1264abcd52bdd98267d109bfc1ff1c96ad27.tar.gz | |
Fix #14710 with more validity checks during renaming
Summary:
#14710 revealed two unfortunate regressions related to kind
polymorphism that had crept in in recent GHC releases:
1. While GHC was able to catch illegal uses of kind polymorphism
   (i.e., if `PolyKinds` wasn't enabled) in limited situations, it
   wasn't able to catch kind polymorphism of the following form:
```lang=haskell
f :: forall a. a -> a
f x = const x g
  where
    g :: Proxy (x :: a)
    g = Proxy
```
Note that the variable `a` is being used as a kind variable in the
type signature of `g`, but GHC happily accepts it, even without
the use of `PolyKinds`.
2. If you have `PolyKinds` (but not `TypeInType`) enabled, then GHC
   incorrectly accepts the following definition:
```lang=haskell
f :: forall k (a :: k). Proxy a
f = Proxy
```
Even though `k` is explicitly bound and then later used as a kind
variable within the same telescope.
This patch fixes these two bugs as follows:
1. Whenever we rename any `HsTyVar`, we check if the following three
   criteria are met:
   (a) It's a type variable
   (b) It's used at the kind level
   (c) `PolyKinds` is not enabled
   If so, then we have found an illegal use of kind polymorphism, so
   throw an error.
   This check replaces the `checkBadKindBndrs` function, which could
   only catch illegal uses of kind polymorphism in very limited
   situations (when the bad kind variable happened to be implicitly
   quantified in the same type signature).
2. In `extract_hs_tv_bndrs`, we must error if `TypeInType` is not
   enabled and either of the following criteria are met:
   (a) An explicitly bound type variable is used in kind position
       in the body of a `forall` type.
   (b) An explicitly bound type variable is used in kind position
       in the kind of a bound type variable in a `forall` type.
   `extract_hs_tv_bndrs` was checking (a), but not (b). Easily fixed.
Test Plan: ./validate
Reviewers: goldfire, simonpj, bgamari, hvr
Reviewed By: simonpj
Subscribers: thomie, carter
GHC Trac Issues: #14710
Differential Revision: https://phabricator.haskell.org/D4554
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rename/RnSource.hs | 2 | ||||
| -rw-r--r-- | compiler/rename/RnTypes.hs | 84 | 
2 files changed, 52 insertions, 34 deletions
| diff --git a/compiler/rename/RnSource.hs b/compiler/rename/RnSource.hs index 8bea770a08..d242ac08c6 100644 --- a/compiler/rename/RnSource.hs +++ b/compiler/rename/RnSource.hs @@ -1929,7 +1929,7 @@ rnConDecl decl@(ConDeclGADT { con_names   = names                mb_ctxt = Just (inHsDocContext ctxt)          ; traceRn "rnConDecl" (ppr names $$ ppr free_tkvs $$ ppr explicit_forall ) -        ; rnImplicitBndrs (not explicit_forall) ctxt free_tkvs $ \ implicit_tkvs -> +        ; rnImplicitBndrs (not explicit_forall) free_tkvs $ \ implicit_tkvs ->            bindLHsTyVarBndrs ctxt mb_ctxt Nothing explicit_tkvs $ \ explicit_tkvs ->      do  { (new_cxt, fvs1)    <- rnMbContext ctxt mcxt          ; (new_args, fvs2)   <- rnConDeclDetails (unLoc (head new_names)) ctxt args diff --git a/compiler/rename/RnTypes.hs b/compiler/rename/RnTypes.hs index 0aada39bd4..c4ab448e61 100644 --- a/compiler/rename/RnTypes.hs +++ b/compiler/rename/RnTypes.hs @@ -125,7 +125,7 @@ rn_hs_sig_wc_type always_bind_free_tvs ctxt         ; (tv_rdrs, nwc_rdrs') <- partition_nwcs free_vars         ; let nwc_rdrs = nubL nwc_rdrs'               bind_free_tvs = always_bind_free_tvs || not (isLHsForAllTy hs_ty) -       ; rnImplicitBndrs bind_free_tvs ctxt tv_rdrs $ \ vars -> +       ; rnImplicitBndrs bind_free_tvs tv_rdrs $ \ vars ->      do { (wcs, hs_ty', fvs1) <- rnWcBody ctxt nwc_rdrs hs_ty         ; let sig_ty' = HsWC { hswc_wcs = wcs, hswc_body = ib_ty' }               ib_ty'  = mk_implicit_bndrs vars hs_ty' fvs1 @@ -294,7 +294,7 @@ rnHsSigType :: HsDocContext -> LHsSigType GhcPs  rnHsSigType ctx (HsIB { hsib_body = hs_ty })    = do { traceRn "rnHsSigType" (ppr hs_ty)         ; vars <- extractFilteredRdrTyVarsDups hs_ty -       ; rnImplicitBndrs (not (isLHsForAllTy hs_ty)) ctx vars $ \ vars -> +       ; rnImplicitBndrs (not (isLHsForAllTy hs_ty)) vars $ \ vars ->      do { (body', fvs) <- rnLHsType ctx hs_ty         ; return ( mk_implicit_bndrs vars body' fvs, fvs ) } } @@ -303,14 +303,13 @@ rnImplicitBndrs :: Bool    -- True <=> bring into scope any free type variables                             --  we do not want to bring 'b' into scope, hence False                             -- But   f :: a -> b                             --  we want to bring both 'a' and 'b' into scope -                -> HsDocContext                  -> FreeKiTyVarsWithDups                                     -- Free vars of hs_ty (excluding wildcards)                                     -- May have duplicates, which is                                     -- checked here                  -> ([Name] -> RnM (a, FreeVars))                  -> RnM (a, FreeVars) -rnImplicitBndrs bind_free_tvs doc +rnImplicitBndrs bind_free_tvs                  fvs_with_dups@(FKTV { fktv_kis = kvs_with_dups                                      , fktv_tys = tvs_with_dups })                  thing_inside @@ -333,8 +332,6 @@ rnImplicitBndrs bind_free_tvs doc         ; loc <- getSrcSpanM         ; vars <- mapM (newLocalBndrRn . L loc . unLoc) (kvs ++ real_tvs) -       ; checkBadKindBndrs doc kvs -         ; traceRn "checkMixedVars2" $             vcat [ text "kvs_with_dups" <+> ppr kvs_with_dups                  , text "tvs_with_dups" <+> ppr tvs_with_dups ] @@ -538,7 +535,14 @@ rnHsTyKi env ty@(HsQualTy { hst_ctxt = lctxt, hst_body = tau })                  , fvs1 `plusFV` fvs2) }  rnHsTyKi env (HsTyVar _ ip (L loc rdr_name)) -  = do { name <- rnTyVar env rdr_name +  = do { when (isRnKindLevel env && isRdrTyVar rdr_name) $ +         unlessXOptM LangExt.PolyKinds $ addErr $ +         withHsDocContext (rtke_ctxt env) $ +         vcat [ text "Unexpected kind variable" <+> quotes (ppr rdr_name) +              , text "Perhaps you intended to use PolyKinds" ] +           -- Any type variable at the kind level is illegal without the use +           -- of PolyKinds (see #14710) +       ; name <- rnTyVar env rdr_name         ; return (HsTyVar noExt ip (L loc name), unitFV name) }  rnHsTyKi env ty@(HsOpTy _ ty1 l_op ty2) @@ -936,7 +940,6 @@ bindHsQTyVars doc mb_in_doc mb_assoc body_kv_occs hsq_bndrs thing_inside                  , text "bndr_kv_occs"   <+> ppr bndr_kv_occs                  , text "wubble" <+> ppr ((kv_occs \\ bndrs) \\ bndr_kv_occs)                  ] -       ; checkBadKindBndrs doc implicit_kvs         ; checkMixedVars kv_occs bndrs         ; implicit_kv_nms <- mapM (newTyVarNameRn mb_assoc) implicit_kvs @@ -1041,6 +1044,34 @@ In implementation terms    scope in the kind of 'a'.  * Similarly in extract_hs_tv_bndrs + +Note [Variables used as both types and kinds] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In (checkMixedVars kvs tvs), we bind the type variables tvs, and kvs is the +set of free variables of the kinds in the scope of the binding. Here is one +typical example: + +   forall a b. a -> (b::k) -> (c::a) + +Here, tvs will be {a,b}, and kvs {k,a}. +Without -XTypeInType we want to complain that `a` is used both +as a type and a kind. + +Specifically, check that there is no overlap between kvs and tvs +See typecheck/should_fail/T11963 for examples. + +We must also make sure that kvs includes all of variables in the kinds of type +variable bindings. For instance: + +   forall k (a :: k). Proxy a + +If we only look in the body of the `forall` type, we will mistakenly conclude +that kvs is {}. But in fact, the type variable `k` is also used as a kind +variable in (a :: k), later in the binding. (This mistake lead to #14710.) +So tvs is {k,a} and kvs is {k}, so we must also reject this without the use +of -XTypeInType. + +NB: we do this only at the binding site of 'tvs'.  -}  bindLHsTyVarBndrs :: HsDocContext @@ -1515,15 +1546,6 @@ unexpectedTypeSigErr ty    = hang (text "Illegal type signature:" <+> quotes (ppr ty))         2 (text "Type signatures are only allowed in patterns with ScopedTypeVariables") -checkBadKindBndrs :: HsDocContext -> [Located RdrName] -> RnM () -checkBadKindBndrs doc kvs -  = unless (null kvs)             $ -    unlessXOptM LangExt.PolyKinds $ -    addErr (withHsDocContext doc  $ -            hang (text "Unexpected kind variable" <> plural kvs -                  <+> pprQuotedList kvs) -               2 (text "Perhaps you intended to use PolyKinds")) -  badKindSigErr :: HsDocContext -> LHsType GhcPs -> TcM ()  badKindSigErr doc (L loc ty)    = setSrcSpan loc $ addErr $ @@ -1866,16 +1888,22 @@ extract_hs_tv_bndrs tv_bndrs    | otherwise    = do { bndr_kvs <- extract_hs_tv_bndrs_kvs tv_bndrs -       ; let tv_bndr_rdrs :: [Located RdrName] +       ; let tv_bndr_rdrs, all_kv_occs :: [Located RdrName]               tv_bndr_rdrs = map hsLTyVarLocName tv_bndrs +             -- We must include both kind variables from the binding as well +             -- as the body of the `forall` type. +             -- See Note [Variables used as both types and kinds]. +             all_kv_occs = bndr_kvs ++ body_kvs         ; traceRn "checkMixedVars1" $ -           vcat [ text "body_kvs"     <+> ppr body_kvs +           vcat [ text "bndr_kvs"     <+> ppr bndr_kvs +                , text "body_kvs"     <+> ppr body_kvs +                , text "all_kv_occs"  <+> ppr all_kv_occs                  , text "tv_bndr_rdrs" <+> ppr tv_bndr_rdrs ] -       ; checkMixedVars body_kvs tv_bndr_rdrs +       ; checkMixedVars all_kv_occs tv_bndr_rdrs         ; return $ -         FKTV (filterOut (`elemRdr` tv_bndr_rdrs) (bndr_kvs ++ body_kvs) +         FKTV (filterOut (`elemRdr` tv_bndr_rdrs) all_kv_occs                      -- NB: delete all tv_bndr_rdrs from bndr_kvs as well                      -- as body_kvs; see Note [Kind variable scoping]                  ++ acc_kvs) @@ -1907,19 +1935,9 @@ nubL = nubBy eqLocated  elemRdr :: Located RdrName -> [Located RdrName] -> Bool  elemRdr x = any (eqLocated x) +-- Check for type variables that are also used as kinds without the use of +-- -XTypeInType. See Note [Variables used as both types and kinds].  checkMixedVars :: [Located RdrName] -> [Located RdrName] -> RnM () --- In (checkMixedVars kvs tvs) we are about to bind the type --- variables tvs, and kvs is the set of free variables of the kinds --- in the scope of the binding.  E.g. ---    forall a b. a -> (b::k) -> (c::a) --- Here tv will be {a,b}, and kvs {k,a}. --- Without -XTypeInType we want to complain that 'a' is used both --- as a type and a kind. --- --- Specifically, check that there is no overlap between kvs and tvs --- See typecheck/should_fail/T11963 for examples --- --- NB: we do this only at the binding site of 'tvs'.  checkMixedVars kvs tvs    = do { type_in_type <- xoptM LangExt.TypeInType         ; unless type_in_type $ | 
