Zubin pushed to branch wip/27627 at Glasgow Haskell Compiler / GHC
Commits:
-
5d1e5e02
by Zubin Duggal at 2026-08-20T20:14:48+05:30
-
efd3a5cf
by Zubin Duggal at 2026-08-20T20:14:48+05:30
-
a307ee3b
by Zubin Duggal at 2026-08-20T20:14:48+05:30
20 changed files:
- compiler/GHC/Core/Opt/Specialise.hs
- compiler/GHC/Core/Opt/WorkWrap/Utils.hs
- compiler/GHC/CoreToStg/Prep.hs
- + testsuite/tests/core-to-stg/T27627f/Callee.hs
- + testsuite/tests/core-to-stg/T27627f/Caller.hs
- + testsuite/tests/core-to-stg/T27627f/Inst.hs
- + testsuite/tests/core-to-stg/T27627f/Main.hs
- + testsuite/tests/core-to-stg/T27627f/T27627f.stdout
- + testsuite/tests/core-to-stg/T27627f/all.T
- + testsuite/tests/core-to-stg/T27717/Callee.hs
- + testsuite/tests/core-to-stg/T27717/Callee.hs-boot
- + testsuite/tests/core-to-stg/T27717/Main.hs
- + testsuite/tests/core-to-stg/T27717/Mid.hs
- + testsuite/tests/core-to-stg/T27717/T27717.stdout
- + testsuite/tests/core-to-stg/T27717/Ty.hs
- + testsuite/tests/core-to-stg/T27717/all.T
- + 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:
| ... | ... | @@ -1647,6 +1647,14 @@ specCalls spec_imp env existing_rules calls_for_me fn rhs |
| 1647 | 1647 | (rhs_bndrs, rhs_body) = collectBindersPushingCo rhs
|
| 1648 | 1648 | -- See Note [Account for casts in binding]
|
| 1649 | 1649 | |
| 1650 | + -- Binders of the stable unfolding template, if there is one.
|
|
| 1651 | + -- See Note [Dead args and stable unfoldings]
|
|
| 1652 | + unf_bndrs | isStableUnfolding fn_unf
|
|
| 1653 | + , Just tmpl <- maybeUnfoldingTemplate fn_unf
|
|
| 1654 | + = Just (fst (collectBinders tmpl))
|
|
| 1655 | + | otherwise
|
|
| 1656 | + = Nothing
|
|
| 1657 | + |
|
| 1650 | 1658 | -- Copy InlinePragma information from the parent Id.
|
| 1651 | 1659 | -- So if f has INLINE[1] so does spec_fn
|
| 1652 | 1660 | spec_inl_prag
|
| ... | ... | @@ -1670,7 +1678,7 @@ specCalls spec_imp env existing_rules calls_for_me fn rhs |
| 1670 | 1678 | | otherwise = UnspecArg
|
| 1671 | 1679 | |
| 1672 | 1680 | ; (useful, subst', rule_bndrs, rule_lhs_args, spec_bndrs, dx_binds, spec_args)
|
| 1673 | - <- specHeader this_mod subst rhs_bndrs all_call_args
|
|
| 1681 | + <- specHeader this_mod subst rhs_bndrs unf_bndrs all_call_args
|
|
| 1674 | 1682 | ; let env' = env { se_subst = subst' }
|
| 1675 | 1683 | |
| 1676 | 1684 | -- Check for (a) usefulness and (b) not already covered
|
| ... | ... | @@ -2049,10 +2057,26 @@ Wrinkles |
| 2049 | 2057 | * If the function has a stable unfolding, specHeader has to come up with
|
| 2050 | 2058 | arguments to pass to that stable unfolding, when building the stable
|
| 2051 | 2059 | unfolding of the specialised function: this is the last field in specHeader's
|
| 2052 | - big result tuple.
|
|
| 2060 | + big result tuple. We pass an absent filler, but only once we have checked
|
|
| 2061 | + that the unfolding does not use the argument either.
|
|
| 2062 | + See Note [Dead args and stable unfoldings]
|
|
| 2063 | + |
|
| 2064 | +Note [Dead args and stable unfoldings]
|
|
| 2065 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| 2066 | +specHeader decides an argument is dead with isDeadBinder on a binder of the
|
|
| 2067 | +optimised RHS, then applies the filler to the stable unfolding template instead.
|
|
| 2068 | +The two may differ, so the argument can be dead in the RHS and not in the
|
|
| 2069 | +template. See #27703 for an instance of this. The specialised function's
|
|
| 2070 | +unfolding then contains the filler, and any call site that inlines it evaluates
|
|
| 2071 | +the error thunk.
|
|
| 2053 | 2072 | |
| 2054 | - The right thing to do is to produce a LitRubbish; it should rapidly
|
|
| 2055 | - disappear. Rather like GHC.Core.Opt.WorkWrap.Utils.mk_absent_let.
|
|
| 2073 | +It is not enough to ask whether the RHS binder occurs free in the template: the
|
|
| 2074 | +template has its own binders, so it never does. We must walk the arguments in
|
|
| 2075 | +lockstep. If the template runs out of binders, the RHS was eta-expanded past
|
|
| 2076 | +it, and we assume the argument is used.
|
|
| 2077 | + |
|
| 2078 | +DmdAnal does the same job in addUnfoldingDemands. See Wrinkle (W3) of
|
|
| 2079 | +Note [Absence analysis for stable unfoldings and RULES].
|
|
| 2056 | 2080 | |
| 2057 | 2081 | Note [Specialisation modulo dictionary selectors]
|
| 2058 | 2082 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| ... | ... | @@ -2577,6 +2601,9 @@ specHeader |
| 2577 | 2601 | :: Module -- The module being compiled, for mkAbsentFiller
|
| 2578 | 2602 | -> Core.Subst -- This substitution applies to the [InBndr]
|
| 2579 | 2603 | -> [InBndr] -- Binders from the original function `f`
|
| 2604 | + -> Maybe [InBndr]
|
|
| 2605 | + -- Binders of f's stable unfolding template, if it has one
|
|
| 2606 | + -- See Note [Dead args and stable unfoldings]
|
|
| 2580 | 2607 | -> [SpecArg] -- From the CallInfo
|
| 2581 | 2608 | -> SpecM ( Bool -- True <=> some useful specialisation happened
|
| 2582 | 2609 | -- Not the same as any (isSpecDict args) because
|
| ... | ... | @@ -2600,13 +2627,13 @@ specHeader |
| 2600 | 2627 | |
| 2601 | 2628 | -- If we run out of binders, stop immediately
|
| 2602 | 2629 | -- See Note [Specialisation Must Preserve Sharing]
|
| 2603 | -specHeader _ subst [] _ = pure (False, subst, [], [], [], [], [])
|
|
| 2604 | -specHeader _ subst _ [] = pure (False, subst, [], [], [], [], [])
|
|
| 2630 | +specHeader _ subst [] _ _ = pure (False, subst, [], [], [], [], [])
|
|
| 2631 | +specHeader _ subst _ _ [] = pure (False, subst, [], [], [], [], [])
|
|
| 2605 | 2632 | |
| 2606 | 2633 | -- We want to specialise on type 'T1', and so we must construct a substitution
|
| 2607 | 2634 | -- 'a->T1', as well as a LHS argument for the resulting RULE and unfolding
|
| 2608 | 2635 | -- details.
|
| 2609 | -specHeader mod subst (bndr:bndrs) (SpecType ty : args)
|
|
| 2636 | +specHeader mod subst (bndr:bndrs) unf_bndrs (SpecType ty : args)
|
|
| 2610 | 2637 | = do { -- Find free_tvs, the type variables to add to the binders for the rule
|
| 2611 | 2638 | -- Namely those deeply free in `ty` that aren't in scope
|
| 2612 | 2639 | -- See (MP2) in Note [Specialising polymorphic dictionaries]
|
| ... | ... | @@ -2619,7 +2646,7 @@ specHeader mod subst (bndr:bndrs) (SpecType ty : args) |
| 2619 | 2646 | |
| 2620 | 2647 | ; let subst2 = Core.extendTvSubst subst1 bndr ty
|
| 2621 | 2648 | ; (useful, subst3, rule_bs, rule_args, spec_bs, dx, spec_args)
|
| 2622 | - <- specHeader mod subst2 bndrs args
|
|
| 2649 | + <- specHeader mod subst2 bndrs (fmap (drop 1) unf_bndrs) args
|
|
| 2623 | 2650 | ; pure ( useful, subst3
|
| 2624 | 2651 | , free_tvs ++ rule_bs, Type ty : rule_args
|
| 2625 | 2652 | , free_tvs ++ spec_bs, dx, Type ty : spec_args ) }
|
| ... | ... | @@ -2628,17 +2655,19 @@ specHeader mod subst (bndr:bndrs) (SpecType ty : args) |
| 2628 | 2655 | -- a substitution on it (in case the type refers to 'a'). Additionally, we need
|
| 2629 | 2656 | -- to produce a binder, LHS argument and RHS argument for the resulting rule,
|
| 2630 | 2657 | -- /and/ a binder for the specialised body.
|
| 2631 | -specHeader mod subst (bndr:bndrs) (UnspecType : args)
|
|
| 2658 | +specHeader mod subst (bndr:bndrs) unf_bndrs (UnspecType : args)
|
|
| 2632 | 2659 | = do { let (subst1, bndr') = Core.substBndr subst bndr
|
| 2633 | 2660 | ; (useful, subst2, rule_bs, rule_es, spec_bs, dx, spec_args)
|
| 2634 | - <- specHeader mod subst1 bndrs args
|
|
| 2661 | + <- specHeader mod subst1 bndrs (fmap (drop 1) unf_bndrs) args
|
|
| 2635 | 2662 | ; let ty_e' = Type (mkTyVarTy bndr')
|
| 2636 | 2663 | ; pure ( useful, subst2
|
| 2637 | 2664 | , bndr' : rule_bs, ty_e' : rule_es
|
| 2638 | 2665 | , bndr' : spec_bs, dx, ty_e' : spec_args ) }
|
| 2639 | 2666 | |
| 2640 | -specHeader mod subst (bndr:bndrs) (_ : args)
|
|
| 2667 | +specHeader mod subst (bndr:bndrs) unf_bndrs (_ : args)
|
|
| 2641 | 2668 | | isDeadBinder bndr
|
| 2669 | + , dead_in_unfolding unf_bndrs
|
|
| 2670 | + -- See Note [Dead args and stable unfoldings]
|
|
| 2642 | 2671 | , let (subst1, bndr') = Core.substBndr subst (zapIdOccInfo bndr)
|
| 2643 | 2672 | , Just filler <- mkAbsentFiller mod bndr' NotMarkedStrict
|
| 2644 | 2673 | -- NB: mkAbsentFiller returns Nothing for a terminating type (e.g. a
|
| ... | ... | @@ -2647,7 +2676,8 @@ specHeader mod subst (bndr:bndrs) (_ : args) |
| 2647 | 2676 | -- See Note [Don't make fillers for dictionary types]
|
| 2648 | 2677 | -- in GHC.Core.Opt.WorkWrap.Utils
|
| 2649 | 2678 | = -- See Note [Drop dead args from specialisations]
|
| 2650 | - do { (useful, subst2, rule_bs, rule_es, spec_bs, dx, spec_args) <- specHeader mod subst1 bndrs args
|
|
| 2679 | + do { (useful, subst2, rule_bs, rule_es, spec_bs, dx, spec_args)
|
|
| 2680 | + <- specHeader mod subst1 bndrs (fmap (drop 1) unf_bndrs) args
|
|
| 2651 | 2681 | ; pure ( useful, subst2
|
| 2652 | 2682 | , bndr' : rule_bs, Var bndr' : rule_es
|
| 2653 | 2683 | , spec_bs, dx, filler : spec_args ) }
|
| ... | ... | @@ -2655,7 +2685,7 @@ specHeader mod subst (bndr:bndrs) (_ : args) |
| 2655 | 2685 | -- Next we want to specialise the 'Eq a' dict away. We need to construct
|
| 2656 | 2686 | -- a wildcard binder to match the dictionary (See Note [Specialising Calls] for
|
| 2657 | 2687 | -- the nitty-gritty), as a LHS rule and unfolding details.
|
| 2658 | -specHeader mod subst (bndr:bndrs) (SpecDict dict_arg : args)
|
|
| 2688 | +specHeader mod subst (bndr:bndrs) unf_bndrs (SpecDict dict_arg : args)
|
|
| 2659 | 2689 | = do { -- Make up a fresh binder to use in the RULE
|
| 2660 | 2690 | -- It might turn into a dict binding (via bindAuxiliaryDict) which we
|
| 2661 | 2691 | -- then float, so we use cloneIdBndr to get a completely fresh binder
|
| ... | ... | @@ -2666,7 +2696,8 @@ specHeader mod subst (bndr:bndrs) (SpecDict dict_arg : args) |
| 2666 | 2696 | -- Extend the substitution to map bndr :-> dict_arg, for use in the RHS
|
| 2667 | 2697 | ; let (subst2, dx_bind, spec_dict) = bindAuxiliaryDict subst1 bndr bndr' dict_arg
|
| 2668 | 2698 | |
| 2669 | - ; (_, subst3, rule_bs, rule_es, spec_bs, dx, spec_args) <- specHeader mod subst2 bndrs args
|
|
| 2699 | + ; (_, subst3, rule_bs, rule_es, spec_bs, dx, spec_args)
|
|
| 2700 | + <- specHeader mod subst2 bndrs (fmap (drop 1) unf_bndrs) args
|
|
| 2670 | 2701 | |
| 2671 | 2702 | ; let dx' = case dx_bind of { Nothing -> dx; Just d -> d : dx }
|
| 2672 | 2703 | ; pure ( True, subst3 -- Ha! A useful specialisation!
|
| ... | ... | @@ -2681,10 +2712,11 @@ specHeader mod subst (bndr:bndrs) (SpecDict dict_arg : args) |
| 2681 | 2712 | -- why 'i' doesn't appear in our RULE above. But we have no guarantee that
|
| 2682 | 2713 | -- there aren't 'UnspecArg's which come /before/ all of the dictionaries, so
|
| 2683 | 2714 | -- this case must be here.
|
| 2684 | -specHeader mod subst (bndr:bndrs) (UnspecArg : args)
|
|
| 2715 | +specHeader mod subst (bndr:bndrs) unf_bndrs (UnspecArg : args)
|
|
| 2685 | 2716 | = do { let (subst1, bndr') = Core.substBndr subst (zapIdOccInfo bndr)
|
| 2686 | 2717 | -- zapIdOccInfo: see Note [Zap occ info in rule binders]
|
| 2687 | - ; (useful, subst2, rule_bs, rule_es, spec_bs, dx, spec_args) <- specHeader mod subst1 bndrs args
|
|
| 2718 | + ; (useful, subst2, rule_bs, rule_es, spec_bs, dx, spec_args)
|
|
| 2719 | + <- specHeader mod subst1 bndrs (fmap (drop 1) unf_bndrs) args
|
|
| 2688 | 2720 | |
| 2689 | 2721 | ; let dummy_arg = varToCoreExpr bndr'
|
| 2690 | 2722 | -- dummy_arg is usually just (Var bndr),
|
| ... | ... | @@ -2698,6 +2730,14 @@ specHeader mod subst (bndr:bndrs) (UnspecArg : args) |
| 2698 | 2730 | , bndrs ++ spec_bs, dx, dummy_arg : spec_args ) }
|
| 2699 | 2731 | |
| 2700 | 2732 | |
| 2733 | +dead_in_unfolding :: Maybe [InBndr] -> Bool
|
|
| 2734 | +-- ^ Is the argument at this position dead in the stable unfolding template?
|
|
| 2735 | +-- Nothing means there is no stable unfolding, so no filler can escape into one.
|
|
| 2736 | +-- If the template runs out of binders we cannot tell, so we say No.
|
|
| 2737 | +dead_in_unfolding Nothing = True
|
|
| 2738 | +dead_in_unfolding (Just (unf_bndr : _)) = isDeadBinder unf_bndr
|
|
| 2739 | +dead_in_unfolding (Just []) = False
|
|
| 2740 | + |
|
| 2701 | 2741 | -- | Binds a dictionary argument to a fresh name, to preserve sharing
|
| 2702 | 2742 | bindAuxiliaryDict
|
| 2703 | 2743 | :: Subst
|
| ... | ... | @@ -32,7 +32,7 @@ import GHC.Core.Multiplicity |
| 32 | 32 | import GHC.Core.Coercion
|
| 33 | 33 | import GHC.Core.Reduction
|
| 34 | 34 | import GHC.Core.FamInstEnv
|
| 35 | -import GHC.Core.Predicate( isEqualityClass, isDictTy )
|
|
| 35 | +import GHC.Core.Predicate( isEqualityClass )
|
|
| 36 | 36 | import GHC.Core.TyCon
|
| 37 | 37 | import GHC.Core.TyCon.Set
|
| 38 | 38 | import GHC.Core.TyCon.RecWalk
|
| ... | ... | @@ -1072,7 +1072,7 @@ mkAbsentFiller mod arg str |
| 1072 | 1072 | -- evaluated or have a field projected out of it.
|
| 1073 | 1073 | -- See (AF4) in Note [Absent fillers], and
|
| 1074 | 1074 | -- Note [Don't make fillers for dictionary types].
|
| 1075 | - | isDictTy arg_ty
|
|
| 1075 | + | ConstraintLike <- typeTypeOrConstraint arg_ty
|
|
| 1076 | 1076 | = Nothing
|
| 1077 | 1077 | |
| 1078 | 1078 | -- The lifted case: bind 'absentError'. See (AF1) in Note [Absent fillers]
|
| ... | ... | @@ -1977,6 +1977,26 @@ prep up `rhs1`, we have to include not only `f1`, but all binders of the group |
| 1977 | 1977 | `f1..fn` in this set, otherwise our fix is not robust wrt. mutual recursive
|
| 1978 | 1978 | DFuns.
|
| 1979 | 1979 | |
| 1980 | +cpe_rec_ids is module-local, so it does not catch a recursion that goes through
|
|
| 1981 | +an hs-boot import. Suppose Mid and Callee form a module loop, and Mid sees
|
|
| 1982 | +Callee through its hs-boot file:
|
|
| 1983 | + |
|
| 1984 | + -- Mid.hs
|
|
| 1985 | + $fCAT = \ @a $dCB -> C:CA ($fxCBT $dCB) ...
|
|
| 1986 | + -- Callee.hs
|
|
| 1987 | + $fCBT = \ @a $dCB -> C:CB ($fCAT $dCB) ...
|
|
| 1988 | + |
|
| 1989 | +Neither module can see that these two call each other, so each looks
|
|
| 1990 | +non-recursive, and we speculate the inner call in both:
|
|
| 1991 | + |
|
| 1992 | + $fCAT = \ @a $dCB -> case $fxCBT $dCB of s { __DEFAULT -> C:CA s ... }
|
|
| 1993 | + $fCBT = \ @a $dCB -> case $fCAT $dCB of s { __DEFAULT -> C:CB s ... }
|
|
| 1994 | + |
|
| 1995 | +Now each forces the other and the program loops. Such a cycle must cross an
|
|
| 1996 | +hs-boot edge, so we do not speculate a call whose callee has a BootUnfolding.
|
|
| 1997 | +Note [Inlining and hs-boot files] in GHC.CoreToIface does the same for
|
|
| 1998 | +infinite inlining.
|
|
| 1999 | + |
|
| 1980 | 2000 | NB: If at some point we decide to have a termination analysis for general
|
| 1981 | 2001 | functions (#8655, !1866), we need to take similar precautions for (guarded)
|
| 1982 | 2002 | recursive functions:
|
| ... | ... | @@ -2323,10 +2343,13 @@ mkNonRecFloat env lev bndr rhs |
| 2323 | 2343 | -- See Note [Controlling Speculative Evaluation]
|
| 2324 | 2344 | call_ok_for_spec x
|
| 2325 | 2345 | | is_rec_call x = False
|
| 2346 | + | is_boot_call x = False
|
|
| 2326 | 2347 | | not (cp_specEval cfg) = False
|
| 2327 | 2348 | | not (cp_specEvalDFun cfg) && isDFunId x = False
|
| 2328 | 2349 | | otherwise = True
|
| 2329 | - is_rec_call = (`elemUnVarSet` cpe_rec_ids env)
|
|
| 2350 | + is_rec_call = (`elemUnVarSet` cpe_rec_ids env)
|
|
| 2351 | + is_boot_call = isBootUnfolding . realIdUnfolding
|
|
| 2352 | + -- See Note [Speculative evaluation], Very Nasty Wrinkle
|
|
| 2330 | 2353 | |
| 2331 | 2354 | -- See Note [Pin evaluatedness on floats]
|
| 2332 | 2355 | bndr' | is_hnf = bndr `setIdUnfolding` evaldUnfolding
|
| 1 | +{-# LANGUAGE GADTs, ConstraintKinds, ScopedTypeVariables #-}
|
|
| 2 | +{-# OPTIONS_GHC -fno-worker-wrapper #-}
|
|
| 3 | +module Callee where
|
|
| 4 | + |
|
| 5 | +-- Not unary: a superclass field and a method field, so (Eq a) can be
|
|
| 6 | +-- speculatively selected out of a (TC a) dictionary.
|
|
| 7 | +class Eq a => TC a where
|
|
| 8 | + tcDummy :: a -> Int
|
|
| 9 | + |
|
| 10 | +data W = W
|
|
| 11 | +instance Eq W where _ == _ = True
|
|
| 12 | + |
|
| 13 | +data Dict c where
|
|
| 14 | + Dict :: c => Dict c
|
|
| 15 | + |
|
| 16 | +{-# NOINLINE discard #-}
|
|
| 17 | +discard :: Dict c -> Int
|
|
| 18 | +discard _ = 42
|
|
| 19 | + |
|
| 20 | +-- Never forces its dictionary, so demand analysis marks it absent, and
|
|
| 21 | +-- Caller's `a` inherits that absence.
|
|
| 22 | +{-# NOINLINE b #-}
|
|
| 23 | +b :: forall a. TC a => a -> Int
|
|
| 24 | +b _ = discard (Dict :: Dict (Eq a)) |
| 1 | +{-# LANGUAGE TypeFamilies, ConstraintKinds, FlexibleContexts #-}
|
|
| 2 | +{-# LANGUAGE UndecidableInstances #-}
|
|
| 3 | +module Caller where
|
|
| 4 | + |
|
| 5 | +import Callee
|
|
| 6 | +import Data.Kind (Constraint)
|
|
| 7 | + |
|
| 8 | +-- A Constraint-kinded type family. It reduces to (TC W), so the dictionary
|
|
| 9 | +-- `a` receives really is a (TC W) dictionary; but (F W) is not class-headed,
|
|
| 10 | +-- so isDictTy says False. Worker/wrapper must still not replace it with a
|
|
| 11 | +-- filler: Callee speculates a superclass selection out of it.
|
|
| 12 | +type family F a :: Constraint
|
|
| 13 | +type instance F W = TC W
|
|
| 14 | + |
|
| 15 | +-- Note there is no `instance TC W` in scope here: `b W` must take its
|
|
| 16 | +-- dictionary from this Given, rather than from an instance.
|
|
| 17 | +{-# NOINLINE a #-}
|
|
| 18 | +a :: F W => Int -> Int
|
|
| 19 | +a x = b W + x |
| 1 | +module Inst where
|
|
| 2 | + |
|
| 3 | +import Callee
|
|
| 4 | + |
|
| 5 | +-- Deliberately not in Callee or Caller: see the comment in Caller.hs
|
|
| 6 | +instance TC W where tcDummy _ = 7 |
| 1 | +module Main where
|
|
| 2 | + |
|
| 3 | +import Caller
|
|
| 4 | +import Inst ()
|
|
| 5 | + |
|
| 6 | +main :: IO ()
|
|
| 7 | +main = print (a 1) |
| 1 | +43 |
| 1 | +test('T27627f',
|
|
| 2 | + [extra_files(['Main.hs', 'Inst.hs', 'Caller.hs', 'Callee.hs'])],
|
|
| 3 | + multimod_compile_and_run,
|
|
| 4 | + ['Main', '-O']) |
| 1 | +{-# LANGUAGE UndecidableInstances, FlexibleInstances, FlexibleContexts #-}
|
|
| 2 | +module Callee where
|
|
| 3 | + |
|
| 4 | +import Ty
|
|
| 5 | +import Mid
|
|
| 6 | + |
|
| 7 | +instance CB a => CB (T a) where
|
|
| 8 | + opB _ = 2
|
|
| 9 | + |
|
| 10 | +instance CB Int where
|
|
| 11 | + opB _ = 8 |
| 1 | +{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
|
|
| 2 | +module Callee where
|
|
| 3 | + |
|
| 4 | +import Ty
|
|
| 5 | + |
|
| 6 | +instance CB a => CB (T a)
|
|
| 7 | +instance CB Int |
| 1 | +module Main where
|
|
| 2 | +import Mid
|
|
| 3 | +import Callee ()
|
|
| 4 | + |
|
| 5 | +main :: IO ()
|
|
| 6 | +main = print (g (1 :: Int)) |
| 1 | +{-# LANGUAGE UndecidableInstances, FlexibleInstances, FlexibleContexts #-}
|
|
| 2 | +{-# LANGUAGE GADTs, ConstraintKinds, ScopedTypeVariables #-}
|
|
| 3 | +module Mid where
|
|
| 4 | + |
|
| 5 | +import Ty
|
|
| 6 | +import {-# SOURCE #-} Callee
|
|
| 7 | + |
|
| 8 | +-- $fCATa calls Callee's $fxCBT, and that calls this one back. The cycle runs
|
|
| 9 | +-- through the hs-boot import, so both dfuns are NonRec in their own module and
|
|
| 10 | +-- cpe_rec_ids cannot see it. Speculating either one loops.
|
|
| 11 | +instance CB a => CA (T a) where
|
|
| 12 | + opA _ = 1
|
|
| 13 | + |
|
| 14 | +instance CA Int where
|
|
| 15 | + opA _ = 9
|
|
| 16 | + |
|
| 17 | +data Dict c where
|
|
| 18 | + Dict :: c => Dict c
|
|
| 19 | + |
|
| 20 | +{-# NOINLINE keep #-}
|
|
| 21 | +keep :: [Dict c] -> Int
|
|
| 22 | +keep xs = length xs + 41
|
|
| 23 | + |
|
| 24 | +{-# NOINLINE g #-}
|
|
| 25 | +g :: forall a. CB a => a -> Int
|
|
| 26 | +g _ = keep [Dict :: Dict (CA (T a))] |
| 1 | +42 |
| 1 | +{-# LANGUAGE UndecidableSuperClasses #-}
|
|
| 2 | +module Ty where
|
|
| 3 | + |
|
| 4 | +data T a = MkT a
|
|
| 5 | + |
|
| 6 | +class CB a => CA a where
|
|
| 7 | + opA :: a -> Int
|
|
| 8 | + |
|
| 9 | +class CA a => CB a where
|
|
| 10 | + opB :: a -> Int |
| 1 | +test('T27717',
|
|
| 2 | + [extra_files(['Main.hs', 'Mid.hs', 'Ty.hs', 'Callee.hs', 'Callee.hs-boot'])],
|
|
| 3 | + multimod_compile_and_run,
|
|
| 4 | + ['Main', '-O -fspec-eval-dictfun']) |
| 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']) |