diff --git a/.gitattributes b/.gitattributes
index 19660e09079d92f72713053dbeb5c05dc32549b0..af298f0fb566cb8038e6e3bb193e697c61e61a59 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -338,7 +338,6 @@ testfont binary
 /src/aclocal.m4 foreign_ident
 /src/alloca.c foreign_ident
 /src/apply_low.h foreign_ident
-/src/array.c foreign_ident
 /src/array.h foreign_ident
 /src/backend.h foreign_ident
 /src/bignum.c foreign_ident
diff --git a/src/array.c b/src/array.c
index 0c8afb300b7fd76a891e736a20eb7bfeb857f026..e2aba3abc6c6e6523b3556d6cde8d8ff0b325f6b 100644
--- a/src/array.c
+++ b/src/array.c
@@ -2,7 +2,7 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: array.c,v 1.229 2010/07/11 12:39:11 jonasw Exp $
+|| $Id$
 */
 
 #include "global.h"
@@ -910,11 +910,12 @@ INT32 *get_order(struct array *v, cmpfun fun)
   return current_order;
 }
 
-/* Returns 2 if no relation is established through lfun calls. */
-static INLINE int lfun_cmp (const struct svalue *a, const struct svalue *b)
+/* Returns 2 if no relation is established through lfun calls, or 3 if
+ * no order defining lfuns (i.e. `< or `>) were found. */
+static int lfun_cmp (const struct svalue *a, const struct svalue *b)
 {
   struct program *p;
-  int fun;
+  int default_res = 3, fun;
 
   if (a->type == T_OBJECT && (p = a->u.object->prog)) {
     if ((fun = FIND_LFUN(p->inherits[a->subtype].prog, LFUN_LT)) != -1) {
@@ -927,6 +928,7 @@ static INLINE int lfun_cmp (const struct svalue *a, const struct svalue *b)
 	return -1;
       }
       pop_stack();
+      default_res = 2;
     }
 
     if ((fun = FIND_LFUN(p->inherits[a->subtype].prog, LFUN_GT)) != -1) {
@@ -939,6 +941,7 @@ static INLINE int lfun_cmp (const struct svalue *a, const struct svalue *b)
 	return 1;
       }
       pop_stack();
+      default_res = 2;
     }
 
     if ((fun = FIND_LFUN(p->inherits[a->subtype].prog, LFUN_EQ)) != -1) {
@@ -964,6 +967,7 @@ static INLINE int lfun_cmp (const struct svalue *a, const struct svalue *b)
 	return 1;
       }
       pop_stack();
+      default_res = 2;
     }
 
     if ((fun = FIND_LFUN(p->inherits[b->subtype].prog, LFUN_GT)) != -1) {
@@ -976,6 +980,7 @@ static INLINE int lfun_cmp (const struct svalue *a, const struct svalue *b)
 	return -1;
       }
       pop_stack();
+      default_res = 2;
     }
 
     if ((fun = FIND_LFUN(p->inherits[b->subtype].prog, LFUN_EQ)) != -1) {
@@ -990,182 +995,209 @@ static INLINE int lfun_cmp (const struct svalue *a, const struct svalue *b)
     }
   }
 
-  return 2;
+  return default_res;
 }
 
-int set_svalue_cmpfun(const struct svalue *a, const struct svalue *b)
+static int obj_or_func_cmp (const struct svalue *a, const struct svalue *b)
+/* Call with either two T_OBJECT or two T_FUNCTION. */
 {
-  int res, typediff = a->type - b->type;
+  int a_subtype, b_subtype, res;
+  struct svalue tmp_a, tmp_b;
 
-  if (!typediff)
-  {
-    switch(a->type)
-    {
-      case T_FLOAT:
-	if(a->u.float_number < b->u.float_number) return -1;
-	if(a->u.float_number > b->u.float_number) return 1;
-	return 0;
-	
-      case T_FUNCTION:
-	if(a->u.refs < b->u.refs) return -1;
-	if(a->u.refs > b->u.refs) return 1;
-	return a->subtype - b->subtype;
-	
-      case T_INT:
-	if(a->u.integer < b->u.integer) return -1;
-	if(a->u.integer > b->u.integer) return 1;
-	return 0;
+  assert ((a->type == T_OBJECT && b->type == T_OBJECT) ||
+	  (a->type == T_FUNCTION && b->type == T_FUNCTION));
 
-      default:
-	if(a->u.refs < b->u.refs) return -1;
-	if(a->u.refs > b->u.refs) return 1;
-	return 0;
+  if (a->u.object == b->u.object)
+    return a->subtype - b->subtype;
 
-      case T_OBJECT:
-	if((a->u.object == b->u.object) && (a->subtype == b->subtype)) {
-	  return 0;
-	}
-	break;
-    }
+  /* Destructed objects are considered equal to each other, and
+   * (arbitrarily chosen) greater than others. */
+  if (!a->u.object->prog)
+    return !b->u.object->prog ? 0 : 1;
+  else if (!b->u.object->prog)
+    return -1;
+
+  if (a->type == T_FUNCTION) {
+    if (a->u.object->prog != b->u.object->prog)
+      return a->u.object->prog < b->u.object->prog ? -1 : 1;
+    if (a->subtype != b->subtype)
+      return a->subtype - b->subtype;
+
+    /* We have the same function but in different objects. Compare the
+     * objects themselves. */
+    /* FIXME: Should we try to convert the subtypes to the ones for
+     * the closest inherits? That'd make some sense if the functions
+     * are private, but otherwise it's doubtful. */
+    a_subtype = b_subtype = a->subtype;
+    tmp_a = *a;
+    tmp_a.type = T_OBJECT;
+    tmp_a.subtype = 0;
+    a = &tmp_a;
+    tmp_b = *b;
+    tmp_b.type = T_OBJECT;
+    tmp_b.subtype = 0;
+    b = &tmp_b;
+  }
+
+  else {
+    a_subtype = a->subtype;
+    b_subtype = b->subtype;
   }
 
   res = lfun_cmp (a, b);
-  if (res != 2) return res;
 
-  if (typediff) return typediff;
+  if (res == 3) {
+    /* If the objects had no inequality comparison lfuns to call, use
+     * their pointers to get a well defined internal sort order. Let's
+     * also group objects cloned from the same program. */
+    if (a->u.object->prog == b->u.object->prog)
+      return a->u.object < b->u.object ? -1 : 1;
+    else
+      return a->u.object->prog < b->u.object->prog ? -1 : 1;
+  }
 
-#ifdef PIKE_DEBUG
-  if (a->type != T_OBJECT)
-    Pike_fatal ("Expected objects when both types are the same.\n");
-#endif
+  return res == 2 ? 0 : res;
+}
+
+int set_svalue_cmpfun(const struct svalue *a, const struct svalue *b)
+{
+  int typediff = a->type - b->type;
+  if (typediff) {
+    if (a->type == T_OBJECT || b->type == T_OBJECT) {
+      int res = lfun_cmp (a, b);
+      if (res < 2) return res;
+    }
+    return typediff;
+  }
 
-  /* FIXME: Take subtype into account! */
+  switch(a->type)
+  {
+    case T_FLOAT:
+      if(a->u.float_number < b->u.float_number) return -1;
+      if(a->u.float_number > b->u.float_number) return 1;
+      return 0;
 
-  if(a->u.object->prog == b->u.object->prog) {
-    if (a->u.object->prog) {
-      if(a->u.object < b->u.object) {
-	return -1;
-      } else {
-	return 1;
-      }
-    } else {
-      /* Destructed objects are considered equal. */
+    case T_INT:
+      if(a->u.integer < b->u.integer) return -1;
+      if(a->u.integer > b->u.integer) return 1;
+      return 0;
+
+    case T_OBJECT:
+    case T_FUNCTION:
+      return obj_or_func_cmp (a, b);
+
+    default:
+      if(a->u.refs < b->u.refs) return -1;
+      if(a->u.refs > b->u.refs) return 1;
       return 0;
-    }
-  } else {
-    /* Attempt to group objects cloned from the same program */
-    if (a->u.object->prog < b->u.object->prog) {
-      return -1;
-    } else {
-      return 1;
-    }
   }
+  /* NOT REACHED */
 }
 
 static int switch_svalue_cmpfun(const struct svalue *a, const struct svalue *b)
 {
-  if(a->type == b->type)
+  int typediff = a->type - b->type;
+  if (typediff)
+    return typediff;
+
+  switch(a->type)
   {
-    switch(a->type)
-    {
-      case T_INT:
-	if(a->u.integer < b->u.integer) return -1;
-	if(a->u.integer > b->u.integer) return 1;
-	return 0;
-	
-      case T_FLOAT:
-	if(a->u.float_number < b->u.float_number) return -1;
-	if(a->u.float_number > b->u.float_number) return 1;
-	return 0;
-	
-      case T_STRING:
-	return DO_NOT_WARN((int)my_quick_strcmp(a->u.string, b->u.string));
-	
-      default:
-	return set_svalue_cmpfun(a,b);
-    }
-  }else{
-    return a->type - b->type;
+    case T_INT:
+      if(a->u.integer < b->u.integer) return -1;
+      if(a->u.integer > b->u.integer) return 1;
+      return 0;
+
+    case T_FLOAT:
+      if(a->u.float_number < b->u.float_number) return -1;
+      if(a->u.float_number > b->u.float_number) return 1;
+      return 0;
+
+    case T_STRING:
+      return DO_NOT_WARN((int)my_quick_strcmp(a->u.string, b->u.string));
+
+    case T_OBJECT:
+    case T_FUNCTION:
+      return obj_or_func_cmp (a, b);
+
+    default:
+      if(a->u.refs < b->u.refs) return -1;
+      if(a->u.refs > b->u.refs) return 1;
+      return 0;
   }
+  /* NOT REACHED */
 }
 
 int alpha_svalue_cmpfun(const struct svalue *a, const struct svalue *b)
 {
-  int res, typediff = a->type - b->type;
+  int typediff = a->type - b->type;
+  if (typediff) {
+    if (a->type == T_OBJECT || b->type == T_OBJECT) {
+      int res = lfun_cmp (a, b);
+      if (res < 2) return res;
+    }
+    return typediff;
+  }
 
-  if (!typediff)
+  switch(a->type)
   {
-    switch(a->type)
-    {
-      case T_INT:
-	if(a->u.integer < b->u.integer) return -1;
-	if(a->u.integer > b->u.integer) return  1;
-	return 0;
-	
-      case T_FLOAT:
-	if(a->u.float_number < b->u.float_number) return -1;
-	if(a->u.float_number > b->u.float_number) return  1;
-	return 0;
-	
-      case T_STRING:
-	return DO_NOT_WARN((int)my_quick_strcmp(a->u.string, b->u.string));
-	
-      case T_ARRAY:
-	if(a==b) return 0;
-	if (!a->u.array->size)
-	  if (!b->u.array->size) /* There are several different empty arrays. */
+    case T_INT:
+      if(a->u.integer < b->u.integer) return -1;
+      if(a->u.integer > b->u.integer) return  1;
+      return 0;
+
+    case T_FLOAT:
+      if(a->u.float_number < b->u.float_number) return -1;
+      if(a->u.float_number > b->u.float_number) return  1;
+      return 0;
+
+    case T_STRING:
+      return DO_NOT_WARN((int)my_quick_strcmp(a->u.string, b->u.string));
+
+    case T_ARRAY:
+      if(a==b) return 0;
+      if (!a->u.array->size)
+	if (!b->u.array->size) /* There are several different empty arrays. */
+	  return 0;
+	else
+	  return -1;
+      else
+	if (!b->u.array->size)
+	  return 1;
+      return alpha_svalue_cmpfun(ITEM(a->u.array), ITEM(b->u.array));
+
+    case T_MULTISET:
+      if (a == b) return 0;
+      {
+	ptrdiff_t a_pos = multiset_first (a->u.multiset);
+	ptrdiff_t b_pos = multiset_first (b->u.multiset);
+	int res;
+	struct svalue ind_a, ind_b;
+	if (a_pos < 0)
+	  if (b_pos < 0)
 	    return 0;
 	  else
 	    return -1;
 	else
-	  if (!b->u.array->size)
+	  if (b_pos < 0)
 	    return 1;
-	return alpha_svalue_cmpfun(ITEM(a->u.array), ITEM(b->u.array));
+	res = alpha_svalue_cmpfun (
+	  use_multiset_index (a->u.multiset, a_pos, ind_a),
+	  use_multiset_index (b->u.multiset, b_pos, ind_b));
+	sub_msnode_ref (a->u.multiset);
+	sub_msnode_ref (b->u.multiset);
+	return res;
+      }
 
-      case T_MULTISET:
-	if (a == b) return 0;
-	{
-	  ptrdiff_t a_pos = multiset_first (a->u.multiset);
-	  ptrdiff_t b_pos = multiset_first (b->u.multiset);
-	  struct svalue ind_a, ind_b;
-	  if (a_pos < 0)
-	    if (b_pos < 0)
-	      return 0;
-	    else
-	      return -1;
-	  else
-	    if (b_pos < 0)
-	      return 1;
-	  res = alpha_svalue_cmpfun (
-	    use_multiset_index (a->u.multiset, a_pos, ind_a),
-	    use_multiset_index (b->u.multiset, b_pos, ind_b));
-	  sub_msnode_ref (a->u.multiset);
-	  sub_msnode_ref (b->u.multiset);
-	  return res;
-	}
+    case T_OBJECT:
+    case T_FUNCTION:
+      return obj_or_func_cmp (a, b);
 
-      case T_OBJECT:
-	if((a->u.object == b->u.object) && (a->subtype == b->subtype)) {
-	  return 0;
-	}
-	break;
-	
-      default:
-#if 1
-	/* I think it would be better to leave the order undefined in
-	 * these cases since the addresses aren't observable
-	 * properties in pike. /mast */
-	if(a->u.refs < b->u.refs) return -1;
-	if(a->u.refs > b->u.refs) return 1;
-#endif
-	return 0;
-    }
+    default:
+      if(a->u.ptr < b->u.ptr) return -1;
+      if(a->u.ptr > b->u.ptr) return 1;
+      return 0;
   }
-
-  res = lfun_cmp (a, b);
-  if (res != 2) return res;
-
-  return typediff;
+  /* NOT REACHED */
 }
 
 #define CMP(X,Y) alpha_svalue_cmpfun(X,Y)