summaryrefslogtreecommitdiff
path: root/ghc/compiler/specialise/SpecTyFuns.lhs
blob: 39fbd1796ddd5f73c440cef19843bbcf80e9b50e (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
%
% (c) The GRASP/AQUA Project, Glasgow University, 1993-1995
%
\section[Specialise]{Stamping out overloading, and (optionally) polymorphism}

\begin{code}
#include "HsVersions.h"

module SpecTyFuns (
	specialiseCallTys,
	ConstraintVector(..),
	mkConstraintVector,
	isUnboxedSpecialisation,

	specialiseConstrTys,
	mkSpecialisedCon,

	argTysMatchSpecTys_error,

	pprSpecErrs,

	Maybe(..), Pretty(..), UniType
    ) where

import AbsUniType
import Bag		( Bag, isEmptyBag, bagToList )
import FiniteMap	( FiniteMap, emptyFM, addListToFM_C,
			  keysFM, lookupWithDefaultFM
			)
import Id		( mkSameSpecCon, getIdUniType,
			  isDictFunId, isConstMethodId, Id )
import Maybes	
import Outputable
import Pretty
import Util
\end{code}

%************************************************************************
%*									*
\subsection[@specialiseTys@]{Determine specialising types}
%*									*
%************************************************************************

@specialiseCallTys@ works out which type args don't need to be specialised on,
based on flags, the overloading constraint vector, and the types.

\begin{code}
specialiseCallTys :: Bool 		-- Specialise on all type args
		  -> Bool 		-- Specialise on unboxed type args
		  -> Bool		-- Specialise on overloaded type args
		  -> ConstraintVector	-- Tells which type args are overloaded
		  -> [UniType]		-- Type args
		  -> [Maybe UniType]	-- Nothings replace non-specialised type args

specialiseCallTys True _ _ cvec tys
  = map Just tys
specialiseCallTys False spec_unboxed spec_overloading cvec tys
  = zipWith spec_ty_other cvec tys
  where
    spec_ty_other c ty | (spec_unboxed && isUnboxedDataType ty)
		         || (spec_overloading && c)
		         = Just ty
		       | otherwise
                         = Nothing

type ConstraintVector = [Bool]	-- True for constrained tyvar, false otherwise

mkConstraintVector :: [TyVarTemplate] 
		   -> [(Class,TyVarTemplate)]
	           -> ConstraintVector

mkConstraintVector tyvars class_tyvar_pairs
  = [tyvar `elem` constrained_tyvars | tyvar <- tyvars]
  where
    constrained_tyvars   = map snd class_tyvar_pairs 	-- May contain dups
\end{code}

\begin{code}
isUnboxedSpecialisation :: [Maybe UniType] -> Bool
isUnboxedSpecialisation tys
  = any is_unboxed tys
  where
    is_unboxed (Just ty) = isUnboxedDataType ty
    is_unboxed Nothing   = False
\end{code}

@specialiseConstrTys@ works out which type args don't need to be
specialised on. We only speciailise on unboxed types.

\begin{code}
specialiseConstrTys :: [UniType]
		    -> [Maybe UniType]

specialiseConstrTys tys
  = map maybe_unboxed_ty tys
  where
    maybe_unboxed_ty ty = case isUnboxedDataType ty of
			    True  -> Just ty
			    False -> Nothing
\end{code}

\begin{code}
mkSpecialisedCon :: Id -> [UniType] -> Id
mkSpecialisedCon con tys
  = if spec_reqd
    then mkSameSpecCon spec_tys con
    else con
  where
    spec_tys  = specialiseConstrTys tys
    spec_reqd = maybeToBool (firstJust spec_tys)
\end{code}

@argTysMatchSpecTys@ checks if a list of argument types is consistent
with a list of specialising types. An error message is returned if not.
\begin{code}
argTysMatchSpecTys_error :: [Maybe UniType]
			 -> [UniType] 
			 -> Maybe Pretty
argTysMatchSpecTys_error spec_tys arg_tys
  = if match spec_tys arg_tys
    then Nothing
    else Just (ppSep [ppStr "Spec and Arg Types Inconsistent:",
		      ppStr "spectys=", ppSep [pprMaybeTy PprDebug ty | ty <- spec_tys],
		      ppStr "argtys=", ppSep [pprParendUniType PprDebug ty | ty <- arg_tys]])
  where
    match (Nothing:spec_tys) (arg:arg_tys)
      = not (isUnboxedDataType arg) &&
        match spec_tys arg_tys
    match (Just spec:spec_tys) (arg:arg_tys)
      = case (cmpUniType True{-properly-} spec arg) of
          EQ_   -> match spec_tys arg_tys
          other -> False
    match [] [] = True
    match _  _  = False
\end{code}

@pprSpecErrs@ prints error and warning information
about imported specialisations which do not exist.

\begin{code}
pprSpecErrs :: PprStyle
	    -> (Bag (Id,[Maybe UniType]))	-- errors
	    -> (Bag (Id,[Maybe UniType]))	-- warnings
	    -> (Bag (TyCon,[Maybe UniType]))	-- errors
	    -> Pretty

pprSpecErrs sty spec_errs spec_warn spec_tyerrs
  | not any_errs && not any_warn
  = ppNil

  | otherwise
  = ppAboves [if any_errs then ppAboves [
 		  ppStr "SPECIALISATION ERRORS (Essential):",
		  ppAboves (map pp_module_errs use_modules),
		  ppStr "***"
                  ]
	      else
		  ppNil,
	      if any_warn then ppAboves [
 		  ppStr "SPECIALISATION MESSAGES (Desirable):",
		  ppAboves (map pp_module_warn use_modules),
		  ppStr "***"
                  ]
	      else
		  ppNil
	     ]
  where
    any_errs = not (isEmptyBag spec_errs) || not (isEmptyBag spec_tyerrs)
    any_warn = not (isEmptyBag spec_warn)

    mk_module_fm errs_bag
      = addListToFM_C (++) emptyFM errs_list
      where
        errs_list = map add_name (bagToList errs_bag)

    add_name (id, tys) = (mod, [(name, id, tys)])
		       where
			 (mod,name) = getOrigName id

    tyerrs_fm = mk_module_fm spec_tyerrs
    errs_fm   = mk_module_fm spec_errs
    warn_fm   = mk_module_fm spec_warn

    module_names   = concat [keysFM errs_fm, keysFM warn_fm, keysFM tyerrs_fm]
    sorted_modules = map head (equivClasses _CMP_STRING_ module_names)

	-- Ensure any dfun instance specialisations (module _NIL_) are printed last
	-- ToDo: Print instance specialisations with the instance module
	--       This requires the module which defined the instance to be known:
	--	 add_name could then extract the instance module for a dfun id
	--	 and pp_dfun made a special case of pp_err
    use_modules = if (head sorted_modules == _NIL_)
		  then tail sorted_modules ++ [_NIL_]
		  else sorted_modules


    pp_module_errs :: FAST_STRING -> Pretty
    pp_module_errs mod
      | have_errs && mod == _NIL_ 
	-- A _NIL_ module string corresponds to internal Ids
	-- The only ones for which call instances should arise are
	--   dfuns which correspond to instance specialisations
      = ASSERT (null mod_tyerrs)
        ppAboves [
	    ppStr "*** INSTANCES",
	    ppAboves (map (pp_dfun sty) mod_errs)
            ]

      | have_errs
      = ppAboves [
	    pp_module mod,
	    ppAboves (map (pp_err sty) mod_errs),
	    ppAboves (map (pp_tyerr sty) mod_tyerrs)
	    ]

      | otherwise
      = ppNil

      where
        mod_tyerrs = lookupWithDefaultFM tyerrs_fm [] mod
        mod_errs   = lookupWithDefaultFM errs_fm [] mod
	have_errs  = not (null mod_tyerrs) || not (null mod_errs)


    pp_module_warn :: FAST_STRING -> Pretty
    pp_module_warn mod
      | have_warn && mod == _NIL_
	-- A _NIL_ module string corresponds to internal Ids
	-- The only ones for which call instances should arise are
	--   dfuns which correspond to instance specialisations
      = ppAboves [
	    ppStr "*** INSTANCES",
	    ppAboves (map (pp_dfun sty) mod_warn)
            ]

      | have_warn
      = ppAboves [
	    pp_module mod,
	    ppAboves (map (pp_err sty) mod_warn)
            ]

      | otherwise
      = ppNil

      where
        mod_warn  = lookupWithDefaultFM warn_fm [] mod
        have_warn = not (null mod_warn)


pp_module mod
  = ppCat [ppStr "*** module", ppPStr mod, ppStr "***"]


pp_tyerr :: PprStyle -> (FAST_STRING, TyCon, [Maybe UniType]) -> Pretty

pp_tyerr sty (_, tycon, tys)
  = ppCat [ppStr "{-# SPECIALIZE data",
	   pprNonOp sty tycon, ppCat (map (pprParendUniType sty) spec_tys),
	   ppStr "#-}" ]
  where
    tvs = getTyConTyVarTemplates tycon
    (spec_args, tv_maybes) = unzip (map choose_ty (tvs `zip` tys))
    spec_tys = map (mkForallTy (catMaybes tv_maybes)) spec_args

    choose_ty (tv, Nothing) = (mkTyVarTemplateTy tv, Just tv)
    choose_ty (tv, Just ty) = (ty, Nothing)

pp_err sty (_, id, tys)
  = ppCat [ppStr "{-# SPECIALIZE",
	   pprNonOp sty id, ppStr "::",
	   pprUniType sty spec_ty,
	   ppStr "#-}" ]
  where
    spec_ty = specialiseTy (getIdUniType id) tys 100   -- HACK to drop all dicts!!!

pp_dfun sty (_, id, tys)
  | isDictFunId id
  = ppCat [ppStr "{-# SPECIALIZE instance",
	   pprUniType sty spec_ty,
	   ppStr "#-}" ]
  | isConstMethodId id
  = pp_comment sty "OVERLOADED METHOD" id spec_ty
  | otherwise
  = pp_comment sty "HELP ..." id spec_ty
  where
    spec_ty = specialiseTy (getIdUniType id) tys 100   -- HACK to drop all dicts!!!

pp_comment sty msg id spec_ty
  = ppCat [ppStr "{-", ppStr msg,
	   pprNonOp sty id, ppStr "::",
	   pprUniType sty spec_ty,
	   ppStr "-}" ]
\end{code}