diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index ac9f3b46e4a25e6130408898c980124fff5248e8..a52464a20e4495f3953cf76a5daf7b1ab8543742 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,21 @@
+2005-07-15  Mark Mitchell  <mark@codesourcery.com>
+
+	PR c++/22132
+	* call.c (implicit_conversion): Add c_cast_p parameter.
+	(standard_conversion): Likewise.  Allow conversions between
+	differently-qualified pointer types when performing a C-style
+	cast.
+	(add_function_candidate): Adjust callee.
+	(build_builtin_candidate): Likewise.
+	(build_user_type_conversion_1): Likewise.
+	(conditional_conversion): Likewise.
+	(can_convert_arg): Likewise.
+	(can_convert_arg_bad): Likewise.
+	(perform_implicit_conversion): Likewise.
+	* cp-tree.h (comp_ptr_ttypes_const): Declare.
+	* typeck.c (comp_ptr_ttypes_const): Give it external linkage.
+	Return bool.
+	
 2005-07-12  Volker Reichelt  <reichelt@igpm.rwth-aachen.de>
 	    Nathan Sidwell  <nathan@codesourcery.com>
 
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 382d6ea8e8b89ec1037fe50949151071f2d08dc5..1035f88441a468a486cd5c7659038439a9b99557 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -169,8 +169,8 @@ static struct z_candidate *add_conv_candidate
 	(struct z_candidate **, tree, tree, tree, tree, tree);
 static struct z_candidate *add_function_candidate
 	(struct z_candidate **, tree, tree, tree, tree, tree, int);
-static conversion *implicit_conversion (tree, tree, tree, int);
-static conversion *standard_conversion (tree, tree, tree, int);
+static conversion *implicit_conversion (tree, tree, tree, bool, int);
+static conversion *standard_conversion (tree, tree, tree, bool, int);
 static conversion *reference_binding (tree, tree, tree, int);
 static conversion *build_conv (conversion_kind, tree, conversion *);
 static bool is_subseq (conversion *, conversion *);
@@ -578,10 +578,12 @@ strip_top_quals (tree t)
 
 /* Returns the standard conversion path (see [conv]) from type FROM to type
    TO, if any.  For proper handling of null pointer constants, you must
-   also pass the expression EXPR to convert from.  */
+   also pass the expression EXPR to convert from.  If C_CAST_P is true,
+   this conversion is coming from a C-style cast.  */
 
 static conversion *
-standard_conversion (tree to, tree from, tree expr, int flags)
+standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
+		     int flags)
 {
   enum tree_code fcode, tcode;
   conversion *conv;
@@ -631,7 +633,7 @@ standard_conversion (tree to, tree from, tree expr, int flags)
 	 the standard conversion sequence to perform componentwise
 	 conversion.  */
       conversion *part_conv = standard_conversion
-	(TREE_TYPE (to), TREE_TYPE (from), NULL_TREE, flags);
+	(TREE_TYPE (to), TREE_TYPE (from), NULL_TREE, c_cast_p, flags);
 
       if (part_conv)
 	{
@@ -737,7 +739,12 @@ standard_conversion (tree to, tree from, tree expr, int flags)
 
       if (same_type_p (from, to))
 	/* OK */;
-      else if (comp_ptr_ttypes (to_pointee, from_pointee))
+      else if (c_cast_p && comp_ptr_ttypes_const (to, from))
+	/* In a C-style cast, we ignore CV-qualification because we
+	   are allowed to perform a static_cast followed by a
+	   const_cast.  */
+	conv = build_conv (ck_qual, to, conv);
+      else if (!c_cast_p && comp_ptr_ttypes (to_pointee, from_pointee))
 	conv = build_conv (ck_qual, to, conv);
       else if (expr && string_conv_p (to, expr, 0))
 	/* converting from string constant to char *.  */
@@ -1197,7 +1204,8 @@ reference_binding (tree rto, tree rfrom, tree expr, int flags)
   if (related_p && !at_least_as_qualified_p (to, from))
     return NULL;
 
-  conv = implicit_conversion (to, from, expr, flags);
+  conv = implicit_conversion (to, from, expr, /*c_cast_p=*/false, 
+			      flags);
   if (!conv)
     return NULL;
 
@@ -1209,13 +1217,15 @@ reference_binding (tree rto, tree rfrom, tree expr, int flags)
   return conv;
 }
 
-/* Returns the implicit conversion sequence (see [over.ics]) from type FROM
-   to type TO.  The optional expression EXPR may affect the conversion.
-   FLAGS are the usual overloading flags.  Only LOOKUP_NO_CONVERSION is
-   significant.  */
+/* Returns the implicit conversion sequence (see [over.ics]) from type
+   FROM to type TO.  The optional expression EXPR may affect the
+   conversion.  FLAGS are the usual overloading flags.  Only
+   LOOKUP_NO_CONVERSION is significant.  If C_CAST_P is true, this
+   conversion is coming from a C-style cast.  */
 
 static conversion *
-implicit_conversion (tree to, tree from, tree expr, int flags)
+implicit_conversion (tree to, tree from, tree expr, bool c_cast_p,
+		     int flags)
 {
   conversion *conv;
 
@@ -1226,7 +1236,7 @@ implicit_conversion (tree to, tree from, tree expr, int flags)
   if (TREE_CODE (to) == REFERENCE_TYPE)
     conv = reference_binding (to, from, expr, flags);
   else
-    conv = standard_conversion (to, from, expr, flags);
+    conv = standard_conversion (to, from, expr, c_cast_p, flags);
 
   if (conv)
     return conv;
@@ -1383,7 +1393,8 @@ add_function_candidate (struct z_candidate **candidates,
 	      parmtype = build_pointer_type (parmtype);
 	    }
 
-	  t = implicit_conversion (parmtype, argtype, arg, flags);
+	  t = implicit_conversion (parmtype, argtype, arg, 
+				   /*c_cast_p=*/false, flags);
 	}
       else
 	{
@@ -1456,11 +1467,13 @@ add_conv_candidate (struct z_candidate **candidates, tree fn, tree obj,
       conversion *t;
 
       if (i == 0)
-	t = implicit_conversion (totype, argtype, arg, flags);
+	t = implicit_conversion (totype, argtype, arg, /*c_cast_p=*/false,
+				 flags);
       else if (parmnode == void_list_node)
 	break;
       else if (parmnode)
-	t = implicit_conversion (TREE_VALUE (parmnode), argtype, arg, flags);
+	t = implicit_conversion (TREE_VALUE (parmnode), argtype, arg, 
+				 /*c_cast_p=*/false, flags);
       else
 	{
 	  t = build_identity_conv (argtype, arg);
@@ -1514,7 +1527,8 @@ build_builtin_candidate (struct z_candidate **candidates, tree fnname,
       if (! args[i])
 	break;
 
-      t = implicit_conversion (types[i], argtypes[i], args[i], flags);
+      t = implicit_conversion (types[i], argtypes[i], args[i], 
+			       /*c_cast_p=*/false, flags);
       if (! t)
 	{
 	  viable = 0;
@@ -1531,7 +1545,8 @@ build_builtin_candidate (struct z_candidate **candidates, tree fnname,
     {
       convs[2] = convs[1];
       convs[1] = convs[0];
-      t = implicit_conversion (boolean_type_node, argtypes[2], args[2], flags);
+      t = implicit_conversion (boolean_type_node, argtypes[2], args[2], 
+			       /*c_cast_p=*/false, flags);
       if (t)
 	convs[0] = t;
       else
@@ -2590,7 +2605,8 @@ build_user_type_conversion_1 (tree totype, tree expr, int flags)
 	      conversion *ics
 		= implicit_conversion (totype,
 				       TREE_TYPE (TREE_TYPE (cand->fn)),
-				       0, convflags);
+				       0, 
+				       /*c_cast_p=*/false, convflags);
 
 	      cand->second_conv = ics;
 
@@ -3060,6 +3076,7 @@ conditional_conversion (tree e1, tree e2)
       conv = implicit_conversion (build_reference_type (t2),
 				  t1,
 				  e1,
+				  /*c_cast_p=*/false,
 				  LOOKUP_NO_TEMP_BIND);
       if (conv)
 	return conv;
@@ -3097,7 +3114,8 @@ conditional_conversion (tree e1, tree e2)
        Otherwise: E1 can be converted to match E2 if E1 can be implicitly
        converted to the type that expression E2 would have if E2 were
        converted to an rvalue (or the type it has, if E2 is an rvalue).  */
-    return implicit_conversion (t2, t1, e1, LOOKUP_NORMAL);
+    return implicit_conversion (t2, t1, e1, /*c_cast_p=*/false,
+				LOOKUP_NORMAL);
 }
 
 /* Implement [expr.cond].  ARG1, ARG2, and ARG3 are the three
@@ -6271,7 +6289,8 @@ can_convert_arg (tree to, tree from, tree arg)
   /* Get the high-water mark for the CONVERSION_OBSTACK.  */
   p = conversion_obstack_alloc (0);
 
-  t  = implicit_conversion (to, from, arg, LOOKUP_NORMAL);
+  t  = implicit_conversion (to, from, arg, /*c_cast_p=*/false, 
+			    LOOKUP_NORMAL);
   ok_p = (t && !t->bad_p);
 
   /* Free all the conversions we allocated.  */
@@ -6291,7 +6310,8 @@ can_convert_arg_bad (tree to, tree from, tree arg)
   /* Get the high-water mark for the CONVERSION_OBSTACK.  */
   p = conversion_obstack_alloc (0);
   /* Try to perform the conversion.  */
-  t  = implicit_conversion (to, from, arg, LOOKUP_NORMAL);
+  t  = implicit_conversion (to, from, arg, /*c_cast_p=*/false,
+			    LOOKUP_NORMAL);
   /* Free all the conversions we allocated.  */
   obstack_free (&conversion_obstack, p);
 
@@ -6317,6 +6337,7 @@ perform_implicit_conversion (tree type, tree expr)
   p = conversion_obstack_alloc (0);
 
   conv = implicit_conversion (type, TREE_TYPE (expr), expr,
+			      /*c_cast_p=*/false,
 			      LOOKUP_NORMAL);
   if (!conv)
     {
@@ -6370,6 +6391,7 @@ perform_direct_initialization_if_possible (tree type,
   p = conversion_obstack_alloc (0);
 
   conv = implicit_conversion (type, TREE_TYPE (expr), expr,
+			      c_cast_p,
 			      LOOKUP_NORMAL);
   if (!conv || conv->bad_p)
     expr = NULL_TREE;
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index db70017f5d2dfabf59e6c080412198865f94e46a..440bdff1c6bc20165d824d0cb582cc6397189ee9 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4289,6 +4289,7 @@ extern tree build_modify_expr			(tree, enum tree_code, tree);
 extern tree convert_for_initialization		(tree, tree, tree, int,
 						 const char *, tree, int);
 extern int comp_ptr_ttypes			(tree, tree);
+extern bool comp_ptr_ttypes_const               (tree, tree);
 extern int ptr_reasonably_similar		(tree, tree);
 extern tree build_ptrmemfunc			(tree, tree, int, bool);
 extern int cp_type_quals			(tree);
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index c6989ee6832f4f1a4ea58c7f83a8ad10d6a70aae..b13c8cf6264ee9e099b8c6e94b2ba4f33d11ecea 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -47,7 +47,6 @@ static tree convert_for_assignment (tree, tree, const char *, tree, int);
 static tree cp_pointer_int_sum (enum tree_code, tree, tree);
 static tree rationalize_conditional_expr (enum tree_code, tree);
 static int comp_ptr_ttypes_real (tree, tree, int);
-static int comp_ptr_ttypes_const (tree, tree);
 static bool comp_except_types (tree, tree, bool);
 static bool comp_array_types (tree, tree, bool);
 static tree common_base_type (tree, tree);
@@ -6414,15 +6413,17 @@ ptr_reasonably_similar (tree to, tree from)
     }
 }
 
-/* Like comp_ptr_ttypes, for const_cast.  */
+/* Return true if TO and FROM (both of which are POINTER_TYPEs or
+   pointer-to-member types) are the same, ignoring cv-qualification at
+   all levels.  */
 
-static int
+bool
 comp_ptr_ttypes_const (tree to, tree from)
 {
   for (; ; to = TREE_TYPE (to), from = TREE_TYPE (from))
     {
       if (TREE_CODE (to) != TREE_CODE (from))
-	return 0;
+	return false;
 
       if (TREE_CODE (from) == OFFSET_TYPE
 	  && same_type_p (TYPE_OFFSET_BASETYPE (from),
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 6bef685f53d5689963af260b30b0c70ecdc8f75d..4787bccf5b39a1f883eda3bb207674459f468de3 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2005-07-15  Mark Mitchell  <mark@codesourcery.com>
+
+	PR c++/22132
+	* g++.dg/expr/cast4.C: New test.
+
 2005-07-15  Richard Guenther  <rguenther@suse.de>
 
 	* gcc.dg/tree-ssa/flatten-1.c: New testcase.
diff --git a/gcc/testsuite/g++.dg/expr/cast4.C b/gcc/testsuite/g++.dg/expr/cast4.C
new file mode 100644
index 0000000000000000000000000000000000000000..72be9a6af19a2bb9b9e51e60e29caf17592691b4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/expr/cast4.C
@@ -0,0 +1,25 @@
+// { dg-do run }
+// PR c++/22132
+
+extern "C" void abort ();
+
+struct foo {
+  int a;
+  int b;
+};
+
+class Foobar : public foo {
+public:
+  Foobar() { a = 1; b = 2; };
+  virtual ~Foobar() {};
+};
+
+Foobar obj;
+const Foobar* objPtr = &obj;
+foo* f = (foo*)objPtr;
+
+int main () {
+  if (f->a != 1 || f->b != 2)
+    abort ();
+}
+