Ticket #1038: 1038.patch

File 1038.patch, 23.0 KB (added by simonmar, 8 years ago)
Line 
1
2New patches:
3
4[FIX #1038: failure of selector-thunk machinery to do its job
5Simon Marlow <[email protected]>**20070831152131
6 After a couple of abortive attempts, I think I've got this right.
7 When the GC sees a chain of the form
8 
9    snd (_, snd (_, snd (_, ...)))
10 
11 it can now deal with an arbitrary nesting depth, whereas previously it
12 had a depth limit which was necessitated by the use of recursion.  Now
13 we chain all the selector thunks together in the heap, and go back and
14 update them all when we find the value at the end of the chain.
15 
16 While I was here I removed some old cruft in eval_thunk_selector()
17 which was carefully manintaing invariants that aren't necessary any
18 more, the main one being that evacuate() can safely be passed a
19 to-space pointer now.
20 
21 In addition to validate I've tested building a stage3 compiler with
22 and without +RTS -c, so I'm reasonably sure this is safe.
23 
24] {
25hunk ./rts/sm/Evac.c 27
26-#define MAX_THUNK_SELECTOR_DEPTH 8
27+#define MAX_THUNK_SELECTOR_DEPTH 16
28hunk ./rts/sm/Evac.c 29
29-static StgClosure * eval_thunk_selector ( nat field, StgSelector * p );
30+static StgClosure * eval_thunk_selector (StgSelector * p, rtsBool);
31hunk ./rts/sm/Evac.c 526
32-    {
33-       StgClosure *p;
34-       const StgInfoTable *info_ptr;
35-
36-       if (thunk_selector_depth > MAX_THUNK_SELECTOR_DEPTH) {
37-           return copy(q,THUNK_SELECTOR_sizeW(),stp);
38-       }
39-
40-       // stashed away for LDV profiling, see below
41-       info_ptr = q->header.info;
42-
43-       p = eval_thunk_selector(info->layout.selector_offset,
44-                               (StgSelector *)q);
45-
46-       if (p == NULL) {
47-           return copy(q,THUNK_SELECTOR_sizeW(),stp);
48-       } else {
49-           StgClosure *val;
50-           // q is still BLACKHOLE'd.
51-           thunk_selector_depth++;
52-           val = evacuate(p);
53-           thunk_selector_depth--;
54-
55-#ifdef PROFILING
56-           // For the purposes of LDV profiling, we have destroyed
57-           // the original selector thunk.
58-           SET_INFO(q, info_ptr);
59-           LDV_RECORD_DEAD_FILL_SLOP_DYNAMIC(q);
60-#endif
61-
62-           // Update the THUNK_SELECTOR with an indirection to the
63-           // EVACUATED closure now at p.  Why do this rather than
64-           // upd_evacuee(q,p)?  Because we have an invariant that an
65-           // EVACUATED closure always points to an object in the
66-           // same or an older generation (required by the short-cut
67-           // test in the EVACUATED case, below).
68-           SET_INFO(q, &stg_IND_info);
69-           ((StgInd *)q)->indirectee = p;
70-
71-           // For the purposes of LDV profiling, we have created an
72-           // indirection.
73-           LDV_RECORD_CREATE(q);
74-
75-           return val;
76-       }
77-    }
78+    return eval_thunk_selector((StgSelector *)q, rtsTrue);
79hunk ./rts/sm/Evac.c 649
80-/* -----------------------------------------------------------------------------
81-   Evaluate a THUNK_SELECTOR if possible.
82-
83-   returns: NULL if we couldn't evaluate this THUNK_SELECTOR, or
84-   a closure pointer if we evaluated it and this is the result.  Note
85-   that "evaluating" the THUNK_SELECTOR doesn't necessarily mean
86-   reducing it to HNF, just that we have eliminated the selection.
87-   The result might be another thunk, or even another THUNK_SELECTOR.
88-
89-   If the return value is non-NULL, the original selector thunk has
90-   been BLACKHOLE'd, and should be updated with an indirection or a
91-   forwarding pointer.  If the return value is NULL, then the selector
92-   thunk is unchanged.
93-
94-   ***
95-   ToDo: the treatment of THUNK_SELECTORS could be improved in the
96-   following way (from a suggestion by Ian Lynagh):
97-
98-   We can have a chain like this:
99-
100-      sel_0 --> (a,b)
101-                 |
102-                 |-----> sel_0 --> (a,b)
103-                                    |
104-                                    |-----> sel_0 --> ...
105-
106-   and the depth limit means we don't go all the way to the end of the
107-   chain, which results in a space leak.  This affects the recursive
108-   call to evacuate() in the THUNK_SELECTOR case in evacuate(): *not*
109-   the recursive call to eval_thunk_selector() in
110-   eval_thunk_selector().
111-
112-   We could eliminate the depth bound in this case, in the following
113-   way:
114+static void
115+unchain_thunk_selectors(StgSelector *p, StgClosure *val)
116+{
117+    StgSelector *prev;
118hunk ./rts/sm/Evac.c 654
119-      - traverse the chain once to discover the *value* of the
120-        THUNK_SELECTOR.  Mark all THUNK_SELECTORS that we
121-        visit on the way as having been visited already (somehow).
122+    prev = NULL;
123+    while (p)
124+    {
125+        ASSERT(p->header.info == &stg_BLACKHOLE_info);
126+        prev = (StgSelector*)((StgClosure *)p)->payload[0];
127hunk ./rts/sm/Evac.c 660
128-      - in a second pass, traverse the chain again updating all
129-        THUNK_SEELCTORS that we find on the way with indirections to
130-        the value.
131+        // Update the THUNK_SELECTOR with an indirection to the
132+        // EVACUATED closure now at p.  Why do this rather than
133+        // upd_evacuee(q,p)?  Because we have an invariant that an
134+        // EVACUATED closure always points to an object in the
135+        // same or an older generation (required by the short-cut
136+        // test in the EVACUATED case, below).
137+        SET_INFO(p, &stg_IND_info);
138+        ((StgInd *)p)->indirectee = val;
139hunk ./rts/sm/Evac.c 669
140-      - if we encounter a "marked" THUNK_SELECTOR in a normal
141-        evacuate(), we konw it can't be updated so just evac it.
142+        // For the purposes of LDV profiling, we have created an
143+        // indirection.
144+        LDV_RECORD_CREATE(p);
145hunk ./rts/sm/Evac.c 673
146-   Program that illustrates the problem:
147+        p = prev;
148+    }
149+}
150hunk ./rts/sm/Evac.c 677
151-       foo [] = ([], [])
152-       foo (x:xs) = let (ys, zs) = foo xs
153-                    in if x >= 0 then (x:ys, zs) else (ys, x:zs)
154+/* -----------------------------------------------------------------------------
155+   Evaluate a THUNK_SELECTOR if possible.
156hunk ./rts/sm/Evac.c 680
157-       main = bar [1..(100000000::Int)]
158-       bar xs = (\(ys, zs) -> print ys >> print zs) (foo xs)
159+   p points to a THUNK_SELECTOR that we want to evaluate.  The
160+   result of "evaluating" it will be evacuated and a pointer to the
161+   to-space closure will be returned.
162hunk ./rts/sm/Evac.c 684
163+   If the THUNK_SELECTOR could not be evaluated (its selectee is still
164+   a THUNK, for example), then the THUNK_SELECTOR itself will be
165+   evacuated.
166hunk ./rts/sm/Evac.c 689
167-static inline rtsBool
168-is_to_space ( StgClosure *p )
169-{
170-    bdescr *bd;
171-
172-    bd = Bdescr((StgPtr)p);
173-    if (HEAP_ALLOCED(p) &&
174-       ((bd->flags & BF_EVACUATED)
175-        || ((bd->flags & BF_COMPACTED) &&
176-            is_marked((P_)p,bd)))) {
177-       return rtsTrue;
178-    } else {
179-       return rtsFalse;
180-    }
181-}   
182-
183hunk ./rts/sm/Evac.c 690
184-eval_thunk_selector( nat field, StgSelector * p )
185+eval_thunk_selector (StgSelector * p, rtsBool evac)
186hunk ./rts/sm/Evac.c 692
187+    nat field;
188hunk ./rts/sm/Evac.c 696
189+    StgSelector *prev_thunk_selector;
190+    bdescr *bd;
191+    StgClosure *val;
192hunk ./rts/sm/Evac.c 700
193+    prev_thunk_selector = NULL;
194+    // this is a chain of THUNK_SELECTORs that we are going to update
195+    // to point to the value of the current THUNK_SELECTOR.  Each
196+    // closure on the chain is a BLACKHOLE, and points to the next in the
197+    // chain with payload[0].
198+
199+selector_chain:
200+
201hunk ./rts/sm/Evac.c 714
202+    field = get_itbl(p)->layout.selector_offset;
203hunk ./rts/sm/Evac.c 716
204-    // If the THUNK_SELECTOR is in a generation that we are not
205-    // collecting, then bail out early.  We won't be able to save any
206-    // space in any case, and updating with an indirection is trickier
207-    // in an old gen.
208-    if (Bdescr((StgPtr)p)->gen_no > N) {
209-       return NULL;
210+    // If the THUNK_SELECTOR is in to-space or in a generation that we
211+    // are not collecting, then bail out early.  We won't be able to
212+    // save any space in any case, and updating with an indirection is
213+    // trickier in an old gen.
214+    bd = Bdescr((StgPtr)p);
215+    if (HEAP_ALLOCED(p) &&
216+       ((bd->gen_no > N)
217+         || (bd->flags & BF_EVACUATED)
218+        || ((bd->flags & BF_COMPACTED) &&
219+            is_marked((P_)p,bd)))) {
220+        unchain_thunk_selectors(prev_thunk_selector, (StgClosure *)p);
221+        return (StgClosure *)p;
222hunk ./rts/sm/Evac.c 736
223-
224-    // We don't want to end up in to-space, because this causes
225-    // problems when the GC later tries to evacuate the result of
226-    // eval_thunk_selector().  There are various ways this could
227-    // happen:
228-    //
229-    // 1. following an IND_STATIC
230-    //
231-    // 2. when the old generation is compacted, the mark phase updates
232-    //    from-space pointers to be to-space pointers, and we can't
233-    //    reliably tell which we're following (eg. from an IND_STATIC).
234-    //
235-    // 3. compacting GC again: if we're looking at a constructor in
236-    //    the compacted generation, it might point directly to objects
237-    //    in to-space.  We must bale out here, otherwise doing the selection
238-    //    will result in a to-space pointer being returned.
239-    //
240-    //  (1) is dealt with using a BF_EVACUATED test on the
241-    //  selectee. (2) and (3): we can tell if we're looking at an
242-    //  object in the compacted generation that might point to
243-    //  to-space objects by testing that (a) it is BF_COMPACTED, (b)
244-    //  the compacted generation is being collected, and (c) the
245-    //  object is marked.  Only a marked object may have pointers that
246-    //  point to to-space objects, because that happens when
247-    //  scavenging.
248-    //
249-    //  The to-space test is now embodied in the in_to_space() inline
250-    //  function, as it is re-used below.
251-    //
252-    if (is_to_space(selectee)) {
253-       goto bale_out;
254-    }
255+    // selectee now points to the closure that we're trying to select
256+    // a field from.  It may or may not be in to-space: we try not to
257+    // end up in to-space, but it's impractical to avoid it in
258+    // general.  The compacting GC scatters to-space pointers in
259+    // from-space during marking, for example.  We rely on the property
260+    // that evacuate() doesn't mind if it gets passed a to-space pointer.
261hunk ./rts/sm/Evac.c 753
262-         // check that the size is in range
263-         ASSERT(field <  (StgWord32)(info->layout.payload.ptrs +
264-                                     info->layout.payload.nptrs));
265+          {
266+              // check that the size is in range
267+              ASSERT(field <  (StgWord32)(info->layout.payload.ptrs +
268+                                          info->layout.payload.nptrs));
269hunk ./rts/sm/Evac.c 758
270-         // Select the right field from the constructor, and check
271-         // that the result isn't in to-space.  It might be in
272-         // to-space if, for example, this constructor contains
273-         // pointers to younger-gen objects (and is on the mut-once
274-         // list).
275-         //
276-         {
277-             StgClosure *q;
278-             q = selectee->payload[field];
279-             if (is_to_space(UNTAG_CLOSURE(q))) {
280-                 goto bale_out;
281-             } else {
282-                 return q;
283-             }
284-         }
285+              // Select the right field from the constructor
286+              val = selectee->payload[field];
287+             
288+#ifdef PROFILING
289+              // For the purposes of LDV profiling, we have destroyed
290+              // the original selector thunk, p.
291+              SET_INFO(p, info_ptr);
292+              LDV_RECORD_DEAD_FILL_SLOP_DYNAMIC(p);
293+              SET_INFO(p, &stg_BLACKHOLE_info);
294+#endif
295+
296+              // the closure in val is now the "value" of the
297+              // THUNK_SELECTOR in p.  However, val may itself be a
298+              // THUNK_SELECTOR, in which case we want to continue
299+              // evaluating until we find the real value, and then
300+              // update the whole chain to point to the value.
301+          val_loop:
302+              info = get_itbl(UNTAG_CLOSURE(val));
303+              switch (info->type) {
304+              case IND:
305+              case IND_PERM:
306+              case IND_OLDGEN:
307+              case IND_OLDGEN_PERM:
308+              case IND_STATIC:
309+                  val = ((StgInd *)val)->indirectee;
310+                  goto val_loop;
311+              case THUNK_SELECTOR:
312+                  ((StgClosure*)p)->payload[0] = (StgClosure *)prev_thunk_selector;
313+                  prev_thunk_selector = p;
314+                  p = (StgSelector*)val;
315+                  goto selector_chain;
316+              default:
317+                  ((StgClosure*)p)->payload[0] = (StgClosure *)prev_thunk_selector;
318+                  prev_thunk_selector = p;
319+
320+                  if (evac) val = evacuate(val);
321+                  // evacuate() cannot recurse through
322+                  // eval_thunk_selector(), because we know val is not
323+                  // a THUNK_SELECTOR.
324+                  unchain_thunk_selectors(prev_thunk_selector, val);
325+                  return val;
326+              }
327+          }
328hunk ./rts/sm/Evac.c 807
329-       // Again, we might need to untag a constructor.
330-       selectee = UNTAG_CLOSURE( ((StgInd *)selectee)->indirectee );
331+          // Again, we might need to untag a constructor.
332+          selectee = UNTAG_CLOSURE( ((StgInd *)selectee)->indirectee );
333hunk ./rts/sm/Evac.c 815
334-         break;
335+         goto bale_out;
336hunk ./rts/sm/Evac.c 821
337-         // check that we don't recurse too much, re-using the
338-         // depth bound also used in evacuate().
339-         if (thunk_selector_depth >= MAX_THUNK_SELECTOR_DEPTH) {
340-             break;
341-         }
342-
343hunk ./rts/sm/Evac.c 823
344-         // that result, this causes confusion later.
345+         // that result, this causes confusion later
346+         // (scavenge_mark_stack doesn't deal with IND).
347hunk ./rts/sm/Evac.c 826
348-             break;
349+             goto bale_out;
350hunk ./rts/sm/Evac.c 829
351-         thunk_selector_depth++;
352-
353-         val = eval_thunk_selector(info->layout.selector_offset,
354-                                   (StgSelector *)selectee);
355+          // recursively evaluate this selector.  We don't want to
356+          // recurse indefinitely, so we impose a depth bound.
357+         if (thunk_selector_depth >= MAX_THUNK_SELECTOR_DEPTH) {
358+             goto bale_out;
359+         }
360hunk ./rts/sm/Evac.c 835
361+         thunk_selector_depth++;
362+          // rtsFalse says "don't evacuate the result".  It will,
363+          // however, update any THUNK_SELECTORs that are evaluated
364+          // along the way.
365+         val = eval_thunk_selector((StgSelector *)selectee, rtsFalse);
366hunk ./rts/sm/Evac.c 842
367-         if (val == NULL) {
368-             break;
369-         } else {
370-             // We evaluated this selector thunk, so update it with
371-             // an indirection.  NOTE: we don't use UPD_IND here,
372-             // because we are guaranteed that p is in a generation
373-             // that we are collecting, and we never want to put the
374-             // indirection on a mutable list.
375-#ifdef PROFILING
376-             // For the purposes of LDV profiling, we have destroyed
377-             // the original selector thunk.
378-             SET_INFO(selectee, info_ptr);
379-             LDV_RECORD_DEAD_FILL_SLOP_DYNAMIC(selectee);
380-#endif
381-             ((StgInd *)selectee)->indirectee = val;
382-             SET_INFO(selectee,&stg_IND_info);
383-
384-             // For the purposes of LDV profiling, we have created an
385-             // indirection.
386-             LDV_RECORD_CREATE(selectee);
387+          // did we actually manage to evaluate it?
388+          if (val == selectee) goto bale_out;
389hunk ./rts/sm/Evac.c 845
390-             // Of course this pointer might be tagged
391-             selectee = UNTAG_CLOSURE(val);
392-             goto selector_loop;
393-         }
394+          // Of course this pointer might be tagged...
395+          selectee = UNTAG_CLOSURE(val);
396+          goto selector_loop;
397hunk ./rts/sm/Evac.c 864
398-         break;
399+         goto bale_out;
400hunk ./rts/sm/Evac.c 872
401-    // We didn't manage to evaluate this thunk; restore the old info pointer
402+    // We didn't manage to evaluate this thunk; restore the old info
403+    // pointer.  But don't forget: we still need to evacuate the thunk itself.
404hunk ./rts/sm/Evac.c 875
405-    return NULL;
406+    if (evac) {
407+        val = copy((StgClosure *)p,THUNK_SELECTOR_sizeW(),bd->step->to);
408+    } else {
409+        val = (StgClosure *)p;
410+    }
411+    unchain_thunk_selectors(prev_thunk_selector, val);
412+    return val;
413}
414
415Context:
416
417[remove "special Ids" section, replace with a link to GHC.Prim
418Simon Marlow <[email protected]>**20070830112139
419 This documentation was just duplicating what is in GHC.Prim now.
420]
421[expand docs for unsafeCoerce#, as a result of investigations for #1616
422Simon Marlow <[email protected]>**20070830111909]
423[Remove text about ghcprof.  It almost certainly doesn't work.
424Simon Marlow <[email protected]>**20070829122126]
425[fix compiling GHC 6.7+ with itself - compat needs -package containers now
426Simon Marlow <[email protected]>**20070829113500]
427[fix typo
428Simon Marlow <[email protected]>**20070824141039]
429[Wibble
430Pepe Iborra <[email protected]>**20070829085305]
431[FIX: Remove accidential change to darcs-all in type families patch
432Manuel M T Chakravarty <[email protected]>**20070829010011
433 - The type families patch includes a change to darcs-all that breaks it for
434   ssh repos at least for Perl 5.8.8 (on MacOS).
435 - My Perl-fu is not sufficient to try to fix the modification, which was
436   supposed to improve darcs-all on windows, so I just revert to the old
437   code.
438]
439[Remove INSTALL_INCLUDES; no longer used
440Ian Lynagh <[email protected]>**20070828205636]
441[Use DESTDIR when installing
442Ian Lynagh <[email protected]>**20070828205119]
443[Copy LICENSE files into the bindist, as Cabal now installs them
444Ian Lynagh <[email protected]>**20070828130428]
445[TAG 2007-08-28
446Ian Lynagh <[email protected]>**20070828215445]
447[Better handling of join points in spill cleaner
448[email protected]**20070828133050]
449[comment wibble
450[email protected]**20070824160314]
451[Add count of reg-reg moves remaining for linear allocator stats
452[email protected]**20070824155732]
453[Add a count of how many spill/reloads/reg-reg-moves remain to dump-asm-stats
454[email protected]**20070824152142]
455[Use INSTALL_HEADERS in includes/ rather than abusing INSTALL_DATAS
456Ian Lynagh <[email protected]>**20070828124918]
457[Make some header-installing machinery, and use it to install gmp.h
458Ian Lynagh <[email protected]>**20070828124759]
459[Tidy up the gmp/ Makefile a bit
460Ian Lynagh <[email protected]>**20070828123218]
461[:stepover ---> :steplocal, :stepmodule
462Pepe Iborra <[email protected]>**20070827180949
463 
464  :stepover is declared a failed experiment.
465 :steplocal steps only on ticks contained in the current
466 top level declaration.
467 :stepmodule steps only on ticks contained on the current
468  module.
469 The current top level declaration and module are with
470  respect to the breakpoint we are stopped on.
471 
472  The main reason for the failure of :stepover
473 (apart from lacking a lexical call stack of course)
474 is that it fails to detect when the expression being
475 evaluated is "complete", i.e. there are no ticks left in it.
476 My assumption of the rightmost tick as the "last one",
477 signaling that the expression is completely evaluated,
478 is not true at all under laziness.
479 This assumption was key in the implementation of :stepover.
480 
481]
482[Use a version of obtainTerm that takes a max depth bound
483Pepe Iborra <[email protected]>**20070827172315
484 when printing the contents of binding at a breakpoint
485]
486[Be more careful when calculating the enclosing top level declaration of a srcspan
487Pepe Iborra <[email protected]>**20070827114437]
488[Style: remove trailing spaces
489Pepe Iborra <[email protected]>**20070826213357]
490[Print contents of bindings when stopping at a breakpoint
491Pepe Iborra <[email protected]>**20070826213339]
492[A partial attempt to improve :stepover
493Pepe Iborra <[email protected]>*-20070824134022
494   
495   With this patch, :stepover can effectively appear to step over recursive calls and
496   calls to locally bound functions (in a where clause).
497   
498   However, when we run out of ticks in the current expression,
499   the illusion vanishes and laziness brings us to the body of the last function
500   we "stepped over".
501   This is not desired at all, it is potentially very confusing.
502   As a countermeasure, when this happens :stepover emits a warning
503 
504      "Warning: no more breakpoints in this function body, switching to :step" 
505 
506]
507[Better document :stepover and its limitations
508Pepe Iborra <[email protected]>*-20070822084254
509 
510 :stepover only works lexically locally, in the context of the
511 current expression. I have tried to make this point clear
512 in the users guide with an example.
513 
514]
515[no -auto-all for CorePrep
516Simon Marlow <[email protected]>**20070829092414]
517[improvements to findPtr(), a useful hack for space-leak debugging in gdb
518Simon Marlow <[email protected]>**20070829092400]
519[fix up some old text, remove things that aren't true any more
520Simon Marlow <[email protected]>**20070828125821]
521[FIX #1533: foreign exporing the same identifier multiple times gave a link error
522Simon Marlow <[email protected]>**20070828091550
523 We were generating a new top-level binding derived from the name of
524 the existing top-level name, and making the name external.  Multiple
525 instances therefore clashed.  The fix is to make each name unique, by
526 appending an actual Unique to the derived name.
527]
528[convert to use System.FilePath
529Simon Marlow <[email protected]>**20070826151903]
530[Refactoring only: remove [Id] field from ForeignStubs
531Simon Marlow <[email protected]>**20070826073322
532 We used to pass the list of top-level foreign exported bindings to the
533 code generator so that it could create StablePtrs for them in the
534 stginit code.  Now we don't use stginit unless profiling, and the
535 StablePtrs are generated by C functions marked with
536 attribute((constructor)).  This patch removes various bits associated
537 with the old way of doing things, which were previously left in place
538 in case we wanted to switch back, I presume.  Also I refactored
539 dsForeigns to clean it up a bit.
540]
541[Type checking for type synonym families
542Manuel M T Chakravarty <[email protected]>**20070828061851
543 
544 This patch introduces type checking for type families of which associated
545 type synonyms are a special case. E.g.
546 
547         type family Sum n m
548 
549         type instance Sum Zero n = n
550         type instance Sum (Succ n) m = Succ (Sum n m)
551 
552 where
553 
554         data Zero       -- empty type
555         data Succ n     -- empty type
556 
557 In addition we support equational constraints of the form:
558 
559         ty1 ~ ty2
560 
561 (where ty1 and ty2 are arbitrary tau types) in any context where
562 type class constraints are already allowed, e.g.
563 
564         data Equals a b where
565                 Equals :: a ~ b => Equals a b
566 
567 The above two syntactical extensions are disabled by default. Enable
568 with the -XTypeFamilies flag.
569 
570 For further documentation about the patch, see:
571 
572         * the master plan
573           http://hackage.haskell.org/trac/ghc/wiki/TypeFunctions
574 
575         * the user-level documentation
576           http://haskell.org/haskellwiki/GHC/Indexed_types
577 
578 The patch is mostly backwards compatible, except for:
579 
580         * Some error messages have been changed slightly.
581 
582         * Type checking of GADTs now requires a bit more type declarations:
583           not only should the type of a GADT case scrutinee be given, but also
584           that of any identifiers used in the branches and the return type.
585 
586 Please report any unexpected behavior and incomprehensible error message
587 for existing code.
588 
589 Contributors (code and/or ideas):
590         Tom Schrijvers
591         Manuel Chakravarty
592         Simon Peyton-Jones
593         Martin Sulzmann
594 with special thanks to Roman Leshchinskiy
595]
596[TAG Before type family merge
597Manuel M T Chakravarty <[email protected]>**20070828011143]
598Patch bundle hash:
5997bafc781baf6fc82ce630230743e3b6a1d5d3d41