Magnus pushed to branch wip/mangoiv/9.12.5-rc4 at Glasgow Haskell Compiler / GHC
Commits:
-
1b66cc1e
by Zubin Duggal at 2026-09-04T14:07:07+02:00
-
e7b125d6
by Zubin Duggal at 2026-09-04T15:53:18+02:00
30 changed files:
- + changelog.d/27703
- + changelog.d/27704
- compiler/GHC/Core.hs
- compiler/GHC/Core/Opt/Specialise.hs
- compiler/GHC/Core/TyCon.hs
- compiler/GHC/Core/Type.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/IfaceToCore.hs
- compiler/GHC/Types/Id/Info.hs
- compiler/GHC/Types/Id/Make.hs
- + testsuite/tests/core-to-stg/T27704/Callee.hs
- + testsuite/tests/core-to-stg/T27704/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27704/Main.hs
- + testsuite/tests/core-to-stg/T27704/Mid.hs
- + testsuite/tests/core-to-stg/T27704/T27704.stdout
- + testsuite/tests/core-to-stg/T27704/all.T
- + testsuite/tests/core-to-stg/T27704a/Callee.hs
- + testsuite/tests/core-to-stg/T27704a/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27704a/Main.hs
- + testsuite/tests/core-to-stg/T27704a/Mid.hs
- + testsuite/tests/core-to-stg/T27704a/T27704a.stdout
- + testsuite/tests/core-to-stg/T27704a/all.T
- testsuite/tests/simplCore/should_compile/T17966.stderr
- testsuite/tests/simplCore/should_compile/T7785.stderr
- testsuite/tests/simplCore/should_compile/spec004.hs
- testsuite/tests/simplCore/should_compile/spec004.stderr
- + testsuite/tests/simplCore/should_run/T27703/Lib.hs
- + testsuite/tests/simplCore/should_run/T27703/Main.hs
- + testsuite/tests/simplCore/should_run/T27703/T27703.stdout
- + testsuite/tests/simplCore/should_run/T27703/all.T
Changes:
| 1 | +section: compiler
|
|
| 2 | +synopsis: Fix a bug where the specialiser could drop an argument that the
|
|
| 3 | + function's stable unfolding still used, resulting in a runtime crash.
|
|
| 4 | +mrs: !16519
|
|
| 5 | +issues: #27703 |
| 1 | +section: compiler
|
|
| 2 | +synopsis: Fix a bug where a class declared abstractly in an hs-boot file was
|
|
| 3 | + assumed not to be unary, so GHC speculatively evaluated a dictionary that
|
|
| 4 | + could be bottom, crashing the program.
|
|
| 5 | +mrs: !16519
|
|
| 6 | +issues: #27704 |
| ... | ... | @@ -574,6 +574,17 @@ Wrinkle (NBD1) |
| 574 | 574 | say that /all/ unary-class constraints might diverge. Remember, almost all
|
| 575 | 575 | classes are non-unary, and thus definitely non-bottom.
|
| 576 | 576 | |
| 577 | +Wrinkle (NBD2)
|
|
| 578 | + A class declared in an hs-boot file is an AbstractTyCon inside the module
|
|
| 579 | + loop:
|
|
| 580 | + module Callee where { class UC (a :: Type) } -- Callee.hs-boot
|
|
| 581 | + module Callee where { class UC a where { ucm :: a -> a } } -- Callee.hs
|
|
| 582 | + isUnaryClassTyCon returns False for it, but compiling the real declaration
|
|
| 583 | + may reveal a UnaryClassTyCon, whose dictionary is `ucm` and can be bottom.
|
|
| 584 | + |
|
| 585 | + Speculating a superclass selection from such a dictionary crashes (#27704).
|
|
| 586 | + So we must treat an AbstractTyCon as possibly unary.
|
|
| 587 | + |
|
| 577 | 588 | Note [Case expression invariants]
|
| 578 | 589 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 579 | 590 | Case expressions are one of the more complicated elements of the Core
|
| ... | ... | @@ -20,13 +20,12 @@ import GHC.Core.Multiplicity |
| 20 | 20 | import GHC.Core.SimpleOpt( defaultSimpleOpts, simpleOptExprWith )
|
| 21 | 21 | import GHC.Core.Predicate
|
| 22 | 22 | import GHC.Core.Coercion( Coercion )
|
| 23 | -import GHC.Core.DataCon ( StrictnessMark (..) )
|
|
| 24 | -import GHC.Core.Opt.Monad
|
|
| 23 | +import GHC.Core.DataCon (dataConTyCon)
|
|
| 25 | 24 | |
| 26 | 25 | import qualified GHC.Core.Subst as Core
|
| 27 | 26 | import GHC.Core.Unfold.Make
|
| 28 | 27 | import GHC.Core
|
| 29 | -import GHC.Core.Opt.WorkWrap.Utils ( mkAbsentFiller )
|
|
| 28 | +import GHC.Core.Make ( wrapFloats )
|
|
| 30 | 29 | import GHC.Core.Unify ( tcMatchTy )
|
| 31 | 30 | import GHC.Core.Rules
|
| 32 | 31 | import GHC.Core.Utils ( exprIsTrivial, exprIsTopLevelBindable
|
| ... | ... | @@ -70,6 +69,7 @@ import GHC.Core.Unfold |
| 70 | 69 | import Data.List( partition )
|
| 71 | 70 | import Data.List.NonEmpty ( NonEmpty (..) )
|
| 72 | 71 | import GHC.Core.Subst (substTickish)
|
| 72 | +import GHC.Core.Opt.Monad
|
|
| 73 | 73 | |
| 74 | 74 | {-
|
| 75 | 75 | ************************************************************************
|
| ... | ... | @@ -1713,7 +1713,7 @@ specCalls spec_imp env existing_rules calls_for_me fn rhs |
| 1713 | 1713 | |
| 1714 | 1714 | ; ( useful, rhs_env2, leftover_bndrs
|
| 1715 | 1715 | , rule_bndrs, rule_lhs_args
|
| 1716 | - , spec_bndrs1, dx_binds, spec_args) <- specHeader this_mod env rhs_bndrs all_call_args
|
|
| 1716 | + , spec_bndrs1, dx_binds, spec_args) <- specHeader env rhs_bndrs all_call_args
|
|
| 1717 | 1717 | |
| 1718 | 1718 | -- ; pprTrace "spec_call" (vcat
|
| 1719 | 1719 | -- [ text "fun: " <+> ppr fn
|
| ... | ... | @@ -1794,7 +1794,7 @@ specCalls spec_imp env existing_rules calls_for_me fn rhs |
| 1794 | 1794 | spec_fn_details
|
| 1795 | 1795 | = case idDetails fn of
|
| 1796 | 1796 | JoinId join_arity _ -> JoinId (join_arity - join_arity_decr) Nothing
|
| 1797 | - DFunId is_nt -> DFunId is_nt
|
|
| 1797 | + DFunId terminating -> DFunId terminating
|
|
| 1798 | 1798 | _ -> VanillaId
|
| 1799 | 1799 | |
| 1800 | 1800 | ; spec_fn <- newSpecIdSM (idName fn) spec_fn_ty spec_fn_details spec_fn_info
|
| ... | ... | @@ -2014,49 +2014,22 @@ In the rule, d1 and d2 are just wildcards, not used in the RHS. Note |
| 2014 | 2014 | additionally that 'x' isn't captured by this rule --- we bind only
|
| 2015 | 2015 | enough etas in order to capture all of the *specialised* arguments.
|
| 2016 | 2016 | |
| 2017 | -Note [Drop dead args from specialisations]
|
|
| 2018 | -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 2019 | -When specialising a function, it’s possible some of the arguments may
|
|
| 2020 | -actually be dead. For example, consider:
|
|
| 2017 | +Note [Do not drop dead args from specialisations]
|
|
| 2018 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 2019 | +When specialising a function, some of its arguments may be dead. For example
|
|
| 2021 | 2020 | |
| 2022 | 2021 | f :: forall a. () -> Show a => a -> String
|
| 2023 | 2022 | f x y = show y ++ "!"
|
| 2024 | 2023 | |
| 2025 | -We might generate the following CallInfo for `f @Int`:
|
|
| 2026 | - |
|
| 2027 | - [SpecType Int, UnspecArg, SpecDict $dShowInt, UnspecArg]
|
|
| 2028 | - |
|
| 2029 | -Normally we’d include both the x and y arguments in the
|
|
| 2030 | -specialisation, since we’re not specialising on either of them. But
|
|
| 2031 | -that’s silly, since x is actually unused! So we might as well drop it
|
|
| 2032 | -in the specialisation:
|
|
| 2033 | - |
|
| 2034 | - $sf :: Int -> String
|
|
| 2035 | - $sf y = show y ++ "!"
|
|
| 2036 | - |
|
| 2037 | - {-# RULE "SPEC f @Int" forall x. f @Int x $dShow = $sf #-}
|
|
| 2038 | - |
|
| 2039 | -This doesn’t save us much, since the arg would be removed later by
|
|
| 2040 | -worker/wrapper, anyway, but it’s easy to do.
|
|
| 2041 | - |
|
| 2042 | -Wrinkles
|
|
| 2043 | - |
|
| 2044 | -* Note that we only drop dead arguments if:
|
|
| 2045 | - 1. We don’t specialise on them.
|
|
| 2046 | - 2. They come before an argument we do specialise on.
|
|
| 2047 | - Doing the latter would require eta-expanding the RULE, which could
|
|
| 2048 | - make it match less often, so it’s not worth it. Doing the former could
|
|
| 2049 | - be more useful --- it would stop us from generating pointless
|
|
| 2050 | - specialisations --- but it’s more involved to implement and unclear if
|
|
| 2051 | - it actually provides much benefit in practice.
|
|
| 2052 | - |
|
| 2053 | -* If the function has a stable unfolding, specHeader has to come up with
|
|
| 2054 | - arguments to pass to that stable unfolding, when building the stable
|
|
| 2055 | - unfolding of the specialised function: this is the last field in specHeader's
|
|
| 2056 | - big result tuple.
|
|
| 2024 | +gives the CallInfo [SpecType Int, UnspecArg, SpecDict $dShowInt, UnspecArg]
|
|
| 2025 | +for `f @Int`, in which `x` is dead. We used to drop such an argument from the
|
|
| 2026 | +specialisation and pass an absent filler in its place.
|
|
| 2057 | 2027 | |
| 2058 | - The right thing to do is to produce a LitRubbish; it should rapidly
|
|
| 2059 | - disappear. Rather like GHC.Core.Opt.WorkWrap.Utils.mk_absent_let.
|
|
| 2028 | +We no longer do so, because the filler is applied to the function's stable
|
|
| 2029 | +unfolding template rather than to its optimised RHS, and the two may differ:
|
|
| 2030 | +the argument can be dead in the RHS and not in the template. The specialised
|
|
| 2031 | +function's unfolding then contains the filler, and any call site that inlines
|
|
| 2032 | +it evaluates the error thunk. See #27703 for an instance of how this goes wrong.
|
|
| 2060 | 2033 | |
| 2061 | 2034 | Note [Specialisation modulo dictionary selectors]
|
| 2062 | 2035 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| ... | ... | @@ -2121,8 +2094,7 @@ since we’ll generate a RULE like |
| 2121 | 2094 | RULE "SPEC f @Int" forall x [Occ=Dead].
|
| 2122 | 2095 | f @Int x $dShow = $sf
|
| 2123 | 2096 | |
| 2124 | -and Core Lint complains, even though x only appears on the LHS (due to
|
|
| 2125 | -Note [Drop dead args from specialisations]).
|
|
| 2097 | +and Core Lint complains, even though x only appears on the LHS.
|
|
| 2126 | 2098 | |
| 2127 | 2099 | Why is that a Lint error? Because the arguments on the LHS of a rule
|
| 2128 | 2100 | are syntactically expressions, not patterns, so Lint treats the
|
| ... | ... | @@ -2564,8 +2536,7 @@ isSpecDict _ = False |
| 2564 | 2536 | -- , [T1, T2, c, i, dEqT1, dShow1]
|
| 2565 | 2537 | -- )
|
| 2566 | 2538 | specHeader
|
| 2567 | - :: Module -- The module being compiled, for mkAbsentFiller
|
|
| 2568 | - -> SpecEnv
|
|
| 2539 | + :: SpecEnv
|
|
| 2569 | 2540 | -> [InBndr] -- The binders from the original function 'f'
|
| 2570 | 2541 | -> [SpecArg] -- From the CallInfo
|
| 2571 | 2542 | -> SpecM ( Bool -- True <=> some useful specialisation happened
|
| ... | ... | @@ -2591,7 +2562,7 @@ specHeader |
| 2591 | 2562 | -- We want to specialise on type 'T1', and so we must construct a substitution
|
| 2592 | 2563 | -- 'a->T1', as well as a LHS argument for the resulting RULE and unfolding
|
| 2593 | 2564 | -- details.
|
| 2594 | -specHeader mod env (bndr : bndrs) (SpecType ty : args)
|
|
| 2565 | +specHeader env (bndr : bndrs) (SpecType ty : args)
|
|
| 2595 | 2566 | = do { -- Find qvars, the type variables to add to the binders for the rule
|
| 2596 | 2567 | -- Namely those free in `ty` that aren't in scope
|
| 2597 | 2568 | -- See (MP2) in Note [Specialising polymorphic dictionaries]
|
| ... | ... | @@ -2603,7 +2574,7 @@ specHeader mod env (bndr : bndrs) (SpecType ty : args) |
| 2603 | 2574 | ty' = substTy env1 ty
|
| 2604 | 2575 | env2 = extendTvSubst env1 bndr ty'
|
| 2605 | 2576 | ; (useful, env3, leftover_bndrs, rule_bs, rule_es, bs', dx, spec_args)
|
| 2606 | - <- specHeader mod env2 bndrs args
|
|
| 2577 | + <- specHeader env2 bndrs args
|
|
| 2607 | 2578 | ; pure ( useful
|
| 2608 | 2579 | , env3
|
| 2609 | 2580 | , leftover_bndrs
|
| ... | ... | @@ -2619,10 +2590,10 @@ specHeader mod env (bndr : bndrs) (SpecType ty : args) |
| 2619 | 2590 | -- a substitution on it (in case the type refers to 'a'). Additionally, we need
|
| 2620 | 2591 | -- to produce a binder, LHS argument and RHS argument for the resulting rule,
|
| 2621 | 2592 | -- /and/ a binder for the specialised body.
|
| 2622 | -specHeader mod env (bndr : bndrs) (UnspecType : args)
|
|
| 2593 | +specHeader env (bndr : bndrs) (UnspecType : args)
|
|
| 2623 | 2594 | = do { let (env', bndr') = substBndr env bndr
|
| 2624 | 2595 | ; (useful, env'', leftover_bndrs, rule_bs, rule_es, bs', dx, spec_args)
|
| 2625 | - <- specHeader mod env' bndrs args
|
|
| 2596 | + <- specHeader env' bndrs args
|
|
| 2626 | 2597 | ; pure ( useful
|
| 2627 | 2598 | , env''
|
| 2628 | 2599 | , leftover_bndrs
|
| ... | ... | @@ -2636,14 +2607,14 @@ specHeader mod env (bndr : bndrs) (UnspecType : args) |
| 2636 | 2607 | -- Next we want to specialise the 'Eq a' dict away. We need to construct
|
| 2637 | 2608 | -- a wildcard binder to match the dictionary (See Note [Specialising Calls] for
|
| 2638 | 2609 | -- the nitty-gritty), as a LHS rule and unfolding details.
|
| 2639 | -specHeader mod env (bndr : bndrs) (SpecDict d : args)
|
|
| 2610 | +specHeader env (bndr : bndrs) (SpecDict d : args)
|
|
| 2640 | 2611 | | not (isDeadBinder bndr)
|
| 2641 | 2612 | , allVarSet (`elemInScopeSet` in_scope) (exprFreeVars d)
|
| 2642 | 2613 | -- See Note [Weird special case for SpecDict]
|
| 2643 | 2614 | = do { (env1, bndr') <- newDictBndr env bndr -- See Note [Zap occ info in rule binders]
|
| 2644 | 2615 | ; let (env2, dx_bind, spec_dict) = bindAuxiliaryDict env1 bndr bndr' d
|
| 2645 | 2616 | ; (_, env3, leftover_bndrs, rule_bs, rule_es, bs', dx, spec_args)
|
| 2646 | - <- specHeader mod env2 bndrs args
|
|
| 2617 | + <- specHeader env2 bndrs args
|
|
| 2647 | 2618 | ; pure ( True -- Ha! A useful specialisation!
|
| 2648 | 2619 | , env3
|
| 2649 | 2620 | , leftover_bndrs
|
| ... | ... | @@ -2668,27 +2639,14 @@ specHeader mod env (bndr : bndrs) (SpecDict d : args) |
| 2668 | 2639 | -- why 'i' doesn't appear in our RULE above. But we have no guarantee that
|
| 2669 | 2640 | -- there aren't 'UnspecArg's which come /before/ all of the dictionaries, so
|
| 2670 | 2641 | -- this case must be here.
|
| 2671 | -specHeader mod env (bndr : bndrs) (_ : args)
|
|
| 2642 | +specHeader env (bndr : bndrs) (_ : args)
|
|
| 2672 | 2643 | -- The "_" can be UnSpecArg, or SpecDict where the bndr is dead
|
| 2673 | 2644 | = do { -- see Note [Zap occ info in rule binders]
|
| 2674 | 2645 | let (env', bndr') = substBndr env (zapIdOccInfo bndr)
|
| 2675 | 2646 | ; (useful, env'', leftover_bndrs, rule_bs, rule_es, bs', dx, spec_args)
|
| 2676 | - <- specHeader mod env' bndrs args
|
|
| 2677 | - |
|
| 2678 | - ; let -- See Note [Drop dead args from specialisations]
|
|
| 2679 | - -- C.f. GHC.Core.Opt.WorkWrap.Utils.mk_absent_let
|
|
| 2680 | - (mb_spec_bndr, spec_arg)
|
|
| 2681 | - | isDeadBinder bndr
|
|
| 2682 | - , Just filler <- mkAbsentFiller mod bndr' NotMarkedStrict
|
|
| 2683 | - -- NB: mkAbsentFiller returns Nothing for a terminating type (e.g. a
|
|
| 2684 | - -- dictionary), so this guard fails and we fall through, keeping the
|
|
| 2685 | - -- argument instead of dropping it.
|
|
| 2686 | - -- See Note [Don't make fillers for terminating types]
|
|
| 2687 | - -- in GHC.Core.Opt.WorkWrap.Utils
|
|
| 2688 | - = (Nothing, filler)
|
|
| 2689 | - | otherwise
|
|
| 2690 | - = (Just bndr', varToCoreExpr bndr')
|
|
| 2647 | + <- specHeader env' bndrs args
|
|
| 2691 | 2648 | |
| 2649 | + ; let (mb_spec_bndr, spec_arg) = (Just bndr', varToCoreExpr bndr')
|
|
| 2692 | 2650 | ; pure ( useful
|
| 2693 | 2651 | , env''
|
| 2694 | 2652 | , leftover_bndrs
|
| ... | ... | @@ -2704,12 +2662,12 @@ specHeader mod env (bndr : bndrs) (_ : args) |
| 2704 | 2662 | |
| 2705 | 2663 | -- If we run out of binders, stop immediately
|
| 2706 | 2664 | -- See Note [Specialisation Must Preserve Sharing]
|
| 2707 | -specHeader _ env [] _ = pure (False, env, [], [], [], [], [], [])
|
|
| 2665 | +specHeader env [] _ = pure (False, env, [], [], [], [], [], [])
|
|
| 2708 | 2666 | |
| 2709 | 2667 | -- Return all remaining binders from the original function. These have the
|
| 2710 | 2668 | -- invariant that they should all correspond to unspecialised arguments, so
|
| 2711 | 2669 | -- it's safe to stop processing at this point.
|
| 2712 | -specHeader _ env bndrs []
|
|
| 2670 | +specHeader env bndrs []
|
|
| 2713 | 2671 | = pure (False, env', bndrs', [], [], [], [], [])
|
| 2714 | 2672 | where
|
| 2715 | 2673 | (env', bndrs') = substBndrs env bndrs
|
| ... | ... | @@ -46,8 +46,9 @@ module GHC.Core.TyCon( |
| 46 | 46 | noTcTyConScopedTyVars,
|
| 47 | 47 | |
| 48 | 48 | -- ** Predicates on TyCons
|
| 49 | - isAlgTyCon, isVanillaAlgTyCon,
|
|
| 50 | - isClassTyCon, isFamInstTyCon,
|
|
| 49 | + isAlgTyCon, isVanillaAlgTyCon, isClassTyCon,
|
|
| 50 | + isUnaryClassTyCon, isUnaryClassTyCon_maybe, isTerminatingTyCon,
|
|
| 51 | + isFamInstTyCon,
|
|
| 51 | 52 | isPrimTyCon,
|
| 52 | 53 | isTupleTyCon, isUnboxedTupleTyCon, isBoxedTupleTyCon,
|
| 53 | 54 | isUnboxedSumTyCon, isPromotedTupleTyCon,
|
| ... | ... | @@ -2721,6 +2722,27 @@ famTyConFlav_maybe (TyCon { tyConDetails = details }) |
| 2721 | 2722 | | FamilyTyCon {famTcFlav = flav} <- details = Just flav
|
| 2722 | 2723 | | otherwise = Nothing
|
| 2723 | 2724 | |
| 2725 | +isUnaryClassTyCon :: TyCon -> Bool
|
|
| 2726 | +isUnaryClassTyCon tc = isClassTyCon tc && isNewTyCon tc
|
|
| 2727 | + |
|
| 2728 | +isTerminatingTyCon :: TyCon -> Bool
|
|
| 2729 | +-- ^ True <=> a value of type (T t1..tn), where `T` is this TyCon, cannot be bottom.
|
|
| 2730 | +-- The only TyCons that satisfy `isTerminatingTyCon` are non-unary class TyCons.
|
|
| 2731 | +-- See (UCM3) in Note [Unary class magic], and
|
|
| 2732 | +-- Note [NON-BOTTOM-DICTS invariant] in GHC.Core.
|
|
| 2733 | +-- An AbstractTyCon returns False: it comes from an hs-boot file, and the real
|
|
| 2734 | +-- declaration may turn out to be a unary class. See (NBD2) in that Note.
|
|
| 2735 | +isTerminatingTyCon tc = isClassTyCon tc && not (isUnaryClassTyCon tc)
|
|
| 2736 | + && not (isAbstractTyCon tc)
|
|
| 2737 | + |
|
| 2738 | +isUnaryClassTyCon_maybe :: TyCon -> Maybe (Class, DataCon)
|
|
| 2739 | +isUnaryClassTyCon_maybe (TyCon { tyConDetails = details })
|
|
| 2740 | + | AlgTyCon { algTcFlavour = ClassTyCon cls _
|
|
| 2741 | + , algTcRhs = NewTyCon {data_con = dc} } <- details
|
|
| 2742 | + = Just (cls, dc)
|
|
| 2743 | + | otherwise
|
|
| 2744 | + = Nothing
|
|
| 2745 | + |
|
| 2724 | 2746 | -- | Is this 'TyCon' that for a class instance?
|
| 2725 | 2747 | isClassTyCon :: TyCon -> Bool
|
| 2726 | 2748 | isClassTyCon (TyCon { tyConDetails = details })
|
| ... | ... | @@ -2479,7 +2479,7 @@ isTerminatingType :: HasDebugCallStack => Type -> Bool |
| 2479 | 2479 | -- NB: unlifted types are not terminating types!
|
| 2480 | 2480 | -- e.g. you can write a term (loop 1)::Int# that diverges.
|
| 2481 | 2481 | isTerminatingType ty = case tyConAppTyCon_maybe ty of
|
| 2482 | - Just tc -> isClassTyCon tc && not (isNewTyCon tc)
|
|
| 2482 | + Just tc -> isTerminatingTyCon tc
|
|
| 2483 | 2483 | _ -> False
|
| 2484 | 2484 | |
| 2485 | 2485 | isPrimitiveType :: Type -> Bool
|
| ... | ... | @@ -1934,7 +1934,7 @@ app_ok fun_ok primop_ok fun args |
| 1934 | 1934 | |
| 1935 | 1935 | | otherwise
|
| 1936 | 1936 | = case idDetails fun of
|
| 1937 | - DFunId new_type -> not new_type
|
|
| 1937 | + DFunId terminating -> terminating
|
|
| 1938 | 1938 | -- DFuns terminate, unless the dict is implemented
|
| 1939 | 1939 | -- with a newtype in which case they may not
|
| 1940 | 1940 |
| ... | ... | @@ -1749,8 +1749,7 @@ tcIfaceDataAlt mult con inst_tys arg_strs rhs |
| 1749 | 1749 | tcIdDetails :: Name -> Type -> IfaceIdDetails -> IfL IdDetails
|
| 1750 | 1750 | tcIdDetails _ _ IfVanillaId = return VanillaId
|
| 1751 | 1751 | tcIdDetails _ _ (IfWorkerLikeId dmds) = return $ WorkerLikeId dmds
|
| 1752 | -tcIdDetails _ ty IfDFunId
|
|
| 1753 | - = return (DFunId (isNewTyCon (classTyCon cls)))
|
|
| 1752 | +tcIdDetails _ ty IfDFunId = return (mkDFunIdDetails cls)
|
|
| 1754 | 1753 | where
|
| 1755 | 1754 | (_, _, cls, _) = tcSplitDFunTy ty
|
| 1756 | 1755 |
| ... | ... | @@ -20,6 +20,7 @@ Haskell. [WDP 94/11]) |
| 20 | 20 | module GHC.Types.Id.Info (
|
| 21 | 21 | -- * The IdDetails type
|
| 22 | 22 | IdDetails(..), pprIdDetails, coVarDetails, isCoVarDetails,
|
| 23 | + mkDFunIdDetails,
|
|
| 23 | 24 | JoinArity, isJoinIdDetails_maybe,
|
| 24 | 25 | RecSelParent(..), recSelParentName, recSelFirstConName,
|
| 25 | 26 | recSelParentCons, idDetailsConcreteTvs,
|
| ... | ... | @@ -201,9 +202,8 @@ data IdDetails |
| 201 | 202 | | TickBoxOpId TickBoxOp -- ^ The 'Id' is for a HPC tick box (both traditional and binary)
|
| 202 | 203 | |
| 203 | 204 | | DFunId Bool -- ^ A dictionary function.
|
| 204 | - -- Bool = True <=> the class has only one method, so may be
|
|
| 205 | - -- implemented with a newtype, so it might be bad
|
|
| 206 | - -- to be strict on this dictionary
|
|
| 205 | + -- Bool = True <=> a dictionary of this class cannot be bottom
|
|
| 206 | + -- See Note [NON-BOTTOM-DICTS invariant] in GHC.Core
|
|
| 207 | 207 | |
| 208 | 208 | | CoVarId -- ^ A coercion variable
|
| 209 | 209 | -- This only covers /un-lifted/ coercions, of type
|
| ... | ... | @@ -372,6 +372,9 @@ isJoinIdDetails_maybe :: IdDetails -> Maybe (JoinArity, (Maybe [CbvMark])) |
| 372 | 372 | isJoinIdDetails_maybe (JoinId join_arity marks) = Just (join_arity, marks)
|
| 373 | 373 | isJoinIdDetails_maybe _ = Nothing
|
| 374 | 374 | |
| 375 | +mkDFunIdDetails :: Class -> IdDetails
|
|
| 376 | +mkDFunIdDetails cls = DFunId (isTerminatingTyCon (classTyCon cls))
|
|
| 377 | + |
|
| 375 | 378 | instance Outputable IdDetails where
|
| 376 | 379 | ppr = pprIdDetails
|
| 377 | 380 | |
| ... | ... | @@ -388,7 +391,8 @@ pprIdDetails other = brackets (pp other) |
| 388 | 391 | pp (PrimOpId {}) = text "PrimOp"
|
| 389 | 392 | pp (FCallId _) = text "ForeignCall"
|
| 390 | 393 | pp (TickBoxOpId _) = text "TickBoxOp"
|
| 391 | - pp (DFunId nt) = text "DFunId" <> ppWhen nt (text "(nt)")
|
|
| 394 | + pp (DFunId terminating) = text "DFunId"
|
|
| 395 | + <> ppWhen (not terminating) (text "(may-be-bottom)")
|
|
| 392 | 396 | pp (RecSelId { sel_naughty = is_naughty })
|
| 393 | 397 | = brackets $ text "RecSel" <>
|
| 394 | 398 | ppWhen is_naughty (text "(naughty)")
|
| ... | ... | @@ -1849,11 +1849,10 @@ mkDictFunId :: Name -- Name to use for the dict fun; |
| 1849 | 1849 | -- See Note [Dict funs and default methods]
|
| 1850 | 1850 | |
| 1851 | 1851 | mkDictFunId dfun_name tvs theta clas tys
|
| 1852 | - = mkExportedLocalId (DFunId is_nt)
|
|
| 1852 | + = mkExportedLocalId (mkDFunIdDetails clas)
|
|
| 1853 | 1853 | dfun_name
|
| 1854 | 1854 | dfun_ty
|
| 1855 | 1855 | where
|
| 1856 | - is_nt = isNewTyCon (classTyCon clas)
|
|
| 1857 | 1856 | dfun_ty = TcType.tcMkDFunSigmaTy tvs theta (mkClassPred clas tys)
|
| 1858 | 1857 | |
| 1859 | 1858 | {-
|
| 1 | +module Callee where
|
|
| 2 | + |
|
| 3 | +import Mid
|
|
| 4 | + |
|
| 5 | +-- The real UC is a *unary* class: one method, no superclasses. So a (UC a)
|
|
| 6 | +-- dictionary *is* the function ucm, and it can be bottom.
|
|
| 7 | +class UC a where
|
|
| 8 | + ucm :: a -> a
|
|
| 9 | + |
|
| 10 | +instance UC Int where
|
|
| 11 | + ucm = undefined
|
|
| 12 | + |
|
| 13 | +instance MC Int where
|
|
| 14 | + mcm _ = 7 |
| 1 | +{-# LANGUAGE KindSignatures #-}
|
|
| 2 | +module Callee where
|
|
| 3 | + |
|
| 4 | +import Data.Kind (Type)
|
|
| 5 | + |
|
| 6 | +-- Abstract class declaration: no context, no methods, no associated types,
|
|
| 7 | +-- so GHC.Tc.TyCl builds an AbstractTyCon rather than a UnaryClassTyCon.
|
|
| 8 | +class UC (a :: Type) |
| 1 | +module Main where
|
|
| 2 | +import Mid
|
|
| 3 | +import Callee ()
|
|
| 4 | + |
|
| 5 | +main :: IO ()
|
|
| 6 | +main = print (f (1 :: Int)) |
| 1 | +{-# LANGUAGE GADTs, ConstraintKinds, ScopedTypeVariables #-}
|
|
| 2 | +module Mid where
|
|
| 3 | + |
|
| 4 | +import {-# SOURCE #-} Callee
|
|
| 5 | + |
|
| 6 | +class UC a => MC a where
|
|
| 7 | + mcm :: a -> Int
|
|
| 8 | + |
|
| 9 | +data Dict c where
|
|
| 10 | + Dict :: c => Dict c
|
|
| 11 | + |
|
| 12 | +{-# NOINLINE keep #-}
|
|
| 13 | +keep :: [Dict c] -> Int
|
|
| 14 | +keep xs = length xs + 41
|
|
| 15 | + |
|
| 16 | +{-# NOINLINE f #-}
|
|
| 17 | +f :: forall a. MC a => a -> Int
|
|
| 18 | +f _ = keep [Dict :: Dict (UC a)] |
| 1 | +42 |
| 1 | +test('T27704',
|
|
| 2 | + [extra_files(['Main.hs', 'Mid.hs', 'Callee.hs', 'Callee.hs-boot'])],
|
|
| 3 | + multimod_compile_and_run,
|
|
| 4 | + ['Main', '-O']) |
| 1 | +module Callee where
|
|
| 2 | + |
|
| 3 | +import Mid ()
|
|
| 4 | + |
|
| 5 | +-- Really unary: the dictionary /is/ ucm, so it can be bottom.
|
|
| 6 | +class UC a where
|
|
| 7 | + ucm :: a -> a
|
|
| 8 | + |
|
| 9 | +instance UC Int where
|
|
| 10 | + ucm = id
|
|
| 11 | + |
|
| 12 | +instance UC a => UC [a] where
|
|
| 13 | + ucm = error "ucm[] forced" |
| 1 | +{-# LANGUAGE KindSignatures #-}
|
|
| 2 | +module Callee where
|
|
| 3 | + |
|
| 4 | +import Data.Kind (Type)
|
|
| 5 | + |
|
| 6 | +-- No context, no methods, no associated types, so buildAbstractClass makes an
|
|
| 7 | +-- AbstractTyCon rather than a UnaryClassTyCon: Mid cannot see that UC is unary.
|
|
| 8 | +class UC (a :: Type)
|
|
| 9 | + |
|
| 10 | +instance UC a => UC [a] |
| 1 | +module Main where
|
|
| 2 | +import Mid
|
|
| 3 | +import Callee ()
|
|
| 4 | + |
|
| 5 | +main :: IO ()
|
|
| 6 | +main = print (f (1 :: Int)) |
| 1 | +{-# LANGUAGE GADTs, ScopedTypeVariables #-}
|
|
| 2 | +module Mid where
|
|
| 3 | + |
|
| 4 | +import {-# SOURCE #-} Callee
|
|
| 5 | + |
|
| 6 | +data Dict c where
|
|
| 7 | + Dict :: c => Dict c
|
|
| 8 | + |
|
| 9 | +{-# NOINLINE keep #-}
|
|
| 10 | +keep :: [Dict c] -> Int
|
|
| 11 | +keep xs = length xs + 41
|
|
| 12 | + |
|
| 13 | +-- Builds a (UC [a]) dictionary but never forces it. The dfun comes from the
|
|
| 14 | +-- hs-boot file, where UC looks non-unary, so its DFunId must not claim to
|
|
| 15 | +-- terminate: with -fspec-eval-dictfun, CorePrep would speculate it.
|
|
| 16 | +{-# NOINLINE f #-}
|
|
| 17 | +f :: forall a. UC a => a -> Int
|
|
| 18 | +f _ = keep [Dict :: Dict (UC [a])] |
| 1 | +42 |
| 1 | +test('T27704a',
|
|
| 2 | + [extra_files(['Main.hs', 'Mid.hs', 'Callee.hs', 'Callee.hs-boot'])],
|
|
| 3 | + multimod_compile_and_run,
|
|
| 4 | + ['Main', '-O -fspec-eval-dictfun']) |
| ... | ... | @@ -113,19 +113,15 @@ $cm_aHo |
| 113 | 113 | (show @c_aHr $dShow_aHs c_aBh))
|
| 114 | 114 | end Rec }
|
| 115 | 115 | |
| 116 | --- RHS size: {terms: 1, types: 0, coercions: 10, joins: 0/0}
|
|
| 117 | -T17966.$fCBoolb [InlPrag=INLINE (sat-args=0)]
|
|
| 118 | - :: forall b. Show b => C Bool b
|
|
| 119 | -[LclIdX[DFunId(nt)],
|
|
| 120 | - Arity=5,
|
|
| 121 | - Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
|
|
| 122 | - WorkFree=True, Expandable=True,
|
|
| 123 | - Guidance=ALWAYS_IF(arity=1,unsat_ok=False,boring_ok=True)
|
|
| 124 | - Tmpl= $cm_aHo
|
|
| 125 | - `cast` (forall (b :: <*>_N).
|
|
| 126 | - <Show b>_R %<'Many>_N ->_R Sym (T17966.N:C[0] <Bool>_N <b>_N)
|
|
| 127 | - :: (forall {b} c. (Show b, Show c) => Bool -> b -> c -> String)
|
|
| 128 | - ~R# (forall {b}. Show b => C Bool b))}]
|
|
| 116 | +-- RHS size: {terms: 5, types: 6, coercions: 0, joins: 0/0}
|
|
| 117 | +T17966.$fCBoolb [InlPrag=CONLIKE] :: forall b. Show b => C Bool b
|
|
| 118 | +[LclIdX[DFunId(may-be-bottom)],
|
|
| 119 | + Arity=1,
|
|
| 120 | + Unf=Unf{Src=StableSystem, TopLvl=True,
|
|
| 121 | + Value=True, ConLike=True, WorkFree=True, Expandable=True,
|
|
| 122 | + Guidance=ALWAYS_IF(arity=1,unsat_ok=True,boring_ok=False)
|
|
| 123 | + Tmpl= \ (@b_anX) (v_B1 [Occ=Once1] :: Show b_anX) ->
|
|
| 124 | + T17966.C:C @Bool @b_anX ($cm_aJp @b_anX v_B1)}]
|
|
| 129 | 125 | T17966.$fCBoolb
|
| 130 | 126 | = $cm_aHo
|
| 131 | 127 | `cast` (forall (b :: <*>_N).
|
| ... | ... | @@ -37,7 +37,7 @@ $cmyfmap_aG7 |
| 37 | 37 | |
| 38 | 38 | -- RHS size: {terms: 1, types: 0, coercions: 3, joins: 0/0}
|
| 39 | 39 | Foo.$fMyFunctor[] [InlPrag=INLINE (sat-args=0)] :: MyFunctor []
|
| 40 | -[LclIdX[DFunId(nt)],
|
|
| 40 | +[LclIdX[DFunId(may-be-bottom)],
|
|
| 41 | 41 | Arity=4,
|
| 42 | 42 | Str=<A><A><U><SU>,
|
| 43 | 43 | Unf=Unf{Src=InlineStable, TopLvl=True, Value=True, ConLike=True,
|
| 1 | 1 | {-# LANGUAGE RankNTypes #-}
|
| 2 | 2 | |
| 3 | --- Dead arguments should be dropped in specialisations. See !2913.
|
|
| 3 | +-- Dead arguments are kept in specialisations.
|
|
| 4 | +-- See Note [Do not drop dead args from specialisations] in GHC.Core.Opt.Specialise
|
|
| 4 | 5 | |
| 5 | 6 | module ShouldCompile where
|
| 6 | 7 |
| 1 | 1 | |
| 2 | 2 | ==================== Specialise ====================
|
| 3 | 3 | Result size of Specialise
|
| 4 | - = {terms: 52, types: 41, coercions: 0, joins: 0/0}
|
|
| 4 | + = {terms: 39, types: 38, coercions: 0, joins: 0/0}
|
|
| 5 | 5 | |
| 6 | --- RHS size: {terms: 13, types: 10, coercions: 0, joins: 0/0}
|
|
| 7 | -$sfoo [InlPrag=NOINLINE[0]] :: Int -> [Char]
|
|
| 8 | -[LclId, Arity=1]
|
|
| 6 | +-- RHS size: {terms: 14, types: 11, coercions: 0, joins: 0/0}
|
|
| 7 | +$sfoo [InlPrag=NOINLINE[0]] :: () -> Int -> [Char]
|
|
| 8 | +[LclId, Arity=2]
|
|
| 9 | 9 | $sfoo
|
| 10 | - = \ (y :: Int) ->
|
|
| 11 | - GHC.Base.build
|
|
| 10 | + = \ (ds :: ()) (y :: Int) ->
|
|
| 11 | + GHC.Internal.Base.build
|
|
| 12 | 12 | @Char
|
| 13 | 13 | (\ (@b) (c [OS=OneShot] :: Char -> b -> b) (n [OS=OneShot] :: b) ->
|
| 14 | 14 | GHC.Base.foldr
|
| 15 | 15 | @Char
|
| 16 | 16 | @b
|
| 17 | 17 | c
|
| 18 | - (GHC.CString.unpackFoldrCString# @b "!"# c n)
|
|
| 19 | - (GHC.Show.$fShowInt_$cshow y))
|
|
| 18 | + (GHC.Internal.Types.unpackFoldrCString# @b "!"# c n)
|
|
| 19 | + (GHC.Internal.Show.$fShowInt_$cshow y))
|
|
| 20 | 20 | |
| 21 | 21 | -- RHS size: {terms: 17, types: 15, coercions: 0, joins: 0/0}
|
| 22 | 22 | foo [InlPrag=NOINLINE[0]] :: forall a. () -> Show a => a -> String
|
| ... | ... | @@ -25,7 +25,9 @@ foo [InlPrag=NOINLINE[0]] :: forall a. () -> Show a => a -> String |
| 25 | 25 | Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
|
| 26 | 26 | WorkFree=True, Expandable=True, Guidance=IF_ARGS [0 30 0] 150 40},
|
| 27 | 27 | RULES: "SPEC foo @Int" [0]
|
| 28 | - forall (ds :: ()) ($dShow :: Show Int). foo @Int ds $dShow = $sfoo]
|
|
| 28 | + forall (ds [Occ=Once1] :: ()) ($dShow [Occ=Dead] :: Show Int).
|
|
| 29 | + foo @Int ds $dShow
|
|
| 30 | + = $sfoo ds]
|
|
| 29 | 31 | foo
|
| 30 | 32 | = \ (@a) _ [Occ=Dead] ($dShow :: Show a) (y :: a) ->
|
| 31 | 33 | GHC.Base.build
|
| ... | ... | @@ -35,44 +37,9 @@ foo |
| 35 | 37 | @Char
|
| 36 | 38 | @b
|
| 37 | 39 | c
|
| 38 | - (GHC.CString.unpackFoldrCString# @b "!"# c n)
|
|
| 40 | + (GHC.Internal.Types.unpackFoldrCString# @b "!"# c n)
|
|
| 39 | 41 | (show @a $dShow y))
|
| 40 | 42 | |
| 41 | --- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
|
|
| 42 | -$trModule :: GHC.Prim.Addr#
|
|
| 43 | -[LclId,
|
|
| 44 | - Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
|
|
| 45 | - WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 20 0}]
|
|
| 46 | -$trModule = "main"#
|
|
| 47 | - |
|
| 48 | --- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
|
|
| 49 | -$trModule :: GHC.Types.TrName
|
|
| 50 | -[LclId,
|
|
| 51 | - Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
|
|
| 52 | - WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
|
|
| 53 | -$trModule = GHC.Types.TrNameS $trModule
|
|
| 54 | - |
|
| 55 | --- RHS size: {terms: 1, types: 0, coercions: 0, joins: 0/0}
|
|
| 56 | -$trModule :: GHC.Prim.Addr#
|
|
| 57 | -[LclId,
|
|
| 58 | - Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
|
|
| 59 | - WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 50 0}]
|
|
| 60 | -$trModule = "ShouldCompile"#
|
|
| 61 | - |
|
| 62 | --- RHS size: {terms: 2, types: 0, coercions: 0, joins: 0/0}
|
|
| 63 | -$trModule :: GHC.Types.TrName
|
|
| 64 | -[LclId,
|
|
| 65 | - Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
|
|
| 66 | - WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
|
|
| 67 | -$trModule = GHC.Types.TrNameS $trModule
|
|
| 68 | - |
|
| 69 | --- RHS size: {terms: 3, types: 0, coercions: 0, joins: 0/0}
|
|
| 70 | -ShouldCompile.$trModule :: GHC.Types.Module
|
|
| 71 | -[LclIdX,
|
|
| 72 | - Unf=Unf{Src=<vanilla>, TopLvl=True, Value=True, ConLike=True,
|
|
| 73 | - WorkFree=True, Expandable=True, Guidance=IF_ARGS [] 10 10}]
|
|
| 74 | -ShouldCompile.$trModule = GHC.Types.Module $trModule $trModule
|
|
| 75 | - |
|
| 76 | 43 | -- RHS size: {terms: 5, types: 1, coercions: 0, joins: 0/0}
|
| 77 | 44 | bar :: String
|
| 78 | 45 | [LclIdX,
|
| 1 | +{-# LANGUAGE RankNTypes #-}
|
|
| 2 | +module Lib (g) where
|
|
| 3 | + |
|
| 4 | +{-# NOINLINE consume #-}
|
|
| 5 | +consume :: Int -> Int
|
|
| 6 | +consume n = n `seq` 0
|
|
| 7 | + |
|
| 8 | +{-# RULES "consume/drop" [~1] forall n. consume n = 0 #-}
|
|
| 9 | + |
|
| 10 | +-- A dead value argument (n) that comes *before* the dictionary we
|
|
| 11 | +-- specialise on. No implicit parameters involved.
|
|
| 12 | +{-# INLINE [1] f #-}
|
|
| 13 | +f :: forall a. Int -> Show a => a -> String
|
|
| 14 | +f n y = show y ++ replicate (consume n) '!'
|
|
| 15 | + |
|
| 16 | +{-# NOINLINE g #-}
|
|
| 17 | +g :: Bool -> String
|
|
| 18 | +g b = f 7 b ++ "." |
| 1 | +module Main where
|
|
| 2 | +import Lib
|
|
| 3 | + |
|
| 4 | +main :: IO ()
|
|
| 5 | +main = putStrLn (g True) |
| 1 | +True. |
| 1 | +test('T27703',
|
|
| 2 | + [extra_files(['Main.hs', 'Lib.hs'])],
|
|
| 3 | + multimod_compile_and_run,
|
|
| 4 | + ['Main', '-O']) |