diff --git a/src/operators.c b/src/operators.c
index 32f1ca487b2aae391468d009fc7559cbf6b73f2b..3616d43c2d02a99c778bd76c58d405a05c64daa8 100644
--- a/src/operators.c
+++ b/src/operators.c
@@ -1080,10 +1080,16 @@ void o_check_soft_cast(struct svalue *s, struct pike_type *type)
      */
     struct pike_type *sval_type = get_type_of_svalue(s);
     struct pike_string *t1;
-    struct pike_string *t2;
+    struct string_builder s;
     char *fname = "__soft-cast";
+    ONERROR tmp0;
     ONERROR tmp1;
-    ONERROR tmp2;
+
+    init_string_builder(&s, 0);
+
+    SET_ONERROR(tmp0, free_string_builder, &s);
+
+    string_builder_explain_nonmatching_types(&s, type, sval_type);
 
     if (Pike_fp->current_program) {
       /* Look up the function-name */
@@ -1096,19 +1102,14 @@ void o_check_soft_cast(struct svalue *s, struct pike_type *type)
     t1 = describe_type(type);
     SET_ONERROR(tmp1, do_free_string, t1);
 	  
-    t2 = describe_type(sval_type);
-    SET_ONERROR(tmp2, do_free_string, t2);
-	  
     free_type(sval_type);
 
     bad_arg_error(NULL, Pike_sp-1, 1, 1, t1->str, Pike_sp-1,
-		  "%s(): Soft cast failed. Expected %s, got %s\n",
-		  fname, t1->str, t2->str);
+		  "%s(): Soft cast failed.\n%S",
+		  fname, s.s);
     /* NOT_REACHED */
-    UNSET_ONERROR(tmp2);
-    UNSET_ONERROR(tmp1);
-    free_string(t2);
-    free_string(t1);
+    CALL_AND_UNSET_ONERROR(tmp1);
+    CALL_AND_UNSET_ONERROR(tmp0);
   }
 }
 
diff --git a/src/pike_types.c b/src/pike_types.c
index a66677ecf5f0e9f1d2f7b8230298beca34f3782c..d09ef2b0418573f5bdbfbf8d36e2d51804c9a663 100644
--- a/src/pike_types.c
+++ b/src/pike_types.c
@@ -7,8 +7,8 @@
 #include "global.h"
 #include <ctype.h>
 #include "svalue.h"
-#include "pike_types.h"
 #include "stralloc.h"
+#include "pike_types.h"
 #include "stuff.h"
 #include "array.h"
 #include "program.h"
@@ -7679,6 +7679,50 @@ void yyexplain_nonmatching_types(int severity_level,
   END_CYCLIC();
 }
 
+/* FIXME: Code duplication! */
+void string_builder_explain_nonmatching_types(struct string_builder *s,
+					      struct pike_type *type_a,
+					      struct pike_type *type_b)
+{
+  DECLARE_CYCLIC();
+
+  implements_a=0;
+  implements_b=0;
+  implements_mode=0;
+
+  /* Note the argument order. */
+  pike_types_le(type_b, type_a);
+
+#if 0
+  if(!(implements_a && implements_b &&
+       type_a->str[0]==T_OBJECT &&
+       type_b->str[0]==T_OBJECT))
+#endif /* 0 */
+  {
+    ref_push_type_value(type_a);
+    ref_push_type_value(type_b);
+    string_builder_sprintf(s,
+			   "Expected: %O.\n"
+			   "Got     : %O.\n",
+			   Pike_sp-2, Pike_sp-1);
+  }
+
+  /* Protect against circularities. */
+  if (BEGIN_CYCLIC(type_a, type_b)) {
+    END_CYCLIC();
+    return;
+  }
+  SET_CYCLIC_RET(1);
+
+  if(implements_a && implements_b) {
+    if (implements_mode) {
+      string_builder_explain_not_implements(s, implements_a, implements_b);
+    } else {
+      string_builder_explain_not_compatible(s, implements_a, implements_b);
+    }
+  }
+  END_CYCLIC();
+}
 
 /******/
 
diff --git a/src/pike_types.h b/src/pike_types.h
index 277ec34b82e633b3cc558f3b63894af61ec3cf80..6feee0a0259ca538d15b409f25c8e4e83723c87d 100644
--- a/src/pike_types.h
+++ b/src/pike_types.h
@@ -304,6 +304,9 @@ void yyexplain_nonmatching_types(int severity_level,
 				 struct pike_string *b_file,
 				 INT32 b_line,
 				 struct pike_type *type_b);
+void string_builder_explain_nonmatching_types(struct string_builder *s,
+					      struct pike_type *type_a,
+					      struct pike_type *type_b);
 struct pike_type *debug_make_pike_type(const char *t);
 struct pike_string *type_to_string(struct pike_type *t);
 int pike_type_allow_premature_toss(struct pike_type *type);
diff --git a/src/program.c b/src/program.c
index 100ff0ec9c59cee2a673b6d3be3eb0bf48aaca5a..f210607d6bebdc3e02df7902b140ec22ad1d8b18 100644
--- a/src/program.c
+++ b/src/program.c
@@ -11440,6 +11440,134 @@ void yyexplain_not_implements(int severity_level,
   END_CYCLIC();
 }
 
+/* FIXME: Code duplication of yyexplain_not_compatible() above! */
+/* Explains why a is not compatible with b */
+void string_builder_explain_not_compatible(struct string_builder *s,
+					   struct program *a,
+					   struct program *b)
+{
+  int e;
+  struct pike_string *init_string = findstring("__INIT");
+  int res = 1;
+  DECLARE_CYCLIC();
+
+  /* Optimize the loop somewhat */
+  if (a->num_identifier_references < b->num_identifier_references) {
+    struct program *tmp = a;
+    a = b;
+    b = tmp;
+  }
+
+  if (BEGIN_CYCLIC(a, b)) {
+    END_CYCLIC();
+    return;
+  }
+  SET_CYCLIC_RET(1);
+
+  for(e=0;e<b->num_identifier_references;e++)
+  {
+    struct identifier *bid;
+    int i;
+    if (b->identifier_references[e].id_flags & (ID_PROTECTED|ID_HIDDEN))
+      continue;		/* Skip protected & hidden */
+
+    /* FIXME: What if they aren't protected & hidden in a? */
+
+    bid = ID_FROM_INT(b,e);
+    if(init_string == bid->name) continue;	/* Skip __INIT */
+    i = find_shared_string_identifier(bid->name,a);
+    if (i == -1) {
+      continue;		/* It's ok... */
+    }
+
+    /* Note: Uses weaker check for constant integers. */
+    if(((bid->run_time_type != PIKE_T_INT) ||
+	(ID_FROM_INT(a, i)->run_time_type != PIKE_T_INT)) &&
+       !match_types(ID_FROM_INT(a,i)->type, bid->type)) {
+      ref_push_program(a);
+      ref_push_program(b);
+      ref_push_type_value(ID_FROM_INT(a, i)->type);
+      ref_push_type_value(bid->type);
+      string_builder_sprintf(s,
+			     "Identifier %S in %O is incompatible with "
+			     "the same in %O.\n"
+			     "Expected: %O\n"
+			     "Got     : %O\n",
+			     bid->name, Pike_sp-4,
+			     Pike_sp-3,
+			     Pike_sp-2,
+			     Pike_sp-1);
+      pop_n_elems(4);
+    }
+  }
+  END_CYCLIC();
+  return;
+}
+
+/* FIXME: code duplication of yyexplain_not_implements() above! */
+/* Explains why a does not implement b */
+void string_builder_explain_not_implements(struct string_builder *s,
+					   struct program *a,
+					   struct program *b)
+{
+  int e;
+  struct pike_string *init_string = findstring("__INIT");
+  DECLARE_CYCLIC();
+
+  if (BEGIN_CYCLIC(a, b)) {
+    END_CYCLIC();
+    return;
+  }
+  SET_CYCLIC_RET(1);
+
+  for(e=0;e<b->num_identifier_references;e++)
+  {
+    struct identifier *bid;
+    int i;
+    if (b->identifier_references[e].id_flags & (ID_PROTECTED|ID_HIDDEN))
+      continue;		/* Skip protected & hidden */
+    bid = ID_FROM_INT(b,e);
+    if(init_string == bid->name) continue;	/* Skip __INIT */
+    i = find_shared_string_identifier(bid->name,a);
+    if (i == -1) {
+      if (b->identifier_references[e].id_flags & (ID_OPTIONAL))
+	continue;		/* It's ok... */
+      ref_push_type_value(bid->type);
+      string_builder_sprintf(s,
+			     "Missing identifier %S.\n"
+			     "Expected: %O.\n",
+			     bid->name, Pike_sp-1);
+      pop_stack();
+      continue;
+    }
+
+    if (!pike_types_le(bid->type, ID_FROM_INT(a, i)->type)) {
+      ref_push_type_value(bid->type);
+      ref_push_type_value(ID_FROM_INT(a, i)->type);
+      if(!match_types(ID_FROM_INT(a,i)->type, bid->type)) {
+	string_builder_sprintf(s,
+			       "Type of identifier %S does not match.\n"
+			       "Expected: %O.\n"
+			       "Got     : %O.\n",
+			       bid->name,
+			       Pike_sp-2,
+			       Pike_sp-1);
+      } else {
+	string_builder_sprintf(s,
+			       "Type of identifier %S is not strictly compatible.",
+			       "Expected: %O.\n"
+			       "Got     : %O.\n",
+			       bid->name,
+			       Pike_sp-2,
+			       Pike_sp-1);
+      }
+      pop_n_elems(2);
+      continue;
+    }
+  }
+  END_CYCLIC();
+}
+
 PMOD_EXPORT void *parent_storage(int depth)
 {
   struct external_variable_context loc;
diff --git a/src/program.h b/src/program.h
index 9e1f5f6829bb6d850f90f751aaa5e7f25b99bb7e..488337ebfc7e01e8ea4d5a44322437fa92096a0e 100644
--- a/src/program.h
+++ b/src/program.h
@@ -1044,6 +1044,12 @@ void yyexplain_not_compatible(int severity_level,
 			      struct program *a, struct program *b);
 void yyexplain_not_implements(int severity_level,
 			      struct program *a, struct program *b);
+void string_builder_explain_not_compatible(struct string_builder *s,
+					   struct program *a,
+					   struct program *b);
+void string_builder_explain_not_implements(struct string_builder *s,
+					   struct program *a,
+					   struct program *b);
 PMOD_EXPORT void *parent_storage(int depth);
 PMOD_EXPORT void change_compiler_compatibility(int major, int minor);
 void make_area_executable (char *start, size_t len);