summaryrefslogtreecommitdiff
path: root/ghc/compiler/rename/Rename.lhs
blob: ca22b19a0ef5d85d8b11b2f2a7a9a15de367507b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
%
% (c) The GRASP Project, Glasgow University, 1992-1998
%
\section[Rename]{Renaming and dependency analysis passes}

\begin{code}
module Rename ( renameModule ) where

#include "HsVersions.h"

import HsSyn
import RdrHsSyn		( RdrNameHsModule )
import RnHsSyn		( RenamedHsModule, RenamedHsDecl, 
			  extractHsTyNames, extractHsCtxtTyNames
			)

import CmdLineOpts	( opt_HiMap, opt_D_dump_rn_trace,
			  opt_D_dump_rn, opt_D_dump_rn_stats,
			  opt_WarnUnusedBinds, opt_WarnUnusedImports
		        )
import RnMonad
import RnNames		( getGlobalNames )
import RnSource		( rnSourceDecls, rnDecl )
import RnIfaces		( getImportedInstDecls, importDecl, getImportVersions,
			  getImportedRules, loadHomeInterface, getSlurped, removeContext
			)
import RnEnv		( availName, availNames, availsToNameSet, 
			  warnUnusedTopNames, mapFvRn, lookupImplicitOccRn,
			  FreeVars, plusFVs, plusFV, unitFV, emptyFVs, isEmptyFVs
			)
import Module           ( Module, ModuleName, pprModule, mkSearchPath, mkThisModule )
import Name		( Name, isLocallyDefined,
			  NamedThing(..), ImportReason(..), Provenance(..),
			  pprOccName, nameOccName,
			  getNameProvenance, 
			  maybeWiredInTyConName, maybeWiredInIdName, isWiredInName
			)
import Id		( idType )
import DataCon		( dataConTyCon, dataConType )
import TyCon		( TyCon, tyConDataCons, isSynTyCon, getSynTyConDefn )
import RdrName		( RdrName )
import NameSet
import PrelMods		( mAIN_Name, pREL_MAIN_Name )
import TysWiredIn	( unitTyCon, intTyCon, doubleTyCon, boolTyCon )
import PrelInfo		( ioTyCon_NAME, numClass_RDR, thinAirIdNames, derivingOccurrences )
import Type		( namesOfType, funTyCon )
import ErrUtils		( pprBagOfErrors, pprBagOfWarnings,
			  doIfSet, dumpIfSet, ghcExit
			)
import BasicTypes	( NewOrData(..) )
import Bag		( isEmptyBag, bagToList )
import FiniteMap	( fmToList, delListFromFM, addToFM, sizeFM, eltsFM )
import UniqSupply	( UniqSupply )
import UniqFM		( lookupUFM )
import Util		( equivClasses )
import Maybes		( maybeToBool )
import Outputable
\end{code}



\begin{code}
renameModule :: UniqSupply
	     -> RdrNameHsModule
	     -> IO (Maybe 
	              ( Module
		      , RenamedHsModule   -- Output, after renaming
		      , InterfaceDetails  -- Interface; for interface file generation
		      , RnNameSupply      -- Final env; for renaming derivings
		      , [ModuleName]	  -- Imported modules; for profiling
		      ))

renameModule us this_mod@(HsModule mod_name vers exports imports local_decls loc)
  = 	-- Initialise the renamer monad
    initRn mod_name us (mkSearchPath opt_HiMap) loc
	   (rename this_mod)				>>=
	\ (maybe_rn_stuff, rn_errs_bag, rn_warns_bag) ->

	-- Check for warnings
    doIfSet (not (isEmptyBag rn_warns_bag))
	    (printErrs (pprBagOfWarnings rn_warns_bag))	>>

	-- Check for errors; exit if so
    doIfSet (not (isEmptyBag rn_errs_bag))
	    (printErrs (pprBagOfErrors rn_errs_bag)	 >>
	     ghcExit 1
	    )						 >>

	-- Dump output, if any
    (case maybe_rn_stuff of
	Nothing  -> return ()
	Just results@(_, rn_mod, _, _, _)
		 -> dumpIfSet opt_D_dump_rn "Renamer:"
			      (ppr rn_mod)
    )							>>

	-- Return results
    return maybe_rn_stuff
\end{code}


\begin{code}
rename this_mod@(HsModule mod_name vers exports imports local_decls loc)
  =  	-- FIND THE GLOBAL NAME ENVIRONMENT
    getGlobalNames this_mod			`thenRn` \ maybe_stuff ->

	-- CHECK FOR EARLY EXIT
    if not (maybeToBool maybe_stuff) then
	-- Everything is up to date; no need to recompile further
	rnStats []		`thenRn_`
	returnRn Nothing
    else
    let
  	Just (export_env, gbl_env, fixity_env, global_avail_env) = maybe_stuff
    in

	-- RENAME THE SOURCE
    initRnMS gbl_env fixity_env SourceMode (
	rnSourceDecls local_decls
    )					`thenRn` \ (rn_local_decls, source_fvs) ->

	-- SLURP IN ALL THE NEEDED DECLARATIONS
    implicitFVs mod_name rn_local_decls 	`thenRn` \ implicit_fvs -> 
    let
	real_source_fvs = implicit_fvs `plusFV` source_fvs
		-- It's important to do the "plus" this way round, so that
		-- when compiling the prelude, locally-defined (), Bool, etc
		-- override the implicit ones. 
    in
    slurpImpDecls real_source_fvs	`thenRn` \ rn_imp_decls ->

	-- EXIT IF ERRORS FOUND
    checkErrsRn				`thenRn` \ no_errs_so_far ->
    if not no_errs_so_far then
	-- Found errors already, so exit now
	rnStats []		`thenRn_`
	returnRn Nothing
    else

	-- GENERATE THE VERSION/USAGE INFO
    getImportVersions mod_name exports			`thenRn` \ my_usages ->
    getNameSupplyRn					`thenRn` \ name_supply ->

	-- REPORT UNUSED NAMES
    reportUnusedNames gbl_env global_avail_env
		      export_env
		      source_fvs			`thenRn_`

	-- RETURN THE RENAMED MODULE
    let
	has_orphans        = any isOrphanDecl rn_local_decls
	direct_import_mods = [mod | ImportDecl mod _ _ _ _ _ <- imports]
	rn_all_decls	   = rn_imp_decls ++ rn_local_decls 
	renamed_module = HsModule mod_name vers 
				  trashed_exports trashed_imports
				  rn_all_decls
			          loc
    in
    rnStats rn_imp_decls	`thenRn_`
    returnRn (Just (mkThisModule mod_name,
		    renamed_module, 
		    (has_orphans, my_usages, export_env),
		    name_supply,
		    direct_import_mods))
  where
    trashed_exports  = {-trace "rnSource:trashed_exports"-} Nothing
    trashed_imports  = {-trace "rnSource:trashed_imports"-} []
\end{code}

@implicitFVs@ forces the renamer to slurp in some things which aren't
mentioned explicitly, but which might be needed by the type checker.

\begin{code}
implicitFVs mod_name decls
  = mapRn lookupImplicitOccRn implicit_occs	`thenRn` \ implicit_names ->
    returnRn (implicit_main		`plusFV` 
	      mkNameSet default_tys	`plusFV`
	      mkNameSet thinAirIdNames	`plusFV`
	      mkNameSet implicit_names)
    
  where
	-- Add occurrences for Int, Double, and (), because they
	-- are the types to which ambigious type variables may be defaulted by
	-- the type checker; so they won't always appear explicitly.
	-- [The () one is a GHC extension for defaulting CCall results.]
	-- ALSO: funTyCon, since it occurs implicitly everywhere!
	--  	 (we don't want to be bothered with making funTyCon a
	--	  free var at every function application!)
    default_tys = [getName intTyCon, getName doubleTyCon,
		   getName unitTyCon, getName funTyCon, getName boolTyCon]

	-- Add occurrences for IO or PrimIO
    implicit_main |  mod_name == mAIN_Name
		  || mod_name == pREL_MAIN_Name = unitFV ioTyCon_NAME
		  |  otherwise 		        = emptyFVs

	-- Now add extra "occurrences" for things that
	-- the deriving mechanism, or defaulting, will later need in order to
	-- generate code
    implicit_occs = foldr ((++) . get) [] decls

    get (DefD _) = [numClass_RDR]
    get (TyClD (TyData _ _ _ _ _ (Just deriv_classes) _ _))
       = concat (map get_deriv deriv_classes)
    get other = []

    get_deriv cls = case lookupUFM derivingOccurrences cls of
			Nothing   -> []
			Just occs -> occs
\end{code}

\begin{code}
isOrphanDecl (InstD (InstDecl inst_ty _ _ _ _))
  = not (foldNameSet ((||) . isLocallyDefined) False (extractHsTyNames (removeContext inst_ty)))
	-- The 'removeContext' is because of
	--	instance Foo a => Baz T where ...
	-- The decl is an orphan if Baz and T are both not locally defined,
	--	even if Foo *is* locally defined

isOrphanDecl (RuleD (RuleDecl _ _ _ lhs _ _))
  = check lhs
  where
    check (HsVar v)   = not (isLocallyDefined v)
    check (HsApp f a) = check f && check a
    check other	      = True
isOrphanDecl other = False
\end{code}


%*********************************************************
%*						 	 *
\subsection{Slurping declarations}
%*							 *
%*********************************************************

\begin{code}
-------------------------------------------------------
slurpImpDecls source_fvs
  = traceRn (text "slurpImp" <+> fsep (map ppr (nameSetToList source_fvs))) `thenRn_`

	-- The current slurped-set records all local things
    getSlurped					`thenRn` \ source_binders ->
    slurpSourceRefs source_binders source_fvs	`thenRn` \ (decls1, needed1, inst_gates) ->

	-- Now we can get the instance decls
    slurpInstDecls decls1 needed1 inst_gates	`thenRn` \ (decls2, needed2) ->

	-- And finally get everything else
    closeDecls	 decls2 needed2

-------------------------------------------------------
slurpSourceRefs :: NameSet			-- Variables defined in source
		-> FreeVars			-- Variables referenced in source
		-> RnMG ([RenamedHsDecl],
			 FreeVars,		-- Un-satisfied needs
			 FreeVars)		-- "Gates"
-- The declaration (and hence home module) of each gate has
-- already been loaded

slurpSourceRefs source_binders source_fvs
  = go [] 				-- Accumulating decls
       emptyFVs 			-- Unsatisfied needs
       source_fvs			-- Accumulating gates
       (nameSetToList source_fvs)	-- Gates whose defn hasn't been loaded yet
  where
    go decls fvs gates []
	= returnRn (decls, fvs, gates)

    go decls fvs gates (wanted_name:refs) 
	| isWiredInName wanted_name
 	= load_home wanted_name		`thenRn_`
	  go decls fvs (gates `plusFV` getWiredInGates wanted_name) refs

	| otherwise
	= importDecl wanted_name 		`thenRn` \ maybe_decl ->
	  case maybe_decl of
		-- No declaration... (already slurped, or local)
	    Nothing   -> go decls fvs gates refs
	    Just decl -> rnIfaceDecl decl		`thenRn` \ (new_decl, fvs1) ->
			 go (new_decl : decls)
			    (fvs1 `plusFV` fvs)
			    (gates `plusFV` getGates source_fvs new_decl)
			    refs

	-- When we find a wired-in name we must load its
	-- home module so that we find any instance decls therein
    load_home name 
	| name `elemNameSet` source_binders = returnRn ()
		-- When compiling the prelude, a wired-in thing may
		-- be defined in this module, in which case we don't
		-- want to load its home module!
		-- Using 'isLocallyDefined' doesn't work because some of
		-- the free variables returned are simply 'listTyCon_Name',
		-- with a system provenance.  We could look them up every time
		-- but that seems a waste.
	| otherwise			      = loadHomeInterface doc name	`thenRn_`
						returnRn ()
        where
	  doc = ptext SLIT("need home module for wired in thing") <+> ppr name
\end{code}
%
@slurpInstDecls@ imports appropriate instance decls.
It has to incorporate a loop, because consider
\begin{verbatim}
	instance Foo a => Baz (Maybe a) where ...
\end{verbatim}
It may be that @Baz@ and @Maybe@ are used in the source module,
but not @Foo@; so we need to chase @Foo@ too.

\begin{code}
slurpInstDecls decls needed gates
  = go decls needed gates gates
  where
    go decls needed all_gates new_gates
	| isEmptyFVs new_gates
	= returnRn (decls, needed)

	| otherwise
	= getImportedInstDecls all_gates		`thenRn` \ inst_decls ->
	  rnInstDecls decls needed emptyFVs inst_decls	`thenRn` \ (decls1, needed1, new_gates) ->
	  go decls1 needed1 (all_gates `plusFV` new_gates) new_gates

    rnInstDecls decls fvs gates []
	= returnRn (decls, fvs, gates)
    rnInstDecls decls fvs gates (d:ds) 
	= rnIfaceDecl d		`thenRn` \ (new_decl, fvs1) ->
	  rnInstDecls (new_decl:decls) 
		      (fvs1 `plusFV` fvs)
		      (gates `plusFV` getInstDeclGates new_decl)
		      ds
    

-------------------------------------------------------
-- closeDecls keeps going until the free-var set is empty
closeDecls decls needed
  | not (isEmptyFVs needed)
  = slurpDecls decls needed	`thenRn` \ (decls1, needed1) ->
    closeDecls decls1 needed1

  | otherwise
  = getImportedRules 			`thenRn` \ rule_decls ->
    case rule_decls of
	[]    -> returnRn decls	-- No new rules, so we are done
	other -> rnIfaceDecls decls emptyFVs rule_decls 	`thenRn` \ (decls1, needed1) ->
		 closeDecls decls1 needed1
		 

-------------------------------------------------------
rnIfaceDecls :: [RenamedHsDecl] -> FreeVars
	     -> [(Module, RdrNameHsDecl)]
	     -> RnM d ([RenamedHsDecl], FreeVars)
rnIfaceDecls decls fvs []     = returnRn (decls, fvs)
rnIfaceDecls decls fvs (d:ds) = rnIfaceDecl d		`thenRn` \ (new_decl, fvs1) ->
				rnIfaceDecls (new_decl:decls) (fvs1 `plusFV` fvs) ds

rnIfaceDecl (mod, decl) = initIfaceRnMS mod (rnDecl decl)	
			

-------------------------------------------------------
-- Augment decls with any decls needed by needed.
-- Return also free vars of the new decls (only)
slurpDecls decls needed
  = go decls emptyFVs (nameSetToList needed) 
  where
    go decls fvs []         = returnRn (decls, fvs)
    go decls fvs (ref:refs) = slurpDecl decls fvs ref	`thenRn` \ (decls1, fvs1) ->
			      go decls1 fvs1 refs

-------------------------------------------------------
slurpDecl decls fvs wanted_name
  = importDecl wanted_name 		`thenRn` \ maybe_decl ->
    case maybe_decl of
	-- No declaration... (wired in thing)
	Nothing -> returnRn (decls, fvs)

	-- Found a declaration... rename it
	Just decl -> rnIfaceDecl decl		`thenRn` \ (new_decl, fvs1) ->
		     returnRn (new_decl:decls, fvs1 `plusFV` fvs)
\end{code}


%*********************************************************
%*						 	 *
\subsection{Extracting the `gates'}
%*							 *
%*********************************************************

When we import a declaration like
\begin{verbatim}
	data T = T1 Wibble | T2 Wobble
\end{verbatim}
we don't want to treat @Wibble@ and @Wobble@ as gates
{\em unless} @T1@, @T2@ respectively are mentioned by the user program.
If only @T@ is mentioned
we want only @T@ to be a gate;
that way we don't suck in useless instance
decls for (say) @Eq Wibble@, when they can't possibly be useful.

@getGates@ takes a newly imported (and renamed) decl, and the free
vars of the source program, and extracts from the decl the gate names.

\begin{code}
getGates source_fvs (SigD (IfaceSig _ ty _ _))
  = extractHsTyNames ty

getGates source_fvs (TyClD (ClassDecl ctxt cls tvs sigs _ _ _ _ _ _))
  = delListFromNameSet (foldr (plusFV . get) (extractHsCtxtTyNames ctxt) sigs)
		       (map getTyVarName tvs)
    `addOneToNameSet` cls
  where
    get (ClassOpSig n _ ty _) 
	| n `elemNameSet` source_fvs = extractHsTyNames ty
	| otherwise		     = emptyFVs

getGates source_fvs (TyClD (TySynonym tycon tvs ty _))
  = delListFromNameSet (extractHsTyNames ty)
		       (map getTyVarName tvs)
	-- A type synonym type constructor isn't a "gate" for instance decls

getGates source_fvs (TyClD (TyData _ ctxt tycon tvs cons _ _ _))
  = delListFromNameSet (foldr (plusFV . get) (extractHsCtxtTyNames ctxt) cons)
		       (map getTyVarName tvs)
    `addOneToNameSet` tycon
  where
    get (ConDecl n tvs ctxt details _)
	| n `elemNameSet` source_fvs
		-- If the constructor is method, get fvs from all its fields
	= delListFromNameSet (get_details details `plusFV` 
		  	      extractHsCtxtTyNames ctxt)
			     (map getTyVarName tvs)
    get (ConDecl n tvs ctxt (RecCon fields) _)
		-- Even if the constructor isn't mentioned, the fields
		-- might be, as selectors.  They can't mention existentially
		-- bound tyvars (typechecker checks for that) so no need for 
		-- the deleteListFromNameSet part
	= foldr (plusFV . get_field) emptyFVs fields
	
    get other_con = emptyFVs

    get_details (VanillaCon tys) = plusFVs (map get_bang tys)
    get_details (InfixCon t1 t2) = get_bang t1 `plusFV` get_bang t2
    get_details (RecCon fields)  = plusFVs [get_bang t | (_, t) <- fields]
    get_details (NewCon t _)	 = extractHsTyNames t

    get_field (fs,t) | any (`elemNameSet` source_fvs) fs = get_bang t
		     | otherwise			 = emptyFVs

    get_bang (Banged   t) = extractHsTyNames t
    get_bang (Unbanged t) = extractHsTyNames t
    get_bang (Unpacked t) = extractHsTyNames t

getGates source_fvs other_decl = emptyFVs
\end{code}

@getWiredInGates@ is just like @getGates@, but it sees a wired-in @Name@
rather than a declaration.

\begin{code}
getWiredInGates :: Name -> FreeVars
getWiredInGates name 	-- No classes are wired in
  | is_id	         = getWiredInGates_s (namesOfType (idType the_id))
  | isSynTyCon the_tycon = getWiredInGates_s
	 (delListFromNameSet (namesOfType ty) (map getName tyvars))
  | otherwise 	         = unitFV name
  where
    maybe_wired_in_id    = maybeWiredInIdName name
    is_id		 = maybeToBool maybe_wired_in_id
    maybe_wired_in_tycon = maybeWiredInTyConName name
    Just the_id 	 = maybe_wired_in_id
    Just the_tycon	 = maybe_wired_in_tycon
    (tyvars,ty) 	 = getSynTyConDefn the_tycon

getWiredInGates_s names = foldr (plusFV . getWiredInGates) emptyFVs (nameSetToList names)
\end{code}

\begin{code}
getInstDeclGates (InstD (InstDecl inst_ty _ _ _ _)) = extractHsTyNames inst_ty
getInstDeclGates other				    = emptyFVs
\end{code}


%*********************************************************
%*						 	 *
\subsection{Unused names}
%*							 *
%*********************************************************

\begin{code}
reportUnusedNames gbl_env avail_env (ExportEnv export_avails _) mentioned_names
  = let
	used_names = mentioned_names `unionNameSets` availsToNameSet export_avails

	-- Now, a use of C implies a use of T,
	-- if C was brought into scope by T(..) or T(C)
	really_used_names = used_names `unionNameSets`
	  mkNameSet [ availName avail	
		    | sub_name <- nameSetToList used_names,
	              let avail = case lookupNameEnv avail_env sub_name of
			    Just avail -> avail
		            Nothing -> WARN( True, text "reportUnusedName: not in avail_env" <+> ppr sub_name )
				       Avail sub_name
		    ]

	defined_names = mkNameSet (concat (rdrEnvElts gbl_env))
	defined_but_not_used =
	   nameSetToList (defined_names `minusNameSet` really_used_names)

	-- Filter out the ones only defined implicitly
	bad_guys = filter reportableUnusedName defined_but_not_used
    in
    warnUnusedTopNames bad_guys

reportableUnusedName :: Name -> Bool
reportableUnusedName name
  = explicitlyImported (getNameProvenance name)
  where
    explicitlyImported (LocalDef _ _) 		             = True
	-- Report unused defns of local vars
    explicitlyImported (NonLocalDef (UserImport _ _ expl) _) = expl
 	-- Report unused explicit imports
    explicitlyImported other			             = False
	-- Don't report others

rnStats :: [RenamedHsDecl] -> RnMG ()
rnStats imp_decls
        | opt_D_dump_rn_trace || 
	  opt_D_dump_rn_stats ||
	  opt_D_dump_rn 
 	= getRnStats imp_decls		`thenRn` \ msg ->
	  ioToRnM (printErrs msg)	`thenRn_`
	  returnRn ()

	| otherwise = returnRn ()
\end{code}



%*********************************************************
%*							*
\subsection{Statistics}
%*							*
%*********************************************************

\begin{code}
getRnStats :: [RenamedHsDecl] -> RnMG SDoc
getRnStats imported_decls
  = getIfacesRn 		`thenRn` \ ifaces ->
    let
	n_mods = length [() | (_, _, Just _) <- eltsFM (iImpModInfo ifaces)]

	decls_read     = [decl | (_, avail, True, (_,decl)) <- nameEnvElts (iDecls ifaces),
				-- Data, newtype, and class decls are in the decls_fm
				-- under multiple names; the tycon/class, and each
				-- constructor/class op too.
				-- The 'True' selects just the 'main' decl
				 not (isLocallyDefined (availName avail))
			     ]

	(cd_rd, dd_rd, nd_rd, sd_rd, vd_rd,     _) = count_decls decls_read
	(cd_sp, dd_sp, nd_sp, sd_sp, vd_sp, id_sp) = count_decls imported_decls

	unslurped_insts       = iInsts ifaces
	inst_decls_unslurped  = length (bagToList unslurped_insts)
	inst_decls_read	      = id_sp + inst_decls_unslurped

	stats = vcat 
		[int n_mods <+> text "interfaces read",
		 hsep [ int cd_sp, text "class decls imported, out of", 
		        int cd_rd, text "read"],
		 hsep [ int dd_sp, text "data decls imported, out of",  
			int dd_rd, text "read"],
		 hsep [ int nd_sp, text "newtype decls imported, out of",  
		        int nd_rd, text "read"],
		 hsep [int sd_sp, text "type synonym decls imported, out of",  
		        int sd_rd, text "read"],
		 hsep [int vd_sp, text "value signatures imported, out of",  
		        int vd_rd, text "read"],
		 hsep [int id_sp, text "instance decls imported, out of",  
		        int inst_decls_read, text "read"],
		 text "cls dcls slurp" <+> fsep (map (ppr . tyClDeclName) 
					   [d | TyClD d <- imported_decls, isClassDecl d]),
		 text "cls dcls read"  <+> fsep (map (ppr . tyClDeclName) 
					   [d | TyClD d <- decls_read, isClassDecl d])]
    in
    returnRn (hcat [text "Renamer stats: ", stats])

count_decls decls
  = (class_decls, 
     data_decls, 
     newtype_decls,
     syn_decls, 
     val_decls, 
     inst_decls)
  where
    tycl_decls = [d | TyClD d <- decls]
    (class_decls, data_decls, newtype_decls, syn_decls) = countTyClDecls tycl_decls

    val_decls     = length [() | SigD _	  <- decls]
    inst_decls    = length [() | InstD _  <- decls]
\end{code}