[Git][ghc/ghc][wip/spj-try-opt-coercion] 3 commits: Update Notes about core binding invariants
Simon Peyton Jones pushed to branch wip/spj-try-opt-coercion at Glasgow Haskell Compiler / GHC Commits: 88d60e1d by Simon Peyton Jones at 2026-01-15T12:56:19+00:00 Update Notes about core binding invariants - - - - - 8624f459 by Simon Peyton Jones at 2026-01-15T12:56:36+00:00 T26332 really should fail with -dlinear-core-lint - - - - - 6e78fe41 by Simon Peyton Jones at 2026-01-15T12:57:16+00:00 Try switching off the big optCoercion except in O2 - - - - - 18 changed files: - compiler/GHC/Builtin/PrimOps.hs - compiler/GHC/Core.hs - compiler/GHC/Core/Coercion/Opt.hs - compiler/GHC/Core/Make.hs - compiler/GHC/Core/Opt/CSE.hs - compiler/GHC/Core/Opt/ConstantFold.hs - compiler/GHC/Core/Opt/FloatIn.hs - compiler/GHC/Core/Opt/SetLevels.hs - compiler/GHC/Core/Opt/Simplify/Env.hs - compiler/GHC/Core/Opt/Simplify/Iteration.hs - compiler/GHC/Core/Opt/Simplify/Utils.hs - compiler/GHC/Core/Opt/Specialise.hs - compiler/GHC/Core/SimpleOpt.hs - compiler/GHC/Core/Utils.hs - compiler/GHC/CoreToStg/Prep.hs - compiler/GHC/Driver/DynFlags.hs - testsuite/tests/linear/should_compile/T26332.hs - testsuite/tests/linear/should_compile/all.T Changes: ===================================== compiler/GHC/Builtin/PrimOps.hs ===================================== @@ -438,7 +438,7 @@ follows, in decreasing order of permissiveness: In particular, we cannot safely rewrite such an invalid call to a runtime error; we must emit code that produces a valid Word32#. (If we're lucky, Core Lint may complain that the result of such a rewrite violates - Note [Core binding invariants: nested non-rec] (#16742), but the rewrite + Note [Nested non-rec binding invariants] (#16742), but the rewrite is always wrong!) See also Note [Guarding against silly shifts] in GHC.Core.Opt.ConstantFold. @@ -581,7 +581,7 @@ Several predicates on primops test this flag: * The "no-float-out" thing is achieved by ensuring that we never let-bind a saturated primop application unless it has NoEffect. The RHS of a let-binding (which can float in and out freely) satisfies - exprOkForSpeculation; this is Note [Core binding invariants: nested non-rec]. + exprOkForSpeculation; this is Note [Nested non-rec binding invariants]. And exprOkForSpeculation is false of a saturated primop application unless it has NoEffect. ===================================== compiler/GHC/Core.hs ===================================== @@ -397,27 +397,27 @@ Note [Core binding invariants] A core binding, `CoreBind`, obeys these invariants: * For /top level/ or /recursive/ bindings, - see Note [Top-level binding invariants] + see Note [Top/rec binding invariants] * For /nested/ (not top-level) /non-recursive/ bindings, - see Note [Nested binding invariants] + see Note [Nested non-rec binding invariants] -Note [Top-level binding invariants] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Note [Top/rec binding invariants] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A /top-level/ or /recursive/ binding must - * be of lifted type -OR + * be of lifted type, OR + * have a RHS that is a primitive string literal - (see Note [Core top-level string literals], or -OR - * have a rhs that is (Coercion co) -OR - * be a worker or wrapper for an unlifted non-newtype data constructor; see (TL1). + (see Note [Core top-level string literals], OR + + * have a rhs that is (Coercion co), OR + + * be a worker or wrapper for an unlifted non-newtype + data constructor; see (TL1). -For the non-top-level, non-recursive case see Note [Nested binding invariants]. -(NB: this Note applies to recursive as well as top-level bindings, but I wanted -a short title!) +For the non-top-level, non-recursive case +see Note [Nested non-rec binding invariants]. See "Type#type_classification" in GHC.Core.Type for the meaning of "lifted" vs. "unlifted". @@ -439,8 +439,8 @@ constructor worker or wrapper S1 = S1 We allow this top-level unlifted binding to exist. -Note [Nested binding invariants] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Note [Nested non-rec binding invariants] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A /non-top-level/, /non-recursive/ binding must * Be a join point; see Note [Invariants on join points] OR @@ -471,7 +471,7 @@ The Core binding invariants are initially enforced by mkCoreLet in GHC.Core.Make Historical Note [The let/app invariant] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Before 2022 GHC used the "let/app invariant", which applied -Note [Nested binding invariants] to the argument of an application, +Note [Nested non-rec binding invariants] to the argument of an application, as well as to the RHS of a let. This made some kind of sense, because 'let' can always be encoded as application: let x=rhs in b = (\x.b) rhs @@ -641,8 +641,8 @@ checked by Core Lint. multiplicity of the corresponding field /scaled by the multiplicity of the case binder/. Checked in lintCoreAlt. -Note [Core type and coercion invariant] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Note [Core type and coercion invariants] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We allow `let` to bind type and coercion variables. * A type or coercion binding is always /non-recursive/ @@ -887,7 +887,7 @@ Join points must follow these invariants: However, join points have simpler invariants in other ways 5. A join point can have an unboxed type without the RHS being - ok-for-speculation; see + ok-for-speculation; see e.g. let j :: Int# = factorial x in ... 6. The RHS of join point is not required to have a fixed runtime representation, @@ -2095,8 +2095,8 @@ mkDoubleLit d = Lit (mkLitDouble d) mkDoubleLitDouble d = Lit (mkLitDouble (toRational d)) -- | Bind all supplied binding groups over an expression in a nested let expression. --- Assumes that the rhs satisfies Note [Nested binding invariants]. Prefer to use --- 'GHC.Core.Make.mkCoreLets' if possible, which does guarantee the invariant +-- Assumes that the rhs satisfies Note [Nested non-rec binding invariants]. +-- Prefer to use 'GHC.Core.Make.mkCoreLets' if possible, which does guarantee the invariant mkLets :: [Bind b] -> Expr b -> Expr b -- | Bind all supplied binders over an expression in a nested lambda expression. Prefer to -- use 'GHC.Core.Make.mkCoreLams' if possible ===================================== compiler/GHC/Core/Coercion/Opt.hs ===================================== @@ -270,10 +270,12 @@ optCoRefl subst in_co = let out_co = opt_co_refl subst in_co (Pair in_l in_r) = coercionKind in_co (Pair out_l out_r) = coercionKind out_co - in if (in_l `eqType` out_l) && (in_r `eqType` out_r) + in_l' = substTy subst in_l + in_r' = substTy subst in_r + in if (in_l' `eqType` out_l) && (in_r' `eqType` out_r) then out_co - else pprTrace "optReflCo" (vcat [ text "in_l:" <+> ppr in_l - , text "in_r:" <+> ppr in_r + else pprTrace "optReflCo" (vcat [ text "in_l':" <+> ppr in_l' + , text "in_r':" <+> ppr in_r' , text "out_l:" <+> ppr out_l , text "out_r:" <+> ppr out_r , text "in_co:" <+> ppr in_co ===================================== compiler/GHC/Core/Make.hs ===================================== @@ -113,7 +113,7 @@ sortQuantVars vs = sorted_tcvs ++ ids -- appropriate (see "GHC.Core#let_can_float_invariant") mkCoreLet :: HasDebugCallStack => CoreBind -> CoreExpr -> CoreExpr mkCoreLet (NonRec bndr rhs) body - = -- See Note [Core binding invariants: nested non-rec] + = -- See Note [Nested non-rec binding invariants] bindNonRec bndr rhs body mkCoreLet bind body = Let bind body ===================================== compiler/GHC/Core/Opt/CSE.hs ===================================== @@ -287,8 +287,8 @@ Here is another reason that we do not use SUBSTITUTE for all trivial expressions. Consider case x |> co of (y::Array# Int) { ... } -We do not want to extend the substitution with (y -> x |> co); since y -is of unlifted type, this would destroy Note [Nested binding invariants] +We do not want to extend the substitution with (y -> x |> co); since y is of +unlifted type, this would destroy Note [Nested non-rec binding invariants] if (x |> co) was not ok-for-speculation. But surely (x |> co) is ok-for-speculation, because it's a trivial ===================================== compiler/GHC/Core/Opt/ConstantFold.hs ===================================== @@ -1607,7 +1607,7 @@ as follows: in ... This was originally done in the fix to #16449 but this breaks -Note [Nested binding invariants] in GHC.Core, as noted in #16742. For the +Note [Nested non-rec binding invariants] in GHC.Core, as noted in #16742. For the reasons discussed under "NoEffect" in Note [Classifying primop effects] (in GHC.Builtin.PrimOps) there is no safe way to rewrite the argument of I# such that it bottoms. @@ -2177,7 +2177,7 @@ BigNat). These rules implement the same kind of constant folding as we have for Int#/Word#/etc. primops. See builtinBignumRules. These rules are built-in because they can't be expressed as regular rules for -now. The reason is that due to Note [Nested binding invariants] in GHC.Core, +now. The reason is that due to Note [Nested non-rec binding invariants] in GHC.Core, GHC is too conservative with some bignum operations and they don't match rules. For example: @@ -2185,8 +2185,8 @@ For example: doesn't constant-fold into `integerAdd 2 x` with a regular rule. That's because GHC never floats in `integerAdd 1 x` to form `integerAdd 1 (integerAdd 1 x)` -because of Note [Nested binding invariants] (it doesn't know if `integerAdd` -terminates). +because of Note [Nested non-rec binding invariants] (it doesn't know if +`integerAdd` terminates). In the built-in rule for `integerAdd` we can access the unfolding of `r` and we can perform the appropriate substitution. ===================================== compiler/GHC/Core/Opt/FloatIn.hs ===================================== @@ -665,7 +665,7 @@ noFloatIntoRhs is_rec bndr rhs = isRec is_rec -- Joins are one-shot iff non-recursive | definitelyUnliftedType (idType bndr) - = True -- Preserve Note [Nested binding invariants], + = True -- Preserve Note [Nested non-rec binding invariants], -- see Note [noFloatInto considerations] | otherwise @@ -691,7 +691,7 @@ When do we want to float bindings into - noFloatIntoArg: the argument of a function application Definitely don't float into RHS if it has unlifted type; -that would destroy Note [Nested binding invariants]. +that would destroy Note [Nested non-rec binding invariants]. * Wrinkle 1: do not float in if (a) any non-one-shot value lambdas ===================================== compiler/GHC/Core/Opt/SetLevels.hs ===================================== @@ -1003,11 +1003,11 @@ Why? Because it's important /not/ to transform let x = a /# 3 to let x = case bx of I# a -> a /# 3 -because the let binding no longer obeys Note [Nested binding invariants]. +because the let binding no longer obeys Note [Nested non-rec binding invariants]. But (a /# 3) is ok-for-spec due to a special hack that says division operators can't fail when the denominator is definitely non-zero. And yet that same expression says False to exprIsCheap. Simplest way to guarantee -Note [Nested binding invariants] is to use the same function! +Note [Nested non-rec binding invariants] is to use the same function! If an expression is okay for speculation, we could also float it out *without* boxing and unboxing, since evaluating it early is okay. ===================================== compiler/GHC/Core/Opt/Simplify/Env.hs ===================================== @@ -749,8 +749,10 @@ Examples NonRec x# (y +# 3) FltOkSpec -- Unboxed, but ok-for-spec'n NonRec x* (f y) FltCareful -- Strict binding; might fail or diverge - NonRec x# (a /# b) FltCareful -- Might fail; does not satisfy Note [Nested binding invariants] - NonRec x# (f y) FltCareful -- Might diverge; does not satisfy Note [Nested binding invariants] + NonRec x# (a /# b) FltCareful -- Might fail; does not satisfy + -- Note [Nested non-rec binding invariants] + NonRec x# (f y) FltCareful -- Might diverge; does not satisfy + -- Note [Nested non-rec binding invariants] -} data LetFloats = LetFloats (OrdList OutBind) FloatFlag @@ -763,7 +765,8 @@ data FloatFlag = FltLifted -- All bindings are lifted and lazy *or* -- consist of a single primitive string literal -- Hence ok to float to top level, or recursive - -- NB: consequence: all bindings satisfy Note [Nested binding invariants] + -- NB: consequence: all bindings satisfy + -- Note [Nested non-rec binding invariants] | FltOkSpec -- All bindings are FltLifted *or* -- strict (perhaps because unlifted, @@ -772,12 +775,14 @@ data FloatFlag -- Hence ok to float out of the RHS -- of a lazy non-recursive let binding -- (but not to top level, or into a rec group) - -- NB: consequence: all bindings satisfy Note [Nested binding invariants] + -- NB: consequence: all bindings satisfy + -- Note [Nested non-rec binding invariants] | FltCareful -- At least one binding is strict (or unlifted) -- and not guaranteed cheap -- Do not float these bindings out of a lazy let! - -- NB: some bindings may not satisfy Note [Nested binding invariants] + -- NB: some bindings may not satisfy + -- Note [Nested non-rec binding invariants] instance Outputable LetFloats where ppr (LetFloats binds ff) = ppr ff $$ ppr (fromOL binds) @@ -962,8 +967,10 @@ wrapFloats (SimplFloats { sfLetFloats = LetFloats bs flag -- Note: Always safe to put the joins on the inside -- since the values can't refer to them where - mk_let | FltCareful <- flag = mkCoreLet -- Need to enforce Note [Nested binding invariants] - | otherwise = Let -- Note [Nested binding invariants] holds + mk_let | FltCareful <- flag + = mkCoreLet -- Need to enforce Note [Nested non-rec binding invariants] + | otherwise + = Let -- Note [Nested non-rec binding invariants] holds wrapJoinFloatsX :: SimplFloats -> OutExpr -> (SimplFloats, OutExpr) -- Wrap the sfJoinFloats of the env around the expression, ===================================== compiler/GHC/Core/Opt/Simplify/Iteration.hs ===================================== @@ -315,7 +315,7 @@ simplLazyBind :: TopLevelFlag -> RecFlag -> (InExpr, SimplEnv) -- The RHS and its static environment -> SimplM (SimplFloats, SimplEnv) -- Precondition: Ids only, no TyVars; not a JoinId --- Precondition: rhs obeys Note [Nested binding invariants] +-- Precondition: rhs obeys Note [Nested non-rec binding invariants] simplLazyBind top_lvl is_rec (bndr,unf_se) (bndr1,env) (rhs,rhs_se) = assert (isId bndr ) assertPpr (not (isJoinId bndr)) (ppr bndr) $ @@ -397,7 +397,7 @@ simplAuxBind :: String -- The binder comes from a case expression (case binder or alternative) -- and so does not have rules, unfolding, inline pragmas etc. -- --- Precondition: rhs satisfies Note [Nested binding invariants] +-- Precondition: rhs satisfies Note [Nested non-rec binding invariants] simplAuxBind _str env bndr new_rhs | assertPpr (isId bndr && not (isJoinId bndr)) (ppr bndr) $ @@ -950,7 +950,7 @@ completeBind :: BindContext -- * or by adding to the floats in the envt -- -- Binder /can/ be a JoinId --- Precondition: rhs obeys Note [Nested binding invariants] +-- Precondition: rhs obeys Note [Nested non-rec binding invariants] completeBind bind_cxt (old_bndr, unf_se) (new_bndr, new_rhs, env) | isCoVar old_bndr = case new_rhs of @@ -1290,7 +1290,7 @@ simplExprF1 env (Let (NonRec bndr rhs) body) cont ; simplExprF (extendTvSubst env bndr ty') body cont } | Just env' <- preInlineUnconditionally env NotTopLevel bndr rhs env - -- Because of Note [Nested binding invariants], it's ok to + -- Because of Note [Nested non-rec binding invariants], it's ok to -- inline freely, or to drop the binding if it is dead. = do { simplTrace "SimplBindr:inline-uncond2" (ppr bndr <+> ppr rhs) $ tick (PreInlineUnconditionally bndr) @@ -1594,13 +1594,13 @@ rebuild_go env expr cont completeBindX :: SimplEnv -> FromWhat -> InId -> OutExpr -- Non-recursively bind this Id to this (simplified) expression - -- (Note [Nested binding invariants] may not be satisfied) + -- (Note [Nested non-rec binding invariants] may not be satisfied) -> InExpr -- In this body -> SimplCont -- Consumed by this continuation -> SimplM (SimplFloats, OutExpr) completeBindX env from_what bndr rhs body cont | FromBeta arg_levity <- from_what - , needsCaseBindingL arg_levity rhs -- Enforcing Note [Nested binding invariants] + , needsCaseBindingL arg_levity rhs -- Enforcing Note [Nested non-rec binding invariants] = do { (env1, bndr1) <- simplNonRecBndr env bndr -- Lambda binders don't have rules ; (floats, expr') <- simplNonRecBody env1 from_what body cont -- Do not float floats past the Case binder below @@ -1887,7 +1887,7 @@ simplNonRecE :: HasDebugCallStack -- It deals with strict bindings, via the StrictBind continuation, -- which may abort the whole process. -- --- from_what=FromLet => the RHS satisfies Note [Nested binding invariants] +-- from_what=FromLet => the RHS satisfies Note [Nested non-rec binding invariants] -- Otherwise it may or may not satisfy it. simplNonRecE env from_what bndr (rhs, rhs_se) body cont @@ -1909,8 +1909,8 @@ simplNonRecE env from_what bndr (rhs, rhs_se) body cont where is_strict_bind = case from_what of FromBeta Unlifted -> True - -- If we are coming from a beta-reduction (FromBeta) we must - -- establish Note [Nested binding invariants], so go via StrictBind + -- If we are coming from a beta-reduction (FromBeta) we must establish + -- Note [Nested non-rec binding invariants], so go via StrictBind -- If not, the invariant holds already, and it's optional. -- (FromBeta Lifted) or FromLet: look at the demand info @@ -2857,7 +2857,7 @@ this transformation: We treat the unlifted and lifted cases separately: * Unlifted case: 'e' satisfies exprOkForSpeculation - (ok-for-spec is needed to satisfy Note [Nested binding invariants]. + (ok-for-spec is needed to satisfy Note [Nested non-rec binding invariants]. This turns case a +# b of r -> ...r... into let r = a +# b in ...r... and thence .....(a +# b).... @@ -3112,7 +3112,7 @@ rebuildCase env scrut case_bndr alts cont assert (null bs) $ do { (floats1, env') <- simplAuxBind "rebuildCase" env case_bndr case_bndr_rhs -- scrut is a constructor application, - -- hence satisfies Note [Nested binding invariants] + -- hence satisfies Note [Nested non-rec binding invariants] ; (floats2, expr') <- simplExprF env' rhs cont ; case wfloats of [] -> return (floats1 `addFloats` floats2, expr') @@ -3624,13 +3624,14 @@ We pin on a (OtherCon []) unfolding to the case-binder of a Case, even though it'll be over-ridden in every case alternative with a more informative unfolding. Why? Because suppose a later, less clever, pass simply replaces all occurrences of the case binder with the binder itself; -then Lint may complain about failing Note [Nested binding invariants]. Example +then Lint may complain about failing Note [Nested non-rec binding invariants]. +Example: case e of b { DEFAULT -> let v = reallyUnsafePtrEquality# b y in .... ; K -> blah } -Note [Nested binding invariants] requires that y is evaluated in the call to -reallyUnsafePtrEquality#, which it is. But we still want that to be true if we -propagate binders to occurrences. +Note [Nested non-rec binding invariants] requires that y is evaluated in the +call to reallyUnsafePtrEquality#, which it is. But we still want that to be +true if we propagate binders to occurrences. This showed up in #13027. @@ -3732,7 +3733,7 @@ knownCon env scrut dc_floats dc dc_ty_args dc_args bndr bs rhs cont -- occur in the RHS; and simplAuxBind may therefore discard it. -- Nevertheless we must keep it if the case-binder is alive, -- because it may be used in the con_app. See Note [knownCon occ info] - -- NB: arg satisfies Note [Nested binding invariants] + -- NB: arg satisfies Note [Nested non-rec binding invariants] ; (floats1, env2) <- simplAuxBind "knownCon" env' b' arg ; (floats2, env3) <- bind_args env2 bs' args ; return (floats1 `addFloats` floats2, env3) } ===================================== compiler/GHC/Core/Opt/Simplify/Utils.hs ===================================== @@ -1491,8 +1491,8 @@ preInlineUnconditionally :: SimplEnv -> TopLevelFlag -> InId -> InExpr -> StaticEnv -- These two go together -> Maybe SimplEnv -- Returned env has extended substitution --- Precondition: rhs satisfies Note [Nested binding invariants] --- See Note [Nested binding invariants] in GHC.Core +-- Precondition: rhs satisfies Note [Nested non-rec binding invariants] +-- See Note [Nested non-rec binding invariants] in GHC.Core -- Reason: we don't want to inline single uses, or discard dead bindings, -- for unlifted, side-effect-ful bindings preInlineUnconditionally env top_lvl bndr rhs rhs_env @@ -1638,7 +1638,7 @@ postInlineUnconditionally -> InId -> OutId -- The binder (*not* a CoVar), including its unfolding -> OutExpr -> Bool --- Precondition: rhs satisfies Note [Nested binding invariants] in GHC.Core +-- Precondition: rhs satisfies Note [Nested non-rec binding invariants] in GHC.Core -- Reason: we don't want to inline single uses, or discard dead bindings, -- for unlifted, side-effect-ful bindings postInlineUnconditionally env bind_cxt old_bndr bndr rhs ===================================== compiler/GHC/Core/Opt/Specialise.hs ===================================== @@ -1937,14 +1937,14 @@ where Left to itself, the specialiser would float the bindings for `x` and `n` to top level, so we can specialise `wombat`. But we can't have a top-level ByteArray# -(see Note [Core letrec invariant] in GHC.Core). Boo. +(see Note [Top/rec binding invariants] in GHC.Core). Boo. This is pretty exotic, so we take a simple way out: in specBind (the NonRec case) do not float the binding itself unless it satisfies exprIsTopLevelBindable. This is conservative: maybe the RHS of `x` has a free var that would stop it floating to top level anyway; but that is hard to spot (since we don't know what the non-top-level in-scope binders are) and rare (since the binding must satisfy -Note [Nested binding invariants] in GHC.Core). +Note [Nested non-rec binding invariants] in GHC.Core). Note [Specialising Calls] ===================================== compiler/GHC/Core/SimpleOpt.hs ===================================== @@ -381,7 +381,8 @@ simple_app env e0@(Lam {}) as0@(_:_) -- See Note [Dark corner with representation polymorphism] needsCaseBinding (idType b') (snd a) -- This arg must not be inlined (side-effects) and cannot be let-bound, - -- due to Note [Nested binding invariants]. So simply case-bind it here. + -- due to Note [Nested non-rec binding invariants]. + -- So simply case-bind it here. , let a' = simple_opt_clo (soeInScope env) a = mkDefaultCase a' b' $ do_beta env' body as ===================================== compiler/GHC/Core/Utils.hs ===================================== @@ -2042,15 +2042,15 @@ But we restrict it sharply: ; False -> e2 } in ...) ... - Does the RHS of v satisfy Note [Nested binding invariants]? + Does the RHS of v satisfy Note [Nested non-rec binding invariants]? Previously we said yes, on the grounds that y is evaluated. But the binder-swap done by GHC.Core.Opt.SetLevels would transform the inner alternative to DEFAULT -> ... (let v::Int# = case x of { ... } in ...) .... - which does /not/ satisfy Note [Nested bindings invariants], because x is - not evaluated. See Note [Binder-swap during float-out] + which does /not/ satisfy Note [Nested non-rec bindings invariants], + because x is not evaluated. See Note [Binder-swap during float-out] in GHC.Core.Opt.SetLevels. To avoid this awkwardness it seems simpler to stick to unlifted scrutinees where the issue does not arise. @@ -2134,7 +2134,7 @@ extremely useful for float-out, changes these expressions to And now the expression does not obey the let-can-float invariant! Yikes! Moreover we really might float (dataToTagLarge# x) outside the case, -and then it really, really doesn't obey Note [Nested binding invariants]. +and then it really, really doesn't obey Note [Nested non-rec binding invariants]. The solution is simple: exprOkForSpeculation does not try to take advantage of the evaluated-ness of (lifted) variables. And it returns @@ -2144,7 +2144,7 @@ by marking the relevant primops as "ThrowsException" or GHC.Builtin.PrimOps. Note that exprIsHNF /can/ and does take advantage of evaluated-ness; -it doesn't have the trickiness of Note [Nested binding invariants] +it doesn't have the trickiness of Note [Nested non-rec binding invariants] to worry about. ************************************************************************ ===================================== compiler/GHC/CoreToStg/Prep.hs ===================================== @@ -302,7 +302,7 @@ expose the values: see Note [wantFloatLocal].) If `v` is bound at the top-level, we might even float `sat` to top-level; see Note [Floating out of top level bindings]. -For nested let bindings, we have to keep in mind Note [Core letrec invariant] +For nested let bindings, we have to keep in mind Note [Core binding invariants], and may exploit strict contexts; see Note [wantFloatLocal]. There are 3 main categories of floats, encoded in the `FloatingBind` type: @@ -1509,7 +1509,7 @@ Wrinkles: (FS1) We detect string literals in `cpeBind Rec{}` and float them out anyway; otherwise we'd try to bind a string literal in a letrec, violating - Note [Core letrec invariant]. Since we know that literals don't have + Note [Top/rec binding invariants]. Since we know that literals don't have free variables, we float further. Arguably, we could just as well relax the letrec invariant for string literals, or anthing that is a value (lifted or not). @@ -2363,7 +2363,7 @@ Wrinkles: x = f y r y = [x] in e - and now we have violated Note [Core letrec invariant]. + and now we have violated Note [Top/rec binding invariants]. So we preempt this case in `wantFloatLocal`, responding `FloatNone` unless all floats are `TopLvlFloatable`. -} ===================================== compiler/GHC/Driver/DynFlags.hs ===================================== @@ -1233,7 +1233,7 @@ optLevelFlags -- see Note [Documenting optimisation flags] , ([1,2], Opt_DoCleverArgEtaExpansion) -- See Note [Eta expansion of arguments in CorePrep] , ([0,1,2], Opt_DoEtaReduction) -- See Note [Eta-reduction in -O0] , ([0,1,2], Opt_ProfManualCcs ) - , ([0,1,2], Opt_OptCoercion ) -- See Note [Coercion optimisation] in GHC.Core.Coercion.Opt + , ([2], Opt_OptCoercion ) -- See Note [Coercion optimisation] in GHC.Core.Coercion.Opt , ([2], Opt_DictsStrict) , ([0], Opt_IgnoreInterfacePragmas) ===================================== testsuite/tests/linear/should_compile/T26332.hs ===================================== @@ -5,6 +5,12 @@ module T26332 where import Unsafe.Coerce +-- This function should be accepted by the typechecker, and should be +-- linear-correct in the output of the desugarer, but will fail +-- -dlinear-core-lint (which does a linear-lint check after every simplifier +-- pass. Because the optimiser discards a cast on `f` that only affects +-- linearity + toLinear :: forall a b p q. (a %p-> b) %1-> (a %q-> b) ===================================== testsuite/tests/linear/should_compile/all.T ===================================== @@ -42,7 +42,7 @@ test('T19400', unless(compiler_debugged(), skip), compile, ['']) test('T20023', normal, compile, ['']) test('T22546', normal, compile, ['']) test('T23025', normal, compile, ['-dlinear-core-lint']) -test('T26332', normal, compile, ['-O -dlinear-core-lint']) +test('T26332', normal, compile_fail, ['-O']) test('LinearRecUpd', normal, compile, ['']) test('T23814', normal, compile, ['']) test('LinearLet', normal, compile, ['']) View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/088f51ccfcd62799e1fa195f3e3740e... -- View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/088f51ccfcd62799e1fa195f3e3740e... You're receiving this email because of your account on gitlab.haskell.org.
participants (1)
-
Simon Peyton Jones (@simonpj)