Florian Ragwitz pushed to branch wip/rafl/hpc-instances at Glasgow Haskell Compiler / GHC

Commits:

12 changed files:

Changes:

  • changelog.d/hpc-classinsts
    1
    +section: compiler
    
    2
    +synopsis: Improve coverage reporting for class instances and their methods
    
    3
    +description:
    
    4
    +
    
    5
    +  Coverage reporting (:ghc-flag:`-fhpc`) now treats class instances more
    
    6
    +  naturally.
    
    7
    +
    
    8
    +  Compiler-generated instance methods, such as inherited default methods, no
    
    9
    +  longer receive separate top-level coverage boxes. Explicitly implemented
    
    10
    +  instance methods retain their own declaration coverage.
    
    11
    +
    
    12
    +  This means that it is no longer necessary to exercise all of a class' methods
    
    13
    +  in order for it to appear "covered".
    
    14
    +
    
    15
    +  Additionally, top-level coverage boxes are now created for all instances of
    
    16
    +  classes with methods, and ticked by the use of *any* of the class' methods.
    
    17
    +  This indicates that an instance was in fact used at run-time.
    
    18
    +
    
    19
    +  Note that classes without any instance methods are currently excluded from
    
    20
    +  this, and won't have any coverage boxes created.
    
    21
    +
    
    22
    +mrs: !16632
    
    23
    +issues: #17155 #15932

  • compiler/GHC/Core/Opt/Specialise.hs
    ... ... @@ -3082,6 +3082,7 @@ wantCallsFor _env f
    3082 3082
           JoinId {}        -> True
    
    3083 3083
           WorkerLikeId {}  -> True
    
    3084 3084
           RepPolyId {}     -> True
    
    3085
    +      InstMethId {}    -> True
    
    3085 3086
     
    
    3086 3087
     interestingDict :: SpecEnv -> CoreExpr -> Bool
    
    3087 3088
     -- This is a subtle and important function
    

  • compiler/GHC/CoreToIface.hs
    ... ... @@ -454,6 +454,7 @@ toIfaceTopBndr id
    454 454
     toIfaceIdDetails :: Name   -- ^ for trace/panic messages only
    
    455 455
                      -> IdDetails -> IfaceIdDetails
    
    456 456
     toIfaceIdDetails _ VanillaId                      = IfVanillaId
    
    457
    +toIfaceIdDetails _ (InstMethId {})                = IfVanillaId
    
    457 458
     toIfaceIdDetails _ (WorkerLikeId dmds)            = IfWorkerLikeId dmds
    
    458 459
     toIfaceIdDetails _ (DFunId {})                    = IfDFunId
    
    459 460
     toIfaceIdDetails _ (RecSelId { sel_naughty = n
    

  • compiler/GHC/HsToCore.hs
    ... ... @@ -170,7 +170,8 @@ deSugar hsc_env
    170 170
                                            (hsc_logger hsc_env)
    
    171 171
                                            (initTicksConfig (hsc_dflags hsc_env))
    
    172 172
                                            mod mod_loc
    
    173
    -                                       export_set (typeEnvTyCons type_env) binds
    
    173
    +                                       export_set (typeEnvTyCons type_env)
    
    174
    +                                       insts binds
    
    174 175
                                   else return (binds, Nothing)
    
    175 176
             ; let modBreaks
    
    176 177
                     | Just (_, _, breakpointSpecs) <- m_tickInfo
    

  • compiler/GHC/HsToCore/Ticks.hs
    1
    -{-# LANGUAGE NondecreasingIndentation, DataKinds #-}
    
    1
    +{-# LANGUAGE NondecreasingIndentation, DataKinds, ViewPatterns #-}
    
    2 2
     
    
    3 3
     {-# OPTIONS_GHC -Wno-incomplete-record-updates #-}
    
    4 4
     
    
    5 5
     {-
    
    6 6
     (c) Galois, 2006
    
    7 7
     (c) University of Glasgow, 2007
    
    8
    -(c) Florian Ragwitz, 2025
    
    8
    +(c) Florian Ragwitz, 2025-2026
    
    9 9
     -}
    
    10 10
     
    
    11 11
     module GHC.HsToCore.Ticks
    
    ... ... @@ -24,6 +24,8 @@ import GHC.Unit
    24 24
     
    
    25 25
     import GHC.Core.Type
    
    26 26
     import GHC.Core.TyCon
    
    27
    +import GHC.Core.InstEnv
    
    28
    +import GHC.Core.Class
    
    27 29
     
    
    28 30
     import GHC.Data.Maybe
    
    29 31
     import GHC.Data.FastString
    
    ... ... @@ -43,6 +45,7 @@ import GHC.Types.Var.Set
    43 45
     import GHC.Types.Var.Env
    
    44 46
     import GHC.Types.Name.Set
    
    45 47
     import GHC.Types.Name
    
    48
    +import GHC.Types.Name.Env
    
    46 49
     import GHC.Types.CostCentre
    
    47 50
     import GHC.Types.CostCentre.State
    
    48 51
     import GHC.Types.Tickish
    
    ... ... @@ -56,6 +59,7 @@ import Data.Foldable (toList)
    56 59
     import Trace.Hpc.Mix
    
    57 60
     
    
    58 61
     import Data.Bifunctor (second)
    
    62
    +import Data.Functor
    
    59 63
     import Data.List.NonEmpty (NonEmpty (..))
    
    60 64
     import Data.Set (Set)
    
    61 65
     import qualified Data.Set as Set
    
    ... ... @@ -100,11 +104,12 @@ addTicksToBinds
    100 104
                                     -- isExportedId doesn't work yet (the desugarer
    
    101 105
                                     -- hasn't set it), so we have to work from this set.
    
    102 106
             -> [TyCon]              -- ^ Type constructors in this module
    
    107
    +        -> [ClsInst]            -- ^ Class instances in this module
    
    103 108
             -> LHsBinds GhcTc
    
    104 109
             -> IO (LHsBinds GhcTc, Maybe (FilePath, SizedSeq Tick, SizedSeq Tick))
    
    105 110
     
    
    106 111
     addTicksToBinds logger cfg
    
    107
    -                mod mod_loc exports tyCons binds
    
    112
    +                mod mod_loc exports tyCons insts binds
    
    108 113
       | let passes = ticks_passes cfg
    
    109 114
       , not (null passes)
    
    110 115
       , Just orig_file <- ml_hs_file mod_loc = do
    
    ... ... @@ -129,8 +134,13 @@ addTicksToBinds logger cfg
    129 134
                           , this_mod     = mod
    
    130 135
                           , tickishType  = tickish
    
    131 136
                           , recSelBinds  = emptyVarEnv
    
    137
    +                      , instMeths    = emptyVarEnv
    
    138
    +                      , instTicks    = emptyNameEnv
    
    132 139
                           }
    
    133
    -                (binds',_,st') = unTM (addTickLHsBinds binds) env st
    
    140
    +                addTick = do
    
    141
    +                  instTicks <- allocInstTicks insts
    
    142
    +                  withEnv (\e -> e{ instTicks }) $ addTickLHsBinds binds
    
    143
    +                (binds',_,st') = unTM addTick env st
    
    134 144
                 in (binds', st')
    
    135 145
     
    
    136 146
               (binds1,st) = foldr tickPass (binds, initTTState) passes
    
    ... ... @@ -231,7 +241,7 @@ addTickLHsBind :: LHsBind GhcTc -> TM (LHsBind GhcTc)
    231 241
     addTickLHsBind (L pos (XHsBindsLR bind@(AbsBinds { abs_binds = binds
    
    232 242
                                                      , abs_exports = abs_exports
    
    233 243
                                                      }))) =
    
    234
    -  withEnv (add_rec_sels . add_inlines . add_exports) $ do
    
    244
    +  withEnv (add_inst_meths . add_rec_sels . add_inlines . add_exports) $ do
    
    235 245
           binds' <- addTickLHsBinds binds
    
    236 246
           return $ L pos $ XHsBindsLR $ bind { abs_binds = binds' }
    
    237 247
       where
    
    ... ... @@ -259,19 +269,26 @@ addTickLHsBind (L pos (XHsBindsLR bind@(AbsBinds { abs_binds = binds
    259 269
                               | ABE{ abe_poly, abe_mono } <- abs_exports
    
    260 270
                               , RecSelId{} <- [idDetails abe_poly] ] }
    
    261 271
     
    
    272
    +   add_inst_meths env =
    
    273
    +     env{ instMeths = instMeths env `extendVarEnvList`
    
    274
    +                        [ (abe_mono, dfun)
    
    275
    +                        | ABE{ abe_poly, abe_mono } <- abs_exports
    
    276
    +                        , Just dfun <- [instMeth abe_poly] ] }
    
    277
    +     where instMeth (idDetails -> InstMethId dfun) = Just dfun
    
    278
    +           instMeth id = lookupVarEnv (instMeths env) id
    
    279
    +
    
    262 280
     addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id, fun_matches = matches }))) = do
    
    263 281
       let name = getOccString id
    
    264 282
       decl_path <- getPathEntry
    
    265 283
       density <- getDensity
    
    284
    +  env <- getEnv
    
    266 285
     
    
    267
    -  inline_ids <- liftM inlines getEnv
    
    268 286
       -- See Note [inline sccs]
    
    269 287
       let inline   = isInlinePragma (idInlinePragma id)
    
    270
    -                 || id `elemVarSet` inline_ids
    
    288
    +                 || id `elemVarSet` inlines env
    
    271 289
     
    
    272 290
       -- See Note [inline sccs]
    
    273
    -  tickish <- tickishType `liftM` getEnv
    
    274
    -  case tickish of { ProfNotes | inline -> return (L pos funBind); _ -> do
    
    291
    +  case tickishType env of { ProfNotes | inline -> return (L pos funBind); _ -> do
    
    275 292
     
    
    276 293
       -- See Note [Record-selector ticks]
    
    277 294
       selTicks <- recSelTick id
    
    ... ... @@ -294,16 +311,28 @@ addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id, fun_matches = matches
    294 311
           toplev = null decl_path
    
    295 312
           exported = idName id `elemNameSet` exported_names
    
    296 313
     
    
    297
    -  tick <- if not blackListed &&
    
    298
    -               shouldTickBind density toplev exported simple inline
    
    314
    +  -- See Note [Instance Method Coverage].
    
    315
    +  -- We only exclude generated instance methods for now, not generated code in
    
    316
    +  -- general, whose coverage semantics may need to be considered separately.
    
    317
    +  let generatedInstMeth = tickishType env == HpcTicks
    
    318
    +        && id `elemVarEnv` instMeths env
    
    319
    +        && isGenerated (mg_origin (mg_ext matches))
    
    320
    +
    
    321
    +  tick <- if not generatedInstMeth
    
    322
    +               && not blackListed
    
    323
    +               && shouldTickBind density toplev exported simple inline
    
    299 324
                  then
    
    300 325
                     bindTick density name (locA pos) fvs
    
    301 326
                  else
    
    302 327
                     return Nothing
    
    303 328
     
    
    329
    +  -- See Note [Instance Method Coverage].
    
    330
    +  instTick <- instMethTick id
    
    331
    +
    
    304 332
       let mbCons = maybe Prelude.id (:)
    
    305 333
       return $ L pos $ funBind { fun_matches = mg
    
    306
    -                           , fun_ext = second (tick `mbCons`) (fun_ext funBind) }
    
    334
    +                           , fun_ext = second ((instTick `mbCons`) . (tick `mbCons`))
    
    335
    +                                              (fun_ext funBind) }
    
    307 336
       } }
    
    308 337
       where
    
    309 338
         -- See Note [Record-selector ticks]
    
    ... ... @@ -338,6 +367,30 @@ addTickLHsBind (L pos (funBind@(FunBind { fun_id = L _ id, fun_matches = matches
    338 367
     -- This scheme could be extended further in the future, making coverage for
    
    339 368
     -- positional constructor fields mean that the field was accessed at run-time.
    
    340 369
     
    
    370
    +-- Note [Instance Method Coverage]
    
    371
    +-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    372
    +-- Users expect source-level semantics for coverage reports (see #17155,
    
    373
    +-- #15932). That is, code they've written themselves should be marked as covered
    
    374
    +-- iff it is evaluated at run-time. Given that the implementation of class
    
    375
    +-- instances creates top-level bindings for all methods of an instance,
    
    376
    +-- inherited and hand-written, we need to go out of our way to create top-level
    
    377
    +-- tick boxes only for methods which correspond to user-written code.
    
    378
    +--
    
    379
    +-- Instance method 'Id's carry the name of the 'DFunId' of the instance they
    
    380
    +-- belong to. When creating tick boxes for top-level bindings, we can use this
    
    381
    +-- information to skip creating boxes for inherited/generated instance methods.
    
    382
    +--
    
    383
    +-- Additionally, we also want to inform the user whether or not their class
    
    384
    +-- instance as a whole has been used at run-time. To do so, we create a single
    
    385
    +-- tick box at the instance head and tick that box when any method of that
    
    386
    +-- instance is evaluated.
    
    387
    +--
    
    388
    +-- As marker/tag-like class instances without any methods would always appear
    
    389
    +-- as uncovered using the above mechanism, we exclude them when creating
    
    390
    +-- instance tick boxes. This could potentially be improved in the future by
    
    391
    +-- tracking if any functions requiring an instance of an empty class have been
    
    392
    +-- evaluated.
    
    393
    +
    
    341 394
     -- TODO: Revisit this
    
    342 395
     addTickLHsBind (L pos (pat@(PatBind { pat_lhs = lhs
    
    343 396
                                         , pat_rhs = rhs
    
    ... ... @@ -1098,6 +1151,8 @@ data TickTransEnv = TTE { fileName :: FastString
    1098 1151
                             , this_mod     :: Module
    
    1099 1152
                             , tickishType  :: TickishType
    
    1100 1153
                             , recSelBinds  :: IdEnv DVarSet
    
    1154
    +                        , instMeths    :: IdEnv Name
    
    1155
    +                        , instTicks    :: NameEnv CoreTickish
    
    1101 1156
                             }
    
    1102 1157
     
    
    1103 1158
     --      deriving Show
    
    ... ... @@ -1182,6 +1237,9 @@ getDensity = TM $ \env st -> (density env, noFVs, st)
    1182 1237
     ifDensity :: TickDensity -> TM a -> TM a -> TM a
    
    1183 1238
     ifDensity d th el = do d0 <- getDensity; if d == d0 then th else el
    
    1184 1239
     
    
    1240
    +ifTickish :: TickishType -> TM a -> TM a -> TM a
    
    1241
    +ifTickish want th el = tickishType <$> getEnv >>= \got -> if want == got then th else el
    
    1242
    +
    
    1185 1243
     getFreeVars :: TM a -> TM (FreeVars, a)
    
    1186 1244
     getFreeVars (TM m)
    
    1187 1245
       = TM $ \ env st -> case m env st of (a, fv, st') -> ((fv,a), fv, st')
    
    ... ... @@ -1238,6 +1296,20 @@ isBlackListed (RealSrcSpan pos _) = TM $ \ env st -> (Set.member pos (blackList
    1238 1296
     isBlackListed GeneratedSrcSpan{} = return False
    
    1239 1297
     isBlackListed UnhelpfulSpan{} = return False
    
    1240 1298
     
    
    1299
    +-- See Note [Instance Method Coverage].
    
    1300
    +allocInstTicks :: [ClsInst] -> TM (NameEnv CoreTickish)
    
    1301
    +allocInstTicks insts =
    
    1302
    +    ifTickish HpcTicks (mkNameEnv . catMaybes <$> mapM alloc insts) (pure emptyNameEnv)
    
    1303
    +  where
    
    1304
    +    alloc ClsInst{ is_dfun, is_cls }
    
    1305
    +      | null (classMethods is_cls) = pure Nothing
    
    1306
    +      | otherwise = fmap (idName is_dfun,) <$> allocATickBox (TopLevelBox [getOccString is_dfun])
    
    1307
    +                                                             False True (getSrcSpan is_dfun) noFVs
    
    1308
    +
    
    1309
    +-- See Note [Instance Method Coverage].
    
    1310
    +instMethTick :: Id -> TM (Maybe CoreTickish)
    
    1311
    +instMethTick id = getEnv <&> \e -> lookupVarEnv (instMeths e) id >>= lookupNameEnv (instTicks e)
    
    1312
    +
    
    1241 1313
     -- the tick application inherits the source position of its
    
    1242 1314
     -- expression argument to support nested box allocations
    
    1243 1315
     allocTickBox :: BoxLabel -> Bool -> Bool -> SrcSpan -> TM (HsExpr GhcTc)
    
    ... ... @@ -1256,7 +1328,7 @@ allocTickBox boxLabel countEntries topOnly pos m
    1256 1328
           return (this_loc (XExpr $ HsTick tickish $ this_loc e))
    
    1257 1329
     
    
    1258 1330
     recSelTick :: Id -> TM (Maybe [CoreTickish])
    
    1259
    -recSelTick id = ifDensity TickForCoverage maybe_tick (pure Nothing)
    
    1331
    +recSelTick id = ifTickish HpcTicks maybe_tick (pure Nothing)
    
    1260 1332
       where
    
    1261 1333
         maybe_tick = getEnv >>=
    
    1262 1334
           maybe (pure Nothing) tick_all . (`lookupVarEnv` id) . recSelBinds
    

  • compiler/GHC/Tc/TyCl/Instance.hs
    ... ... @@ -59,7 +59,7 @@ import GHC.Core.FamInstEnv
    59 59
     import GHC.Core.Type
    
    60 60
     import GHC.Core.Multiplicity
    
    61 61
     import GHC.Core.InstEnv
    
    62
    -import GHC.Core.Predicate( classMethodInstTy )
    
    62
    +import GHC.Core.Predicate( classMethodInstTy, isCoVarType )
    
    63 63
     import GHC.Core.TyCon
    
    64 64
     import GHC.Core.Coercion.Axiom
    
    65 65
     import GHC.Core.DataCon
    
    ... ... @@ -72,6 +72,7 @@ import GHC.Types.Var.Set
    72 72
     import GHC.Types.Basic
    
    73 73
     import GHC.Types.ForeignCall ( typeCheckCType )
    
    74 74
     import GHC.Types.Id
    
    75
    +import GHC.Types.Id.Info
    
    75 76
     import GHC.Types.InlinePragma
    
    76 77
     import GHC.Types.SourceFile
    
    77 78
     import GHC.Types.SourceText
    
    ... ... @@ -1859,7 +1860,7 @@ tcMethods _skol_info dfun_id clas tyvars dfun_ev_vars inst_tys
    1859 1860
         tc_item :: ClassOpItem -> TcM (Id, LHsBind GhcTc, Maybe Implication)
    
    1860 1861
         tc_item (sel_id, dm_info)
    
    1861 1862
           | Just (user_bind, bndr_loc, prags) <- findMethodBind (idName sel_id) binds prag_fn
    
    1862
    -      = tcMethodBody False clas tyvars dfun_ev_vars inst_tys
    
    1863
    +      = tcMethodBody False dfun_id clas tyvars dfun_ev_vars inst_tys
    
    1863 1864
                          dfun_ev_binds is_derived hs_sig_fn
    
    1864 1865
                          spec_inst_prags prags
    
    1865 1866
                          sel_id user_bind bndr_loc
    
    ... ... @@ -1879,7 +1880,7 @@ tcMethods _skol_info dfun_id clas tyvars dfun_ev_vars inst_tys
    1879 1880
           -- See Note [Implementation of Unsatisfiable constraints],
    
    1880 1881
           -- in GHC.Tc.Errors, point (D).
    
    1881 1882
           _ | (theta_id,unsat_msg) : _ <- unsat_thetas
    
    1882
    -        -> do { (meth_id, _) <- mkMethIds clas tyvars dfun_ev_vars
    
    1883
    +        -> do { (meth_id, _) <- mkMethIds dfun_id clas tyvars dfun_ev_vars
    
    1883 1884
                                              inst_tys sel_id
    
    1884 1885
                  ; unsat_id <- tcLookupKnownKeyId unsatisfiableIdKey
    
    1885 1886
                  -- Recall that unsatisfiable :: forall {rep} (msg :: ErrorMessage) (a :: TYPE rep). Unsatisfiable msg => a
    
    ... ... @@ -1896,7 +1897,7 @@ tcMethods _skol_info dfun_id clas tyvars dfun_ev_vars inst_tys
    1896 1897
           Just (dm_name, dm_spec) ->
    
    1897 1898
             do { (meth_bind, inline_prags) <- mkDefMethBind inst_loc dfun_id clas sel_id dm_name dm_spec
    
    1898 1899
                ; tcMethodBody (is_vanilla_dm dm_spec)
    
    1899
    -                          clas tyvars dfun_ev_vars inst_tys
    
    1900
    +                          dfun_id clas tyvars dfun_ev_vars inst_tys
    
    1900 1901
                               dfun_ev_binds is_derived hs_sig_fn
    
    1901 1902
                               spec_inst_prags inline_prags
    
    1902 1903
                               sel_id meth_bind inst_loc }
    
    ... ... @@ -1904,7 +1905,7 @@ tcMethods _skol_info dfun_id clas tyvars dfun_ev_vars inst_tys
    1904 1905
           -- No default method
    
    1905 1906
           Nothing ->
    
    1906 1907
             do { traceTc "tc_def: warn" (ppr sel_id)
    
    1907
    -           ; (meth_id, _) <- mkMethIds clas tyvars dfun_ev_vars
    
    1908
    +           ; (meth_id, _) <- mkMethIds dfun_id clas tyvars dfun_ev_vars
    
    1908 1909
                                            inst_tys sel_id
    
    1909 1910
                ; dflags <- getDynFlags
    
    1910 1911
                 -- Add a binding whose RHS is an error
    
    ... ... @@ -2030,13 +2031,13 @@ Instead, we take the following approach:
    2030 2031
     tcMethodBody :: Bool   -- True <=> This is a vanilla default method
    
    2031 2032
                            -- See (TRC5) in Note [Tracking needed EvIds]
    
    2032 2033
                            --            in GHC.Tc.Solver.Solve
    
    2033
    -             -> Class -> [TcTyVar] -> [EvVar] -> [TcType]
    
    2034
    +             -> DFunId -> Class -> [TcTyVar] -> [EvVar] -> [TcType]
    
    2034 2035
                  -> TcEvBinds -> Bool
    
    2035 2036
                  -> HsSigFun
    
    2036 2037
                  -> [LTcSpecPrag] -> [LSig GhcRn]
    
    2037 2038
                  -> Id -> LHsBind GhcRn -> SrcSpan
    
    2038 2039
                  -> TcM (TcId, LHsBind GhcTc, Maybe Implication)
    
    2039
    -tcMethodBody is_vanilla_dm clas tyvars dfun_ev_vars inst_tys
    
    2040
    +tcMethodBody is_vanilla_dm dfun_id clas tyvars dfun_ev_vars inst_tys
    
    2040 2041
                  dfun_ev_binds is_derived
    
    2041 2042
                  sig_fn spec_inst_prags prags
    
    2042 2043
                  sel_id (L bind_loc meth_bind) bndr_loc
    
    ... ... @@ -2044,7 +2045,7 @@ tcMethodBody is_vanilla_dm clas tyvars dfun_ev_vars inst_tys
    2044 2045
         do { traceTc "tcMethodBody" (ppr sel_id <+> ppr (idType sel_id) $$ ppr bndr_loc)
    
    2045 2046
            ; let skol_info = MethSkol meth_name is_vanilla_dm
    
    2046 2047
            ; (global_meth_id, local_meth_id) <- setSrcSpan bndr_loc $
    
    2047
    -                                            mkMethIds clas tyvars dfun_ev_vars
    
    2048
    +                                            mkMethIds dfun_id clas tyvars dfun_ev_vars
    
    2048 2049
                                                           inst_tys sel_id
    
    2049 2050
     
    
    2050 2051
            ; let lm_bind = meth_bind { fun_id = L (noAnnSrcSpan bndr_loc)
    
    ... ... @@ -2162,16 +2163,19 @@ tcMethodBodyHelp hs_sig_fn sel_id local_meth_id meth_bind
    2162 2163
                                     -- they are all for meth_id
    
    2163 2164
     
    
    2164 2165
     ------------------------
    
    2165
    -mkMethIds :: Class -> [TcTyVar] -> [EvVar]
    
    2166
    +mkMethIds :: DFunId -> Class -> [TcTyVar] -> [EvVar]
    
    2166 2167
               -> [TcType] -> Id -> TcM (TcId, TcId)
    
    2167 2168
                  -- returns (poly_id, local_id), but ignoring any instance signature
    
    2168 2169
                  -- See Note [Instance method signatures]
    
    2169
    -mkMethIds clas tyvars dfun_ev_vars inst_tys sel_id
    
    2170
    +mkMethIds dfun_id clas tyvars dfun_ev_vars inst_tys sel_id
    
    2170 2171
       = do  { poly_meth_name  <- newName (mkClassOpAuxOcc sel_occ)
    
    2171 2172
             ; local_meth_name <- newName sel_occ
    
    2172 2173
                       -- Base the local_meth_name on the selector name, because
    
    2173 2174
                       -- type errors from tcMethodBody come from here
    
    2174
    -        ; let poly_meth_id  = mkLocalId poly_meth_name  ManyTy poly_meth_ty
    
    2175
    +        ; let poly_meth_id =
    
    2176
    +                assert (not (isCoVarType poly_meth_ty)) $
    
    2177
    +                Var.mkLocalVar (InstMethId (idName dfun_id))
    
    2178
    +                               poly_meth_name ManyTy poly_meth_ty vanillaIdInfo
    
    2175 2179
                   local_meth_id = mkLocalId local_meth_name ManyTy local_meth_ty
    
    2176 2180
     
    
    2177 2181
             ; return (poly_meth_id, local_meth_id) }
    

  • compiler/GHC/Types/Id/Info.hs
    ... ... @@ -222,6 +222,10 @@ data IdDetails
    222 222
             --   - the [CbvMark] is always empty (and ignored) until after Tidy
    
    223 223
             --     for ids from the current module
    
    224 224
             --   - If non-empty, at least is isMarkedCbbv; see (CBV2)
    
    225
    +  | InstMethId
    
    226
    +        -- ^ An instance method.
    
    227
    +      Name
    
    228
    +        -- ^ 'Name' of the dictionary function the instance method belongs to.
    
    225 229
     
    
    226 230
     data RecSelInfo
    
    227 231
       = RSI { rsi_def   :: [ConLike]   -- Record selector defined for these
    
    ... ... @@ -449,6 +453,7 @@ pprIdDetails other = brackets (pp other)
    449 453
                                                ppWhen is_naughty (text "(naughty)")
    
    450 454
        pp CoVarId                 = text "CoVarId"
    
    451 455
        pp (JoinId arity marks)    = text "JoinId" <> parens (int arity) <> parens (ppr marks)
    
    456
    +   pp (InstMethId name)       = text "InstMethId" <> parens (ppr name)
    
    452 457
     
    
    453 458
     {-
    
    454 459
     ************************************************************************
    

  • testsuite/tests/hpc/instmeths/instmeths.hs
    1
    +import Data.List
    
    2
    +import Data.Maybe
    
    3
    +import Trace.Hpc.Mix
    
    4
    +import Trace.Hpc.Reflect
    
    5
    +import Trace.Hpc.Tix
    
    6
    +import Trace.Hpc.Util
    
    7
    +
    
    8
    +class Foo a where
    
    9
    +  defMeth, defMeth', reqMeth :: a -> String
    
    10
    +  defMeth = const "class default"
    
    11
    +  defMeth' = const "class default"
    
    12
    +
    
    13
    +newtype T a = T a deriving (Show, Eq, Functor)
    
    14
    +instance Foo (T a) where
    
    15
    +  reqMeth = const "Foo (T a) instance"
    
    16
    +
    
    17
    +newtype T' a = T' a
    
    18
    +
    
    19
    +instance Foo (T' a) where
    
    20
    +  reqMeth = const "Foo (T' a) instance"
    
    21
    +  defMeth = const "Foo (T' a) instance"
    
    22
    +
    
    23
    +-- no method use could "cover" this instance, so we (for now) omit the top-level box for it
    
    24
    +class Marker a
    
    25
    +instance Marker (T Bool)
    
    26
    +
    
    27
    +interesting :: (Int, MixEntry) -> Maybe (Int, HpcPos, String)
    
    28
    +interesting (n, (pos, TopLevelBox [name])) | isInteresting name = Just (n, pos, name)
    
    29
    +  where isInteresting = (||) <$> isInst <*> isMeth
    
    30
    +        isInst = ("$f" `isPrefixOf`)
    
    31
    +        isMeth = (||) <$> (`elem` ["(==)", "(/=)", "show", "showList", "showsPrec", "fmap", "(<$)"])
    
    32
    +                      <*> ("Meth" `isInfixOf`)
    
    33
    +interesting _ = Nothing
    
    34
    +
    
    35
    +-- candidate for HPC.Utils, maybe?
    
    36
    +sourceAt :: String -> HpcPos -> String
    
    37
    +sourceAt src pos
    
    38
    +  | l1 == l2 = take (c2 - c1 + 1) . drop (c1 - 1) $ ls !! (l1 - 1)
    
    39
    +  | otherwise = intercalate "\n" $ first : (middle ++ [last])
    
    40
    +  where
    
    41
    +    (l1, c1, l2, c2) = fromHpcPos pos
    
    42
    +    ls = lines src
    
    43
    +    first = drop (c1 - 1) $ ls !! (l1 - 1)
    
    44
    +    middle = take (l2 - l1 - 1) $ drop l1 ls
    
    45
    +    last = take c2 $ ls !! (l2 - 1)
    
    46
    +
    
    47
    +main :: IO ()
    
    48
    +main = do
    
    49
    +  print (T 23 == (succ <$> T 22)) -- tick Eq and Functor by using any of their methods
    
    50
    +  print (reqMeth (T' 1)) -- tick `Foo (T' a)` and that instance's `reqMeth`
    
    51
    +
    
    52
    +  Mix source _ _ _ mixEntries <- readMix [".hpc"] (Left "Main")
    
    53
    +  src <- readFileUtf8 source
    
    54
    +  let boxes = mapMaybe interesting $ zip [0 ..] mixEntries
    
    55
    +  Tix [TixModule "Main" _ _ counts] <- examineTix
    
    56
    +  mapM_ print [ (counts !! n, path, sourceAt src pos)
    
    57
    +              | (n, pos, path) <- sortOn (\(_, l, p) -> (p, l)) boxes ]

  • testsuite/tests/hpc/instmeths/instmeths.stdout
    1
    +True
    
    2
    +"Foo (T' a) instance"
    
    3
    +(1,"$fEqT","Eq")
    
    4
    +(0,"$fFooT","Foo (T a)")
    
    5
    +(1,"$fFooT'","Foo (T' a)")
    
    6
    +(1,"$fFunctorT","Functor")
    
    7
    +(0,"$fShowT","Show")
    
    8
    +(0,"defMeth","defMeth = const \"class default\"")
    
    9
    +(0,"defMeth","defMeth = const \"Foo (T' a) instance\"")
    
    10
    +(0,"defMeth'","defMeth' = const \"class default\"")
    
    11
    +(0,"reqMeth","reqMeth = const \"Foo (T a) instance\"")
    
    12
    +(1,"reqMeth","reqMeth = const \"Foo (T' a) instance\"")

  • testsuite/tests/hpc/instmeths/test.T
    1
    +setTestOpts([omit_ghci, when(fast(), skip), js_skip])
    
    2
    +
    
    3
    +test('instmeths',
    
    4
    +     [ignore_extension],
    
    5
    +     compile_and_run, ['-fhpc'])

  • testsuite/tests/simplCore/should_compile/T22375.stderr
    ... ... @@ -5,7 +5,7 @@ Result size of Tidy Core
    5 5
     
    
    6 6
     -- RHS size: {terms: 14, types: 9, coercions: 0, joins: 0/0}
    
    7 7
     T22375.$fEqX_$c== :: X -> X -> Bool
    
    8
    -[GblId,
    
    8
    +[GblId[InstMethId(T22375.$fEqX)],
    
    9 9
      Arity=2,
    
    10 10
      Str=<SL><SL>,
    
    11 11
      Unf=Unf{Src=<vanilla>, TopLvl=True,
    
    ... ... @@ -27,7 +27,7 @@ T22375.$fEqX_$c==
    27 27
     
    
    28 28
     -- RHS size: {terms: 18, types: 9, coercions: 0, joins: 0/0}
    
    29 29
     T22375.$fEqX_$c/= [InlPrag=INLINE (sat-args=2)] :: X -> X -> Bool
    
    30
    -[GblId,
    
    30
    +[GblId[InstMethId(T22375.$fEqX)],
    
    31 31
      Arity=2,
    
    32 32
      Str=<SL><SL>,
    
    33 33
      Unf=Unf{Src=StableUser, TopLvl=True,
    

  • testsuite/tests/simplCore/should_compile/T22375DataFamily.stderr
    ... ... @@ -5,7 +5,7 @@ Result size of Tidy Core
    5 5
     
    
    6 6
     -- RHS size: {terms: 14, types: 11, coercions: 2, joins: 0/0}
    
    7 7
     T22375DataFamily.$fEqX_$c== :: X () -> X () -> Bool
    
    8
    -[GblId,
    
    8
    +[GblId[InstMethId(T22375DataFamily.$fEqX)],
    
    9 9
      Arity=2,
    
    10 10
      Str=<SL><SL>,
    
    11 11
      Unf=Unf{Src=<vanilla>, TopLvl=True,
    
    ... ... @@ -36,7 +36,7 @@ T22375DataFamily.$fEqX_$c==
    36 36
     -- RHS size: {terms: 18, types: 11, coercions: 2, joins: 0/0}
    
    37 37
     T22375DataFamily.$fEqX_$c/= [InlPrag=INLINE (sat-args=2)]
    
    38 38
       :: X () -> X () -> Bool
    
    39
    -[GblId,
    
    39
    +[GblId[InstMethId(T22375DataFamily.$fEqX)],
    
    40 40
      Arity=2,
    
    41 41
      Str=<SL><SL>,
    
    42 42
      Unf=Unf{Src=StableUser, TopLvl=True,