diff --git a/src/gc.c b/src/gc.c
index 7e412b74a2b872d37d7a75906766da90763e579d..c8e6fa239ca5a4b4d3b4e084915c67497324d5e3 100644
--- a/src/gc.c
+++ b/src/gc.c
@@ -29,7 +29,7 @@ struct callback *gc_evaluator_callback=0;
 
 #include "block_alloc.h"
 
-RCSID("$Id: gc.c,v 1.94 2000/06/12 19:35:08 mast Exp $");
+RCSID("$Id: gc.c,v 1.95 2000/06/12 21:41:41 mast Exp $");
 
 /* Run garbage collect approximately every time
  * 20 percent of all arrays, objects and programs is
@@ -114,10 +114,10 @@ static unsigned last_cycle;
  * cyclic reference at a chosen point. Some markers thus get a place
  * earlier on the list even though their corresponding stack frames
  * are later than some other markers they have references to. These
- * markers are given the GC_DONT_POP flag to make them stay on the
- * list when they're popped from the stack. They'll get popped
- * eventually since all markers in the gap between the top one and
- * it's previous gc_rec_last point are popped.
+ * markers are flagged to make them stay on the list when they're
+ * popped from the stack. They'll get popped eventually since all
+ * markers in the gap between the top one and it's previous
+ * gc_rec_last point are popped.
  *
  * Markers for live objects are linked into the beginning of kill_list
  * when they're popped from rec_list.
@@ -408,7 +408,7 @@ void describe_location(void *real_memblock,
 static void describe_marker(struct marker *m)
 {
   if (m)
-    fprintf(stderr, "marker at %p: flags=0x%04x, refs=%d, weak=%d, "
+    fprintf(stderr, "marker at %p: flags=0x%06x, refs=%d, weak=%d, "
 	    "xrefs=%d, cycle=%d, link=%p\n",
 	    m, m->flags, m->refs, m->weak_refs,
 	    m->xrefs, m->cycle, m->link);
@@ -747,8 +747,8 @@ void debug_gc_touch(void *a)
       if (!(m->flags & GC_TOUCHED))
 	gc_fatal(a, 2, "An existing but untouched marker found "
 		 "for object in linked lists.\n");
-      else if (m->flags & (GC_RECURSING|GC_LIVE_RECURSE|GC_DONT_POP|
-			   GC_WEAK_REF|GC_STRONG_REF))
+      else if (m->flags & (GC_ON_STACK|GC_IN_REC_LIST|GC_DONT_POP|
+			   GC_LIVE_RECURSE|GC_WEAK_REF|GC_STRONG_REF))
 	gc_fatal(a, 2, "Marker still got flag from recurse list.\n");
       else if (m->flags & GC_REFERENCED)
 	return;
@@ -1057,7 +1057,6 @@ static void break_cycle (struct marker *beg, struct marker *pos)
   /* There's a cycle from beg to gc_rec_last which should be broken at
    * pos. Do it by switching places of the markers before and after pos. */
   struct marker *p, *q;
-  CYCLE_DEBUG_MSG(pos, "break_cycle");
 #ifdef PIKE_DEBUG
   if (beg == pos)
     gc_fatal(beg->data, 0, "Cycle already broken at requested position.\n");
@@ -1074,12 +1073,15 @@ static void break_cycle (struct marker *beg, struct marker *pos)
   else
     for (p = &rec_list; p->link != beg; p = p->link) {}
 
+  CYCLE_DEBUG_MSG(beg, "break_cycle, break from");
+  CYCLE_DEBUG_MSG(pos, "break_cycle, break at");
+
   p->link = pos;
 
   for (p = beg; p->link != pos; p = p->link) {}
 
   for (q = pos;; q = q->link) {
-    q->flags |= GC_DONT_POP;
+    q->flags |= GC_MOVED_BACK|GC_DONT_POP;
     CYCLE_DEBUG_MSG(q, "break_cycle, mark for don't pop");
     if (q == gc_rec_last) break;
   }
@@ -1178,7 +1180,7 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
   if (weak >= 0) gc_rec_last->flags |= GC_FOLLOWED_NONSTRONG;
 #endif
 
-  if (m->flags & GC_RECURSING) { /* A cyclic reference is found. */
+  if (m->flags & GC_IN_REC_LIST) { /* A cyclic reference is found. */
 #ifdef PIKE_DEBUG
     if (m == &rec_list || gc_rec_last == &rec_list)
       gc_fatal(x, 0, "Cyclic ref involves dummy rec_list marker.\n");
@@ -1259,21 +1261,16 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
 	      if (p == gc_rec_last) break;
 	    }}}}		/* Mmm.. lisp ;) */
 
-      else {			/* A forward reference. */
-	/* It might be a reference to a marker that has been swapped
-	 * further down the list by break_cycle(). In that case we
-	 * must mark this path to stay on the list. */
-	struct marker *q = 0;
-	CYCLE_DEBUG_MSG(m, "gc_cycle_push, forward ref");
-	for (p = rec_list.link; p != gc_rec_last; p = p->link)
-	  if (p->flags & GC_DONT_POP) q = p;
-	if (q)
-	  for (p = q->link;; p = p->link) {
-	    p->flags |= GC_DONT_POP;
-	    CYCLE_DEBUG_MSG(p, "gc_cycle_push, mark for don't pop");
-	    if (p == gc_rec_last) break;
-	  }
-      }
+      else			/* A forward reference. */
+	if (m->flags & GC_ON_STACK) {
+	  /* It's a reference to a marker that has been swapped
+	   * further down the list by break_cycle(). In that case we
+	   * must mark gc_rec_last to stay on the list. */
+	  CYCLE_DEBUG_MSG(m, "gc_cycle_push, mark for don't pop");
+	  gc_rec_last->flags |= GC_DONT_POP;
+	}
+	else
+	  CYCLE_DEBUG_MSG(m, "gc_cycle_push, forward ref");
     }
   }
 
@@ -1287,6 +1284,8 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
       }
 #ifdef PIKE_DEBUG
       cycle_checked++;
+      if (m->flags & GC_ON_STACK)
+	gc_fatal(x, 0, "Recursing twice into thing.\n");
       if (m->flags & GC_LIVE_RECURSE)
 	gc_fatal(x, 0, "GC_LIVE_RECURSE set in normal recursion.\n");
 #endif
@@ -1296,7 +1295,7 @@ int gc_cycle_push(void *x, struct marker *m, int weak)
 	for (; p->link && p->link->cycle == gc_rec_last->cycle; p = p->link) {}
       m->link = p->link;
       p->link = m;
-      m->flags |= GC_CYCLE_CHECKED|GC_RECURSING;
+      m->flags |= GC_CYCLE_CHECKED|GC_ON_STACK|GC_IN_REC_LIST;
       m->cycle = 0;
 
 #ifdef GC_CYCLE_DEBUG
@@ -1376,13 +1375,15 @@ void gc_cycle_pop(void *a)
   gc_cycle_indent -= 2;
 #endif
 
-  if (!(m->flags & GC_RECURSING) || m->flags & GC_LIVE_RECURSE) {
+  if (m->flags & GC_LIVE_RECURSE) {
     m->flags &= ~GC_LIVE_RECURSE;
-    CYCLE_DEBUG_MSG(m, "gc_cycle_pop, pop ignored");
+    CYCLE_DEBUG_MSG(m, "gc_cycle_pop, live pop");
     return;
   }
 
 #ifdef PIKE_DEBUG
+  if (!(m->flags & GC_ON_STACK))
+    gc_fatal(a, 0, "Marker being popped isn't on stack.\n");
   if (m->flags & GC_GOT_DEAD_REF)
     gc_fatal(a, 0, "Didn't expect a dead extra ref.\n");
 #endif
@@ -1393,6 +1394,10 @@ void gc_cycle_pop(void *a)
       gc_fatal(a, 0, "Found GC_DONT_POP marker on top of stack.\n");
 #endif
     CYCLE_DEBUG_MSG(m, "gc_cycle_pop, keep on stack");
+    if (!(m->flags & GC_MOVED_BACK)) {
+      gc_rec_last->flags |= GC_DONT_POP;
+      CYCLE_DEBUG_MSG(gc_rec_last, "gc_cycle_pop, propagate don't pop");
+    }
     return;
   }
 
@@ -1406,14 +1411,16 @@ void gc_cycle_pop(void *a)
 
     if (!p->cycle && !(p->flags & GC_LIVE_OBJ)) {
       ADD_REF_IF_DEAD(p);
-      p->flags &= ~(GC_RECURSING|GC_DONT_POP|GC_WEAK_REF|GC_STRONG_REF);
+      p->flags &= ~(GC_ON_STACK|GC_IN_REC_LIST|GC_DONT_POP|
+		    GC_WEAK_REF|GC_STRONG_REF);
       q->link = p->link;
       DO_IF_DEBUG(p->link = (struct marker *) -1);
       CYCLE_DEBUG_MSG(p, "gc_cycle_pop, pop off");
     }
 
     else {
-      p->flags &= ~(GC_DONT_POP|GC_WEAK_REF|GC_STRONG_REF);
+      p->flags &= ~(GC_ON_STACK|GC_DONT_POP|
+		    GC_WEAK_REF|GC_STRONG_REF);
       q = p;
     }
     if (p == m) break;
@@ -1439,12 +1446,12 @@ void gc_cycle_pop(void *a)
 #ifdef PIKE_DEBUG
 	if (p->cycle && cycle && p->cycle != cycle)
 	  gc_fatal(p->data, 0, "Popping more than one cycle from rec_list.\n");
-	if (!(p->flags & GC_RECURSING))
-	  gc_fatal(p->data, 0, "Marker being cycle popped doesn't have GC_RECURSING.\n");
+	if (!(p->flags & GC_IN_REC_LIST))
+	  gc_fatal(p->data, 0, "Marker being cycle popped doesn't have GC_IN_REC_LIST.\n");
 	if (p->flags & GC_GOT_DEAD_REF)
 	  gc_fatal(p->data, 0, "Didn't expect a dead extra ref.\n");
 #endif
-	p->flags &= ~GC_RECURSING;
+	p->flags &= ~GC_IN_REC_LIST;
 
 	if (p->flags & GC_LIVE_OBJ) {
 	  /* This extra ref is taken away in the kill pass. */
diff --git a/src/gc.h b/src/gc.h
index 46934f1c8957fe1d271daa4c03b609e87b95fb01..74acc78ba2bfc2c73749d4309dd8d1808d4a023b 100644
--- a/src/gc.h
+++ b/src/gc.h
@@ -1,5 +1,5 @@
 /*
- * $Id: gc.h,v 1.49 2000/06/12 19:33:27 mast Exp $
+ * $Id: gc.h,v 1.50 2000/06/12 21:41:41 mast Exp $
  */
 #ifndef GC_H
 #define GC_H
@@ -70,27 +70,33 @@ struct marker
   INT32 xrefs;			/* Known external references. */
 #endif
   unsigned INT16 cycle;		/* Cycle id number. */
+#ifdef PIKE_DEBUG
+  unsigned INT32 flags;
+#else
   unsigned INT16 flags;
+#endif
 };
 
 #define GC_REFERENCED		0x0001
 #define GC_CYCLE_CHECKED	0x0002
 #define GC_LIVE			0x0004
 #define GC_LIVE_OBJ		0x0008
-#define GC_GOT_DEAD_REF		0x0010
-#define GC_RECURSING		0x0020
-#define GC_DONT_POP		0x0040
-#define GC_LIVE_RECURSE		0x0080
-#define GC_WEAK_REF		0x0100
-#define GC_STRONG_REF		0x0200
+#define GC_ON_STACK		0x0010
+#define GC_IN_REC_LIST		0x0020
+#define GC_MOVED_BACK		0x0040
+#define GC_DONT_POP		0x0080
+#define GC_LIVE_RECURSE		0x0100
+#define GC_WEAK_REF		0x0200
+#define GC_STRONG_REF		0x0400
+#define GC_GOT_DEAD_REF		0x0800
 
 /* Debug mode flags. */
-#define GC_TOUCHED		0x0400
-#define GC_IS_REFERENCED	0x0800
-#define GC_XREFERENCED		0x1000
-#define GC_DO_FREE		0x2000
-#define GC_GOT_EXTRA_REF	0x4000
-#define GC_FOLLOWED_NONSTRONG	0x8000
+#define GC_TOUCHED		0x010000
+#define GC_IS_REFERENCED	0x020000
+#define GC_XREFERENCED		0x040000
+#define GC_DO_FREE		0x080000
+#define GC_GOT_EXTRA_REF	0x100000
+#define GC_FOLLOWED_NONSTRONG	0x200000
 
 #include "block_alloc_h.h"
 PTR_HASH_ALLOC(marker,MARKER_CHUNK_SIZE)
diff --git a/src/testsuite.in b/src/testsuite.in
index 07f06450c8b5f9c3aa0a494762cc76cc99ee0de4..9164f631e5d0726d3541a060f9ad166629e74679 100644
--- a/src/testsuite.in
+++ b/src/testsuite.in
@@ -1,4 +1,4 @@
-test_true([["$Id: testsuite.in,v 1.301 2000/06/12 13:52:32 mast Exp $"]]);
+test_true([["$Id: testsuite.in,v 1.302 2000/06/12 21:41:41 mast Exp $"]]);
 
 cond([[all_constants()->_verify_internals]],
 [[
@@ -1698,15 +1698,6 @@ ifefun(gc,
       return gc()>0;
   ]],1)
 
-  test_any([[mapping q=([ "t":class {} ()]); gc(); if(!objectp(q->t)) return -1; set_weak_flag(q,1); gc(); if(objectp(q->t)) return -2; return 0;]],0);
-
-  test_do([[class bar { object foo; void create(void|object tmp) { foo=tmp; } };
-             object o=bar(),o2=o;
-             for(int e=0;e<10000;e++) o=bar(o);
-             o2->foo=o;
-             o=o2=0;
-             gc();
-          ]])
   test_true([[
     object o = class{}();
     mapping m = ([class{}(): o, o: class{}()]);
@@ -2330,6 +2321,18 @@ ifefun(gc,
 		      live[1]->x = live[0];
 		      dead[0]->x = dead[0];
 		    }}),
+      ({1, 2, 0, 2, lambda() {	// 60
+		      live[0]->x = dead_nested[0], live[0]->y = dead_nested[0];
+		      dead[0]->x = dead[0], dead[0]->y = dead_nested[0];
+		      dead[1]->x = dead[1], dead[1]->y = dead_nested[1];
+		      dead_nested[0]->x = live[0], dead_nested[0]->y = dead_nested[1];
+		    }}),
+      ({1, 2, 0, 2, lambda() {	// 61
+		      live[0]->x = dead_nested[0], live[0]->y = dead_nested[0];
+		      dead[0]->x = dead[0], dead[0]->y = dead_nested[0];
+		      dead[1]->x = dead[1], dead[1]->y = dead_nested[1];
+		      dead_nested[0]->y = live[0], dead_nested[0]->x = dead_nested[1];
+		    }}),
       //       ({3, 0, 0, 0, lambda() { // Not possible without weak refs directly in objects.
       // 		   live[0]->x = live[0], live[0]->v[0] = live[1];
       // 		   live[1]->x = live[1], live[1]->w[0] = live[2];
@@ -2398,6 +2401,16 @@ ifefun(gc,
     werror ("%60s\r", "");
     if (test_failed) error ("GC destruct order test failed.\n");
   }]])
+
+  test_any([[mapping q=([ "t":class {} ()]); gc(); if(!objectp(q->t)) return -1; set_weak_flag(q,1); gc(); if(objectp(q->t)) return -2; return 0;]],0);
+
+  test_do([[class bar { object foo; void create(void|object tmp) { foo=tmp; } };
+             object o=bar(),o2=o;
+             for(int e=0;e<10000;e++) o=bar(o);
+             o2->foo=o;
+             o=o2=0;
+             gc();
+          ]])
 ]])
 
 cond([[ sizeof( cpp("__AUTO_BIGNUM__")/"__AUTO_BIGNUM__" ) == 1 ]],