diff options
author | Richard Eisenberg <rae@cs.brynmawr.edu> | 2017-05-02 18:56:30 -0400 |
---|---|---|
committer | Ben Gamari <ben@smart-cactus.org> | 2017-05-02 23:07:26 -0400 |
commit | b460d6c99316deac2b8022a4fb7dddc57c052a2a (patch) | |
tree | 040232c23154f83a2cbf8a438e2521b7774ad18d | |
parent | b1aede61350a9c0a33c6d034de93a249c000a84c (diff) | |
download | haskell-b460d6c99316deac2b8022a4fb7dddc57c052a2a.tar.gz |
Fix #13233 by checking for lev-poly primops
The implementation plan is all in Note [Detecting forced eta expansion]
in DsExpr.
Test Plan: ./validate, codeGen/should_fail/T13233
Reviewers: simonpj, austin, bgamari
Subscribers: rwbarton, thomie
GHC Trac Issues: #13233
Differential Revision: https://phabricator.haskell.org/D3490
-rw-r--r-- | compiler/coreSyn/CoreLint.hs | 4 | ||||
-rw-r--r-- | compiler/coreSyn/CoreSyn.hs | 3 | ||||
-rw-r--r-- | compiler/deSugar/Check.hs | 2 | ||||
-rw-r--r-- | compiler/deSugar/DsArrows.hs | 4 | ||||
-rw-r--r-- | compiler/deSugar/DsExpr.hs | 262 | ||||
-rw-r--r-- | compiler/deSugar/DsMonad.hs | 3 | ||||
-rw-r--r-- | compiler/hsSyn/HsExpr.hs | 3 | ||||
-rw-r--r-- | compiler/hsSyn/HsUtils.hs | 7 | ||||
-rw-r--r-- | compiler/typecheck/TcBinds.hs | 2 | ||||
-rw-r--r-- | compiler/typecheck/TcExpr.hs | 20 | ||||
-rw-r--r-- | compiler/typecheck/TcMatches.hs | 4 | ||||
-rw-r--r-- | compiler/typecheck/TcSplice.hs | 4 | ||||
-rw-r--r-- | compiler/types/Kind.hs | 40 | ||||
-rw-r--r-- | testsuite/tests/codeGen/should_compile/T13233.hs | 12 | ||||
-rw-r--r-- | testsuite/tests/codeGen/should_compile/all.T | 1 | ||||
-rw-r--r-- | testsuite/tests/codeGen/should_fail/T13233.hs | 27 | ||||
-rw-r--r-- | testsuite/tests/codeGen/should_fail/T13233.stderr | 24 | ||||
-rw-r--r-- | testsuite/tests/codeGen/should_fail/all.T | 1 |
18 files changed, 269 insertions, 154 deletions
diff --git a/compiler/coreSyn/CoreLint.hs b/compiler/coreSyn/CoreLint.hs index 8182272be7..0888afbeeb 100644 --- a/compiler/coreSyn/CoreLint.hs +++ b/compiler/coreSyn/CoreLint.hs @@ -1350,8 +1350,8 @@ lintArrow :: SDoc -> LintedKind -> LintedKind -> LintM LintedKind -- See Note [GHC Formalism] lintArrow what k1 k2 -- Eg lintArrow "type or kind `blah'" k1 k2 -- or lintarrow "coercion `blah'" k1 k2 - = do { unless (okArrowArgKind k1) (addErrL (msg (text "argument") k1)) - ; unless (okArrowResultKind k2) (addErrL (msg (text "result") k2)) + = do { unless (classifiesTypeWithValues k1) (addErrL (msg (text "argument") k1)) + ; unless (classifiesTypeWithValues k2) (addErrL (msg (text "result") k2)) ; return liftedTypeKind } where msg ar k diff --git a/compiler/coreSyn/CoreSyn.hs b/compiler/coreSyn/CoreSyn.hs index b5e97f7fa7..a669437c68 100644 --- a/compiler/coreSyn/CoreSyn.hs +++ b/compiler/coreSyn/CoreSyn.hs @@ -457,7 +457,8 @@ See #case_invariants# Note [Levity polymorphism invariants] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The levity-polymorphism invariants are these: +The levity-polymorphism invariants are these (as per "Levity Polymorphism", +PLDI '17): * The type of a term-binder must not be levity-polymorphic, unless it is a let(rec)-bound join point diff --git a/compiler/deSugar/Check.hs b/compiler/deSugar/Check.hs index c08353a15b..1b02502a31 100644 --- a/compiler/deSugar/Check.hs +++ b/compiler/deSugar/Check.hs @@ -580,7 +580,7 @@ translatePat fam_insts pat = case pat of | otherwise -> do ps <- translatePat fam_insts p (xp,xe) <- mkPmId2Forms ty - let g = mkGuard ps (HsWrap wrapper (unLoc xe)) + let g = mkGuard ps (mkHsWrap wrapper (unLoc xe)) return [xp,g] -- (n + k) ===> x (True <- x >= k) (n <- x-k) diff --git a/compiler/deSugar/DsArrows.hs b/compiler/deSugar/DsArrows.hs index c3be55504b..4fe43eb1c0 100644 --- a/compiler/deSugar/DsArrows.hs +++ b/compiler/deSugar/DsArrows.hs @@ -575,8 +575,8 @@ dsCmd ids local_vars stack_ty res_ty let left_id = HsConLikeOut (RealDataCon left_con) right_id = HsConLikeOut (RealDataCon right_con) - left_expr ty1 ty2 e = noLoc $ HsApp (noLoc $ HsWrap (mkWpTyApps [ty1, ty2]) left_id ) e - right_expr ty1 ty2 e = noLoc $ HsApp (noLoc $ HsWrap (mkWpTyApps [ty1, ty2]) right_id) e + left_expr ty1 ty2 e = noLoc $ HsApp (noLoc $ mkHsWrap (mkWpTyApps [ty1, ty2]) left_id ) e + right_expr ty1 ty2 e = noLoc $ HsApp (noLoc $ mkHsWrap (mkWpTyApps [ty1, ty2]) right_id) e -- Prefix each tuple with a distinct series of Left's and Right's, -- in a balanced way, keeping track of the types. diff --git a/compiler/deSugar/DsExpr.hs b/compiler/deSugar/DsExpr.hs index 39f76ea2c0..d4a96e6f3f 100644 --- a/compiler/deSugar/DsExpr.hs +++ b/compiler/deSugar/DsExpr.hs @@ -252,27 +252,33 @@ dsLExprNoLP (L loc e) ; return e' } dsExpr :: HsExpr Id -> DsM CoreExpr -dsExpr (HsPar e) = dsLExpr e -dsExpr (ExprWithTySigOut e _) = dsLExpr e -dsExpr (HsVar (L _ var)) = return (varToCoreExpr var) - -- See Note [Desugaring vars] -dsExpr (HsUnboundVar {}) = panic "dsExpr: HsUnboundVar" -- Typechecker eliminates them -dsExpr (HsConLikeOut con) = return (dsConLike con) -dsExpr (HsIPVar _) = panic "dsExpr: HsIPVar" -dsExpr (HsOverLabel{}) = panic "dsExpr: HsOverLabel" -dsExpr (HsLit lit) = dsLit lit -dsExpr (HsOverLit lit) = dsOverLit lit - -dsExpr (HsWrap co_fn e) - = do { e' <- dsExpr e +dsExpr = ds_expr False + +ds_expr :: Bool -- are we directly inside an HsWrap? + -- See Wrinkle in Note [Detecting forced eta expansion] + -> HsExpr Id -> DsM CoreExpr +ds_expr _ (HsPar e) = dsLExpr e +ds_expr _ (ExprWithTySigOut e _) = dsLExpr e +ds_expr w (HsVar (L _ var)) = dsHsVar w var +ds_expr _ (HsUnboundVar {}) = panic "dsExpr: HsUnboundVar" -- Typechecker eliminates them +ds_expr w (HsConLikeOut con) = dsConLike w con +ds_expr _ (HsIPVar _) = panic "dsExpr: HsIPVar" +ds_expr _ (HsOverLabel{}) = panic "dsExpr: HsOverLabel" +ds_expr _ (HsLit lit) = dsLit lit +ds_expr _ (HsOverLit lit) = dsOverLit lit + +ds_expr _ (HsWrap co_fn e) + = do { e' <- ds_expr True e ; wrap' <- dsHsWrapper co_fn ; dflags <- getDynFlags ; let wrapped_e = wrap' e' - ; warnAboutIdentities dflags e' (exprType wrapped_e) + wrapped_ty = exprType wrapped_e + ; checkForcedEtaExpansion e wrapped_ty -- See Note [Detecting forced eta expansion] + ; warnAboutIdentities dflags e' wrapped_ty ; return wrapped_e } -dsExpr (NegApp (L loc (HsOverLit lit@(OverLit { ol_val = HsIntegral src i }))) - neg_expr) +ds_expr _ (NegApp (L loc (HsOverLit lit@(OverLit { ol_val = HsIntegral src i }))) + neg_expr) = do { expr' <- putSrcSpanDs loc $ do { dflags <- getDynFlags ; warnAboutOverflowedLiterals dflags @@ -280,23 +286,23 @@ dsExpr (NegApp (L loc (HsOverLit lit@(OverLit { ol_val = HsIntegral src i }))) ; dsOverLit' dflags lit } ; dsSyntaxExpr neg_expr [expr'] } -dsExpr (NegApp expr neg_expr) +ds_expr _ (NegApp expr neg_expr) = do { expr' <- dsLExpr expr ; dsSyntaxExpr neg_expr [expr'] } -dsExpr (HsLam a_Match) +ds_expr _ (HsLam a_Match) = uncurry mkLams <$> matchWrapper LambdaExpr Nothing a_Match -dsExpr (HsLamCase matches) +ds_expr _ (HsLamCase matches) = do { ([discrim_var], matching_code) <- matchWrapper CaseAlt Nothing matches ; return $ Lam discrim_var matching_code } -dsExpr e@(HsApp fun arg) +ds_expr _ e@(HsApp fun arg) = do { fun' <- dsLExpr fun ; dsWhenNoErrs (dsLExprNoLP arg) (\arg' -> mkCoreAppDs (text "HsApp" <+> ppr e) fun' arg') } -dsExpr (HsAppTypeOut e _) +ds_expr _ (HsAppTypeOut e _) -- ignore type arguments here; they're in the wrappers instead at this point = dsLExpr e @@ -340,19 +346,19 @@ If \tr{expr} is actually just a variable, say, then the simplifier will sort it out. -} -dsExpr e@(OpApp e1 op _ e2) +ds_expr _ e@(OpApp e1 op _ e2) = -- for the type of y, we need the type of op's 2nd argument do { op' <- dsLExpr op ; dsWhenNoErrs (mapM dsLExprNoLP [e1, e2]) (\exprs' -> mkCoreAppsDs (text "opapp" <+> ppr e) op' exprs') } -dsExpr (SectionL expr op) -- Desugar (e !) to ((!) e) +ds_expr _ (SectionL expr op) -- Desugar (e !) to ((!) e) = do { op' <- dsLExpr op ; dsWhenNoErrs (dsLExprNoLP expr) (\expr' -> mkCoreAppDs (text "sectionl" <+> ppr expr) op' expr') } -- dsLExpr (SectionR op expr) -- \ x -> op x expr -dsExpr e@(SectionR op expr) = do +ds_expr _ e@(SectionR op expr) = do core_op <- dsLExpr op -- for the type of x, we need the type of op's 2nd argument let (x_ty:y_ty:_, _) = splitFunTys (exprType core_op) @@ -363,7 +369,7 @@ dsExpr e@(SectionR op expr) = do Lam x_id (mkCoreAppsDs (text "sectionr" <+> ppr e) core_op [Var x_id, Var y_id])) -dsExpr (ExplicitTuple tup_args boxity) +ds_expr _ (ExplicitTuple tup_args boxity) = do { let go (lam_vars, args) (L _ (Missing ty)) -- For every missing expression, we need -- another lambda in the desugaring. @@ -381,14 +387,14 @@ dsExpr (ExplicitTuple tup_args boxity) ; return $ mkCoreLams lam_vars $ mkCoreTupBoxity boxity args } -dsExpr (ExplicitSum alt arity expr types) +ds_expr _ (ExplicitSum alt arity expr types) = do { core_expr <- dsLExpr expr ; return $ mkCoreConApps (sumDataCon alt arity) (map (Type . getRuntimeRep "dsExpr ExplicitSum") types ++ map Type types ++ [core_expr]) } -dsExpr (HsSCC _ cc expr@(L loc _)) = do +ds_expr _ (HsSCC _ cc expr@(L loc _)) = do dflags <- getDynFlags if gopt Opt_SccProfilingOn dflags then do @@ -399,31 +405,31 @@ dsExpr (HsSCC _ cc expr@(L loc _)) = do <$> dsLExpr expr else dsLExpr expr -dsExpr (HsCoreAnn _ _ expr) +ds_expr _ (HsCoreAnn _ _ expr) = dsLExpr expr -dsExpr (HsCase discrim matches) +ds_expr _ (HsCase discrim matches) = do { core_discrim <- dsLExpr discrim ; ([discrim_var], matching_code) <- matchWrapper CaseAlt (Just discrim) matches ; return (bindNonRec discrim_var core_discrim matching_code) } -- Pepe: The binds are in scope in the body but NOT in the binding group -- This is to avoid silliness in breakpoints -dsExpr (HsLet binds body) = do +ds_expr _ (HsLet binds body) = do body' <- dsLExpr body dsLocalBinds binds body' -- We need the `ListComp' form to use `deListComp' (rather than the "do" form) -- because the interpretation of `stmts' depends on what sort of thing it is. -- -dsExpr (HsDo ListComp (L _ stmts) res_ty) = dsListComp stmts res_ty -dsExpr (HsDo PArrComp (L _ stmts) _) = dsPArrComp (map unLoc stmts) -dsExpr (HsDo DoExpr (L _ stmts) _) = dsDo stmts -dsExpr (HsDo GhciStmtCtxt (L _ stmts) _) = dsDo stmts -dsExpr (HsDo MDoExpr (L _ stmts) _) = dsDo stmts -dsExpr (HsDo MonadComp (L _ stmts) _) = dsMonadComp stmts - -dsExpr (HsIf mb_fun guard_expr then_expr else_expr) +ds_expr _ (HsDo ListComp (L _ stmts) res_ty) = dsListComp stmts res_ty +ds_expr _ (HsDo PArrComp (L _ stmts) _) = dsPArrComp (map unLoc stmts) +ds_expr _ (HsDo DoExpr (L _ stmts) _) = dsDo stmts +ds_expr _ (HsDo GhciStmtCtxt (L _ stmts) _) = dsDo stmts +ds_expr _ (HsDo MDoExpr (L _ stmts) _) = dsDo stmts +ds_expr _ (HsDo MonadComp (L _ stmts) _) = dsMonadComp stmts + +ds_expr _ (HsIf mb_fun guard_expr then_expr else_expr) = do { pred <- dsLExpr guard_expr ; b1 <- dsLExpr then_expr ; b2 <- dsLExpr else_expr @@ -431,7 +437,7 @@ dsExpr (HsIf mb_fun guard_expr then_expr else_expr) Just fun -> dsSyntaxExpr fun [pred, b1, b2] Nothing -> return $ mkIfThenElse pred b1 b2 } -dsExpr (HsMultiIf res_ty alts) +ds_expr _ (HsMultiIf res_ty alts) | null alts = mkErrorExpr @@ -450,16 +456,16 @@ dsExpr (HsMultiIf res_ty alts) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -} -dsExpr (ExplicitList elt_ty wit xs) +ds_expr _ (ExplicitList elt_ty wit xs) = dsExplicitList elt_ty wit xs -- We desugar [:x1, ..., xn:] as -- singletonP x1 +:+ ... +:+ singletonP xn -- -dsExpr (ExplicitPArr ty []) = do +ds_expr _ (ExplicitPArr ty []) = do emptyP <- dsDPHBuiltin emptyPVar return (Var emptyP `App` Type ty) -dsExpr (ExplicitPArr ty xs) = do +ds_expr _ (ExplicitPArr ty xs) = do singletonP <- dsDPHBuiltin singletonPVar appP <- dsDPHBuiltin appPVar xs' <- mapM dsLExprNoLP xs @@ -468,19 +474,19 @@ dsExpr (ExplicitPArr ty xs) = do return . foldr1 (binary appP) $ map (unary singletonP) xs' -dsExpr (ArithSeq expr witness seq) +ds_expr _ (ArithSeq expr witness seq) = case witness of Nothing -> dsArithSeq expr seq Just fl -> do { newArithSeq <- dsArithSeq expr seq ; dsSyntaxExpr fl [newArithSeq] } -dsExpr (PArrSeq expr (FromTo from to)) +ds_expr _ (PArrSeq expr (FromTo from to)) = mkApps <$> dsExpr expr <*> mapM dsLExprNoLP [from, to] -dsExpr (PArrSeq expr (FromThenTo from thn to)) +ds_expr _ (PArrSeq expr (FromThenTo from thn to)) = mkApps <$> dsExpr expr <*> mapM dsLExprNoLP [from, thn, to] -dsExpr (PArrSeq _ _) +ds_expr _ (PArrSeq _ _) = panic "DsExpr.dsExpr: Infinite parallel array!" -- the parser shouldn't have generated it and the renamer and typechecker -- shouldn't have let it through @@ -496,7 +502,7 @@ See Note [Grand plan for static forms] in StaticPtrTable for an overview. g = ... makeStatic loc f ... -} -dsExpr (HsStatic _ expr@(L loc _)) = do +ds_expr _ (HsStatic _ expr@(L loc _)) = do expr_ds <- dsLExprNoLP expr let ty = exprType expr_ds makeStaticId <- dsLookupGlobalId makeStaticName @@ -538,8 +544,8 @@ We also handle @C{}@ as valid construction syntax for an unlabelled constructor @C@, setting all of @C@'s fields to bottom. -} -dsExpr (RecordCon { rcon_con_expr = con_expr, rcon_flds = rbinds - , rcon_con_like = con_like }) +ds_expr _ (RecordCon { rcon_con_expr = con_expr, rcon_flds = rbinds + , rcon_con_like = con_like }) = do { con_expr' <- dsExpr con_expr ; let (arg_tys, _) = tcSplitFunTys (exprType con_expr') @@ -597,10 +603,10 @@ So we need to cast (T a Int) to (T a b). Sigh. -} -dsExpr expr@(RecordUpd { rupd_expr = record_expr, rupd_flds = fields - , rupd_cons = cons_to_upd - , rupd_in_tys = in_inst_tys, rupd_out_tys = out_inst_tys - , rupd_wrap = dict_req_wrap } ) +ds_expr _ expr@(RecordUpd { rupd_expr = record_expr, rupd_flds = fields + , rupd_cons = cons_to_upd + , rupd_in_tys = in_inst_tys, rupd_out_tys = out_inst_tys + , rupd_wrap = dict_req_wrap } ) | null fields = dsLExpr record_expr | otherwise @@ -664,7 +670,7 @@ dsExpr expr@(RecordUpd { rupd_expr = record_expr, rupd_flds = fields mk_val_arg fl pat_arg_id = nlHsVar (lookupNameEnv upd_fld_env (flSelector fl) `orElse` pat_arg_id) - inst_con = noLoc $ HsWrap wrap (HsConLikeOut con) + inst_con = noLoc $ mkHsWrap wrap (HsConLikeOut con) -- Reconstruct with the WrapId so that unpacking happens -- The order here is because of the order in `TcPatSyn`. wrap = mkWpEvVarApps theta_vars <.> @@ -716,16 +722,16 @@ dsExpr expr@(RecordUpd { rupd_expr = record_expr, rupd_flds = fields -- Template Haskell stuff -dsExpr (HsRnBracketOut _ _) = panic "dsExpr HsRnBracketOut" -dsExpr (HsTcBracketOut x ps) = dsBracket x ps -dsExpr (HsSpliceE s) = pprPanic "dsExpr:splice" (ppr s) +ds_expr _ (HsRnBracketOut _ _) = panic "dsExpr HsRnBracketOut" +ds_expr _ (HsTcBracketOut x ps) = dsBracket x ps +ds_expr _ (HsSpliceE s) = pprPanic "dsExpr:splice" (ppr s) -- Arrow notation extension -dsExpr (HsProc pat cmd) = dsProcExpr pat cmd +ds_expr _ (HsProc pat cmd) = dsProcExpr pat cmd -- Hpc Support -dsExpr (HsTick tickish e) = do +ds_expr _ (HsTick tickish e) = do e' <- dsLExpr e return (Tick tickish e') @@ -736,30 +742,30 @@ dsExpr (HsTick tickish e) = do -- (did you go here: YES or NO), but will effect accurate -- tick counting. -dsExpr (HsBinTick ixT ixF e) = do +ds_expr _ (HsBinTick ixT ixF e) = do e2 <- dsLExpr e do { ASSERT(exprType e2 `eqType` boolTy) mkBinaryTickBox ixT ixF e2 } -dsExpr (HsTickPragma _ _ _ expr) = do +ds_expr _ (HsTickPragma _ _ _ expr) = do dflags <- getDynFlags if gopt Opt_Hpc dflags then panic "dsExpr:HsTickPragma" else dsLExpr expr -- HsSyn constructs that just shouldn't be here: -dsExpr (ExprWithTySig {}) = panic "dsExpr:ExprWithTySig" -dsExpr (HsBracket {}) = panic "dsExpr:HsBracket" -dsExpr (HsArrApp {}) = panic "dsExpr:HsArrApp" -dsExpr (HsArrForm {}) = panic "dsExpr:HsArrForm" -dsExpr (EWildPat {}) = panic "dsExpr:EWildPat" -dsExpr (EAsPat {}) = panic "dsExpr:EAsPat" -dsExpr (EViewPat {}) = panic "dsExpr:EViewPat" -dsExpr (ELazyPat {}) = panic "dsExpr:ELazyPat" -dsExpr (HsAppType {}) = panic "dsExpr:HsAppType" -- removed by typechecker -dsExpr (HsDo {}) = panic "dsExpr:HsDo" -dsExpr (HsRecFld {}) = panic "dsExpr:HsRecFld" +ds_expr _ (ExprWithTySig {}) = panic "dsExpr:ExprWithTySig" +ds_expr _ (HsBracket {}) = panic "dsExpr:HsBracket" +ds_expr _ (HsArrApp {}) = panic "dsExpr:HsArrApp" +ds_expr _ (HsArrForm {}) = panic "dsExpr:HsArrForm" +ds_expr _ (EWildPat {}) = panic "dsExpr:EWildPat" +ds_expr _ (EAsPat {}) = panic "dsExpr:EAsPat" +ds_expr _ (EViewPat {}) = panic "dsExpr:EViewPat" +ds_expr _ (ELazyPat {}) = panic "dsExpr:ELazyPat" +ds_expr _ (HsAppType {}) = panic "dsExpr:HsAppType" -- removed by typechecker +ds_expr _ (HsDo {}) = panic "dsExpr:HsDo" +ds_expr _ (HsRecFld {}) = panic "dsExpr:HsRecFld" ------------------------------ dsSyntaxExpr :: SyntaxExpr Id -> [CoreExpr] -> DsM CoreExpr @@ -1007,14 +1013,31 @@ mk_fail_msg dflags pat = "Pattern match failure in do expression at " ++ {- ************************************************************************ * * - Desugaring ConLikes + Desugaring Variables * * ************************************************************************ -} -dsConLike :: ConLike -> CoreExpr -dsConLike (RealDataCon dc) = Var (dataConWrapId dc) -dsConLike (PatSynCon ps) = case patSynBuilder ps of +dsHsVar :: Bool -- are we directly inside an HsWrap? + -- See Wrinkle in Note [Detecting forced eta expansion] + -> Id -> DsM CoreExpr +dsHsVar w var + | not w + , let bad_tys = badUseOfLevPolyPrimop var ty + , not (null bad_tys) + = do { levPolyPrimopErr var ty bad_tys + ; return unitExpr } -- return something eminently safe + + | otherwise + = return (varToCoreExpr var) -- See Note [Desugaring vars] + + where + ty = idType var + +dsConLike :: Bool -- as in dsHsVar + -> ConLike -> DsM CoreExpr +dsConLike w (RealDataCon dc) = dsHsVar w (dataConWrapId dc) +dsConLike _ (PatSynCon ps) = return $ case patSynBuilder ps of Just (id, add_void) | add_void -> mkCoreApp (text "dsConLike" <+> ppr ps) (Var id) (Var voidPrimId) | otherwise -> Var id @@ -1064,3 +1087,90 @@ badMonadBind rhs elt_ty , hang (text "Suppress this warning by saying") 2 (quotes $ text "_ <-" <+> ppr rhs) ] + +{- +************************************************************************ +* * + Forced eta expansion and levity polymorphism +* * +************************************************************************ + +Note [Detecting forced eta expansion] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +We cannot have levity polymorphic function arguments. See +Note [Levity polymorphism invariants] in CoreSyn. But we *can* have +functions that take levity polymorphism arguments, as long as these +functions are eta-reduced. (See #12708 for an example.) + +However, we absolutely cannot do this for functions that have no +binding (i.e., say True to Id.hasNoBinding), like primops and unboxed +tuple constructors. These get eta-expanded in CorePrep.maybeSaturate. + +Detecting when this is about to happen is a bit tricky, though. When +the desugarer is looking at the Id itself (let's be concrete and +suppose we have (#,#)), we don't know whether it will be levity +polymorphic. So the right spot seems to be to look after the Id has +been applied to its type arguments. To make the algorithm efficient, +it's important to be able to spot ((#,#) @a @b @c @d) without looking +past all the type arguments. We thus require that + * The body of an HsWrap is not an HsWrap. +With that representation invariant, we simply look inside every HsWrap +to see if its body is an HsVar whose Id hasNoBinding. Then, we look +at the wrapped type. If it has any levity polymorphic arguments, reject. + +Interestingly, this approach does not look to see whether the Id in +question will be eta expanded. The logic is this: + * Either the Id in question is saturated or not. + * If it is, then it surely can't have levity polymorphic arguments. + If its wrapped type contains levity polymorphic arguments, reject. + * If it's not, then it can't be eta expanded with levity polymorphic + argument. If its wrapped type contains levity polymorphic arguments, reject. +So, either way, we're good to reject. + +Wrinkle +~~~~~~~ +Not all polymorphic Ids are wrapped in +HsWrap, due to the lazy instantiation of TypeApplications. (See "Visible type +application", ESOP '16.) But if we spot a levity-polymorphic hasNoBinding Id +without a wrapper, then that is surely problem and we can reject. + +We thus have a parameter to `dsExpr` that tracks whether or not we are +directly in an HsWrap. If we find a levity-polymorphic hasNoBinding Id when +we're not directly in an HsWrap, reject. + +-} + +-- | Takes an expression and its instantiated type. If the expression is an +-- HsVar with a hasNoBinding primop and the type has levity-polymorphic arguments, +-- issue an error. See Note [Detecting forced eta expansion] +checkForcedEtaExpansion :: HsExpr Id -> Type -> DsM () +checkForcedEtaExpansion expr ty + | Just var <- case expr of + HsVar (L _ var) -> Just var + HsConLikeOut (RealDataCon dc) -> Just (dataConWrapId dc) + _ -> Nothing + , let bad_tys = badUseOfLevPolyPrimop var ty + , not (null bad_tys) + = levPolyPrimopErr var ty bad_tys +checkForcedEtaExpansion _ _ = return () + +-- | Is this a hasNoBinding Id with a levity-polymorphic type? +-- Returns the arguments that are levity polymorphic if they are bad; +-- or an empty list otherwise +-- See Note [Detecting forced eta expansion] +badUseOfLevPolyPrimop :: Id -> Type -> [Type] +badUseOfLevPolyPrimop id ty + | hasNoBinding id + = filter isTypeLevPoly arg_tys + | otherwise + = [] + where + (binders, _) = splitPiTys ty + arg_tys = mapMaybe binderRelevantType_maybe binders + +levPolyPrimopErr :: Id -> Type -> [Type] -> DsM () +levPolyPrimopErr primop ty bad_tys + = errDs $ vcat [ hang (text "Cannot use primitive with levity-polymorphic arguments:") + 2 (ppr primop <+> dcolon <+> ppr ty) + , hang (text "Levity-polymorphic arguments:") + 2 (vcat (map (\t -> ppr t <+> dcolon <+> ppr (typeKind t)) bad_tys)) ] diff --git a/compiler/deSugar/DsMonad.hs b/compiler/deSugar/DsMonad.hs index fdca76c5b8..8345859d92 100644 --- a/compiler/deSugar/DsMonad.hs +++ b/compiler/deSugar/DsMonad.hs @@ -289,8 +289,7 @@ it easier to read debugging output. Note [Levity polymorphism checking] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -According to the Levity Polymorphism paper -<http://cs.brynmawr.edu/~rae/papers/2017/levity/levity.pdf>, levity +According to the "Levity Polymorphism" paper (PLDI '17), levity polymorphism is forbidden in precisely two places: in the type of a bound term-level argument and in the type of an argument to a function. The paper explains it more fully, but briefly: expressions in these contexts need to be diff --git a/compiler/hsSyn/HsExpr.hs b/compiler/hsSyn/HsExpr.hs index f3cc3d0861..64e2ffef73 100644 --- a/compiler/hsSyn/HsExpr.hs +++ b/compiler/hsSyn/HsExpr.hs @@ -689,6 +689,9 @@ data HsExpr id --------------------------------------- -- Finally, HsWrap appears only in typechecker output + -- The contained Expr is *NOT* itself an HsWrap. + -- See Note [Detecting forced eta expansion] in DsExpr. This invariant + -- is maintained by HsUtils.mkHsWrap. | HsWrap HsWrapper -- TRANSLATION (HsExpr id) diff --git a/compiler/hsSyn/HsUtils.hs b/compiler/hsSyn/HsUtils.hs index c7d43b02a1..1be9055402 100644 --- a/compiler/hsSyn/HsUtils.hs +++ b/compiler/hsSyn/HsUtils.hs @@ -196,7 +196,7 @@ mkHsCaseAlt pat expr = mkSimpleMatch CaseAlt [pat] expr nlHsTyApp :: name -> [Type] -> LHsExpr name -nlHsTyApp fun_id tys = noLoc (HsWrap (mkWpTyApps tys) (HsVar (noLoc fun_id))) +nlHsTyApp fun_id tys = noLoc (mkHsWrap (mkWpTyApps tys) (HsVar (noLoc fun_id))) nlHsTyApps :: name -> [Type] -> [LHsExpr name] -> LHsExpr name nlHsTyApps fun_id tys xs = foldl nlHsApp (nlHsTyApp fun_id tys) xs @@ -654,9 +654,12 @@ typeToLHsType ty mkLHsWrap :: HsWrapper -> LHsExpr id -> LHsExpr id mkLHsWrap co_fn (L loc e) = L loc (mkHsWrap co_fn e) +-- Avoid (HsWrap co (HsWrap co' _)). +-- See Note [Detecting forced eta expansion] in DsExpr mkHsWrap :: HsWrapper -> HsExpr id -> HsExpr id mkHsWrap co_fn e | isIdHsWrapper co_fn = e - | otherwise = HsWrap co_fn e +mkHsWrap co_fn (HsWrap co_fn' e) = mkHsWrap (co_fn <.> co_fn') e +mkHsWrap co_fn e = HsWrap co_fn e mkHsWrapCo :: TcCoercionN -- A Nominal coercion a ~N b -> HsExpr id -> HsExpr id diff --git a/compiler/typecheck/TcBinds.hs b/compiler/typecheck/TcBinds.hs index f3874ab1dc..70e444e65c 100644 --- a/compiler/typecheck/TcBinds.hs +++ b/compiler/typecheck/TcBinds.hs @@ -371,7 +371,7 @@ tcLocalBinds (HsIPBinds (IPBinds ip_binds _)) thing_inside -- Coerces a `t` into a dictionry for `IP "x" t`. -- co : t -> IP "x" t - toDict ipClass x ty = HsWrap $ mkWpCastR $ + toDict ipClass x ty = mkHsWrap $ mkWpCastR $ wrapIP $ mkClassPred ipClass [x,ty] {- Note [Implicit parameter untouchables] diff --git a/compiler/typecheck/TcExpr.hs b/compiler/typecheck/TcExpr.hs index e521b735a4..7f7f734ca9 100644 --- a/compiler/typecheck/TcExpr.hs +++ b/compiler/typecheck/TcExpr.hs @@ -211,7 +211,7 @@ tcExpr e@(HsIPVar x) res_ty ip_ty res_ty } where -- Coerces a dictionary for `IP "x" t` into `t`. - fromDict ipClass x ty = HsWrap $ mkWpCastR $ + fromDict ipClass x ty = mkHsWrap $ mkWpCastR $ unwrapIP $ mkClassPred ipClass [x,ty] origin = IPOccOrigin x @@ -230,7 +230,7 @@ tcExpr e@(HsOverLabel mb_fromLabel l) res_ty where -- Coerces a dictionary for `IsLabel "x" t` into `t`, -- or `HasField "x" r a into `r -> a`. - fromDict pred = HsWrap $ mkWpCastR $ unwrapIP pred + fromDict pred = mkHsWrap $ mkWpCastR $ unwrapIP pred origin = OverLabelOrigin l lbl = mkStrLitTy l @@ -354,8 +354,8 @@ tcExpr expr@(OpApp arg1 op fix arg2) res_ty tc_poly_expr_nc arg2 arg2_exp_ty ; arg2_ty <- readExpType arg2_exp_ty ; op_id <- tcLookupId op_name - ; let op' = L loc (HsWrap (mkWpTyApps [arg1_ty, arg2_ty]) - (HsVar (L lv op_id))) + ; let op' = L loc (mkHsWrap (mkWpTyApps [arg1_ty, arg2_ty]) + (HsVar (L lv op_id))) ; return $ OpApp arg1' op' fix arg2' } | (L loc (HsVar (L lv op_name))) <- op @@ -392,10 +392,10 @@ tcExpr expr@(OpApp arg1 op fix arg2) res_ty ; op_id <- tcLookupId op_name ; res_ty <- readExpType res_ty - ; let op' = L loc (HsWrap (mkWpTyApps [ getRuntimeRep "tcExpr ($)" res_ty - , arg2_sigma - , res_ty]) - (HsVar (L lv op_id))) + ; let op' = L loc (mkHsWrap (mkWpTyApps [ getRuntimeRep "tcExpr ($)" res_ty + , arg2_sigma + , res_ty]) + (HsVar (L lv op_id))) -- arg1' :: arg1_ty -- wrap_arg1 :: arg1_ty "->" (arg2_sigma -> op_res_ty) -- wrap_res :: op_res_ty "->" res_ty @@ -1793,7 +1793,7 @@ tcSeq loc fun_name args res_ty ; arg1' <- tcMonoExpr arg1 (mkCheckExpType arg1_ty) ; arg2' <- tcMonoExpr arg2 arg2_exp_ty ; res_ty <- readExpType res_ty -- by now, it's surely filled in - ; let fun' = L loc (HsWrap ty_args (HsVar (L loc fun))) + ; let fun' = L loc (mkHsWrap ty_args (HsVar (L loc fun))) ty_args = WpTyApp res_ty <.> WpTyApp arg1_ty ; return (idHsWrapper, fun', [Left arg1', Left arg2']) } @@ -1835,7 +1835,7 @@ tcTagToEnum loc fun_name args res_ty (mk_error ty' doc2) ; arg' <- tcMonoExpr arg (mkCheckExpType intPrimTy) - ; let fun' = L loc (HsWrap (WpTyApp rep_ty) (HsVar (L loc fun))) + ; let fun' = L loc (mkHsWrap (WpTyApp rep_ty) (HsVar (L loc fun))) rep_ty = mkTyConApp rep_tc rep_args ; return (mkWpCastR (mkTcSymCo coi), fun', [Left arg']) } diff --git a/compiler/typecheck/TcMatches.hs b/compiler/typecheck/TcMatches.hs index e7deedee15..909314e519 100644 --- a/compiler/typecheck/TcMatches.hs +++ b/compiler/typecheck/TcMatches.hs @@ -507,7 +507,7 @@ tcLcStmt m_tc ctxt (TransStmt { trS_form = form, trS_stmts = stmts poly_arg_ty `mkFunTy` poly_res_ty ; using' <- tcPolyExpr using using_poly_ty - ; let final_using = fmap (HsWrap (WpTyApp tup_ty)) using' + ; let final_using = fmap (mkHsWrap (WpTyApp tup_ty)) using' -- 'stmts' returns a result of type (m1_ty tuple_ty), -- typically something like [(Int,Bool,Int)] @@ -689,7 +689,7 @@ tcMcStmt ctxt (TransStmt { trS_stmts = stmts, trS_bndrs = bindersMap -- using :: ((a,b,c)->t) -> m1 (a,b,c) -> m2 (n (a,b,c)) ; using' <- tcPolyExpr using using_poly_ty - ; let final_using = fmap (HsWrap (WpTyApp tup_ty)) using' + ; let final_using = fmap (mkHsWrap (WpTyApp tup_ty)) using' --------------- Bulding the bindersMap ---------------- ; let mk_n_bndr :: Name -> TcId -> TcId diff --git a/compiler/typecheck/TcSplice.hs b/compiler/typecheck/TcSplice.hs index 1e4ec4060b..b90de5e459 100644 --- a/compiler/typecheck/TcSplice.hs +++ b/compiler/typecheck/TcSplice.hs @@ -574,8 +574,8 @@ runAnnotation target expr = do -- and hence ensures the appropriate dictionary is bound by const_binds ; wrapper <- instCall AnnOrigin [expr_ty] [mkClassPred data_class [expr_ty]] ; let specialised_to_annotation_wrapper_expr - = L loc (HsWrap wrapper - (HsVar (L loc to_annotation_wrapper_id))) + = L loc (mkHsWrap wrapper + (HsVar (L loc to_annotation_wrapper_id))) ; return (L loc (HsApp specialised_to_annotation_wrapper_expr expr')) } -- Run the appropriately wrapped expression to get the value of diff --git a/compiler/types/Kind.hs b/compiler/types/Kind.hs index 906c6978bd..ae11c8a651 100644 --- a/compiler/types/Kind.hs +++ b/compiler/types/Kind.hs @@ -11,7 +11,6 @@ module Kind ( isTYPEApp, returnsTyCon, returnsConstraintKind, isConstraintKindCon, - okArrowArgKind, okArrowResultKind, classifiesTypeWithValues, isStarKind, isStarKindSynonymTyCon, @@ -116,17 +115,6 @@ isKindLevPoly k = ASSERT2( isStarKind k || _is_type, ppr k ) = False --------------------------------------------- --- Kinding for arrow (->) --- Says when a kind is acceptable on lhs or rhs of an arrow --- arg -> res --- --- See Note [Levity polymorphism] - -okArrowArgKind, okArrowResultKind :: Kind -> Bool -okArrowArgKind = classifiesTypeWithValues -okArrowResultKind = classifiesTypeWithValues - ----------------------------------------- -- Subkinding -- The tc variants are used during type-checking, where ConstraintKind @@ -162,31 +150,3 @@ isStarKind _ = False -- | Is the tycon @Constraint@? isStarKindSynonymTyCon :: TyCon -> Bool isStarKindSynonymTyCon tc = tc `hasKey` constraintKindTyConKey - - -{- Note [Levity polymorphism] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Is this type legal? - (a :: TYPE rep) -> Int - where 'rep :: RuntimeRep' - -You might think not, because no lambda can have a -runtime-rep-polymorphic binder. So no lambda has the -above type. BUT here's a way it can be useful (taken from -Trac #12708): - - data T rep (a :: TYPE rep) - = MkT (a -> Int) - - x1 :: T LiftedRep Int - x1 = MkT LiftedRep Int (\x::Int -> 3) - - x2 :: T IntRep Int# - x2 = MkT IntRep Int# (\x:Int# -> 3) - -Note that the lambdas are just fine! - -Hence, okArrowArgKind and okArrowResultKind both just -check that the type is of the form (TYPE r) for some -representation type r. --} diff --git a/testsuite/tests/codeGen/should_compile/T13233.hs b/testsuite/tests/codeGen/should_compile/T13233.hs deleted file mode 100644 index bb79856d3b..0000000000 --- a/testsuite/tests/codeGen/should_compile/T13233.hs +++ /dev/null @@ -1,12 +0,0 @@ -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE TypeInType #-} -{-# LANGUAGE UnboxedTuples #-} -module Bug where - -import GHC.Exts (TYPE) - -class Foo (a :: TYPE rep) where - bar :: forall (b :: TYPE rep2). (a -> a -> b) -> a -> a -> b - -baz :: forall (a :: TYPE rep). Foo a => a -> a -> (# a, a #) -baz = bar (#,#) diff --git a/testsuite/tests/codeGen/should_compile/all.T b/testsuite/tests/codeGen/should_compile/all.T index a73a9d65cf..6ae4e1cb4e 100644 --- a/testsuite/tests/codeGen/should_compile/all.T +++ b/testsuite/tests/codeGen/should_compile/all.T @@ -35,4 +35,3 @@ test('T10667', [ when((arch('powerpc64') or arch('powerpc64le')), compile, ['-g']) test('T12115', normal, compile, ['']) test('T12355', normal, compile, ['']) -test('T13233', expect_broken(13233), compile, ['']) diff --git a/testsuite/tests/codeGen/should_fail/T13233.hs b/testsuite/tests/codeGen/should_fail/T13233.hs new file mode 100644 index 0000000000..fa5a37b046 --- /dev/null +++ b/testsuite/tests/codeGen/should_fail/T13233.hs @@ -0,0 +1,27 @@ +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeInType #-} +{-# LANGUAGE UnboxedTuples #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE MagicHash #-} +module Bug where + +import GHC.Exts (TYPE, RuntimeRep, Weak#, State#, RealWorld, mkWeak# ) + +class Foo (a :: TYPE rep) where + bar :: forall (b :: TYPE rep2). (a -> a -> b) -> a -> a -> b + +baz :: forall (a :: TYPE rep). Foo a => a -> a -> (# a, a #) +baz = bar (#,#) + +obscure :: (forall (rep1 :: RuntimeRep) (rep2 :: RuntimeRep) + (a :: TYPE rep1) (b :: TYPE rep2). + a -> b -> (# a, b #)) -> () +obscure _ = () + +quux :: () +quux = obscure (#,#) + +primop :: forall (rep :: RuntimeRep) (a :: TYPE rep) b c. + a -> b -> (State# RealWorld -> (# State# RealWorld, c #)) + -> State# RealWorld -> (# State# RealWorld, Weak# b #) +primop = mkWeak# diff --git a/testsuite/tests/codeGen/should_fail/T13233.stderr b/testsuite/tests/codeGen/should_fail/T13233.stderr new file mode 100644 index 0000000000..2d167cf5f7 --- /dev/null +++ b/testsuite/tests/codeGen/should_fail/T13233.stderr @@ -0,0 +1,24 @@ + +T13233.hs:14:11: error: + Cannot use primitive with levity-polymorphic arguments: + GHC.Prim.(#,#) :: a -> a -> (# a, a #) + Levity polymorphic arguments: + a :: TYPE rep + a :: TYPE rep + +T13233.hs:22:16: error: + Cannot use primitive with levity-polymorphic arguments: + GHC.Prim.(#,#) :: forall (a :: TYPE rep1) (b :: TYPE rep2). + a -> b -> (# a, b #) + Levity polymorphic arguments: + a :: TYPE rep1 + b :: TYPE rep2 + +T13233.hs:27:10: error: + Cannot use primitive with levity-polymorphic arguments: + mkWeak# :: a + -> b + -> (State# RealWorld -> (# State# RealWorld, c #)) + -> State# RealWorld + -> (# State# RealWorld, Weak# b #) + Levity polymorphic arguments: a :: TYPE rep diff --git a/testsuite/tests/codeGen/should_fail/all.T b/testsuite/tests/codeGen/should_fail/all.T index 7e25b5f693..1fe2141caf 100644 --- a/testsuite/tests/codeGen/should_fail/all.T +++ b/testsuite/tests/codeGen/should_fail/all.T @@ -3,3 +3,4 @@ # Only the LLVM code generator consistently forces the alignment of # memcpy operations test('T8131', [cmm_src, only_ways(llvm_ways)], compile_fail, ['']) +test('T13233', normal, compile_fail, ['']) |