Simon Jakobi pushed to branch wip/sjakobi/T27628-rebox-warning at Glasgow Haskell Compiler / GHC
Commits:
-
903a0046
by Simon Jakobi at 2026-08-22T14:12:01+02:00
-
523a633a
by Simon Jakobi at 2026-08-22T14:17:27+02:00
-
16f32940
by Simon Jakobi at 2026-08-22T14:25:01+02:00
-
b01adcd0
by Simon Jakobi at 2026-08-22T14:28:11+02:00
-
96b4e968
by Simon Jakobi at 2026-08-22T15:08:14+02:00
-
c2318213
by Simon Jakobi at 2026-08-22T15:24:25+02:00
-
35025093
by Simon Jakobi at 2026-08-23T04:14:09+02:00
-
92115070
by Simon Jakobi at 2026-08-23T04:22:42+02:00
-
47e5dc94
by Simon Jakobi at 2026-08-23T13:34:21+02:00
-
8d2e130b
by Simon Jakobi at 2026-08-23T13:39:46+02:00
-
c817c224
by Simon Jakobi at 2026-08-23T23:06:52+02:00
-
bbc2f3f9
by Simon Jakobi at 2026-08-23T23:14:48+02:00
-
f0337cb7
by Simon Jakobi at 2026-08-24T01:51:48+02:00
-
147702b0
by Simon Jakobi at 2026-08-24T02:54:10+02:00
23 changed files:
- compiler/GHC/Core/Opt/SetLevels.hs
- compiler/GHC/Core/Opt/SpecConstr.hs
- docs/users_guide/using-warnings.rst
- testsuite/tests/simplCore/should_compile/T27628.stderr
- testsuite/tests/simplCore/should_compile/T27628b.stderr
- + testsuite/tests/simplCore/should_compile/T27628e.hs
- + testsuite/tests/simplCore/should_compile/T27628f.hs
- + testsuite/tests/simplCore/should_compile/T27628f.stderr
- + testsuite/tests/simplCore/should_compile/T27628g.hs
- + testsuite/tests/simplCore/should_compile/T27628g.stderr
- + testsuite/tests/simplCore/should_compile/T27628h.hs
- + testsuite/tests/simplCore/should_compile/T27628h.stderr
- + testsuite/tests/simplCore/should_compile/T27628h_M.hs
- + testsuite/tests/simplCore/should_compile/T27628i.hs
- + testsuite/tests/simplCore/should_compile/T27628i.stderr
- + testsuite/tests/simplCore/should_compile/T27628i_M.hs
- + testsuite/tests/simplCore/should_compile/T27628j.hs
- + testsuite/tests/simplCore/should_compile/T27628j.stderr
- + testsuite/tests/simplCore/should_compile/T27628k.hs
- + testsuite/tests/simplCore/should_compile/T27628k.stderr
- + testsuite/tests/simplCore/should_compile/T27628l.hs
- + testsuite/tests/simplCore/should_compile/T27628l.stderr
- testsuite/tests/simplCore/should_compile/all.T
Changes:
| ... | ... | @@ -108,8 +108,8 @@ import GHC.Types.Var.Env |
| 108 | 108 | import GHC.Types.Literal ( litIsTrivial )
|
| 109 | 109 | import GHC.Types.Demand ( DmdSig, prependArgsDmdSig )
|
| 110 | 110 | import GHC.Types.Cpr ( CprSig, prependArgsCprSig )
|
| 111 | -import GHC.Types.Name ( getOccName )
|
|
| 112 | -import GHC.Types.Name.Occurrence ( occNameFS )
|
|
| 111 | +import GHC.Types.Name ( getOccName, getSrcSpan, mkSystemNameAt )
|
|
| 112 | +import GHC.Types.Name.Occurrence ( occNameFS, mkVarOccFS )
|
|
| 113 | 113 | import GHC.Types.Unique ( hasKey )
|
| 114 | 114 | import GHC.Types.Tickish ( tickishIsCode )
|
| 115 | 115 | import GHC.Types.Unique.Supply
|
| ... | ... | @@ -1872,7 +1872,10 @@ newPolyBndrs dest_lvl |
| 1872 | 1872 | |
| 1873 | 1873 | mk_poly_bndr bndr uniq = transferPolyIdInfo bndr abs_vars $ -- Note [transferPolyIdInfo] in GHC.Types.Id
|
| 1874 | 1874 | transfer_join_info bndr $
|
| 1875 | - mkSysLocal str uniq (idMult bndr) poly_ty
|
|
| 1875 | + -- Keep bndr's srcspan so that diagnostics can
|
|
| 1876 | + -- still point at the original definition
|
|
| 1877 | + mkLocalId (mkSystemNameAt uniq (mkVarOccFS str) (getSrcSpan bndr))
|
|
| 1878 | + (idMult bndr) poly_ty
|
|
| 1876 | 1879 | where
|
| 1877 | 1880 | str = fsLit "poly_" `appendFS` occNameFS (getOccName bndr)
|
| 1878 | 1881 | poly_ty = mkLamTypes abs_vars (substTyUnchecked subst (idType bndr))
|
| ... | ... | @@ -36,6 +36,9 @@ import GHC.Core.Coercion hiding( substCo ) |
| 36 | 36 | import GHC.Core.Rules
|
| 37 | 37 | import GHC.Core.Predicate ( scopedSort, typeDeterminesValue )
|
| 38 | 38 | import GHC.Core.Type hiding ( substTy )
|
| 39 | +import GHC.Core.TyCo.Compare ( eqType )
|
|
| 40 | +import GHC.Core.TyCo.Ppr ( pprSigmaType )
|
|
| 41 | +import GHC.Core.TyCo.Tidy ( tidyTopType )
|
|
| 39 | 42 | import GHC.Core.TyCon (TyCon, tyConName )
|
| 40 | 43 | import GHC.Core.Multiplicity
|
| 41 | 44 | import GHC.Core.Ppr ( pprParendExpr )
|
| ... | ... | @@ -45,12 +48,13 @@ import GHC.Unit.Module.ModGuts |
| 45 | 48 | |
| 46 | 49 | import GHC.Types.InlinePragma
|
| 47 | 50 | import GHC.Types.Error (DiagnosticReason(..))
|
| 48 | -import GHC.Types.Literal ( litIsLifted )
|
|
| 51 | +import GHC.Types.Literal ( Literal, litIsLifted )
|
|
| 49 | 52 | import GHC.Types.Id
|
| 50 | 53 | import GHC.Types.Id.Info ( IdDetails(..) )
|
| 51 | 54 | import GHC.Types.Var.Env
|
| 52 | 55 | import GHC.Types.Var.Set
|
| 53 | 56 | import GHC.Types.Name
|
| 57 | +import GHC.Types.SrcLoc ( isGoodSrcSpan )
|
|
| 54 | 58 | import GHC.Types.Tickish
|
| 55 | 59 | import GHC.Types.Basic
|
| 56 | 60 | import GHC.Types.Demand
|
| ... | ... | @@ -75,7 +79,7 @@ import GHC.Exts( SpecConstrAnnotation(..) ) |
| 75 | 79 | import GHC.Serialized ( deserializeWithData )
|
| 76 | 80 | |
| 77 | 81 | import Control.Monad
|
| 78 | -import Data.List ( sortBy, partition, dropWhileEnd, mapAccumL, nub, unzip4 )
|
|
| 82 | +import Data.List ( sortBy, partition, dropWhileEnd, mapAccumL, nub, nubBy, unzip4 )
|
|
| 79 | 83 | import Data.List.NonEmpty ( NonEmpty (..) )
|
| 80 | 84 | import Data.Maybe( mapMaybe )
|
| 81 | 85 | import Data.Ord( comparing )
|
| ... | ... | @@ -790,8 +794,8 @@ specConstrProgram guts |
| 790 | 794 | is_rebox _ = False
|
| 791 | 795 | |
| 792 | 796 | ; when (not (null forced_ws)) $ diagnostic WarningWithoutFlag (forced_msg forced_ws)
|
| 793 | - ; when (not (null rebox_ws)) $ diagnostic (WarningWithFlag Opt_WarnSpecConstrReboxing)
|
|
| 794 | - (rebox_msg (nub rebox_ws))
|
|
| 797 | + ; mapM_ (diagnostic (WarningWithFlag Opt_WarnSpecConstrReboxing) . rebox_msg)
|
|
| 798 | + (aggregateRebox rebox_ws)
|
|
| 795 | 799 | |
| 796 | 800 | ; return (guts { mg_binds = binds' }) }
|
| 797 | 801 | |
| ... | ... | @@ -802,15 +806,137 @@ specConstrProgram guts |
| 802 | 806 | nest 2 (vcat (map ppr warnings)) $$
|
| 803 | 807 | (text "If this is expected you might want to increase -fmax-forced-spec-args to force specialization anyway.")
|
| 804 | 808 | |
| 809 | + -- One warning per specialised function (all its patterns listed),
|
|
| 810 | + -- then warnings that would render identically merged too; see
|
|
| 811 | + -- Note [Reboxing warning]
|
|
| 812 | + aggregateRebox :: SpecConstrWarnings -> SpecConstrWarnings
|
|
| 813 | + aggregateRebox = mergeBy same_render . mergeBy same_fn
|
|
| 814 | + where
|
|
| 815 | + mergeBy eq ws
|
|
| 816 | + = [ SpecReboxed fn ty parent recur (nubBy same_pat (concat patss)) (nub (concat callerss))
|
|
| 817 | + | w@(SpecReboxed fn ty parent recur _ _) <- nubBy eq ws
|
|
| 818 | + , let (patss, callerss) = unzip [ (pats, callers)
|
|
| 819 | + | w'@(SpecReboxed _ _ _ _ pats callers) <- ws
|
|
| 820 | + , eq w w' ] ]
|
|
| 821 | + |
|
| 822 | + -- Also compare the parents: the fn Name alone is ambiguous
|
|
| 823 | + -- between top-level bindings; see Note [Reboxing warning]
|
|
| 824 | + same_fn (SpecReboxed fn1 _ p1 _ _ _) (SpecReboxed fn2 _ p2 _ _ _)
|
|
| 825 | + = fn1 == fn2 && p1 == p2
|
|
| 826 | + same_fn _ _ = False
|
|
| 827 | + |
|
| 828 | + -- Merge warnings that would render identically: same occurrence
|
|
| 829 | + -- name, type, parent, displayed location, recursivity, and
|
|
| 830 | + -- patterns. The reader could not tell them apart, so printing
|
|
| 831 | + -- both is noise; see Note [Reboxing warning]. Callers are
|
|
| 832 | + -- aggregated, not compared.
|
|
| 833 | + same_render (SpecReboxed fn1 ty1 p1 r1 pats1 _) (SpecReboxed fn2 ty2 p2 r2 pats2 _)
|
|
| 834 | + = getOccName fn1 == getOccName fn2 && p1 == p2
|
|
| 835 | + && nameSrcSpan (rebox_loc_name fn1 p1) == nameSrcSpan (rebox_loc_name fn2 p2)
|
|
| 836 | + && ty1 `eqType` ty2
|
|
| 837 | + && r1 `same_recur` r2
|
|
| 838 | + && equalLength pats1 pats2
|
|
| 839 | + && and (zipWith same_pat (sortBy cmpReboxedPat pats1)
|
|
| 840 | + (sortBy cmpReboxedPat pats2))
|
|
| 841 | + same_render _ _ = False
|
|
| 842 | + |
|
| 843 | + same_pat p1 p2 = cmpReboxedPat p1 p2 == EQ
|
|
| 844 | + |
|
| 845 | + -- Recursivity as displayed: siblings compare by occurrence name,
|
|
| 846 | + -- so span-less copies of one mutual group still merge
|
|
| 847 | + same_recur ReboxSelfRec ReboxSelfRec = True
|
|
| 848 | + same_recur (ReboxNonRec j1) (ReboxNonRec j2) = j1 == j2
|
|
| 849 | + same_recur (ReboxMutualRec s1) (ReboxMutualRec s2)
|
|
| 850 | + = map getOccName (sortBy stableNameCmp s1)
|
|
| 851 | + == map getOccName (sortBy stableNameCmp s2)
|
|
| 852 | + same_recur _ _ = False
|
|
| 853 | + |
|
| 854 | + -- The definition site shown in a reboxing warning: the fn's own when
|
|
| 855 | + -- known, otherwise the parent's (e.g. for simplifier-made join points)
|
|
| 856 | + rebox_loc_name :: Name -> Maybe Name -> Name
|
|
| 857 | + rebox_loc_name fn (Just parent)
|
|
| 858 | + | not (isGoodSrcSpan (nameSrcSpan fn)) = parent
|
|
| 859 | + rebox_loc_name fn _ = fn
|
|
| 860 | + |
|
| 805 | 861 | -- See Note [Reboxing warning]
|
| 806 | - rebox_msg :: SpecConstrWarnings -> SDoc
|
|
| 807 | - rebox_msg warnings = text "SpecConstr specialised the following function(s) on a constructor argument that is also used boxed:" $$
|
|
| 808 | - nest 2 (vcat (map ppr warnings)) $$
|
|
| 809 | - text "The specialised code allocates a fresh constructor at each such use (\"reboxing\")," $$
|
|
| 810 | - text "which can increase allocation and defeat pointer-equality-based sharing." $$
|
|
| 811 | - text "Possible remedies: exclude the type with an {-# ANN type T NoSpecConstr #-} pragma," $$
|
|
| 812 | - text "hide the constructor from SpecConstr by wrapping the call-site argument in GHC.Exts.lazy," $$
|
|
| 813 | - text "or use -fno-spec-constr."
|
|
| 862 | + rebox_msg :: SpecConstrWarning -> SDoc
|
|
| 863 | + rebox_msg w@(SpecFailForcedArgCount {}) = pprPanic "rebox_msg" (ppr w)
|
|
| 864 | + rebox_msg (SpecReboxed fn ty mb_parent recur pats callers)
|
|
| 865 | + = vcat [ hang (text "SpecConstr specialised") 2
|
|
| 866 | + (quotes (ppr fn <+> dcolon <+> pp_ty))
|
|
| 867 | + , nest 2 $ vcat $ catMaybes
|
|
| 868 | + [ Just (fact "source:" pp_source)
|
|
| 869 | + , Just (fact "recursivity:" pp_recur)
|
|
| 870 | + , fact "called from:" <$> pp_callers
|
|
| 871 | + , Just (fact pats_label pp_pats) ]
|
|
| 872 | + , pp_trailer
|
|
| 873 | + , text "See -Wspec-constr-reboxing in the users guide for possible remedies." ]
|
|
| 874 | + where
|
|
| 875 | + -- Aligned label column; $$ overlaps, so a multi-line value keeps
|
|
| 876 | + -- its lines aligned under the first
|
|
| 877 | + fact l v = text l $$ nest 23 v
|
|
| 878 | + |
|
| 879 | + -- Truncate only pathologically large types
|
|
| 880 | + pp_ty = sdocWithContext $ \ctx ->
|
|
| 881 | + case splitAt 10000 (showSDocOneLine ctx pp_tidy) of
|
|
| 882 | + (_, []) -> pp_tidy
|
|
| 883 | + (prefix, _) -> text prefix <> text "..."
|
|
| 884 | + where pp_tidy = pprSigmaType (tidyTopType ty)
|
|
| 885 | + -- pprSigmaType: suppress the printed forall
|
|
| 886 | + |
|
| 887 | + -- A name with no source span reached this module in an interface
|
|
| 888 | + -- unfolding: iface files record no spans for local binders
|
|
| 889 | + pp_source = case (mb_parent, isGoodSrcSpan (nameSrcSpan loc_name)) of
|
|
| 890 | + (Nothing, True) -> pp_loc
|
|
| 891 | + (Just p, True) -> quotes (ppr p) <+> text "at" <+> pp_loc
|
|
| 892 | + (Just p, False) -> quotes (ppr p) <> comma <+> pp_no_loc
|
|
| 893 | + (Nothing, False) -> pp_no_loc
|
|
| 894 | + where
|
|
| 895 | + loc_name = rebox_loc_name fn mb_parent
|
|
| 896 | + pp_loc = ppr (nameSrcLoc loc_name)
|
|
| 897 | + pp_no_loc = text "inlined from another module (no source location)"
|
|
| 898 | + |
|
| 899 | + pp_recur = case recur of
|
|
| 900 | + ReboxSelfRec -> text "self-recursive"
|
|
| 901 | + ReboxNonRec True -> text "non-recursive (a join point)"
|
|
| 902 | + ReboxNonRec False -> text "non-recursive"
|
|
| 903 | + ReboxMutualRec sibs
|
|
| 904 | + -> text "mutually recursive with"
|
|
| 905 | + <+> pprWithCommas (quotes . ppr) named <> pp_rest
|
|
| 906 | + where
|
|
| 907 | + (named, rest) = splitAt 3 (sortBy stableNameCmp sibs)
|
|
| 908 | + pp_rest = case length rest of
|
|
| 909 | + 0 -> empty
|
|
| 910 | + 1 -> text " and 1 other"
|
|
| 911 | + n -> text " and" <+> int n <+> text "others"
|
|
| 912 | + |
|
| 913 | + pp_callers = case sortBy stableNameCmp callers of
|
|
| 914 | + [] -> Nothing
|
|
| 915 | + cs -> Just (pprWithCommas (quotes . ppr) cs)
|
|
| 916 | + |
|
| 917 | + pats_label = case pats of
|
|
| 918 | + [_] -> "call pattern:"
|
|
| 919 | + _ -> "call patterns:"
|
|
| 920 | + |
|
| 921 | + pp_pats = vcat (map pp_pat (sortBy cmpReboxedPat pats))
|
|
| 922 | + |
|
| 923 | + pp_pat (ReboxedPat shapes cons)
|
|
| 924 | + = hang (hang (ppr fn) 2 (fsep (map pprPatShape shapes))) 2
|
|
| 925 | + (text "-- reboxes" <+>
|
|
| 926 | + pprWithCommas pp_con (sortBy stableNameCmp cons))
|
|
| 927 | + |
|
| 928 | + -- Qualify imported constructors: they identify the package to
|
|
| 929 | + -- follow up with when the function itself has no location
|
|
| 930 | + pp_con con
|
|
| 931 | + | Just m <- nameModule_maybe con, m /= mg_module guts
|
|
| 932 | + = quotes (ppr m <> dot <> ppr con)
|
|
| 933 | + | otherwise = quotes (ppr con)
|
|
| 934 | + |
|
| 935 | + all_cons = nub [ con | ReboxedPat _ cons <- pats, con <- cons ]
|
|
| 936 | + |
|
| 937 | + pp_trailer = fsep $ map text $ words $ case all_cons of
|
|
| 938 | + [_] -> "This constructor argument is also used boxed, so the specialisation may increase allocation and defeat pointer-equality-based sharing."
|
|
| 939 | + _ -> "These constructor arguments are also used boxed, so the specialisations may increase allocation and defeat pointer-equality-based sharing."
|
|
| 814 | 940 | scTopBinds :: ScEnv -> [InBind] -> UniqSM (ScUsage, [OutBind], [SpecConstrWarning])
|
| 815 | 941 | scTopBinds _env [] = return (nullUsage, [], [])
|
| 816 | 942 | scTopBinds env (b:bs) = do { (usg, b', bs', warnings) <- scBind TopLevel env b $
|
| ... | ... | @@ -975,7 +1101,12 @@ data ScEnv = SCE { sc_opts :: !SpecConstrOpts, |
| 975 | 1101 | -- Domain is OutIds (*after* applying the substitution)
|
| 976 | 1102 | -- Used even for top-level bindings (but not imported ones)
|
| 977 | 1103 | |
| 978 | - sc_annotations :: UniqFM Name SpecConstrAnnotation
|
|
| 1104 | + sc_annotations :: UniqFM Name SpecConstrAnnotation,
|
|
| 1105 | + |
|
| 1106 | + sc_top_fn :: Maybe Name
|
|
| 1107 | + -- The top-level binder whose RHS we are inside,
|
|
| 1108 | + -- used to attribute warnings about local functions
|
|
| 1109 | + -- See Note [Reboxing warning]
|
|
| 979 | 1110 | }
|
| 980 | 1111 | |
| 981 | 1112 | ---------------------
|
| ... | ... | @@ -1025,7 +1156,8 @@ initScEnv guts |
| 1025 | 1156 | sc_subst = init_subst,
|
| 1026 | 1157 | sc_how_bound = emptyVarEnv,
|
| 1027 | 1158 | sc_vals = emptyVarEnv,
|
| 1028 | - sc_annotations = anns }) }
|
|
| 1159 | + sc_annotations = anns,
|
|
| 1160 | + sc_top_fn = Nothing }) }
|
|
| 1029 | 1161 | where
|
| 1030 | 1162 | init_subst = mkEmptySubst $ mkInScopeSetBndrs (mg_binds guts)
|
| 1031 | 1163 | -- Acccount for top-level bindings that are not in dependency order;
|
| ... | ... | @@ -1046,6 +1178,11 @@ instance Outputable HowBound where |
| 1046 | 1178 | scForce :: ScEnv -> Bool -> ScEnv
|
| 1047 | 1179 | scForce env b = env { sc_force = b }
|
| 1048 | 1180 | |
| 1181 | +-- Keeps the outermost binder; see Note [Reboxing warning]
|
|
| 1182 | +setTopFn :: ScEnv -> OutId -> ScEnv
|
|
| 1183 | +setTopFn env@(SCE { sc_top_fn = Nothing }) bndr = env { sc_top_fn = Just (idName bndr) }
|
|
| 1184 | +setTopFn env _ = env
|
|
| 1185 | + |
|
| 1049 | 1186 | lookupHowBound :: ScEnv -> OutId -> Maybe HowBound
|
| 1050 | 1187 | lookupHowBound env id = lookupVarEnv (sc_how_bound env) id
|
| 1051 | 1188 | |
| ... | ... | @@ -1286,9 +1423,11 @@ data ScUsage |
| 1286 | 1423 | } -- The domain is OutIds
|
| 1287 | 1424 | |
| 1288 | 1425 | type CallEnv = IdEnv [Call] -- Domain is OutIds
|
| 1289 | -data Call = Call OutId [CoreArg] ValueEnv
|
|
| 1426 | +data Call = Call OutId [CoreArg] ValueEnv (Maybe Name)
|
|
| 1290 | 1427 | -- The arguments of the call, together with the
|
| 1291 | 1428 | -- env giving the constructor bindings at the call site
|
| 1429 | + -- and the enclosing top-level binder of the call site
|
|
| 1430 | + -- (sc_top_fn, for reboxing warnings)
|
|
| 1292 | 1431 | -- We keep the function mainly for debug output
|
| 1293 | 1432 | --
|
| 1294 | 1433 | -- The call is not necessarily saturated; we just put
|
| ... | ... | @@ -1300,7 +1439,7 @@ instance Outputable ScUsage where |
| 1300 | 1439 | , text "occs =" <+> ppr occs ])
|
| 1301 | 1440 | |
| 1302 | 1441 | instance Outputable Call where
|
| 1303 | - ppr (Call fn args _) = ppr fn <+> fsep (map pprParendExpr args)
|
|
| 1442 | + ppr (Call fn args _ _) = ppr fn <+> fsep (map pprParendExpr args)
|
|
| 1304 | 1443 | |
| 1305 | 1444 | nullUsage :: ScUsage
|
| 1306 | 1445 | nullUsage = SCU { scu_calls = emptyVarEnv, scu_occs = emptyVarEnv }
|
| ... | ... | @@ -1443,6 +1582,73 @@ that decision bites, without changing which specialisations are made: |
| 1443 | 1582 | SpecReboxed warnings, which specConstrProgram emits under
|
| 1444 | 1583 | -Wspec-constr-reboxing (off by default).
|
| 1445 | 1584 | |
| 1585 | +* One warning per function, listing each call pattern the function was
|
|
| 1586 | + specialised for — the constructor skeletons of the call's arguments,
|
|
| 1587 | + e.g. `go (_ : _) (BMap _) -- reboxes ‘BMap’` — alongside that
|
|
| 1588 | + pattern's reboxed constructors. The shapes show where in the
|
|
| 1589 | + argument each reboxed constructor sits, and picture what SpecConstr
|
|
| 1590 | + did: it made a copy of
|
|
| 1591 | + the function for calls of exactly that shape. The warning also shows
|
|
| 1592 | + the function's type: names like `go1` say nothing, and for span-less
|
|
| 1593 | + functions (last bullet below) the type is the main identifying clue.
|
|
| 1594 | + |
|
| 1595 | + "Per function" means per (parent, function) *pair*: the function's
|
|
| 1596 | + Name alone is ambiguous, because distinct top-level bindings can bind
|
|
| 1597 | + distinct locals that share a unique. That happens when several
|
|
| 1598 | + bindings inline one stable unfolding (e.g. a class's default-method
|
|
| 1599 | + template): each copy keeps the template's uniques, and the simplifier
|
|
| 1600 | + only freshens a binder on an in-scope clash, which cannot arise
|
|
| 1601 | + between sibling top-level RHSs.
|
|
| 1602 | + |
|
| 1603 | + Warnings that would render identically — same name, type, parent,
|
|
| 1604 | + definition site, recursivity, and patterns — are merged too: the
|
|
| 1605 | + reader could not tell them apart, so printing both is noise. For
|
|
| 1606 | + located functions the merged warnings are simplifier-made copies of one
|
|
| 1607 | + binding, addressed by a single source-level remedy; span-less loops
|
|
| 1608 | + inlined from other modules can in principle merge across different
|
|
| 1609 | + origins, but sharing name and type they are almost certainly copies of
|
|
| 1610 | + one function.
|
|
| 1611 | + |
|
| 1612 | +* The warning classifies how the specialised function recurses
|
|
| 1613 | + ("recursivity"), taken from the binding SpecConstr saw: a Rec group of
|
|
| 1614 | + one is self-recursive (the occurrence analyser demotes non-recursive
|
|
| 1615 | + singletons to NonRec), a larger group is mutually recursive and the
|
|
| 1616 | + siblings are named, and a nested NonRec binding — typically a join
|
|
| 1617 | + point, see Note [Specialising local let bindings] — is non-recursive.
|
|
| 1618 | + This reflects the post-optimisation program, whose shape can differ
|
|
| 1619 | + from the source's.
|
|
| 1620 | + |
|
| 1621 | +* Nullary constructors are exempt: "reboxing" a nullary constructor just
|
|
| 1622 | + references its shared static closure, so it costs no allocation and even
|
|
| 1623 | + preserves pointer identity. Such patterns are common (Nil, [], Nothing,
|
|
| 1624 | + ...) and warning about them would be pure noise.
|
|
| 1625 | + |
|
| 1626 | +* Warnings about local functions (join points, local workers) name the
|
|
| 1627 | + enclosing top-level binder too: locals often have meaningless names,
|
|
| 1628 | + and any remedy is applied at the enclosing function anyway.
|
|
| 1629 | + sc_top_fn tracks that binder, set when entering a top-level RHS (or a
|
|
| 1630 | + specialised copy of one) and kept unchanged below that. The location
|
|
| 1631 | + shown is the local's own definition site, so same-named locals can be
|
|
| 1632 | + told apart; only when that is missing (e.g. for simplifier-made join
|
|
| 1633 | + points) does the warning fall back to the parent's location.
|
|
| 1634 | + |
|
| 1635 | +* The warning lists the top-level binders containing the specialised
|
|
| 1636 | + calls ("called from"); each Call records the sc_top_fn of its call
|
|
| 1637 | + site for this purpose. Self-calls of a top-level loop are dropped as
|
|
| 1638 | + uninformative (in callToPat); a *local* loop's self-calls record its
|
|
| 1639 | + parent, indistinguishable from the parent's entry call, so the parent
|
|
| 1640 | + appears among the callers.
|
|
| 1641 | + |
|
| 1642 | +* A function with no source span and no parent reached this module in an
|
|
| 1643 | + interface unfolding: iface files record no spans for local binders, and
|
|
| 1644 | + such loops typically float to top level in the consuming module. The
|
|
| 1645 | + warning says "inlined from another module" instead of showing an
|
|
| 1646 | + unhelpful span. Imported constructors are shown qualified — with the
|
|
| 1647 | + function anonymous, they are what identifies the package to report the
|
|
| 1648 | + reboxing to. For such a warning the call sites ("called from") are the
|
|
| 1649 | + only located code to point at: following the inlining from one of them
|
|
| 1650 | + identifies the reboxed loop.
|
|
| 1651 | + |
|
| 1446 | 1652 | Why BoxPassAlong does not warn: if the callee is specialised at that argument
|
| 1447 | 1653 | position, its RULE rewrites the constructor-shaped call in the specialised
|
| 1448 | 1654 | body and no box is ever rebuilt. That is exactly the good case that
|
| ... | ... | @@ -1534,7 +1740,7 @@ scBind top_lvl env (NonRec bndr rhs) do_body |
| 1534 | 1740 | --
|
| 1535 | 1741 | -- I tried always specialising non-recursive top-level bindings too,
|
| 1536 | 1742 | -- but found some regressions (see !8135). So I backed off.
|
| 1537 | - = do { (rhs_usage, rhs', ws_rhs) <- scExpr env rhs
|
|
| 1743 | + = do { (rhs_usage, rhs', ws_rhs) <- scExpr (setTopFn env bndr) rhs
|
|
| 1538 | 1744 | |
| 1539 | 1745 | -- At top level, we've already put all binders into scope; see initScEnv
|
| 1540 | 1746 | -- Hence no need to call `extendBndr`. But we still want to
|
| ... | ... | @@ -1554,7 +1760,8 @@ scBind top_lvl env (Rec prs) do_body |
| 1554 | 1760 | -- why it only applies at top level. But that's the way it has been
|
| 1555 | 1761 | -- for a while. See #21456.
|
| 1556 | 1762 | do { (body_usg, body', warnings_body) <- do_body rhs_env2
|
| 1557 | - ; (rhs_usgs, rhss', rhs_ws) <- mapAndUnzip3M (scExpr env) rhss
|
|
| 1763 | + ; (rhs_usgs, rhss', rhs_ws) <- mapAndUnzip3M (\(b,r) -> scExpr (setTopFn env b) r)
|
|
| 1764 | + (bndrs' `zip` rhss)
|
|
| 1558 | 1765 | ; let all_usg = (combineUsages rhs_usgs `combineUsage` body_usg)
|
| 1559 | 1766 | `delCallsFor` bndrs'
|
| 1560 | 1767 | bind' = Rec (bndrs' `zip` rhss')
|
| ... | ... | @@ -1799,7 +2006,7 @@ markPassAlongArg _env _other usg = usg |
| 1799 | 2006 | mkVarUsage :: ScEnv -> Id -> [CoreExpr] -> ScUsage
|
| 1800 | 2007 | mkVarUsage env fn args
|
| 1801 | 2008 | = case lookupHowBound env fn of
|
| 1802 | - Just RecFun -> SCU { scu_calls = unitVarEnv fn [Call fn args (sc_vals env)]
|
|
| 2009 | + Just RecFun -> SCU { scu_calls = unitVarEnv fn [Call fn args (sc_vals env) (sc_top_fn env)]
|
|
| 1803 | 2010 | , scu_occs = emptyVarEnv }
|
| 1804 | 2011 | Just RecArg -> SCU { scu_calls = emptyVarEnv
|
| 1805 | 2012 | , scu_occs = unitVarEnv fn arg_occ }
|
| ... | ... | @@ -1812,7 +2019,7 @@ mkVarUsage env fn args |
| 1812 | 2019 | scRecRhs :: ScEnv -> (OutId, InExpr) -> UniqSM (RhsInfo, SpecConstrWarnings)
|
| 1813 | 2020 | scRecRhs env (bndr,rhs)
|
| 1814 | 2021 | = do { let (arg_bndrs,body) = collectBinders rhs
|
| 1815 | - (body_env, arg_bndrs') = extendBndrsWith RecArg env arg_bndrs
|
|
| 2022 | + (body_env, arg_bndrs') = extendBndrsWith RecArg (setTopFn env bndr) arg_bndrs
|
|
| 1816 | 2023 | ; (body_usg, body', body_ws) <- scExpr body_env body
|
| 1817 | 2024 | ; let (rhs_usg, arg_occs) = lookupOccs body_usg arg_bndrs'
|
| 1818 | 2025 | ; return (RI { ri_rhs_usg = rhs_usg
|
| ... | ... | @@ -1891,7 +2098,9 @@ specNonRec :: ScEnv |
| 1891 | 2098 | -- plus details of specialisations
|
| 1892 | 2099 | |
| 1893 | 2100 | specNonRec env body_calls rhs_info
|
| 1894 | - = specialise env body_calls rhs_info (initSpecInfo rhs_info)
|
|
| 2101 | + = specialise env recur body_calls rhs_info (initSpecInfo rhs_info)
|
|
| 2102 | + where
|
|
| 2103 | + recur = ReboxNonRec (isJoinId (ri_fn rhs_info))
|
|
| 1895 | 2104 | |
| 1896 | 2105 | ----------------------
|
| 1897 | 2106 | specRec :: ScEnv
|
| ... | ... | @@ -1911,6 +2120,13 @@ specRec env body_calls rhs_infos |
| 1911 | 2120 | where
|
| 1912 | 2121 | opts = sc_opts env
|
| 1913 | 2122 | |
| 2123 | + -- A Rec group of one is genuinely self-recursive: the occurrence
|
|
| 2124 | + -- analyser demotes non-recursive singletons to NonRec
|
|
| 2125 | + recur ri = case rhs_infos of
|
|
| 2126 | + [_] -> ReboxSelfRec
|
|
| 2127 | + _ -> ReboxMutualRec [ idName (ri_fn ri') | ri' <- rhs_infos
|
|
| 2128 | + , ri_fn ri' /= ri_fn ri ]
|
|
| 2129 | + |
|
| 1914 | 2130 | -- Loop, specialising, until you get no new specialisations
|
| 1915 | 2131 | go, go_again :: Int -- Which iteration of the "until no new specialisations"
|
| 1916 | 2132 | -- loop we are on; first iteration is 1
|
| ... | ... | @@ -1925,7 +2141,8 @@ specRec env body_calls rhs_infos |
| 1925 | 2141 | -- , text "iteration" <+> int n_iter
|
| 1926 | 2142 | -- , text "spec_infos" <+> ppr (map (map os_pat . si_specs) spec_infos)
|
| 1927 | 2143 | -- ]) $
|
| 1928 | - do { specs_w_usg <- zipWithM (specialise env seed_calls) rhs_infos spec_infos
|
|
| 2144 | + do { specs_w_usg <- zipWithM (\ri si -> specialise env (recur ri) seed_calls ri si)
|
|
| 2145 | + rhs_infos spec_infos
|
|
| 1929 | 2146 | |
| 1930 | 2147 | ; let (extra_usg_s, all_spec_infos, extra_ws ) = unzip3 specs_w_usg
|
| 1931 | 2148 | extra_usg = combineUsages extra_usg_s
|
| ... | ... | @@ -1963,6 +2180,7 @@ specRec env body_calls rhs_infos |
| 1963 | 2180 | ----------------------
|
| 1964 | 2181 | specialise
|
| 1965 | 2182 | :: ScEnv
|
| 2183 | + -> ReboxRecursivity -- How the function recurses, for warnings
|
|
| 1966 | 2184 | -> CallEnv -- Info on newly-discovered calls to this function
|
| 1967 | 2185 | -> RhsInfo
|
| 1968 | 2186 | -> SpecInfo -- Original RHS plus patterns dealt with
|
| ... | ... | @@ -1977,8 +2195,8 @@ specialise |
| 1977 | 2195 | -- So when we make a specialised copy of the RHS, we're starting
|
| 1978 | 2196 | -- from an RHS whose nested functions have been optimised already.
|
| 1979 | 2197 | |
| 1980 | -specialise env bind_calls (RI { ri_fn = fn, ri_lam_bndrs = arg_bndrs
|
|
| 1981 | - , ri_lam_body = body, ri_arg_occs = arg_occs })
|
|
| 2198 | +specialise env recur bind_calls (RI { ri_fn = fn, ri_lam_bndrs = arg_bndrs
|
|
| 2199 | + , ri_lam_body = body, ri_arg_occs = arg_occs })
|
|
| 1982 | 2200 | spec_info@(SI { si_specs = specs, si_n_specs = spec_count
|
| 1983 | 2201 | , si_mb_unspec = mb_unspec })
|
| 1984 | 2202 | | isDeadEndId fn -- Note [Do not specialise diverging functions]
|
| ... | ... | @@ -2001,7 +2219,9 @@ specialise env bind_calls (RI { ri_fn = fn, ri_lam_bndrs = arg_bndrs |
| 2001 | 2219 | ; let n_pats = length new_pats
|
| 2002 | 2220 | -- Warn about committed specialisations that will rebox;
|
| 2003 | 2221 | -- see Note [Reboxing warning]
|
| 2004 | - rebox_ws = [ SpecReboxed (idName fn) (cp_rebox p)
|
|
| 2222 | + rebox_ws = [ SpecReboxed (idName fn) (idType fn) (sc_top_fn env)
|
|
| 2223 | + recur [ReboxedPat (patShapes p) (cp_rebox p)]
|
|
| 2224 | + (cp_callers p)
|
|
| 2005 | 2225 | | p <- new_pats, not (null (cp_rebox p)) ]
|
| 2006 | 2226 | -- ; when (not (null new_pats) || isJust mb_unspec) $
|
| 2007 | 2227 | -- pprTraceM "specialise" (vcat [ ppr fn <+> text "with" <+> int n_pats <+> text "good patterns"
|
| ... | ... | @@ -2013,7 +2233,9 @@ specialise env bind_calls (RI { ri_fn = fn, ri_lam_bndrs = arg_bndrs |
| 2013 | 2233 | -- , text "arg_occs" <+> ppr arg_occs
|
| 2014 | 2234 | -- , text "new_pats" <+> ppr new_pats])
|
| 2015 | 2235 | |
| 2016 | - ; let spec_env = decreaseSpecCount env n_pats
|
|
| 2236 | + ; let spec_env = setTopFn (decreaseSpecCount env n_pats) fn
|
|
| 2237 | + -- setTopFn: attribute warnings from re-analysing the
|
|
| 2238 | + -- specialised copies of a top-level fn's body to fn
|
|
| 2017 | 2239 | ; (spec_usgs, new_specs, new_wss) <- mapAndUnzip3M (spec_one spec_env fn arg_bndrs body)
|
| 2018 | 2240 | (new_pats `zip` [spec_count..])
|
| 2019 | 2241 | -- See Note [Specialise original body]
|
| ... | ... | @@ -2565,30 +2787,127 @@ data CallPat = CP { cp_qvars :: [Var] -- Quantified variables |
| 2565 | 2787 | , cp_args :: [CoreExpr] -- Arguments
|
| 2566 | 2788 | , cp_strict_args :: [Var] -- Arguments we want to pass unlifted even if they are boxed
|
| 2567 | 2789 | -- See Note [SpecConstr and strict fields]
|
| 2568 | - , cp_rebox :: [Name] } -- Constructors matched by this pattern whose box
|
|
| 2790 | + , cp_rebox :: [Name] -- Constructors matched by this pattern whose box
|
|
| 2569 | 2791 | -- is also used; see Note [Reboxing warning]
|
| 2792 | + , cp_callers :: [Name] } -- Enclosing top-level binders of the calls this
|
|
| 2793 | + -- pattern came from; see Note [Reboxing warning]
|
|
| 2570 | 2794 | |
| 2571 | 2795 | -- See Note [SpecConstr call patterns]
|
| 2572 | 2796 | |
| 2573 | 2797 | instance Outputable CallPat where
|
| 2574 | - ppr (CP { cp_qvars = qvars, cp_args = args, cp_strict_args = strict, cp_rebox = rebox })
|
|
| 2798 | + ppr (CP { cp_qvars = qvars, cp_args = args, cp_strict_args = strict, cp_rebox = rebox
|
|
| 2799 | + , cp_callers = callers })
|
|
| 2575 | 2800 | = text "CP" <> braces (sep [ text "cp_qvars =" <+> ppr qvars <> comma
|
| 2576 | 2801 | , text "cp_args =" <+> ppr args
|
| 2577 | 2802 | , text "cp_strict_args = " <> ppr strict
|
| 2578 | - , text "cp_rebox = " <> ppr rebox ])
|
|
| 2803 | + , text "cp_rebox = " <> ppr rebox
|
|
| 2804 | + , text "cp_callers = " <> ppr callers ])
|
|
| 2805 | + |
|
| 2806 | +-- | One call pattern as displayed by the reboxing warning: the shapes of
|
|
| 2807 | +-- the pattern's arguments, and the reboxed constructors among them.
|
|
| 2808 | +-- See Note [Reboxing warning]
|
|
| 2809 | +data ReboxedPat = ReboxedPat [PatShape] [Name]
|
|
| 2810 | + |
|
| 2811 | +-- | The constructor skeleton of one call-pattern argument, as displayed
|
|
| 2812 | +-- by the reboxing warning
|
|
| 2813 | +data PatShape = ShapeWild
|
|
| 2814 | + | ShapeLit Literal
|
|
| 2815 | + | ShapeCon DataCon [PatShape]
|
|
| 2816 | + |
|
| 2817 | +-- | The displayed shapes of a pattern's value arguments
|
|
| 2818 | +patShapes :: CallPat -> [PatShape]
|
|
| 2819 | +patShapes (CP { cp_args = args }) = mapMaybe arg_shape args
|
|
| 2820 | + where
|
|
| 2821 | + arg_shape (Type {}) = Nothing
|
|
| 2822 | + arg_shape (Coercion {}) = Nothing
|
|
| 2823 | + arg_shape (Cast e _) = arg_shape e
|
|
| 2824 | + arg_shape (Tick _ e) = arg_shape e
|
|
| 2825 | + arg_shape (Lit l) = Just (ShapeLit l)
|
|
| 2826 | + arg_shape e
|
|
| 2827 | + | (Var f, f_args) <- collectArgs e
|
|
| 2828 | + , Just dc <- isDataConWorkId_maybe f
|
|
| 2829 | + = Just (ShapeCon dc (mapMaybe arg_shape f_args))
|
|
| 2830 | + | otherwise
|
|
| 2831 | + = Just ShapeWild
|
|
| 2832 | + |
|
| 2833 | +-- | Stable comparison, used both to merge identically-rendering warnings
|
|
| 2834 | +-- and to order a warning's patterns deterministically
|
|
| 2835 | +cmpReboxedPat :: ReboxedPat -> ReboxedPat -> Ordering
|
|
| 2836 | +cmpReboxedPat (ReboxedPat ss1 cs1) (ReboxedPat ss2 cs2)
|
|
| 2837 | + = cmpListBy cmpShape ss1 ss2
|
|
| 2838 | + `mappend` cmpListBy stableNameCmp (sortBy stableNameCmp cs1)
|
|
| 2839 | + (sortBy stableNameCmp cs2)
|
|
| 2840 | + |
|
| 2841 | +cmpShape :: PatShape -> PatShape -> Ordering
|
|
| 2842 | +cmpShape ShapeWild ShapeWild = EQ
|
|
| 2843 | +cmpShape ShapeWild _ = LT
|
|
| 2844 | +cmpShape _ ShapeWild = GT
|
|
| 2845 | +cmpShape (ShapeLit l1) (ShapeLit l2) = compare l1 l2
|
|
| 2846 | +cmpShape (ShapeLit _) _ = LT
|
|
| 2847 | +cmpShape _ (ShapeLit _) = GT
|
|
| 2848 | +cmpShape (ShapeCon c1 a1) (ShapeCon c2 a2)
|
|
| 2849 | + = stableNameCmp (dataConName c1) (dataConName c2)
|
|
| 2850 | + `mappend` cmpListBy cmpShape a1 a2
|
|
| 2851 | + |
|
| 2852 | +cmpListBy :: (a -> a -> Ordering) -> [a] -> [a] -> Ordering
|
|
| 2853 | +cmpListBy _ [] [] = EQ
|
|
| 2854 | +cmpListBy _ [] _ = LT
|
|
| 2855 | +cmpListBy _ _ [] = GT
|
|
| 2856 | +cmpListBy cmp (x1:xs1) (x2:xs2) = cmp x1 x2 `mappend` cmpListBy cmp xs1 xs2
|
|
| 2857 | + |
|
| 2858 | +-- Constructors are shown by bare occurrence name: shapes illustrate,
|
|
| 2859 | +-- while the warning's "reboxes" list identifies (with qualification)
|
|
| 2860 | +pprPatShape :: PatShape -> SDoc
|
|
| 2861 | +pprPatShape = go (10 :: Int) -- Depth cap against pathological patterns
|
|
| 2862 | + where
|
|
| 2863 | + go _ ShapeWild = underscore
|
|
| 2864 | + go _ (ShapeLit l) = ppr l
|
|
| 2865 | + go _ (ShapeCon dc []) = ppr (getOccName dc)
|
|
| 2866 | + go 0 (ShapeCon {}) = text "..."
|
|
| 2867 | + go d (ShapeCon dc args)
|
|
| 2868 | + | isTupleDataCon dc
|
|
| 2869 | + = parens (pprWithCommas (go d') args)
|
|
| 2870 | + | isUnboxedTupleDataCon dc
|
|
| 2871 | + = text "(#" <+> pprWithCommas (go d') args <+> text "#)"
|
|
| 2872 | + | dataConIsInfix dc, [a1, a2] <- args
|
|
| 2873 | + = parens (go d' a1 <+> pprInfixOcc (getOccName dc) <+> go d' a2)
|
|
| 2874 | + | otherwise
|
|
| 2875 | + = parens (pprPrefixOcc (getOccName dc) <+> sep (map (go d') args))
|
|
| 2876 | + where d' = d - 1
|
|
| 2877 | + |
|
| 2878 | +-- | How the function that SpecConstr specialised recurses, as bound in
|
|
| 2879 | +-- the post-optimisation program. See Note [Reboxing warning]
|
|
| 2880 | +data ReboxRecursivity
|
|
| 2881 | + = ReboxSelfRec
|
|
| 2882 | + | ReboxMutualRec [Name] -- The sibling binders of its Rec group
|
|
| 2883 | + | ReboxNonRec Bool -- True <=> a join point
|
|
| 2579 | 2884 | |
| 2580 | 2885 | data SpecConstrWarning
|
| 2581 | 2886 | = SpecFailForcedArgCount { spec_failed_fun_name :: Name }
|
| 2582 | 2887 | | SpecReboxed { spec_rebox_fun_name :: Name -- The specialised function
|
| 2583 | - , spec_rebox_cons :: [Name] } -- The reboxed constructor(s)
|
|
| 2888 | + , spec_rebox_fun_ty :: Type -- Its type: often the only clue
|
|
| 2889 | + -- to a span-less function's identity
|
|
| 2890 | + , spec_rebox_parent :: Maybe Name -- Its enclosing top-level
|
|
| 2891 | + -- binder, if fn is local
|
|
| 2892 | + , spec_rebox_recur :: ReboxRecursivity
|
|
| 2893 | + , spec_rebox_pats :: [ReboxedPat] -- The patterns that rebox
|
|
| 2894 | + , spec_rebox_callers :: [Name] } -- Top-level binders containing
|
|
| 2895 | + -- the specialised calls
|
|
| 2584 | 2896 | -- See Note [Reboxing warning]
|
| 2585 | - deriving Eq
|
|
| 2586 | 2897 | |
| 2587 | 2898 | type SpecConstrWarnings = [SpecConstrWarning]
|
| 2588 | 2899 | |
| 2589 | 2900 | instance Outputable SpecConstrWarning where
|
| 2590 | 2901 | ppr (SpecFailForcedArgCount name) = ppr name <+> pprDefinedAt name
|
| 2591 | - ppr (SpecReboxed fn dcs) = ppr fn <+> parens (pprWithCommas ppr dcs) <+> pprDefinedAt fn
|
|
| 2902 | + ppr (SpecReboxed fn _ty mb_parent _recur pats _callers)
|
|
| 2903 | + = ppr fn <+> parens (pprWithCommas ppr dcs) <+> pp_defn
|
|
| 2904 | + where
|
|
| 2905 | + dcs = [ dc | ReboxedPat _ cons <- pats, dc <- cons ]
|
|
| 2906 | + -- A local fn often has no useful location; point at its
|
|
| 2907 | + -- enclosing top-level binder instead
|
|
| 2908 | + pp_defn = case mb_parent of
|
|
| 2909 | + Just parent -> text "in" <+> ppr parent <> comma <+> pprDefinedAt parent
|
|
| 2910 | + Nothing -> pprDefinedAt fn
|
|
| 2592 | 2911 | |
| 2593 | 2912 | combineSpecWarning :: SpecConstrWarnings -> SpecConstrWarnings -> SpecConstrWarnings
|
| 2594 | 2913 | combineSpecWarning = (++)
|
| ... | ... | @@ -2735,7 +3054,7 @@ callToPat :: ScEnv -> [ArgOcc] -> Call -> UniqSM (Maybe CallPat) |
| 2735 | 3054 | -- Type variables come first, since they may scope
|
| 2736 | 3055 | -- over the following term variables
|
| 2737 | 3056 | -- The [CoreExpr] are the argument patterns for the rule
|
| 2738 | -callToPat env bndr_occs call@(Call fn args con_env)
|
|
| 3057 | +callToPat env bndr_occs call@(Call fn args con_env mb_caller)
|
|
| 2739 | 3058 | = do { let in_scope = substInScopeSet (sc_subst env)
|
| 2740 | 3059 | |
| 2741 | 3060 | ; arg_quads <- zipWith3M (argToPat env in_scope con_env) args bndr_occs (map (const NotMarkedStrict) args)
|
| ... | ... | @@ -2784,7 +3103,11 @@ callToPat env bndr_occs call@(Call fn args con_env) |
| 2784 | 3103 | if interesting && null bad_covars
|
| 2785 | 3104 | then do { let cp_res = CP { cp_qvars = qvars', cp_args = pats
|
| 2786 | 3105 | , cp_strict_args = concat cbv_ids
|
| 2787 | - , cp_rebox = concat rebox_cons }
|
|
| 3106 | + , cp_rebox = concat rebox_cons
|
|
| 3107 | + -- Self-recursive calls are no clue
|
|
| 3108 | + -- to the function's identity
|
|
| 3109 | + , cp_callers = [ c | Just c <- [mb_caller]
|
|
| 3110 | + , c /= idName fn ] }
|
|
| 2788 | 3111 | -- ; pprTraceM "callToPatOut" $
|
| 2789 | 3112 | -- vcat [ text "fn:" <+> ppr fn
|
| 2790 | 3113 | -- , text "args:" <+> ppr args
|
| ... | ... | @@ -2888,8 +3211,10 @@ argToPat1 env in_scope val_env arg arg_occ _arg_str |
| 2888 | 3211 | ; let args' = [ p | (_, p, _, _) <- prs ] :: [CoreArg]
|
| 2889 | 3212 | cbvs = concat [ cbv | (_, _, cbv, _) <- prs ]
|
| 2890 | 3213 | rebox_nested = concat [ rbs | (_, _, _, rbs) <- prs ]
|
| 2891 | - -- rebox_here: see Note [Reboxing warning]
|
|
| 2892 | - rebox_here = [ dataConName dc | box_use == BoxOther ]
|
|
| 3214 | + -- rebox_here: see Note [Reboxing warning]; nullary
|
|
| 3215 | + -- constructors rebox for free, so don't warn about them
|
|
| 3216 | + rebox_here = [ dataConName dc
|
|
| 3217 | + | box_use == BoxOther, dataConRepArity dc > 0 ]
|
|
| 2893 | 3218 | ; assertPpr (length con_str == length (filter isRuntimeArg rest_args))
|
| 2894 | 3219 | ( ppr con_str $$ ppr rest_args $$
|
| 2895 | 3220 | ppr (length con_str) $$ ppr (length rest_args)
|
| ... | ... | @@ -534,6 +534,35 @@ of ``-W(no-)*``. |
| 534 | 534 | the call-pattern analysis by wrapping the argument in ``GHC.Exts.lazy``
|
| 535 | 535 | at the call site, or :ghc-flag:`-fno-spec-constr`.
|
| 536 | 536 | |
| 537 | + The warning shows the specialised function's type, which is often the
|
|
| 538 | + clearest clue to its identity when its name carries no meaning,
|
|
| 539 | + followed by a block of labelled facts: ``source:`` — the function's
|
|
| 540 | + definition site (for a local function, also the enclosing top-level
|
|
| 541 | + binding); ``recursivity:`` — whether the function is self-recursive,
|
|
| 542 | + mutually recursive (naming the other functions of its recursive
|
|
| 543 | + group), or non-recursive (for example a join point), as bound in the
|
|
| 544 | + optimised program, whose shape can differ from the source's;
|
|
| 545 | + ``called from:`` — the top-level bindings containing the specialised
|
|
| 546 | + calls; ``call patterns:`` — the calls the function was specialised
|
|
| 547 | + for, shown as the constructor skeletons of their arguments, each
|
|
| 548 | + alongside the constructors that the specialisation reboxes (for
|
|
| 549 | + example ``go (_ : _) (Bin _ _ _) -- reboxes ‘Bin’``). One warning is emitted per
|
|
| 550 | + specialised function, and warnings that would read identically are
|
|
| 551 | + merged into one. Specialisations on nullary constructors are not
|
|
| 552 | + reported, since "reboxing" a nullary constructor simply references
|
|
| 553 | + its shared static closure.
|
|
| 554 | + |
|
| 555 | + A ``source:`` reading ``inlined from another module (no source
|
|
| 556 | + location)`` concerns a function that reached the module being compiled
|
|
| 557 | + through another module's unfolding; interface files record no source
|
|
| 558 | + locations for local functions. Such reboxing cannot be addressed in
|
|
| 559 | + the module being compiled — consider reporting it against the package
|
|
| 560 | + defining the inlined code. Constructors imported from other modules
|
|
| 561 | + are shown qualified with their defining module as a hint to where that
|
|
| 562 | + is. For such a warning the ``called from:`` sites are the only located
|
|
| 563 | + code — following the inlining from one of them identifies the reboxed
|
|
| 564 | + code.
|
|
| 565 | + |
|
| 537 | 566 | The analysis behind this warning is approximate: it can both miss genuine
|
| 538 | 567 | reboxing and report reboxing that later optimisations eliminate or that
|
| 539 | 568 | only occurs on cold code paths.
|
| 1 | 1 | T27628.hs: warning: [-Wspec-constr-reboxing]
|
| 2 | - SpecConstr specialised the following function(s) on a constructor argument that is also used boxed:
|
|
| 3 | - $wgo (LC) Defined at T27628.hs:17:1
|
|
| 4 | - The specialised code allocates a fresh constructor at each such use ("reboxing"),
|
|
| 5 | - which can increase allocation and defeat pointer-equality-based sharing.
|
|
| 6 | - Possible remedies: exclude the type with an {-# ANN type T NoSpecConstr #-} pragma,
|
|
| 7 | - hide the constructor from SpecConstr by wrapping the call-site argument in GHC.Exts.lazy,
|
|
| 8 | - or use -fno-spec-constr.
|
|
| 2 | + SpecConstr specialised
|
|
| 3 | + ‘$wgo :: LC
|
|
| 4 | + -> GHC.Internal.Prim.Int#
|
|
| 5 | + -> GHC.Internal.Prim.Int#
|
|
| 6 | + -> GHC.Internal.Prim.Int#’
|
|
| 7 | + source: T27628.hs:17:1
|
|
| 8 | + recursivity: self-recursive
|
|
| 9 | + called from: ‘f’
|
|
| 10 | + call pattern: $wgo (LC _ _) -- reboxes ‘LC’
|
|
| 11 | + This constructor argument is also used boxed, so the specialisation
|
|
| 12 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 13 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 9 | 14 |
| 1 | 1 | T27628b.hs: warning: [-Wspec-constr-reboxing]
|
| 2 | - SpecConstr specialised the following function(s) on a constructor argument that is also used boxed:
|
|
| 3 | - merge (Bin) Defined at T27628b.hs:10:1
|
|
| 4 | - The specialised code allocates a fresh constructor at each such use ("reboxing"),
|
|
| 5 | - which can increase allocation and defeat pointer-equality-based sharing.
|
|
| 6 | - Possible remedies: exclude the type with an {-# ANN type T NoSpecConstr #-} pragma,
|
|
| 7 | - hide the constructor from SpecConstr by wrapping the call-site argument in GHC.Exts.lazy,
|
|
| 8 | - or use -fno-spec-constr.
|
|
| 2 | + SpecConstr specialised ‘merge :: T -> T -> T’
|
|
| 3 | + source: T27628b.hs:10:1
|
|
| 4 | + recursivity: self-recursive
|
|
| 5 | + called from: ‘f’
|
|
| 6 | + call pattern: merge (Bin _ _ _) -- reboxes ‘Bin’
|
|
| 7 | + This constructor argument is also used boxed, so the specialisation
|
|
| 8 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 9 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 9 | 10 |
| 1 | +-- Like T27628b, but the specialisation pattern is the *nullary*
|
|
| 2 | +-- constructor Tip (the call passes Tip; the box is also returned).
|
|
| 3 | +-- Reboxing a nullary constructor is free (it is a shared static
|
|
| 4 | +-- closure), so no warning should be emitted.
|
|
| 5 | +module T27628e where
|
|
| 6 | + |
|
| 7 | +data T = Tip | Bin Int T T
|
|
| 8 | + |
|
| 9 | +merge :: T -> T -> T
|
|
| 10 | +merge Tip t2 = t2
|
|
| 11 | +merge t1@(Bin k l r) t2 =
|
|
| 12 | + case t2 of
|
|
| 13 | + Tip -> t1
|
|
| 14 | + Bin _ l2 r2 -> Bin k (merge l l2) (merge r r2)
|
|
| 15 | + |
|
| 16 | +g :: T -> T
|
|
| 17 | +g t = merge t Tip |
| 1 | +-- Like T27628b, but the specialised function is a *local* worker.
|
|
| 2 | +-- The warning should attribute it to the enclosing top-level binder f.
|
|
| 3 | +-- (merge captures n so that it is not floated to the top level.)
|
|
| 4 | +module T27628f where
|
|
| 5 | + |
|
| 6 | +data T = Tip | Bin Int T T
|
|
| 7 | + |
|
| 8 | +f :: Int -> T -> T
|
|
| 9 | +f n t0 = merge (Bin n Tip Tip) t0
|
|
| 10 | + where
|
|
| 11 | + merge :: T -> T -> T
|
|
| 12 | + merge Tip _ = Tip
|
|
| 13 | + merge t1@(Bin k l r) t2 =
|
|
| 14 | + case t2 of
|
|
| 15 | + Tip -> t1
|
|
| 16 | + Bin _ l2 r2 -> Bin (k + n) (merge l l2) (merge r r2) |
| 1 | +T27628f.hs: warning: [-Wspec-constr-reboxing]
|
|
| 2 | + SpecConstr specialised ‘merge :: T -> T -> T’
|
|
| 3 | + source: ‘f’ at T27628f.hs:12:5
|
|
| 4 | + recursivity: self-recursive
|
|
| 5 | + called from: ‘f’
|
|
| 6 | + call pattern: merge (Bin _ _ _) -- reboxes ‘Bin’
|
|
| 7 | + This constructor argument is also used boxed, so the specialisation
|
|
| 8 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 9 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 10 | + |
| 1 | +module T27628g where
|
|
| 2 | + |
|
| 3 | +data T a = Tip | Bin a (T a) (T a)
|
|
| 4 | + |
|
| 5 | +-- merge has no free value variables, so the float-out pass lifts it to
|
|
| 6 | +-- top level as poly_merge, abstracted over 'a'. The reboxing warning
|
|
| 7 | +-- should still point at merge's definition site.
|
|
| 8 | +f :: a -> T a -> T a
|
|
| 9 | +f x t = merge (Bin x Tip Tip) t
|
|
| 10 | + where
|
|
| 11 | + merge Tip t2 = t2
|
|
| 12 | + merge t1@(Bin k l r) t2 =
|
|
| 13 | + case t2 of
|
|
| 14 | + Tip -> t1
|
|
| 15 | + Bin _ l2 r2 -> Bin k (merge l l2) (merge r r2) |
| 1 | +T27628g.hs: warning: [-Wspec-constr-reboxing]
|
|
| 2 | + SpecConstr specialised ‘poly_merge :: T a -> T a -> T a’
|
|
| 3 | + source: T27628g.hs:11:5
|
|
| 4 | + recursivity: self-recursive
|
|
| 5 | + called from: ‘f’
|
|
| 6 | + call pattern: poly_merge (Bin _ _ _) -- reboxes ‘Bin’
|
|
| 7 | + This constructor argument is also used boxed, so the specialisation
|
|
| 8 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 9 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 10 | + |
| 1 | +-- Specialising the span-less copy of T27628h_M.merge must warn with the
|
|
| 2 | +-- "inlined from another module" wording and a module-qualified constructor.
|
|
| 3 | +module T27628h where
|
|
| 4 | + |
|
| 5 | +import T27628h_M
|
|
| 6 | + |
|
| 7 | +g :: Int -> T Int -> T Int
|
|
| 8 | +g x t = f x (f x t) |
| 1 | +./T27628h_M.hs: warning: [-Wspec-constr-reboxing]
|
|
| 2 | + SpecConstr specialised ‘poly_merge :: T a -> T a -> T a’
|
|
| 3 | + source: T27628h_M.hs:11:5
|
|
| 4 | + recursivity: self-recursive
|
|
| 5 | + called from: ‘f’
|
|
| 6 | + call pattern: poly_merge (Bin _ _ _) -- reboxes ‘Bin’
|
|
| 7 | + This constructor argument is also used boxed, so the specialisation
|
|
| 8 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 9 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 10 | + |
|
| 11 | +T27628h.hs: warning: [-Wspec-constr-reboxing]
|
|
| 12 | + SpecConstr specialised ‘merge :: T Int -> T Int -> T Int’
|
|
| 13 | + source: inlined from another module (no source location)
|
|
| 14 | + recursivity: self-recursive
|
|
| 15 | + called from: ‘g’
|
|
| 16 | + call pattern: merge (Bin _ _ _) -- reboxes ‘T27628h_M.Bin’
|
|
| 17 | + This constructor argument is also used boxed, so the specialisation
|
|
| 18 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 19 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 20 | + |
| 1 | +-- The INLINE unfolding of f carries the local loop 'merge' into importing
|
|
| 2 | +-- modules, where it is specialised without a source span: iface unfoldings
|
|
| 3 | +-- record no spans for local binders.
|
|
| 4 | +module T27628h_M where
|
|
| 5 | + |
|
| 6 | +data T a = Tip | Bin a (T a) (T a)
|
|
| 7 | + |
|
| 8 | +f :: a -> T a -> T a
|
|
| 9 | +f x t = merge (Bin x Tip Tip) t
|
|
| 10 | + where
|
|
| 11 | + merge Tip t2 = t2
|
|
| 12 | + merge t1@(Bin k l r) t2 =
|
|
| 13 | + case t2 of
|
|
| 14 | + Tip -> t1
|
|
| 15 | + Bin _ l2 r2 -> Bin k (merge l l2) (merge r r2)
|
|
| 16 | +{-# INLINE f #-} |
| 1 | +-- The span-less copies of f1's and f3's 'merge' render identically, so
|
|
| 2 | +-- one warning covers both; f2's 'merge' differs in type and gets its
|
|
| 3 | +-- own warning.
|
|
| 4 | +module T27628i where
|
|
| 5 | + |
|
| 6 | +import T27628i_M
|
|
| 7 | + |
|
| 8 | +g1 :: Int -> T Int -> T Int
|
|
| 9 | +g1 x t = f1 x (f1 x t)
|
|
| 10 | + |
|
| 11 | +g2 :: Int -> S Int -> S Int
|
|
| 12 | +g2 x t = f2 x (f2 x t)
|
|
| 13 | + |
|
| 14 | +g3 :: Int -> T Int -> T Int
|
|
| 15 | +g3 x t = f3 x (f3 x t) |
| 1 | +./T27628i_M.hs: warning: [-Wspec-constr-reboxing]
|
|
| 2 | + SpecConstr specialised ‘poly_merge :: T a -> T a -> T a’
|
|
| 3 | + source: T27628i_M.hs:36:5
|
|
| 4 | + recursivity: self-recursive
|
|
| 5 | + called from: ‘f3’
|
|
| 6 | + call pattern: poly_merge (Bin _ _ _) -- reboxes ‘Bin’
|
|
| 7 | + This constructor argument is also used boxed, so the specialisation
|
|
| 8 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 9 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 10 | + |
|
| 11 | +./T27628i_M.hs: warning: [-Wspec-constr-reboxing]
|
|
| 12 | + SpecConstr specialised ‘poly_merge :: S a -> S a -> S a’
|
|
| 13 | + source: T27628i_M.hs:24:5
|
|
| 14 | + recursivity: self-recursive
|
|
| 15 | + called from: ‘f2’
|
|
| 16 | + call pattern: poly_merge (Node _ _ _) -- reboxes ‘Node’
|
|
| 17 | + This constructor argument is also used boxed, so the specialisation
|
|
| 18 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 19 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 20 | + |
|
| 21 | +./T27628i_M.hs: warning: [-Wspec-constr-reboxing]
|
|
| 22 | + SpecConstr specialised ‘poly_merge :: T a -> T a -> T a’
|
|
| 23 | + source: T27628i_M.hs:14:5
|
|
| 24 | + recursivity: self-recursive
|
|
| 25 | + called from: ‘f1’
|
|
| 26 | + call pattern: poly_merge (Bin _ _ _) -- reboxes ‘Bin’
|
|
| 27 | + This constructor argument is also used boxed, so the specialisation
|
|
| 28 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 29 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 30 | + |
|
| 31 | +T27628i.hs: warning: [-Wspec-constr-reboxing]
|
|
| 32 | + SpecConstr specialised ‘merge :: T Int -> T Int -> T Int’
|
|
| 33 | + source: inlined from another module (no source location)
|
|
| 34 | + recursivity: self-recursive
|
|
| 35 | + called from: ‘g1’, ‘g3’
|
|
| 36 | + call pattern: merge (Bin _ _ _) -- reboxes ‘T27628i_M.Bin’
|
|
| 37 | + This constructor argument is also used boxed, so the specialisation
|
|
| 38 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 39 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 40 | + |
|
| 41 | +T27628i.hs: warning: [-Wspec-constr-reboxing]
|
|
| 42 | + SpecConstr specialised ‘merge :: S Int -> S Int -> S Int’
|
|
| 43 | + source: inlined from another module (no source location)
|
|
| 44 | + recursivity: self-recursive
|
|
| 45 | + called from: ‘g2’
|
|
| 46 | + call pattern: merge (Node _ _ _)
|
|
| 47 | + -- reboxes ‘T27628i_M.Node’
|
|
| 48 | + This constructor argument is also used boxed, so the specialisation
|
|
| 49 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 50 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 51 | + |
| 1 | +-- Three INLINE functions with local loops named 'merge': f1 and f3 over
|
|
| 2 | +-- T, f2 over S. In an importing module all three loops arrive span-less
|
|
| 3 | +-- (iface unfoldings record no spans for local binders). f1's and f3's
|
|
| 4 | +-- copies render identically (same name and type) and must merge into
|
|
| 5 | +-- one warning; f2's differs in type and must stay separate.
|
|
| 6 | +module T27628i_M where
|
|
| 7 | + |
|
| 8 | +data T a = Tip | Bin a (T a) (T a)
|
|
| 9 | +data S a = Leaf | Node a (S a) (S a)
|
|
| 10 | + |
|
| 11 | +f1 :: a -> T a -> T a
|
|
| 12 | +f1 x t = merge (Bin x Tip Tip) t
|
|
| 13 | + where
|
|
| 14 | + merge Tip t2 = t2
|
|
| 15 | + merge t1@(Bin k l r) t2 =
|
|
| 16 | + case t2 of
|
|
| 17 | + Tip -> t1
|
|
| 18 | + Bin _ l2 r2 -> Bin k (merge l l2) (merge r r2)
|
|
| 19 | +{-# INLINE f1 #-}
|
|
| 20 | + |
|
| 21 | +f2 :: a -> S a -> S a
|
|
| 22 | +f2 x t = merge (Node x Leaf Leaf) t
|
|
| 23 | + where
|
|
| 24 | + merge Leaf t2 = t2
|
|
| 25 | + merge t1@(Node k l r) t2 =
|
|
| 26 | + case t2 of
|
|
| 27 | + Leaf -> t1
|
|
| 28 | + Node _ l2 r2 -> Node k (merge l l2) (merge r r2)
|
|
| 29 | +{-# INLINE f2 #-}
|
|
| 30 | + |
|
| 31 | +-- Like f1 but recursing with the children swapped, so the two loops
|
|
| 32 | +-- stay distinct functions while their warnings render the same
|
|
| 33 | +f3 :: a -> T a -> T a
|
|
| 34 | +f3 x t = merge (Bin x Tip Tip) t
|
|
| 35 | + where
|
|
| 36 | + merge Tip t2 = t2
|
|
| 37 | + merge t1@(Bin k l r) t2 =
|
|
| 38 | + case t2 of
|
|
| 39 | + Tip -> t1
|
|
| 40 | + Bin _ l2 r2 -> Bin k (merge r r2) (merge l l2)
|
|
| 41 | +{-# INLINE f3 #-} |
| 1 | +-- A mutually recursive pair in the style of T27628b. g calls both
|
|
| 2 | +-- functions with a constructor argument: the non-loop-breaker inlines
|
|
| 3 | +-- into g, but the loop breaker's call survives to SpecConstr, whose
|
|
| 4 | +-- warning classifies it as mutually recursive and names the sibling.
|
|
| 5 | +module T27628j where
|
|
| 6 | + |
|
| 7 | +data T = Tip | Bin Int T T
|
|
| 8 | + |
|
| 9 | +mergeA :: T -> T -> T
|
|
| 10 | +mergeA Tip t2 = t2
|
|
| 11 | +mergeA t1@(Bin k l r) t2 =
|
|
| 12 | + case t2 of
|
|
| 13 | + Tip -> t1
|
|
| 14 | + Bin _ l2 r2 -> Bin k (mergeB l l2) (mergeB r r2)
|
|
| 15 | + |
|
| 16 | +mergeB :: T -> T -> T
|
|
| 17 | +mergeB Tip t2 = t2
|
|
| 18 | +mergeB t1@(Bin k l r) t2 =
|
|
| 19 | + case t2 of
|
|
| 20 | + Tip -> t1
|
|
| 21 | + Bin _ l2 r2 -> Bin k (mergeA r r2) (mergeA l l2)
|
|
| 22 | + |
|
| 23 | +g :: Int -> T -> T
|
|
| 24 | +g x t = mergeA (Bin x Tip Tip) (mergeB (Bin x Tip Tip) t) |
| 1 | +T27628j.hs: warning: [-Wspec-constr-reboxing]
|
|
| 2 | + SpecConstr specialised ‘mergeB :: T -> T -> T’
|
|
| 3 | + source: T27628j.hs:17:1
|
|
| 4 | + recursivity: mutually recursive with ‘mergeA’
|
|
| 5 | + called from: ‘g’
|
|
| 6 | + call pattern: mergeB (Bin _ _ _) -- reboxes ‘Bin’
|
|
| 7 | + This constructor argument is also used boxed, so the specialisation
|
|
| 8 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 9 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 10 | + |
| 1 | +-- 'go' is specialised for two different call patterns, A and B, and
|
|
| 2 | +-- both rebox (t is scrutinised but also passed whole to 'sink').
|
|
| 3 | +-- Expect one warning for 'go' listing both call patterns.
|
|
| 4 | +module T27628k where
|
|
| 5 | + |
|
| 6 | +data T = A Int | B Int | C
|
|
| 7 | + |
|
| 8 | +-- The guard makes the use of 't' lazy, so boxity analysis keeps the
|
|
| 9 | +-- box: $wsink wants it.
|
|
| 10 | +sink :: T -> Int -> Int
|
|
| 11 | +sink t k
|
|
| 12 | + | k < 0 = k
|
|
| 13 | + | otherwise = case t of A n -> n; B n -> n; C -> 0
|
|
| 14 | +{-# NOINLINE sink #-}
|
|
| 15 | + |
|
| 16 | +go :: T -> Int -> Int
|
|
| 17 | +go t k = case t of
|
|
| 18 | + A n -> if k == 0 then sink t k else go (B n) (k - 1)
|
|
| 19 | + B n -> if k == 0 then sink t k else go (A n) (k - 1)
|
|
| 20 | + C -> 0 |
| 1 | +T27628k.hs: warning: [-Wspec-constr-reboxing]
|
|
| 2 | + SpecConstr specialised ‘go :: T -> Int -> Int’
|
|
| 3 | + source: T27628k.hs:17:1
|
|
| 4 | + recursivity: self-recursive
|
|
| 5 | + call patterns: go (A _) (I# _) -- reboxes ‘A’
|
|
| 6 | + go (B _) (I# _) -- reboxes ‘B’
|
|
| 7 | + These constructor arguments are also used boxed, so the
|
|
| 8 | + specialisations may increase allocation and defeat
|
|
| 9 | + pointer-equality-based sharing.
|
|
| 10 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 11 | + |
| 1 | +-- Max and Min (mimicking GHC.Internal.Data.Functor.Utils) both inline
|
|
| 2 | +-- base's foldl' and stimes templates, so each instance's methods bind
|
|
| 3 | +-- local loops with the SAME uniques as the other's. The warnings must
|
|
| 4 | +-- stay separate per instance (keyed on (parent, function)), not be
|
|
| 5 | +-- fused into one with doubled callers; see Note [Reboxing warning].
|
|
| 6 | +module T27628l where
|
|
| 7 | + |
|
| 8 | +import Data.List (foldl')
|
|
| 9 | + |
|
| 10 | +newtype Max a = Max (Maybe a)
|
|
| 11 | + |
|
| 12 | +instance Ord a => Semigroup (Max a) where
|
|
| 13 | + {-# INLINE (<>) #-}
|
|
| 14 | + m <> Max Nothing = m
|
|
| 15 | + Max Nothing <> n = n
|
|
| 16 | + (Max m@(Just x)) <> (Max n@(Just y))
|
|
| 17 | + | x >= y = Max m
|
|
| 18 | + | otherwise = Max n
|
|
| 19 | + |
|
| 20 | +instance Ord a => Monoid (Max a) where
|
|
| 21 | + mempty = Max Nothing
|
|
| 22 | + mconcat = foldl' (<>) mempty
|
|
| 23 | + {-# INLINE mconcat #-}
|
|
| 24 | + |
|
| 25 | +newtype Min a = Min (Maybe a)
|
|
| 26 | + |
|
| 27 | +instance Ord a => Semigroup (Min a) where
|
|
| 28 | + {-# INLINE (<>) #-}
|
|
| 29 | + m <> Min Nothing = m
|
|
| 30 | + Min Nothing <> n = n
|
|
| 31 | + (Min m@(Just x)) <> (Min n@(Just y))
|
|
| 32 | + | x <= y = Min m
|
|
| 33 | + | otherwise = Min n
|
|
| 34 | + |
|
| 35 | +instance Ord a => Monoid (Min a) where
|
|
| 36 | + mempty = Min Nothing
|
|
| 37 | + mconcat = foldl' (<>) mempty
|
|
| 38 | + {-# INLINE mconcat #-} |
| 1 | +T27628l.hs: warning: [-Wspec-constr-reboxing]
|
|
| 2 | + SpecConstr specialised ‘go1 :: [Max a] -> Max a -> Max a’
|
|
| 3 | + source: ‘$cmconcat’ at T27628l.hs:22:5
|
|
| 4 | + recursivity: self-recursive
|
|
| 5 | + called from: ‘$cmconcat’
|
|
| 6 | + call pattern: go1 _ (Just _)
|
|
| 7 | + -- reboxes ‘GHC.Internal.Maybe.Just’
|
|
| 8 | + This constructor argument is also used boxed, so the specialisation
|
|
| 9 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 10 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 11 | + |
|
| 12 | +T27628l.hs: warning: [-Wspec-constr-reboxing]
|
|
| 13 | + SpecConstr specialised ‘exit :: Max a -> b -> Max a’
|
|
| 14 | + source: ‘$cstimes’ at T27628l.hs:12:10
|
|
| 15 | + recursivity: non-recursive (a join point)
|
|
| 16 | + called from: ‘$cstimes’
|
|
| 17 | + call pattern: exit (Just _) _
|
|
| 18 | + -- reboxes ‘GHC.Internal.Maybe.Just’
|
|
| 19 | + This constructor argument is also used boxed, so the specialisation
|
|
| 20 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 21 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 22 | + |
|
| 23 | +T27628l.hs: warning: [-Wspec-constr-reboxing]
|
|
| 24 | + SpecConstr specialised ‘g :: Max a -> b -> Max a -> Max a’
|
|
| 25 | + source: ‘$cstimes’ at T27628l.hs:12:10
|
|
| 26 | + recursivity: self-recursive
|
|
| 27 | + called from: ‘$cstimes’
|
|
| 28 | + call pattern: g (Just _) _ _
|
|
| 29 | + -- reboxes ‘GHC.Internal.Maybe.Just’
|
|
| 30 | + This constructor argument is also used boxed, so the specialisation
|
|
| 31 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 32 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 33 | + |
|
| 34 | +T27628l.hs: warning: [-Wspec-constr-reboxing]
|
|
| 35 | + SpecConstr specialised ‘go1 :: [Min a] -> Min a -> Min a’
|
|
| 36 | + source: ‘$cmconcat’ at T27628l.hs:37:5
|
|
| 37 | + recursivity: self-recursive
|
|
| 38 | + called from: ‘$cmconcat’
|
|
| 39 | + call pattern: go1 _ (Just _)
|
|
| 40 | + -- reboxes ‘GHC.Internal.Maybe.Just’
|
|
| 41 | + This constructor argument is also used boxed, so the specialisation
|
|
| 42 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 43 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 44 | + |
|
| 45 | +T27628l.hs: warning: [-Wspec-constr-reboxing]
|
|
| 46 | + SpecConstr specialised ‘exit :: Min a -> b -> Min a’
|
|
| 47 | + source: ‘$cstimes’ at T27628l.hs:27:10
|
|
| 48 | + recursivity: non-recursive (a join point)
|
|
| 49 | + called from: ‘$cstimes’
|
|
| 50 | + call pattern: exit (Just _) _
|
|
| 51 | + -- reboxes ‘GHC.Internal.Maybe.Just’
|
|
| 52 | + This constructor argument is also used boxed, so the specialisation
|
|
| 53 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 54 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 55 | + |
|
| 56 | +T27628l.hs: warning: [-Wspec-constr-reboxing]
|
|
| 57 | + SpecConstr specialised ‘g :: Min a -> b -> Min a -> Min a’
|
|
| 58 | + source: ‘$cstimes’ at T27628l.hs:27:10
|
|
| 59 | + recursivity: self-recursive
|
|
| 60 | + called from: ‘$cstimes’
|
|
| 61 | + call pattern: g (Just _) _ _
|
|
| 62 | + -- reboxes ‘GHC.Internal.Maybe.Just’
|
|
| 63 | + This constructor argument is also used boxed, so the specialisation
|
|
| 64 | + may increase allocation and defeat pointer-equality-based sharing.
|
|
| 65 | + See -Wspec-constr-reboxing in the users guide for possible remedies.
|
|
| 66 | + |
| ... | ... | @@ -617,3 +617,11 @@ test('T27628', normal, compile, ['-O2 -Wspec-constr-reboxing']) |
| 617 | 617 | test('T27628b', normal, compile, ['-O2 -Wspec-constr-reboxing'])
|
| 618 | 618 | test('T27628c', normal, compile, ['-O2 -Wspec-constr-reboxing'])
|
| 619 | 619 | test('T27628d', normal, compile, ['-O2 -Wspec-constr-reboxing'])
|
| 620 | +test('T27628e', normal, compile, ['-O2 -Wspec-constr-reboxing'])
|
|
| 621 | +test('T27628f', normal, compile, ['-O2 -Wspec-constr-reboxing'])
|
|
| 622 | +test('T27628g', normal, compile, ['-O2 -Wspec-constr-reboxing -dsuppress-uniques'])
|
|
| 623 | +test('T27628h', [extra_files(['T27628h_M.hs'])], multimod_compile, ['T27628h', '-v0 -O2 -Wspec-constr-reboxing -dsuppress-uniques'])
|
|
| 624 | +test('T27628i', [extra_files(['T27628i_M.hs'])], multimod_compile, ['T27628i', '-v0 -O2 -Wspec-constr-reboxing -dsuppress-uniques'])
|
|
| 625 | +test('T27628j', normal, compile, ['-O2 -Wspec-constr-reboxing'])
|
|
| 626 | +test('T27628k', normal, compile, ['-O2 -Wspec-constr-reboxing'])
|
|
| 627 | +test('T27628l', normal, compile, ['-O2 -Wspec-constr-reboxing -dsuppress-uniques']) |