Simon Peyton Jones pushed to branch wip/T20264 at Glasgow Haskell Compiler / GHC
Commits:
-
687f3662
by Simon Peyton Jones at 2026-03-03T10:11:06+00:00
6 changed files:
- compiler/GHC/Core.hs
- compiler/GHC/Core/Lint.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/CoreToStg/Prep.hs
- compiler/GHC/Driver/Config/Core/Lint.hs
- compiler/GHC/HsToCore/Utils.hs
Changes:
| ... | ... | @@ -465,8 +465,8 @@ TL;DR: we relaxed the let/app invariant to become the let-can-float invariant. |
| 465 | 465 | |
| 466 | 466 | Note [Type and coercion lets]
|
| 467 | 467 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 468 | -We allow
|
|
| 469 | - let @a = TYPE ty in ...
|
|
| 468 | +We allow non-recursive type lets:
|
|
| 469 | + let a = TYPE ty in ...
|
|
| 470 | 470 | and similarly for coercions.
|
| 471 | 471 | |
| 472 | 472 | TODO: fill this out
|
| ... | ... | @@ -475,7 +475,7 @@ Wrinkles: |
| 475 | 475 | |
| 476 | 476 | (TCL1) In a type let (Let @a = TYPE ty in body), we do /not/ insist that
|
| 477 | 477 | the binder `a` has a TyVarUnfolding. But if it does not, then `body`
|
| 478 | - must be well-typed without paying atention to the binding. More precisely,
|
|
| 478 | + must be well-typed without paying attention to the binding. More precisely,
|
|
| 479 | 479 | let @a = TYPE ty in body
|
| 480 | 480 | where `a` has no TyVarUnfolding, is well-typed iff
|
| 481 | 481 | (/\a. body) ty
|
| ... | ... | @@ -489,15 +489,32 @@ Wrinkles: |
| 489 | 489 | (which is always substituted) with the tyvar-replete-with-unfolding, rather
|
| 490 | 490 | than merely extending the in-scope set as we do for Ids.
|
| 491 | 491 | |
| 492 | +(TCL3) In the output of the desugarer it is very convenient to allow
|
|
| 493 | + let a = <type> in ...a....
|
|
| 494 | + where the occurrences of `a` do /not/ have an unfolding, but yet it is essential
|
|
| 495 | + to substitute <type> for `a` when Linting. Why? When compiling nested pattern
|
|
| 496 | + matching we may combine patterns
|
|
| 497 | + K @a1 (co1 :: a1 ~ T) pat1 -> e1
|
|
| 498 | + K @a2 (co2 :: a2 ~ T) pat2 -> e2
|
|
| 499 | + to get a single, shared pattern, something like
|
|
| 500 | + K @a1 (co1 :: a1 ~ T) x -> let { a2 = a1; co2 = co1 } in
|
|
| 501 | + case x of
|
|
| 502 | + pat1 -> e1
|
|
| 503 | + pat2 -> e2
|
|
| 504 | + The bindings { a2=a1; co2=co1 } just make the binders in the two patterns line
|
|
| 505 | + up. But for this to be Lint-correct we must actually substitute `a1` for `a2`.
|
|
| 506 | + |
|
| 507 | + So, in the ouptut of the desugarer only, if there is no unfolding on the binder,
|
|
| 508 | + we just extend the subustitution.
|
|
| 509 | + |
|
| 510 | + It's a bit of a hack, but the first roun dof simplification esablishes (TCL1) or
|
|
| 511 | + (TCL2).
|
|
| 512 | + |
|
| 492 | 513 | So: (TCL1) + (TCL2) =
|
| 493 | 514 | EITHER `a` has an unfolding at its binding site,
|
| 494 | 515 | and that unfolding is replicated at every occurrence site
|
| 495 | 516 | OR it doesn't and the occurrences don't either.
|
| 496 | 517 | |
| 497 | - |
|
| 498 | -OR we could insist that tyvar bindings always have an unfolding, and use
|
|
| 499 | -a beta-redex if not.
|
|
| 500 | - |
|
| 501 | 518 | Note [Core top-level string literals]
|
| 502 | 519 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 503 | 520 | As an exception to the usual rule that top-level binders must be lifted,
|
| ... | ... | @@ -235,8 +235,6 @@ in GHC.Core.Opt.WorkWrap.Utils. (Maybe there are other "clients" of this featur |
| 235 | 235 | for this purpose -- it contains only TyCoVars. Instead we have a separate
|
| 236 | 236 | le_ids for the in-scope Id binders.
|
| 237 | 237 | |
| 238 | -Sigh. We might want to explore getting rid of type-let!
|
|
| 239 | - |
|
| 240 | 238 | Note [Bad unsafe coercion]
|
| 241 | 239 | ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 242 | 240 | For discussion see https://gitlab.haskell.org/ghc/ghc/wikis/bad-unsafe-coercions
|
| ... | ... | @@ -570,23 +568,31 @@ lintLetExpr :: TyVarSet -- Enclosing let-bound tyvars, all with unfoldings |
| 570 | 568 | lintLetExpr tvs (Let (NonRec tv (Type rhs_ty)) body)
|
| 571 | 569 | | isTyVar tv
|
| 572 | 570 | = -- See Note [Linting type lets]
|
| 573 | - do { case tyVarUnfolding_maybe tv of
|
|
| 574 | - Nothing -> return () -- See GHC.Core Note [Type and coercion lets] wrinkle (TCL1)
|
|
| 575 | - Just unf_ty -> -- These comparisons compare InTypes, which is fine
|
|
| 576 | - do { ensureEqTys (tyVarKind tv) (typeKind rhs_ty) $
|
|
| 577 | - tv_err unf_ty "Let-bound tyvar kind incompatible with RHS:"
|
|
| 578 | - ; ensureEqTys unf_ty rhs_ty $
|
|
| 579 | - tv_err unf_ty "Let-bound tyvar unfolding not same as RHS:" }
|
|
| 580 | - |
|
| 581 | - ; addLoc (RhsOf tv) $ lintType rhs_ty
|
|
| 582 | - |
|
| 583 | - ; lintTyCoBndr tv $ \ tv' ->
|
|
| 584 | - addLoc (BodyOfLet tv) $
|
|
| 585 | - lintLetExpr (tvs `extendVarSet` tv') body }
|
|
| 571 | + do { -- Lint the RHS type
|
|
| 572 | + rhs_ty' <- addLoc (RhsOf tv) $ lintTypeAndSubst rhs_ty
|
|
| 573 | + |
|
| 574 | + ; lintTyCoBndr tv $ \ tv' ->
|
|
| 575 | + do { -- Check that the RHS has the same kind as the tyvar
|
|
| 576 | + addLoc (RhsOf tv) $
|
|
| 577 | + lintTyKind tv' rhs_ty'
|
|
| 578 | + |
|
| 579 | + -- Check the unfolding
|
|
| 580 | + -- See GHC.Core Note [Type and coercion lets] wrinkle (TCL1)
|
|
| 581 | + ; case tyVarUnfolding_maybe tv' of
|
|
| 582 | + Nothing -> return ()
|
|
| 583 | + Just unf_ty' -> ensureEqTys unf_ty' rhs_ty' $
|
|
| 584 | + tv_err tv' unf_ty' rhs_ty'
|
|
| 585 | + |
|
| 586 | + -- Check the body
|
|
| 587 | + ; extendTvLetSubst tv rhs_ty' $
|
|
| 588 | + addLoc (BodyOfLet tv) $
|
|
| 589 | + lintLetExpr (tvs `extendVarSet` tv') body
|
|
| 590 | + } }
|
|
| 586 | 591 | where
|
| 587 | - tv_err unf_ty msg = hang (text msg <+> pprTyVarWithKind tv)
|
|
| 588 | - 2 (vcat [ text "Unfolding:" <+> ppr unf_ty
|
|
| 589 | - , text "RHS: " <+> ppr rhs_ty ])
|
|
| 592 | + tv_err tv unf_ty rhs_ty = hang (text "Let-bound tyvar unfolding not same as RHS:"
|
|
| 593 | + <+> pprTyVarWithKind tv)
|
|
| 594 | + 2 (vcat [ text "Unfolding:" <+> ppr unf_ty
|
|
| 595 | + , text "RHS: " <+> ppr rhs_ty ])
|
|
| 590 | 596 | |
| 591 | 597 | lintLetExpr tvs (Let (NonRec bndr rhs) body)
|
| 592 | 598 | | isId bndr
|
| ... | ... | @@ -3086,6 +3092,7 @@ data LintFlags |
| 3086 | 3092 | , lf_check_linearity :: Bool -- ^ See Note [Linting linearity]
|
| 3087 | 3093 | , lf_check_fixed_rep :: Bool -- ^ See Note [Checking for representation polymorphism]
|
| 3088 | 3094 | , lf_check_rubbish_lits :: Bool -- ^ See Note [Checking for rubbish literals]
|
| 3095 | + , lf_inline_type_lets :: Bool -- ^ See Note [Linting type lets] XXX TODO
|
|
| 3089 | 3096 | }
|
| 3090 | 3097 | |
| 3091 | 3098 | -- See Note [Checking StaticPtrs]
|
| ... | ... | @@ -3505,10 +3512,12 @@ getLintFlags :: LintM LintFlags |
| 3505 | 3512 | getLintFlags = LintM $ \ env errs -> fromBoxedLResult (Just (le_flags env), errs)
|
| 3506 | 3513 | |
| 3507 | 3514 | updLintFlags :: (LintFlags -> LintFlags) -> LintM a -> LintM a
|
| 3508 | -updLintFlags upd_flags thing_inside
|
|
| 3509 | - = LintM $ \env errs ->
|
|
| 3510 | - let env' = env { le_flags = upd_flags (le_flags env) }
|
|
| 3511 | - in unLintM thing_inside env' errs
|
|
| 3515 | +updLintFlags upd_flags
|
|
| 3516 | + = updLintEnv (\env -> env { le_flags = upd_flags (le_flags env) })
|
|
| 3517 | + |
|
| 3518 | +updLintEnv :: (LintEnv -> LintEnv) -> LintM a -> LintM a
|
|
| 3519 | +updLintEnv upd thing_inside
|
|
| 3520 | + = LintM $ \env errs -> unLintM thing_inside (upd env) errs
|
|
| 3512 | 3521 | |
| 3513 | 3522 | checkL :: Bool -> SDoc -> LintM ()
|
| 3514 | 3523 | checkL True _ = return ()
|
| ... | ... | @@ -3634,6 +3643,17 @@ addInScopeTyCoVar tcv tcv_type thing_inside |
| 3634 | 3643 | Just unf_ty -> setTyVarUnfolding tcv2 (substTy subst unf_ty)
|
| 3635 | 3644 | Nothing -> tcv2
|
| 3636 | 3645 | |
| 3646 | +extendTvLetSubst :: TyVar -> Type -> LintM a -> LintM a
|
|
| 3647 | +extendTvLetSubst tv ty thing_inside
|
|
| 3648 | + | isJust (tyVarUnfolding_maybe tv)
|
|
| 3649 | + = thing_inside
|
|
| 3650 | + | otherwise
|
|
| 3651 | + = do { flags <- getLintFlags
|
|
| 3652 | + ; if (lf_inline_type_lets flags)
|
|
| 3653 | + then updLintEnv (\ env -> env { le_subst = Type.extendTvSubst (le_subst env) tv ty })
|
|
| 3654 | + thing_inside
|
|
| 3655 | + else thing_inside }
|
|
| 3656 | + |
|
| 3637 | 3657 | getInVarEnv :: LintM (VarEnv (InId, OutVar))
|
| 3638 | 3658 | getInVarEnv = LintM (\env errs -> fromBoxedLResult (Just (le_in_vars env), errs))
|
| 3639 | 3659 | |
| ... | ... | @@ -3742,7 +3762,7 @@ checkBndrOccCompatibility in_bndr v_occ |
| 3742 | 3762 | sameUnfolding :: InVar -- Binder
|
| 3743 | 3763 | -> InVar -- Occurrence
|
| 3744 | 3764 | -> LintM Bool
|
| 3745 | --- Check that any unfolding in the /occurence/ is the same as that in the /binder/
|
|
| 3765 | +-- Check that any unfolding in the /occurrence/ is the same as that in the /binder/
|
|
| 3746 | 3766 | -- An unfolding in the occurrence is optional for Ids, but compulsory for type-let-boud
|
| 3747 | 3767 | -- TyVars. Somewhat lazily, we only check the latter.
|
| 3748 | 3768 | -- We also just compare them as InTypes (as we do the type of the variable);
|
| ... | ... | @@ -3323,11 +3323,7 @@ mkPolyAbsLams (getter,setter) bndrs body |
| 3323 | 3323 | = go emptyVarSet [] bndrs
|
| 3324 | 3324 | where
|
| 3325 | 3325 | wrap_bind :: Expr b -> (b,Expr b) -> Expr b
|
| 3326 | - -- wrap_bind e (bndr, rhs) = (\bndr.e) rhs
|
|
| 3327 | - -- Very like let bndr=rhs in e
|
|
| 3328 | - -- but, for type-bindings at least, does not require that the occurrences
|
|
| 3329 | - -- of bndr have the unfolding from the let-binding
|
|
| 3330 | - wrap_bind e (bndr, rhs) = App (Lam bndr e) rhs
|
|
| 3326 | + wrap_bind e (bndr, rhs) = Let (NonRec bndr rhs) e
|
|
| 3331 | 3327 | |
| 3332 | 3328 | go :: TyVarSet -- Earlier TyVar bndrs that have TyVarUnfoldings
|
| 3333 | 3329 | -> [(b,Expr b)] -- Accumulated impedence-matching bindings (reversed)
|
| ... | ... | @@ -1527,7 +1527,7 @@ cpeArg :: CorePrepEnv -> Demand |
| 1527 | 1527 | -> CoreArg -> UniqSM (Floats, CpeArg)
|
| 1528 | 1528 | cpeArg env dmd arg
|
| 1529 | 1529 | = do { (floats1, arg1) <- cpeRhsE env arg -- arg1 can be a lambda
|
| 1530 | - ; let arg_ty = exprType arg1
|
|
| 1530 | + ; let arg_ty = exprType arg
|
|
| 1531 | 1531 | lev = typeLevity arg_ty
|
| 1532 | 1532 | dec = wantFloatLocal NonRecursive dmd lev floats1 arg1
|
| 1533 | 1533 | ; (floats2, arg2) <- executeFloatDecision env dec floats1 arg1
|
| ... | ... | @@ -119,7 +119,8 @@ perPassFlags dflags pass |
| 119 | 119 | , lf_check_inline_loop_breakers = check_lbs
|
| 120 | 120 | , lf_check_static_ptrs = check_static_ptrs
|
| 121 | 121 | , lf_check_linearity = check_linearity
|
| 122 | - , lf_check_rubbish_lits = check_rubbish }
|
|
| 122 | + , lf_check_rubbish_lits = check_rubbish
|
|
| 123 | + , lf_inline_type_lets = inline_type_lets }
|
|
| 123 | 124 | where
|
| 124 | 125 | -- See Note [Checking for global Ids]
|
| 125 | 126 | check_globals = case pass of
|
| ... | ... | @@ -156,6 +157,11 @@ perPassFlags dflags pass |
| 156 | 157 | CorePrep -> True
|
| 157 | 158 | _ -> False
|
| 158 | 159 | |
| 160 | + -- See Note [Linting type lets] in GHC.Core.Lint
|
|
| 161 | + inline_type_lets = case pass of
|
|
| 162 | + CoreDesugar -> True
|
|
| 163 | + _ -> False
|
|
| 164 | + |
|
| 159 | 165 | initLintConfig :: DynFlags -> [Var] -> LintConfig
|
| 160 | 166 | initLintConfig dflags vars =LintConfig
|
| 161 | 167 | { l_diagOpts = initDiagOpts dflags
|
| ... | ... | @@ -172,4 +178,5 @@ defaultLintFlags dflags = LF { lf_check_global_ids = False |
| 172 | 178 | , lf_report_unsat_syns = True
|
| 173 | 179 | , lf_check_fixed_rep = True
|
| 174 | 180 | , lf_check_rubbish_lits = True
|
| 181 | + , lf_inline_type_lets = False
|
|
| 175 | 182 | } |
| ... | ... | @@ -245,6 +245,8 @@ wrapBinds [] e = e |
| 245 | 245 | wrapBinds ((new,old):prs) e = wrapBind new old (wrapBinds prs e)
|
| 246 | 246 | |
| 247 | 247 | wrapBind :: Var -> Var -> CoreExpr -> CoreExpr
|
| 248 | +-- Used only to line up varaibles when combining case patterns,
|
|
| 249 | +-- in GHC.HsToCore.Match.Constructor and GHC.HsToCore.Match.Literal
|
|
| 248 | 250 | wrapBind new old body -- NB: this function must deal with term
|
| 249 | 251 | | new==old = body -- variables, type variables or coercion variables
|
| 250 | 252 | | otherwise = Let (NonRec new (varToCoreExpr old)) body
|