* classobject.[ch], {float,long,int}object.c, bltinmodule.c:
  coercion is now completely generic.
* ceval.c: for instances, don't coerce for + and *; * reverses
  arguments if left one is non-instance numeric and right one sequence.
diff --git a/Include/classobject.h b/Include/classobject.h
index 1d24a7c..b33cacc 100644
--- a/Include/classobject.h
+++ b/Include/classobject.h
@@ -43,5 +43,4 @@
 extern object *instancemethodgetfunc PROTO((object *));
 extern object *instancemethodgetself PROTO((object *));
 
-extern int instance_coerce PROTO((object **, object **));
 extern object *instance_convert PROTO((object *, char *));
diff --git a/Include/object.h b/Include/object.h
index d987efb..a66629c 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -142,6 +142,7 @@
 	object *(*nb_and) FPROTO((object *, object *));
 	object *(*nb_xor) FPROTO((object *, object *));
 	object *(*nb_or) FPROTO((object *, object *));
+	int (*nb_coerce) FPROTO((object **, object **));
 } number_methods;
 
 typedef struct {
diff --git a/Objects/classobject.c b/Objects/classobject.c
index 40fe0c9..8164ced 100644
--- a/Objects/classobject.c
+++ b/Objects/classobject.c
@@ -417,13 +417,19 @@
 instance_concat(inst, other)
 	instanceobject *inst, *other;
 {
-	object *func, *res;
+	object *func, *arg, *res;
 
 	func = instance_getattr(inst, "__add__");
 	if (func == NULL)
 		return NULL;
-	res = call_object(func, (object *)other);
+	arg = mkvalue("(O)", other);
+	if (arg == NULL) {
+		DECREF(func);
+		return NULL;
+	}
+	res = call_object(func, arg);
 	DECREF(func);
+	DECREF(arg);
 	return res;
 }
 
@@ -568,12 +574,18 @@
 	object *other;
 	char *methodname;
 {
-	object *func, *res;
+	object *func, *arg, *res;
 
 	if ((func = instance_getattr(self, methodname)) == NULL)
 		return NULL;
-	res = call_object(func, other);
+	arg = mkvalue("O", other);
+	if (arg == NULL) {
+		DECREF(func);
+		return NULL;
+	}
+	res = call_object(func, arg);
 	DECREF(func);
+	DECREF(arg);
 	return res;
 }
 
@@ -653,6 +665,45 @@
 BINARY(instance_xor, "__xor__")
 BINARY(instance_or, "__or__")
 
+static int
+instance_coerce(pv, pw)
+	object **pv, **pw;
+{
+	object *v =  *pv;
+	object *w = *pw;
+	object *func;
+	object *res;
+	int outcome;
+
+	if (!is_instanceobject(v))
+		return 1; /* XXX shouldn't be possible */
+	func = instance_getattr((instanceobject *)v, "__coerce__");
+	if (func == NULL) {
+		err_clear();
+		return 1;
+	}
+	res = call_object(func, w);
+	if (res == NULL)
+		return -1;
+	if (res == None) {
+		DECREF(res);
+		return 1;
+	}
+	outcome = getargs(res, "(OO)", &v, &w);
+	if (!outcome || v->ob_type != w->ob_type ||
+			v->ob_type->tp_as_number == NULL) {
+		DECREF(res);
+		err_setstr(TypeError, "bad __coerce__ result");
+		return -1;
+	}
+	INCREF(v);
+	INCREF(w);
+	DECREF(res);
+	*pv = v;
+	*pw = w;
+	return 0;
+}
+
 static number_methods instance_as_number = {
 	instance_add,		/*nb_add*/
 	instance_sub,		/*nb_subtract*/
@@ -671,6 +722,7 @@
 	instance_and,		/*nb_and*/
 	instance_xor,		/*nb_xor*/
 	instance_or,		/*nb_or*/
+	instance_coerce,	/*nb_coerce*/
 };
 
 typeobject Instancetype = {
@@ -690,58 +742,6 @@
 	&instance_as_mapping,	/*tp_as_mapping*/
 };
 
-static int
-one_coerce(pv, pw)
-	object **pv, **pw;
-{
-	object *v = *pv;
-	object *w = *pw;
-	object *func;
-
-	if (!is_instanceobject(v))
-		return 1;
-	func = instance_getattr((instanceobject *)v, "__coerce__");
-	if (func == NULL) {
-		err_clear();
-		return 1;
-	}
-	if (func != NULL) {
-		object *res = call_object(func, w);
-		int outcome;
-		if (res == NULL)
-			return -1;
-		outcome = getargs(res, "(OO)", &v, &w);
-		if (!outcome || v->ob_type != w->ob_type ||
-			        v->ob_type->tp_as_number == NULL) {
-			DECREF(res);
-			err_setstr(TypeError, "bad __coerce__ result");
-			return -1;
-		}
-		INCREF(v);
-		INCREF(w);
-		DECREF(res);
-		*pv = v;
-		*pw = w;
-		return 0;
-	}
-}
-
-int
-instance_coerce(pv, pw)
-	object **pv, **pw;
-{
-	int outcome;
-	outcome = one_coerce(pv, pw);
-	if (outcome > 0) {
-		outcome = one_coerce(pw, pv);
-		if (outcome > 0) {
-			err_setstr(TypeError, "uncoerceable instance");
-			outcome = -1;
-		}
-	}
-	return outcome;
-}
-
 object *
 instance_convert(inst, methodname)
 	object *inst;
diff --git a/Objects/floatobject.c b/Objects/floatobject.c
index d154cab..5ce6202 100644
--- a/Objects/floatobject.c
+++ b/Objects/floatobject.c
@@ -299,6 +299,25 @@
 	return v->ob_fval != 0.0;
 }
 
+int
+float_coerce(pv, pw)
+	object **pv;
+	object **pw;
+{
+	if (is_intobject(*pw)) {
+		long x = getintvalue(*pw);
+		*pw = newfloatobject((double)x);
+		INCREF(*pv);
+		return 0;
+	}
+	else if (is_longobject(*pw)) {
+		*pw = newfloatobject(dgetlongvalue(*pw));
+		INCREF(*pv);
+		return 0;
+	}
+	return 1; /* Can't do it */
+}
+
 static number_methods float_as_number = {
 	float_add,	/*nb_add*/
 	float_sub,	/*nb_subtract*/
@@ -317,6 +336,7 @@
 	0,		/*nb_and*/
 	0,		/*nb_xor*/
 	0,		/*nb_or*/
+	float_coerce,	/*nb_coerce*/
 };
 
 typeobject Floattype = {
diff --git a/Objects/longobject.c b/Objects/longobject.c
index bf62c1f..f9e3765 100644
--- a/Objects/longobject.c
+++ b/Objects/longobject.c
@@ -1253,6 +1253,19 @@
 	return long_bitwise(a, '|', b);
 }
 
+int
+long_coerce(pv, pw)
+	object **pv;
+	object **pw;
+{
+	if (is_intobject(*pw)) {
+		*pw = newlongobject(getintvalue(*pw));
+		INCREF(*pv);
+		return 0;
+	}
+	return 1; /* Can't do it */
+}
+
 #define UF (object* (*) FPROTO((object *))) /* Unary function */
 #define BF (object* (*) FPROTO((object *, object *))) /* Binary function */
 #define IF (int (*) FPROTO((object *))) /* Int function */
@@ -1275,6 +1288,8 @@
 	BF long_and,	/*nb_and*/
 	BF long_xor,	/*nb_xor*/
 	BF long_or,	/*nb_or*/
+	(int (*) FPROTO((object **, object **)))
+	long_coerce,	/*nb_coerce*/
 };
 
 typeobject Longtype = {
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index cce19e1..86deac3 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -781,10 +781,7 @@
    Increment the reference count on each argument.
    Return -1 and raise an exception if no coercion is possible
    (and then no reference count is incremented).
-   XXX This should be distributed over the various numeric types,
-   XXX but for now I don't see how to implement that.
-   XXX So, for now, if you add a new numeric type,
-   XXX you must add to this function as well. */
+*/
 
 int
 coerce(pv, pw)
@@ -792,36 +789,23 @@
 {
 	register object *v = *pv;
 	register object *w = *pw;
+	int res;
+
 	if (v->ob_type == w->ob_type) {
 		INCREF(v);
 		INCREF(w);
 		return 0;
 	}
-	if (is_instanceobject(v) || is_instanceobject(w))
-		return instance_coerce(pv, pw);
-	if (v->ob_type->tp_as_number == NULL ||
-					w->ob_type->tp_as_number == NULL) {
-		err_setstr(TypeError, "mixing number and non-number");
-		return -1;
+	if (v->ob_type->tp_as_number && v->ob_type->tp_as_number->nb_coerce) {
+		res = (*v->ob_type->tp_as_number->nb_coerce)(pv, pw);
+		if (res <= 0)
+			return res;
 	}
-	if (is_floatobject(v) || is_floatobject(w)) {
-		v = builtin_float((object *)0, v);
-		w = builtin_float((object *)0, w);
+	if (w->ob_type->tp_as_number && w->ob_type->tp_as_number->nb_coerce) {
+		res = (*w->ob_type->tp_as_number->nb_coerce)(pw, pv);
+		if (res <= 0)
+			return res;
 	}
-	else if (is_longobject(v) || is_longobject(w)) {
-		v = builtin_long((object *)0, v);
-		w = builtin_long((object *)0, w);
-	}
-	else {
-		err_setstr(TypeError, "can't coerce numeric types?!?!?");
-		return -1;
-	}
-	if (v == NULL || w == NULL) {
-		XDECREF(v);
-		XDECREF(w);
-		return -1;
-	}
-	*pv = v;
-	*pw = w;
-	return 0;
+	err_setstr(TypeError, "number coercion failed");
+	return -1;
 }
diff --git a/Python/ceval.c b/Python/ceval.c
index ad679a6..5b72d8e 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -1532,7 +1532,9 @@
 add(v, w)
 	object *v, *w;
 {
-	if (v->ob_type->tp_as_number != NULL) {
+	if (v->ob_type->tp_as_sequence != NULL)
+		return (*v->ob_type->tp_as_sequence->sq_concat)(v, w);
+	else if (v->ob_type->tp_as_number != NULL) {
 		object *x;
 		if (coerce(&v, &w) != 0)
 			return NULL;
@@ -1541,8 +1543,6 @@
 		DECREF(w);
 		return x;
 	}
-	else if (v->ob_type->tp_as_sequence != NULL)
-		return (*v->ob_type->tp_as_sequence->sq_concat)(v, w);
 	else {
 		err_setstr(TypeError, "+ not supported by operands");
 		return NULL;
@@ -1571,16 +1571,27 @@
 	object *v, *w;
 {
 	typeobject *tp;
-	if (is_intobject(v) && w->ob_type->tp_as_sequence != NULL) {
-		/* int*sequence -- swap v and w */
+	tp = v->ob_type;
+	if (tp->tp_as_number != NULL &&
+	    w->ob_type->tp_as_sequence != NULL &&
+	    !is_instanceobject(v)) {
+		/* number*sequence -- swap v and w */
 		object *tmp = v;
 		v = w;
 		w = tmp;
+		tp = v->ob_type;
 	}
-	tp = v->ob_type;
 	if (tp->tp_as_number != NULL) {
 		object *x;
-		if (coerce(&v, &w) != 0)
+		if (is_instanceobject(v)) {
+			/* Instances of user-defined classes get their
+			   other argument uncoerced, so they may
+			   implement sequence*number as well as
+			   number*number. */
+			INCREF(v);
+			INCREF(w);
+		}
+		else if (coerce(&v, &w) != 0)
 			return NULL;
 		x = (*v->ob_type->tp_as_number->nb_multiply)(v, w);
 		DECREF(v);