diff mbox

Many C++ PATCHes for SFINAE fixes

Message ID 4D9E2D7B.508@redhat.com
State New
Headers show

Commit Message

Jason Merrill April 7, 2011, 9:32 p.m. UTC
People have been finding other places where we haven't been handling 
SFINAE properly.  In many cases, there are two patches for each fix; the 
first one fixes the bug conservatively (for possible application to 
4.6), and the second one reorganizes things to be cleaner.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit 441f2c6d80756bed9c72aba05a315c34f2db2343
Author: Jason Merrill <jason@redhat.com>
Date:   Sun Apr 3 11:42:47 2011 -0400

    	* semantics.c (finish_decltype_type): Add complain parm.
    	* cp-tree.h: Adjust.
    	* parser.c (cp_parser_decltype): Adjust.
    	* pt.c (tsubst): Adjust.
commit c02c3cbfe41af41ffdb8a729c763b395c14979b3
Author: Jason Merrill <jason@redhat.com>
Date:   Mon Apr 4 17:18:38 2011 -0400

    	* semantics.c (finish_decltype_type): Simplify handling of unknown
    	type.

diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 80ec028..5cbba33 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -4788,7 +4788,6 @@ tree
 finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
 		      tsubst_flags_t complain)
 {
-  tree orig_expr = expr;
   tree type = NULL_TREE;
 
   if (!expr || error_operand_p (expr))
@@ -4826,6 +4825,13 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
 
   expr = resolve_nondeduced_context (expr);
 
+  if (type_unknown_p (expr))
+    {
+      if (complain & tf_error)
+	error ("decltype cannot resolve address of overloaded function");
+      return error_mark_node;
+    }
+
   /* To get the size of a static data member declared as an array of
      unknown bound, we need to instantiate it.  */
   if (TREE_CODE (expr) == VAR_DECL
@@ -4855,28 +4861,9 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
         expr = TREE_OPERAND (expr, 1);
 
       if (TREE_CODE (expr) == BASELINK)
-        /* See through BASELINK nodes to the underlying functions.  */
+        /* See through BASELINK nodes to the underlying function.  */
         expr = BASELINK_FUNCTIONS (expr);
 
-      if (TREE_CODE (expr) == TEMPLATE_ID_EXPR)
-	expr = TREE_OPERAND (expr, 0);
-
-      if (TREE_CODE (expr) == OVERLOAD)
-        {
-          if (OVL_CHAIN (expr)
-	      || TREE_CODE (OVL_FUNCTION (expr)) == TEMPLATE_DECL)
-            {
-	      if (complain & tf_error)
-		error ("%qE refers to a set of overloaded functions",
-		       orig_expr);
-              return error_mark_node;
-            }
-          else
-            /* An overload set containing only one function: just look
-               at that function.  */
-            expr = OVL_FUNCTION (expr);
-        }
-
       switch (TREE_CODE (expr))
         {
         case FIELD_DECL:
@@ -4918,10 +4905,7 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
           break;
 
         default:
-	  gcc_assert (TYPE_P (expr) || DECL_P (expr)
-		      || TREE_CODE (expr) == SCOPE_REF);
-	  if (complain & tf_error)
-	    error ("argument to decltype must be an expression");
+	  gcc_unreachable ();
           return error_mark_node;
         }
     }
@@ -4957,13 +4941,6 @@ finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
 	}
     }
 
-  if (!type || type == unknown_type_node)
-    {
-      if (complain & tf_error)
-	error ("type of %qE is unknown", expr);
-      return error_mark_node;
-    }
-
   return type;
 }
commit a4a17314e1356211a523d0673e0d3a83a11b2338
Author: Jason Merrill <jason@redhat.com>
Date:   Mon Apr 4 20:48:54 2011 -0400

    	PR c++/48449
    	* typeck2.c (build_functional_cast): Check complain consistently.
    	Use build_value_init and abstract_virtuals_error_sfinae.
    	(abstract_virtuals_error_sfinae): Split out.
    	* cp-tree.h: Declare it.
    	* init.c (build_new_1): Use it.
    	(build_value_init_noctor): Handle FUNCTION_TYPE.

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 06b0b3e..94bd3ce 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5596,6 +5596,7 @@ extern tree binfo_or_else			(tree, tree);
 extern void cxx_readonly_error			(tree, enum lvalue_use);
 extern void complete_type_check_abstract	(tree);
 extern int abstract_virtuals_error		(tree, tree);
+extern int abstract_virtuals_error_sfinae	(tree, tree, tsubst_flags_t);
 
 extern tree store_init_value			(tree, tree, int);
 extern void check_narrowing			(tree, tree);
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index e1961c8..2e9eb680 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -458,6 +458,12 @@ build_value_init_noctor (tree type, tsubst_flags_t complain)
       /* Build a constructor to contain the initializations.  */
       return build_constructor (type, v);
     }
+  else if (TREE_CODE (type) == FUNCTION_TYPE)
+    {
+      if (complain & tf_error)
+	error ("value-initialization of function type %qT", type);
+      return error_mark_node;
+    }
 
   return build_zero_init (type, NULL_TREE, /*static_storage_p=*/false);
 }
@@ -2030,7 +2036,7 @@ build_new_1 (VEC(tree,gc) **placement, tree type, tree nelts,
       return error_mark_node;
     }
 
-  if (abstract_virtuals_error (NULL_TREE, elt_type))
+  if (abstract_virtuals_error_sfinae (NULL_TREE, elt_type, complain))
     return error_mark_node;
 
   is_initialized = (TYPE_NEEDS_CONSTRUCTING (elt_type) || *init != NULL);
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index 82218f0..f2046f7 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -250,7 +250,7 @@ complete_type_check_abstract (tree type)
    occurred; zero if all was well.  */
 
 int
-abstract_virtuals_error (tree decl, tree type)
+abstract_virtuals_error_sfinae (tree decl, tree type, tsubst_flags_t complain)
 {
   VEC(tree,gc) *pure;
 
@@ -301,11 +301,14 @@ abstract_virtuals_error (tree decl, tree type)
   if (!pure)
     return 0;
 
+  if (decl && TREE_CODE (decl) == RESULT_DECL)
+    return 0;
+
+  if (!(complain & tf_error))
+    return 1;
+
   if (decl)
     {
-      if (TREE_CODE (decl) == RESULT_DECL)
-	return 0;
-
       if (TREE_CODE (decl) == VAR_DECL)
 	error ("cannot declare variable %q+D to be of abstract "
 	       "type %qT", decl, type);
@@ -354,6 +357,14 @@ abstract_virtuals_error (tree decl, tree type)
   return 1;
 }
 
+/* Wrapper for the above function in the common case of wanting errors.  */
+
+int
+abstract_virtuals_error (tree decl, tree type)
+{
+  return abstract_virtuals_error_sfinae (decl, type, tf_warning_or_error);
+}
+
 /* Print an error message for invalid use of an incomplete type.
    VALUE is the expression that was used (or 0 if that isn't known)
    and TYPE is the type that was invalid.  DIAG_KIND indicates the
@@ -1527,7 +1538,8 @@ build_functional_cast (tree exp, tree parms, tsubst_flags_t complain)
 
   if (TREE_CODE (type) == REFERENCE_TYPE && !parms)
     {
-      error ("invalid value-initialization of reference type");
+      if (complain & tf_error)
+	error ("invalid value-initialization of reference type");
       return error_mark_node;
     }
 
@@ -1542,7 +1554,7 @@ build_functional_cast (tree exp, tree parms, tsubst_flags_t complain)
   if (! MAYBE_CLASS_TYPE_P (type))
     {
       if (parms == NULL_TREE)
-	return cp_convert (type, integer_zero_node);
+	return build_value_init (type, complain);
 
       /* This must build a C cast.  */
       parms = build_x_compound_expr_from_list (parms, ELK_FUNC_CAST, complain);
@@ -1558,7 +1570,7 @@ build_functional_cast (tree exp, tree parms, tsubst_flags_t complain)
 
   if (!complete_type_or_maybe_complain (type, NULL_TREE, complain))
     return error_mark_node;
-  if (abstract_virtuals_error (NULL_TREE, type))
+  if (abstract_virtuals_error_sfinae (NULL_TREE, type, complain))
     return error_mark_node;
 
   /* [expr.type.conv]
diff --git a/gcc/testsuite/g++.dg/cpp0x/sfinae8.C b/gcc/testsuite/g++.dg/cpp0x/sfinae8.C
new file mode 100644
index 0000000..7f3012f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/sfinae8.C
@@ -0,0 +1,14 @@
+// PR c++/48449
+// { dg-options -std=c++0x }
+
+template<class T, class = decltype(T())>
+char f(int);
+
+template<class>
+char (&f(...))[2];
+
+struct A { virtual ~A() = 0; };
+
+static_assert(sizeof(f<int&>(0)) != 1, "Error");
+static_assert(sizeof(f<void()>(0)) != 1, "Error");
+static_assert(sizeof(f<A>(0)) != 1, "Error");
commit d8489e38cc7e65238665e0321650f356509cb03e
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Apr 7 17:11:44 2011 -0400

    	* init.c (build_value_init_noctor): Handle REFERENCE_TYPE at top
    	level.
    	(perform_member_init): Not here.
    	* typeck2.c (build_functional_cast): Limit REFERENCE_TYPE special
    	case to templates.
    	(abstract_virtuals_error_sfinae): Remove RESULT_DECL special case.

diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 2e9eb680..005f8d6 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -388,14 +388,6 @@ build_value_init_noctor (tree type, tsubst_flags_t complain)
 
 	      ftype = TREE_TYPE (field);
 
-	      if (TREE_CODE (ftype) == REFERENCE_TYPE)
-		{
-		  if (complain & tf_error)
-		    error ("value-initialization of reference");
-		  else
-		    return error_mark_node;
-		}
-
 	      /* We could skip vfields and fields of types with
 		 user-defined constructors, but I think that won't improve
 		 performance at all; it should be simpler in general just
@@ -408,6 +400,9 @@ build_value_init_noctor (tree type, tsubst_flags_t complain)
 		 all of the subobjects.  */
 	      value = build_value_init (ftype, complain);
 
+	      if (value == error_mark_node)
+		return error_mark_node;
+
 	      if (value)
 		CONSTRUCTOR_APPEND_ELT(v, field, value);
 	    }
@@ -450,6 +445,9 @@ build_value_init_noctor (tree type, tsubst_flags_t complain)
 
 	  ce->value = build_value_init (TREE_TYPE (type), complain);
 
+	  if (ce->value == error_mark_node)
+	    return error_mark_node;
+
 	  /* The gimplifier can't deal with a RANGE_EXPR of TARGET_EXPRs.  */
 	  gcc_assert (TREE_CODE (ce->value) != TARGET_EXPR
 		      && TREE_CODE (ce->value) != AGGR_INIT_EXPR);
@@ -464,6 +462,12 @@ build_value_init_noctor (tree type, tsubst_flags_t complain)
 	error ("value-initialization of function type %qT", type);
       return error_mark_node;
     }
+  else if (TREE_CODE (type) == REFERENCE_TYPE)
+    {
+      if (complain & tf_error)
+	error ("value-initialization of reference type %qT", type);
+      return error_mark_node;
+    }
 
   return build_zero_init (type, NULL_TREE, /*static_storage_p=*/false);
 }
@@ -504,16 +508,9 @@ perform_member_init (tree member, tree init)
 	}
       else
 	{
-	  if (TREE_CODE (type) == REFERENCE_TYPE)
-	    permerror (DECL_SOURCE_LOCATION (current_function_decl),
-		       "value-initialization of %q#D, which has reference type",
-		       member);
-	  else
-	    {
-	      init = build2 (INIT_EXPR, type, decl,
-			     build_value_init (type, tf_warning_or_error));
-	      finish_expr_stmt (init);
-	    }
+	  init = build2 (INIT_EXPR, type, decl,
+			 build_value_init (type, tf_warning_or_error));
+	  finish_expr_stmt (init);
 	}
     }
   /* Deal with this here, as we will get confused if we try to call the
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index f2046f7..f3a0079 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -301,9 +301,6 @@ abstract_virtuals_error_sfinae (tree decl, tree type, tsubst_flags_t complain)
   if (!pure)
     return 0;
 
-  if (decl && TREE_CODE (decl) == RESULT_DECL)
-    return 0;
-
   if (!(complain & tf_error))
     return 1;
 
@@ -1536,16 +1533,21 @@ build_functional_cast (tree exp, tree parms, tsubst_flags_t complain)
   else
     type = exp;
 
-  if (TREE_CODE (type) == REFERENCE_TYPE && !parms)
-    {
-      if (complain & tf_error)
-	error ("invalid value-initialization of reference type");
-      return error_mark_node;
-    }
-
   if (processing_template_decl)
     {
-      tree t = build_min (CAST_EXPR, type, parms);
+      tree t;
+
+      /* Diagnose this even in a template.  We could also try harder
+	 to give all the usual errors when the type and args are
+	 non-dependent...  */
+      if (TREE_CODE (type) == REFERENCE_TYPE && !parms)
+	{
+	  if (complain & tf_error)
+	    error ("invalid value-initialization of reference type");
+	  return error_mark_node;
+	}
+
+      t = build_min (CAST_EXPR, type, parms);
       /* We don't know if it will or will not have side effects.  */
       TREE_SIDE_EFFECTS (t) = 1;
       return t;
commit c1fb6653e14d8922f140569b606d681906779984
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Apr 5 14:19:56 2011 -0400

    	PR c++/48450
    	* tree.c (build_cplus_new, build_aggr_init_expr): Take complain.
    	(bot_manip): Adjust.
    	* cp-tree.h: Adjust.
    	* call.c (convert_like_real, build_cxx_call): Adjust.
    	(perform_direct_initialization_if_possible): Adjust.
    	* cvt.c (ocp_convert): Adjust.
    	* init.c (build_value_init): Adjust.
    	* semantics.c (maybe_add_lambda_conv_op): Adjust.
    	* typeck.c (unary_complex_lvalue, cp_build_modify_expr): Adjust.
    	* typeck2.c (build_functional_cast): Adjust.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index ad2de43..c273027 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -5414,7 +5414,7 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	   we need to build up a TARGET_EXPR.  */
 	if (DECL_CONSTRUCTOR_P (convfn))
 	  {
-	    expr = build_cplus_new (totype, expr);
+	    expr = build_cplus_new (totype, expr, complain);
 
 	    /* Remember that this was list-initialization.  */
 	    if (convs->check_narrowing)
@@ -5559,7 +5559,7 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 	  else if (diag_kind == DK_ERROR)
 	    return error_mark_node;
 	}
-      return build_cplus_new (totype, expr);
+      return build_cplus_new (totype, expr, complain);
 
     case ck_ref_bind:
       {
@@ -6476,7 +6476,7 @@ build_cxx_call (tree fn, int nargs, tree *argarray)
     return error_mark_node;
 
   if (MAYBE_CLASS_TYPE_P (TREE_TYPE (fn)))
-    fn = build_cplus_new (TREE_TYPE (fn), fn);
+    fn = build_cplus_new (TREE_TYPE (fn), fn, tf_warning_or_error);
   return convert_from_reference (fn);
 }
 
@@ -8119,7 +8119,7 @@ perform_direct_initialization_if_possible (tree type,
       expr = build_special_member_call (NULL_TREE, complete_ctor_identifier,
 					&args, type, LOOKUP_NORMAL, complain);
       release_tree_vector (args);
-      return build_cplus_new (type, expr);
+      return build_cplus_new (type, expr, complain);
     }
 
   /* Get the high-water mark for the CONVERSION_OBSTACK.  */
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 94bd3ce..ea251a8 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5395,8 +5395,8 @@ extern tree build_min				(enum tree_code, tree, ...);
 extern tree build_min_nt			(enum tree_code, ...);
 extern tree build_min_non_dep			(enum tree_code, tree, ...);
 extern tree build_min_non_dep_call_vec		(tree, tree, VEC(tree,gc) *);
-extern tree build_cplus_new			(tree, tree);
-extern tree build_aggr_init_expr		(tree, tree);
+extern tree build_cplus_new			(tree, tree, tsubst_flags_t);
+extern tree build_aggr_init_expr		(tree, tree, tsubst_flags_t);
 extern tree get_target_expr			(tree);
 extern tree build_cplus_array_type		(tree, tree);
 extern tree build_array_of_n_type		(tree, int);
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 8ab0001..46c6eb4 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -813,7 +813,7 @@ ocp_convert (tree type, tree expr, int convtype, int flags)
 	  release_tree_vector (ctor_vec);
 	}
       if (ctor)
-	return build_cplus_new (type, ctor);
+	return build_cplus_new (type, ctor, tf_warning_or_error);
     }
 
   if (flags & LOOKUP_COMPLAIN)
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 005f8d6..3131690 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -342,7 +342,8 @@ build_value_init (tree type, tsubst_flags_t complain)
 	  (type,
 	   build_special_member_call (NULL_TREE, complete_ctor_identifier,
 				      NULL, type, LOOKUP_NORMAL,
-				      complain));
+				      complain),
+	   complain);
       else if (TREE_CODE (type) != UNION_TYPE && TYPE_NEEDS_CONSTRUCTING (type))
 	{
 	  /* This is a class that needs constructing, but doesn't have
@@ -354,7 +355,7 @@ build_value_init (tree type, tsubst_flags_t complain)
 	     NULL, type, LOOKUP_NORMAL, complain);
 	  if (ctor != error_mark_node)
 	    {
-	      ctor = build_aggr_init_expr (type, ctor);
+	      ctor = build_aggr_init_expr (type, ctor, complain);
 	      AGGR_INIT_ZERO_FIRST (ctor) = 1;
 	    }
 	  return ctor;
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 5cbba33..30175af 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -8429,7 +8429,7 @@ maybe_add_lambda_conv_op (tree type)
 		       VEC_address (tree, argvec));
   CALL_FROM_THUNK_P (call) = 1;
   if (MAYBE_CLASS_TYPE_P (TREE_TYPE (call)))
-    call = build_cplus_new (TREE_TYPE (call), call);
+    call = build_cplus_new (TREE_TYPE (call), call, tf_warning_or_error);
   call = convert_from_reference (call);
   finish_return_stmt (call);
 
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 070ba81..c2aa389 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -373,7 +373,7 @@ build_aggr_init_array (tree return_type, tree fn, tree slot, int nargs,
    callable.  */
 
 tree
-build_aggr_init_expr (tree type, tree init)
+build_aggr_init_expr (tree type, tree init, tsubst_flags_t complain)
 {
   tree fn;
   tree slot;
@@ -382,7 +382,9 @@ build_aggr_init_expr (tree type, tree init)
 
   /* Make sure that we're not trying to create an instance of an
      abstract class.  */
-  abstract_virtuals_error (NULL_TREE, type);
+  if (abstract_virtuals_error_sfinae (NULL_TREE, type, complain)
+      && !(complain & tf_error))
+    return error_mark_node;
 
   if (TREE_CODE (init) == CALL_EXPR)
     fn = CALL_EXPR_FN (init);
@@ -437,9 +439,9 @@ build_aggr_init_expr (tree type, tree init)
    and language-specific expression expanders.  */
 
 tree
-build_cplus_new (tree type, tree init)
+build_cplus_new (tree type, tree init, tsubst_flags_t complain)
 {
-  tree rval = build_aggr_init_expr (type, init);
+  tree rval = build_aggr_init_expr (type, init, complain);
   tree slot;
 
   if (TREE_CODE (rval) == AGGR_INIT_EXPR)
@@ -1805,7 +1807,8 @@ bot_manip (tree* tp, int* walk_subtrees, void* data)
       tree u;
 
       if (TREE_CODE (TREE_OPERAND (t, 1)) == AGGR_INIT_EXPR)
-	u = build_cplus_new (TREE_TYPE (t), TREE_OPERAND (t, 1));
+	u = build_cplus_new (TREE_TYPE (t), TREE_OPERAND (t, 1),
+			     tf_warning_or_error);
       else
 	u = build_target_expr_with_type (TREE_OPERAND (t, 1), TREE_TYPE (t));
 
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index a45ed2d..71cfd8c 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -5320,7 +5320,7 @@ unary_complex_lvalue (enum tree_code code, tree arg)
 	if (TREE_CODE (arg) == SAVE_EXPR)
 	  targ = arg;
 	else
-	  targ = build_cplus_new (TREE_TYPE (arg), arg);
+	  targ = build_cplus_new (TREE_TYPE (arg), arg, tf_warning_or_error);
 	return build1 (ADDR_EXPR, build_pointer_type (TREE_TYPE (arg)), targ);
       }
 
@@ -6742,7 +6742,7 @@ cp_build_modify_expr (tree lhs, enum tree_code modifycode, tree rhs,
     {
       if (TREE_CODE (newrhs) == CALL_EXPR
 	  && TYPE_NEEDS_CONSTRUCTING (lhstype))
-	newrhs = build_cplus_new (lhstype, newrhs);
+	newrhs = build_cplus_new (lhstype, newrhs, complain);
 
       /* Can't initialize directly from a TARGET_EXPR, since that would
 	 cause the lhs to be constructed twice, and possibly result in
diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c
index f3a0079..f67073b 100644
--- a/gcc/cp/typeck2.c
+++ b/gcc/cp/typeck2.c
@@ -1614,7 +1614,7 @@ build_functional_cast (tree exp, tree parms, tsubst_flags_t complain)
   if (exp == error_mark_node)
     return error_mark_node;
 
-  return build_cplus_new (type, exp);
+  return build_cplus_new (type, exp, complain);
 }
commit a6b0d17f42b4bb62fe638253cb8e032d416eb883
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Apr 6 00:27:18 2011 -0400

    	* tree.c (build_aggr_init_expr): Always return error_mark_node
    	on abstract violation.

diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index c2aa389..014986d 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -382,8 +382,7 @@ build_aggr_init_expr (tree type, tree init, tsubst_flags_t complain)
 
   /* Make sure that we're not trying to create an instance of an
      abstract class.  */
-  if (abstract_virtuals_error_sfinae (NULL_TREE, type, complain)
-      && !(complain & tf_error))
+  if (abstract_virtuals_error_sfinae (NULL_TREE, type, complain))
     return error_mark_node;
 
   if (TREE_CODE (init) == CALL_EXPR)
commit e55c94447e50bdfaa1ca5c06ce57896364cbfeaf
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Apr 5 14:28:59 2011 -0400

    	PR c++/48450
    	* typeck.c (check_for_casting_away_constness): Take complain.
    	(build_static_cast_1, build_reinterpret_cast_1): Pass it.
    	(build_const_cast_1): Pass it.  Take full complain parm.
    	(build_const_cast, cp_build_c_cast): Adjust.

diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 71cfd8c..8e3796e 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -5579,42 +5579,47 @@ cp_build_compound_expr (tree lhs, tree rhs, tsubst_flags_t complain)
 }
 
 /* Issue a diagnostic message if casting from SRC_TYPE to DEST_TYPE
-   casts away constness.  CAST gives the type of cast.  
+   casts away constness.  CAST gives the type of cast.  Returns true
+   if the cast is ill-formed, false if it is well-formed.
 
    ??? This function warns for casting away any qualifier not just
    const.  We would like to specify exactly what qualifiers are casted
    away.
 */
 
-static void
+static bool
 check_for_casting_away_constness (tree src_type, tree dest_type,
-				  enum tree_code cast)
+				  enum tree_code cast, tsubst_flags_t complain)
 {
   /* C-style casts are allowed to cast away constness.  With
      WARN_CAST_QUAL, we still want to issue a warning.  */
   if (cast == CAST_EXPR && !warn_cast_qual)
-      return;
+    return false;
   
   if (!casts_away_constness (src_type, dest_type))
-    return;
+    return false;
 
   switch (cast)
     {
     case CAST_EXPR:
-      warning (OPT_Wcast_qual, 
-	       "cast from type %qT to type %qT casts away qualifiers",
-	       src_type, dest_type);
-      return;
+      if (complain & tf_warning)
+	warning (OPT_Wcast_qual,
+		 "cast from type %qT to type %qT casts away qualifiers",
+		 src_type, dest_type);
+      return false;
       
     case STATIC_CAST_EXPR:
-      error ("static_cast from type %qT to type %qT casts away qualifiers",
-	     src_type, dest_type);
-      return;
+      if (complain & tf_error)
+	error ("static_cast from type %qT to type %qT casts away qualifiers",
+	       src_type, dest_type);
+      return true;
       
     case REINTERPRET_CAST_EXPR:
-      error ("reinterpret_cast from type %qT to type %qT casts away qualifiers",
-	     src_type, dest_type);
-      return;
+      if (complain & tf_error)
+	error ("reinterpret_cast from type %qT to type %qT casts away qualifiers",
+	       src_type, dest_type);
+      return true;
+
     default:
       gcc_unreachable();
     }
@@ -5832,8 +5837,10 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p,
     {
       tree base;
 
-      if (!c_cast_p)
-	check_for_casting_away_constness (intype, type, STATIC_CAST_EXPR);
+      if (!c_cast_p
+	  && check_for_casting_away_constness (intype, type, STATIC_CAST_EXPR,
+					       complain))
+	return error_mark_node;
       base = lookup_base (TREE_TYPE (type), TREE_TYPE (intype),
 			  c_cast_p ? ba_unique : ba_check,
 			  NULL);
@@ -5868,10 +5875,13 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p,
 	}
       if (can_convert (t1, t2) || can_convert (t2, t1))
 	{
-	  if (!c_cast_p)
-	    check_for_casting_away_constness (intype, type, STATIC_CAST_EXPR);
+	  if (!c_cast_p
+	      && check_for_casting_away_constness (intype, type,
+						   STATIC_CAST_EXPR,
+						   complain))
+	    return error_mark_node;
 	  return convert_ptrmem (type, expr, /*allow_inverse_p=*/1,
-				 c_cast_p, tf_warning_or_error);
+				 c_cast_p, complain);
 	}
     }
 
@@ -5885,8 +5895,10 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p,
       && VOID_TYPE_P (TREE_TYPE (intype))
       && TYPE_PTROB_P (type))
     {
-      if (!c_cast_p)
-	check_for_casting_away_constness (intype, type, STATIC_CAST_EXPR);
+      if (!c_cast_p
+	  && check_for_casting_away_constness (intype, type, STATIC_CAST_EXPR,
+					       complain))
+	return error_mark_node;
       return build_nop (type, expr);
     }
 
@@ -6090,8 +6102,11 @@ build_reinterpret_cast_1 (tree type, tree expr, bool c_cast_p,
     {
       tree sexpr = expr;
 
-      if (!c_cast_p)
-	check_for_casting_away_constness (intype, type, REINTERPRET_CAST_EXPR);
+      if (!c_cast_p
+	  && check_for_casting_away_constness (intype, type,
+					       REINTERPRET_CAST_EXPR,
+					       complain))
+	return error_mark_node;
       /* Warn about possible alignment problems.  */
       if (STRICT_ALIGNMENT && warn_cast_align
           && (complain & tf_warning)
@@ -6168,7 +6183,7 @@ build_reinterpret_cast (tree type, tree expr, tsubst_flags_t complain)
    whether or not the conversion succeeded.  */
 
 static tree
-build_const_cast_1 (tree dst_type, tree expr, bool complain,
+build_const_cast_1 (tree dst_type, tree expr, tsubst_flags_t complain,
 		    bool *valid_p)
 {
   tree src_type;
@@ -6187,7 +6202,7 @@ build_const_cast_1 (tree dst_type, tree expr, bool complain,
 
   if (!POINTER_TYPE_P (dst_type) && !TYPE_PTRMEM_P (dst_type))
     {
-      if (complain)
+      if (complain & tf_error)
 	error ("invalid use of const_cast with type %qT, "
 	       "which is not a pointer, "
 	       "reference, nor a pointer-to-data-member type", dst_type);
@@ -6196,7 +6211,7 @@ build_const_cast_1 (tree dst_type, tree expr, bool complain,
 
   if (TREE_CODE (TREE_TYPE (dst_type)) == FUNCTION_TYPE)
     {
-      if (complain)
+      if (complain & tf_error)
 	error ("invalid use of const_cast with type %qT, which is a pointer "
 	       "or reference to a function type", dst_type);
       return error_mark_node;
@@ -6221,7 +6236,7 @@ build_const_cast_1 (tree dst_type, tree expr, bool complain,
       reference_type = dst_type;
       if (! real_lvalue_p (expr))
 	{
-	  if (complain)
+	  if (complain & tf_error)
 	    error ("invalid const_cast of an rvalue of type %qT to type %qT",
 		   src_type, dst_type);
 	  return error_mark_node;
@@ -6248,12 +6263,12 @@ build_const_cast_1 (tree dst_type, tree expr, bool complain,
 	  *valid_p = true;
 	  /* This cast is actually a C-style cast.  Issue a warning if
 	     the user is making a potentially unsafe cast.  */
-	  check_for_casting_away_constness (src_type, dst_type, CAST_EXPR);
+	  check_for_casting_away_constness (src_type, dst_type, CAST_EXPR,
+					    complain);
 	}
       if (reference_type)
 	{
-	  expr = cp_build_addr_expr (expr,
-				     complain ? tf_warning_or_error : tf_none);
+	  expr = cp_build_addr_expr (expr, complain);
 	  expr = build_nop (reference_type, expr);
 	  return convert_from_reference (expr);
 	}
@@ -6270,7 +6285,7 @@ build_const_cast_1 (tree dst_type, tree expr, bool complain,
 	}
     }
 
-  if (complain)
+  if (complain & tf_error)
     error ("invalid const_cast from type %qT to type %qT",
 	   src_type, dst_type);
   return error_mark_node;
@@ -6293,7 +6308,7 @@ build_const_cast (tree type, tree expr, tsubst_flags_t complain)
       return convert_from_reference (t);
     }
 
-  return build_const_cast_1 (type, expr, complain & tf_error,
+  return build_const_cast_1 (type, expr, complain,
 			     /*valid_p=*/NULL);
 }
 
@@ -6379,7 +6394,7 @@ cp_build_c_cast (tree type, tree expr, tsubst_flags_t complain)
 		"cast to pointer from integer of different size");
 
   /* A C-style cast can be a const_cast.  */
-  result = build_const_cast_1 (type, value, /*complain=*/false,
+  result = build_const_cast_1 (type, value, complain & tf_warning,
 			       &valid_p);
   if (valid_p)
     return result;
diff --git a/gcc/testsuite/c-c++-common/Wcast-qual-1.c b/gcc/testsuite/c-c++-common/Wcast-qual-1.c
new file mode 100644
index 0000000..640e4f0
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wcast-qual-1.c
@@ -0,0 +1,162 @@
+/* { dg-do compile } */
+/* { dg-options "-Wcast-qual" } */
+
+void
+f1 (void *bar)
+{
+  const void *p1 = (const void *) bar;
+  const char *p2 = (const char *) bar;
+  const void **p3 = (const void **) bar;
+  const char **p4 = (const char **) bar;
+  const void * const *p5 = (const void * const *) bar;
+  const char * const *p6 = (const char * const *) bar;
+  void * const *p7 = (void * const *) bar;
+  char * const *p8 = (char * const *) bar;
+  const void ***p9 = (const void ***) bar;
+  const char ***p10 = (const char ***) bar;
+  void * const **p11 = (void * const **) bar;
+  char * const **p12 = (char * const **) bar;
+  void ** const *p13 = (void ** const *) bar;
+  char ** const *p14 = (char ** const *) bar;
+  const void * const **p15 = (const void * const **) bar;
+  const char * const **p16 = (const char * const **) bar;
+  const void ** const *p17 = (const void ** const *) bar;
+  const char ** const *p18 = (const char ** const *) bar;
+  void * const * const * p19 = (void * const * const *) bar;
+  char * const * const * p20 = (char * const * const *) bar;
+  const void * const * const *p21 = (const void * const * const *) bar;
+  const char * const * const *p22 = (const char * const * const *) bar;
+}
+
+void
+f2 (void **bar)
+{
+  const void *p1 = (const void *) bar;
+  const char *p2 = (const char *) bar;
+  const void **p3 = (const void **) bar; /* { dg-warning "cast" } */
+  const char **p4 = (const char **) bar;
+  const void * const *p5 = (const void * const *) bar;
+  const char * const *p6 = (const char * const *) bar;
+  void * const *p7 = (void * const *) bar;
+  char * const *p8 = (char * const *) bar;
+  const void ***p9 = (const void ***) bar;
+  const char ***p10 = (const char ***) bar;
+  void * const **p11 = (void * const **) bar;
+  char * const **p12 = (char * const **) bar;
+  void ** const *p13 = (void ** const *) bar;
+  char ** const *p14 = (char ** const *) bar;
+  const void * const **p15 = (const void * const **) bar;
+  const char * const **p16 = (const char * const **) bar;
+  const void ** const *p17 = (const void ** const *) bar;
+  const char ** const *p18 = (const char ** const *) bar;
+  void * const * const * p19 = (void * const * const *) bar;
+  char * const * const * p20 = (char * const * const *) bar;
+  const void * const * const *p21 = (const void * const * const *) bar;
+  const char * const * const *p22 = (const char * const * const *) bar;
+}
+
+void
+f3 (void ***bar)
+{
+  const void *p1 = (const void *) bar;
+  const char *p2 = (const char *) bar;
+  const void **p3 = (const void **) bar;
+  const char **p4 = (const char **) bar;
+  const void * const *p5 = (const void * const *) bar;
+  const char * const *p6 = (const char * const *) bar;
+  void * const *p7 = (void * const *) bar;
+  char * const *p8 = (char * const *) bar;
+  const void ***p9 = (const void ***) bar; /* { dg-warning "cast" } */
+  const char ***p10 = (const char ***) bar;
+  void * const **p11 = (void * const **) bar; /* { dg-warning "cast" } */
+  char * const **p12 = (char * const **) bar;
+  void ** const *p13 = (void ** const *) bar;
+  char ** const *p14 = (char ** const *) bar;
+  const void * const **p15 = (const void * const **) bar; /* { dg-warning "cast" } */
+  const char * const **p16 = (const char * const **) bar;
+  const void ** const *p17 = (const void ** const *) bar; /* { dg-warning "cast" } */
+  const char ** const *p18 = (const char ** const *) bar;
+  void * const * const * p19 = (void * const * const *) bar;
+  char * const * const * p20 = (char * const * const *) bar;
+  const void * const * const *p21 = (const void * const * const *) bar;
+  const char * const * const *p22 = (const char * const * const *) bar;
+}
+
+void
+f4 (void * const **bar)
+{
+  const void ***p9 = (const void ***) bar; /* { dg-warning "cast" } */
+  void * const **p11 = (void * const **) bar;
+  void ** const *p13 = (void ** const *) bar; /* { dg-warning "cast" } */
+  const void * const **p15 = (const void * const **) bar; /* { dg-warning "cast" } */
+  const void ** const *p17 = (const void ** const *) bar; /* { dg-warning "cast" } */
+  void * const * const * p19 = (void * const * const *) bar;
+  const void * const * const *p21 = (const void * const * const *) bar;
+}
+
+void
+f5 (char ***bar)
+{
+  volatile const char ***p9 = (volatile const char ***) bar; /* { dg-warning "cast" } */
+  volatile char * const **p11 = (volatile char * const **) bar; /* { dg-warning "cast" } */
+  volatile char ** const *p13 = (volatile char ** const *) bar; /* { dg-warning "cast" } */
+  volatile const char * const **p15 = (volatile const char * const **) bar; /* { dg-warning "cast" } */
+  volatile const char ** const *p17 = (volatile const char ** const *) bar; /* { dg-warning "cast" } */
+  volatile char * const * const * p19 = (volatile char * const * const *) bar;
+  volatile const char * const * const *p21 = (volatile const char * const * const *) bar;
+}
+
+void
+f6 (char ***bar)
+{
+  const char * volatile **p9 = (const char * volatile **) bar; /* { dg-warning "cast" } */
+  char * volatile const **p11 = (char * volatile const **) bar; /* { dg-warning "cast" } */
+  char * volatile * const *p13 = (char * volatile * const *) bar;
+  const char * volatile const **p15 = (const char * volatile const **) bar; /* { dg-warning "cast" } */
+  const char * volatile * const *p17 = (const char * volatile * const *) bar; /* { dg-warning "cast" } */
+  char * volatile const * const * p19 = (char * volatile const * const *) bar;
+  const char * volatile const * const *p21 = (const char * volatile const * const *) bar;
+}
+
+void
+f7 (char ***bar)
+{
+  const char ** volatile *p9 = (const char ** volatile *) bar; /* { dg-warning "cast" } */
+  char * const * volatile *p11 = (char * const * volatile *) bar; /* { dg-warning "cast" } */
+  char ** volatile const *p13 = (char ** volatile const *) bar;
+  const char * const * volatile *p15 = (const char * const * volatile *) bar; /* { dg-warning "cast" } */
+  const char ** volatile const *p17 = (const char ** volatile const *) bar; /* { dg-warning "cast" } */
+  char * const * volatile const * p19 = (char * const * volatile const *) bar;
+  const char * const * volatile const *p21 = (const char * const * volatile const *) bar;
+}
+
+typedef int (intfn) (int);
+typedef intfn *pintfn;
+typedef const intfn *constfn;
+
+void
+f8 (constfn ***bar)
+{
+  const constfn *p1 = (const constfn *) bar;
+  const pintfn *p2 = (const pintfn *) bar;
+  const constfn **p3 = (const constfn **) bar;
+  const pintfn **p4 = (const pintfn **) bar;
+  const constfn * const *p5 = (const constfn * const *) bar;
+  const pintfn * const *p6 = (const pintfn * const *) bar;
+  constfn * const *p7 = (constfn * const *) bar;
+  pintfn * const *p8 = (pintfn * const *) bar;
+  const constfn ***p9 = (const constfn ***) bar; /* { dg-warning "cast" } */
+  const pintfn ***p10 = (const pintfn ***) bar; /* { dg-warning "cast" } */
+  constfn * const **p11 = (constfn * const **) bar; /* { dg-warning "cast" } */
+  pintfn * const **p12 = (pintfn * const **) bar; /* { dg-warning "cast" } */
+  constfn ** const *p13 = (constfn ** const *) bar;
+  pintfn ** const *p14 = (pintfn ** const *) bar;
+  const constfn * const **p15 = (const constfn * const **) bar; /* { dg-warning "cast" } */
+  const pintfn * const **p16 = (const pintfn * const **) bar; /* { dg-warning "cast" } */
+  const constfn ** const *p17 = (const constfn ** const *) bar; /* { dg-warning "cast" } */
+  const pintfn ** const *p18 = (const pintfn ** const *) bar; /* { dg-warning "cast" } */
+  constfn * const * const * p19 = (constfn * const * const *) bar;
+  pintfn * const * const * p20 = (pintfn * const * const *) bar;
+  const constfn * const * const *p21 = (const constfn * const * const *) bar;
+  const pintfn * const * const *p22 = (const pintfn * const * const *) bar;
+}
diff --git a/gcc/testsuite/g++.dg/warn/Wcast-qual2.C b/gcc/testsuite/g++.dg/warn/Wcast-qual2.C
deleted file mode 100644
index 88fdcfb..0000000
--- a/gcc/testsuite/g++.dg/warn/Wcast-qual2.C
+++ /dev/null
@@ -1,167 +0,0 @@
-/* { dg-do compile } */
-/* { dg-options "-Wcast-qual" } */
-
-/* The files gcc.dg/cast-qual-3.c and g++.dg/warn/Wcast-qual2.c are
-   duals.  they are intended to show that gcc -Wcast-qual and g++
-   -Wcast-qual emit warnings in the same cases.  If you change this
-   file, please also change the other one.  */
-
-void
-f1 (void *bar)
-{
-  const void *p1 = (const void *) bar;
-  const char *p2 = (const char *) bar;
-  const void **p3 = (const void **) bar;
-  const char **p4 = (const char **) bar;
-  const void * const *p5 = (const void * const *) bar;
-  const char * const *p6 = (const char * const *) bar;
-  void * const *p7 = (void * const *) bar;
-  char * const *p8 = (char * const *) bar;
-  const void ***p9 = (const void ***) bar;
-  const char ***p10 = (const char ***) bar;
-  void * const **p11 = (void * const **) bar;
-  char * const **p12 = (char * const **) bar;
-  void ** const *p13 = (void ** const *) bar;
-  char ** const *p14 = (char ** const *) bar;
-  const void * const **p15 = (const void * const **) bar;
-  const char * const **p16 = (const char * const **) bar;
-  const void ** const *p17 = (const void ** const *) bar;
-  const char ** const *p18 = (const char ** const *) bar;
-  void * const * const * p19 = (void * const * const *) bar;
-  char * const * const * p20 = (char * const * const *) bar;
-  const void * const * const *p21 = (const void * const * const *) bar;
-  const char * const * const *p22 = (const char * const * const *) bar;
-}
-
-void
-f2 (void **bar)
-{
-  const void *p1 = (const void *) bar;
-  const char *p2 = (const char *) bar;
-  const void **p3 = (const void **) bar; /* { dg-warning "cast" } */
-  const char **p4 = (const char **) bar;
-  const void * const *p5 = (const void * const *) bar;
-  const char * const *p6 = (const char * const *) bar;
-  void * const *p7 = (void * const *) bar;
-  char * const *p8 = (char * const *) bar;
-  const void ***p9 = (const void ***) bar;
-  const char ***p10 = (const char ***) bar;
-  void * const **p11 = (void * const **) bar;
-  char * const **p12 = (char * const **) bar;
-  void ** const *p13 = (void ** const *) bar;
-  char ** const *p14 = (char ** const *) bar;
-  const void * const **p15 = (const void * const **) bar;
-  const char * const **p16 = (const char * const **) bar;
-  const void ** const *p17 = (const void ** const *) bar;
-  const char ** const *p18 = (const char ** const *) bar;
-  void * const * const * p19 = (void * const * const *) bar;
-  char * const * const * p20 = (char * const * const *) bar;
-  const void * const * const *p21 = (const void * const * const *) bar;
-  const char * const * const *p22 = (const char * const * const *) bar;
-}
-
-void
-f3 (void ***bar)
-{
-  const void *p1 = (const void *) bar;
-  const char *p2 = (const char *) bar;
-  const void **p3 = (const void **) bar;
-  const char **p4 = (const char **) bar;
-  const void * const *p5 = (const void * const *) bar;
-  const char * const *p6 = (const char * const *) bar;
-  void * const *p7 = (void * const *) bar;
-  char * const *p8 = (char * const *) bar;
-  const void ***p9 = (const void ***) bar; /* { dg-warning "cast" } */
-  const char ***p10 = (const char ***) bar;
-  void * const **p11 = (void * const **) bar; /* { dg-warning "cast" } */
-  char * const **p12 = (char * const **) bar;
-  void ** const *p13 = (void ** const *) bar;
-  char ** const *p14 = (char ** const *) bar;
-  const void * const **p15 = (const void * const **) bar; /* { dg-warning "cast" } */
-  const char * const **p16 = (const char * const **) bar;
-  const void ** const *p17 = (const void ** const *) bar; /* { dg-warning "cast" } */
-  const char ** const *p18 = (const char ** const *) bar;
-  void * const * const * p19 = (void * const * const *) bar;
-  char * const * const * p20 = (char * const * const *) bar;
-  const void * const * const *p21 = (const void * const * const *) bar;
-  const char * const * const *p22 = (const char * const * const *) bar;
-}
-
-void
-f4 (void * const **bar)
-{
-  const void ***p9 = (const void ***) bar; /* { dg-warning "cast" } */
-  void * const **p11 = (void * const **) bar;
-  void ** const *p13 = (void ** const *) bar; /* { dg-warning "cast" } */
-  const void * const **p15 = (const void * const **) bar; /* { dg-warning "cast" } */
-  const void ** const *p17 = (const void ** const *) bar; /* { dg-warning "cast" } */
-  void * const * const * p19 = (void * const * const *) bar;
-  const void * const * const *p21 = (const void * const * const *) bar;
-}
-
-void
-f5 (char ***bar)
-{
-  volatile const char ***p9 = (volatile const char ***) bar; /* { dg-warning "cast" } */
-  volatile char * const **p11 = (volatile char * const **) bar; /* { dg-warning "cast" } */
-  volatile char ** const *p13 = (volatile char ** const *) bar; /* { dg-warning "cast" } */
-  volatile const char * const **p15 = (volatile const char * const **) bar; /* { dg-warning "cast" } */
-  volatile const char ** const *p17 = (volatile const char ** const *) bar; /* { dg-warning "cast" } */
-  volatile char * const * const * p19 = (volatile char * const * const *) bar;
-  volatile const char * const * const *p21 = (volatile const char * const * const *) bar;
-}
-
-void
-f6 (char ***bar)
-{
-  const char * volatile **p9 = (const char * volatile **) bar; /* { dg-warning "cast" } */
-  char * volatile const **p11 = (char * volatile const **) bar; /* { dg-warning "cast" } */
-  char * volatile * const *p13 = (char * volatile * const *) bar;
-  const char * volatile const **p15 = (const char * volatile const **) bar; /* { dg-warning "cast" } */
-  const char * volatile * const *p17 = (const char * volatile * const *) bar; /* { dg-warning "cast" } */
-  char * volatile const * const * p19 = (char * volatile const * const *) bar;
-  const char * volatile const * const *p21 = (const char * volatile const * const *) bar;
-}
-
-void
-f7 (char ***bar)
-{
-  const char ** volatile *p9 = (const char ** volatile *) bar; /* { dg-warning "cast" } */
-  char * const * volatile *p11 = (char * const * volatile *) bar; /* { dg-warning "cast" } */
-  char ** volatile const *p13 = (char ** volatile const *) bar;
-  const char * const * volatile *p15 = (const char * const * volatile *) bar; /* { dg-warning "cast" } */
-  const char ** volatile const *p17 = (const char ** volatile const *) bar; /* { dg-warning "cast" } */
-  char * const * volatile const * p19 = (char * const * volatile const *) bar;
-  const char * const * volatile const *p21 = (const char * const * volatile const *) bar;
-}
-
-typedef int (intfn) (int);
-typedef intfn *pintfn;
-typedef const intfn *constfn;
-
-void
-f8 (constfn ***bar)
-{
-  const constfn *p1 = (const constfn *) bar;
-  const pintfn *p2 = (const pintfn *) bar;
-  const constfn **p3 = (const constfn **) bar;
-  const pintfn **p4 = (const pintfn **) bar;
-  const constfn * const *p5 = (const constfn * const *) bar;
-  const pintfn * const *p6 = (const pintfn * const *) bar;
-  constfn * const *p7 = (constfn * const *) bar;
-  pintfn * const *p8 = (pintfn * const *) bar;
-  const constfn ***p9 = (const constfn ***) bar; /* { dg-warning "cast" } */
-  const pintfn ***p10 = (const pintfn ***) bar; /* { dg-warning "cast" } */
-  constfn * const **p11 = (constfn * const **) bar; /* { dg-warning "cast" } */
-  pintfn * const **p12 = (pintfn * const **) bar; /* { dg-warning "cast" } */
-  constfn ** const *p13 = (constfn ** const *) bar;
-  pintfn ** const *p14 = (pintfn ** const *) bar;
-  const constfn * const **p15 = (const constfn * const **) bar; /* { dg-warning "cast" } */
-  const pintfn * const **p16 = (const pintfn * const **) bar; /* { dg-warning "cast" } */
-  const constfn ** const *p17 = (const constfn ** const *) bar; /* { dg-warning "cast" } */
-  const pintfn ** const *p18 = (const pintfn ** const *) bar; /* { dg-warning "cast" } */
-  constfn * const * const * p19 = (constfn * const * const *) bar;
-  pintfn * const * const * p20 = (pintfn * const * const *) bar;
-  const constfn * const * const *p21 = (const constfn * const * const *) bar;
-  const pintfn * const * const *p22 = (const pintfn * const * const *) bar;
-}
diff --git a/gcc/testsuite/gcc.dg/cast-qual-3.c b/gcc/testsuite/gcc.dg/cast-qual-3.c
deleted file mode 100644
index 88fdcfb..0000000
--- a/gcc/testsuite/gcc.dg/cast-qual-3.c
+++ /dev/null
@@ -1,167 +0,0 @@
-/* { dg-do compile } */
-/* { dg-options "-Wcast-qual" } */
-
-/* The files gcc.dg/cast-qual-3.c and g++.dg/warn/Wcast-qual2.c are
-   duals.  they are intended to show that gcc -Wcast-qual and g++
-   -Wcast-qual emit warnings in the same cases.  If you change this
-   file, please also change the other one.  */
-
-void
-f1 (void *bar)
-{
-  const void *p1 = (const void *) bar;
-  const char *p2 = (const char *) bar;
-  const void **p3 = (const void **) bar;
-  const char **p4 = (const char **) bar;
-  const void * const *p5 = (const void * const *) bar;
-  const char * const *p6 = (const char * const *) bar;
-  void * const *p7 = (void * const *) bar;
-  char * const *p8 = (char * const *) bar;
-  const void ***p9 = (const void ***) bar;
-  const char ***p10 = (const char ***) bar;
-  void * const **p11 = (void * const **) bar;
-  char * const **p12 = (char * const **) bar;
-  void ** const *p13 = (void ** const *) bar;
-  char ** const *p14 = (char ** const *) bar;
-  const void * const **p15 = (const void * const **) bar;
-  const char * const **p16 = (const char * const **) bar;
-  const void ** const *p17 = (const void ** const *) bar;
-  const char ** const *p18 = (const char ** const *) bar;
-  void * const * const * p19 = (void * const * const *) bar;
-  char * const * const * p20 = (char * const * const *) bar;
-  const void * const * const *p21 = (const void * const * const *) bar;
-  const char * const * const *p22 = (const char * const * const *) bar;
-}
-
-void
-f2 (void **bar)
-{
-  const void *p1 = (const void *) bar;
-  const char *p2 = (const char *) bar;
-  const void **p3 = (const void **) bar; /* { dg-warning "cast" } */
-  const char **p4 = (const char **) bar;
-  const void * const *p5 = (const void * const *) bar;
-  const char * const *p6 = (const char * const *) bar;
-  void * const *p7 = (void * const *) bar;
-  char * const *p8 = (char * const *) bar;
-  const void ***p9 = (const void ***) bar;
-  const char ***p10 = (const char ***) bar;
-  void * const **p11 = (void * const **) bar;
-  char * const **p12 = (char * const **) bar;
-  void ** const *p13 = (void ** const *) bar;
-  char ** const *p14 = (char ** const *) bar;
-  const void * const **p15 = (const void * const **) bar;
-  const char * const **p16 = (const char * const **) bar;
-  const void ** const *p17 = (const void ** const *) bar;
-  const char ** const *p18 = (const char ** const *) bar;
-  void * const * const * p19 = (void * const * const *) bar;
-  char * const * const * p20 = (char * const * const *) bar;
-  const void * const * const *p21 = (const void * const * const *) bar;
-  const char * const * const *p22 = (const char * const * const *) bar;
-}
-
-void
-f3 (void ***bar)
-{
-  const void *p1 = (const void *) bar;
-  const char *p2 = (const char *) bar;
-  const void **p3 = (const void **) bar;
-  const char **p4 = (const char **) bar;
-  const void * const *p5 = (const void * const *) bar;
-  const char * const *p6 = (const char * const *) bar;
-  void * const *p7 = (void * const *) bar;
-  char * const *p8 = (char * const *) bar;
-  const void ***p9 = (const void ***) bar; /* { dg-warning "cast" } */
-  const char ***p10 = (const char ***) bar;
-  void * const **p11 = (void * const **) bar; /* { dg-warning "cast" } */
-  char * const **p12 = (char * const **) bar;
-  void ** const *p13 = (void ** const *) bar;
-  char ** const *p14 = (char ** const *) bar;
-  const void * const **p15 = (const void * const **) bar; /* { dg-warning "cast" } */
-  const char * const **p16 = (const char * const **) bar;
-  const void ** const *p17 = (const void ** const *) bar; /* { dg-warning "cast" } */
-  const char ** const *p18 = (const char ** const *) bar;
-  void * const * const * p19 = (void * const * const *) bar;
-  char * const * const * p20 = (char * const * const *) bar;
-  const void * const * const *p21 = (const void * const * const *) bar;
-  const char * const * const *p22 = (const char * const * const *) bar;
-}
-
-void
-f4 (void * const **bar)
-{
-  const void ***p9 = (const void ***) bar; /* { dg-warning "cast" } */
-  void * const **p11 = (void * const **) bar;
-  void ** const *p13 = (void ** const *) bar; /* { dg-warning "cast" } */
-  const void * const **p15 = (const void * const **) bar; /* { dg-warning "cast" } */
-  const void ** const *p17 = (const void ** const *) bar; /* { dg-warning "cast" } */
-  void * const * const * p19 = (void * const * const *) bar;
-  const void * const * const *p21 = (const void * const * const *) bar;
-}
-
-void
-f5 (char ***bar)
-{
-  volatile const char ***p9 = (volatile const char ***) bar; /* { dg-warning "cast" } */
-  volatile char * const **p11 = (volatile char * const **) bar; /* { dg-warning "cast" } */
-  volatile char ** const *p13 = (volatile char ** const *) bar; /* { dg-warning "cast" } */
-  volatile const char * const **p15 = (volatile const char * const **) bar; /* { dg-warning "cast" } */
-  volatile const char ** const *p17 = (volatile const char ** const *) bar; /* { dg-warning "cast" } */
-  volatile char * const * const * p19 = (volatile char * const * const *) bar;
-  volatile const char * const * const *p21 = (volatile const char * const * const *) bar;
-}
-
-void
-f6 (char ***bar)
-{
-  const char * volatile **p9 = (const char * volatile **) bar; /* { dg-warning "cast" } */
-  char * volatile const **p11 = (char * volatile const **) bar; /* { dg-warning "cast" } */
-  char * volatile * const *p13 = (char * volatile * const *) bar;
-  const char * volatile const **p15 = (const char * volatile const **) bar; /* { dg-warning "cast" } */
-  const char * volatile * const *p17 = (const char * volatile * const *) bar; /* { dg-warning "cast" } */
-  char * volatile const * const * p19 = (char * volatile const * const *) bar;
-  const char * volatile const * const *p21 = (const char * volatile const * const *) bar;
-}
-
-void
-f7 (char ***bar)
-{
-  const char ** volatile *p9 = (const char ** volatile *) bar; /* { dg-warning "cast" } */
-  char * const * volatile *p11 = (char * const * volatile *) bar; /* { dg-warning "cast" } */
-  char ** volatile const *p13 = (char ** volatile const *) bar;
-  const char * const * volatile *p15 = (const char * const * volatile *) bar; /* { dg-warning "cast" } */
-  const char ** volatile const *p17 = (const char ** volatile const *) bar; /* { dg-warning "cast" } */
-  char * const * volatile const * p19 = (char * const * volatile const *) bar;
-  const char * const * volatile const *p21 = (const char * const * volatile const *) bar;
-}
-
-typedef int (intfn) (int);
-typedef intfn *pintfn;
-typedef const intfn *constfn;
-
-void
-f8 (constfn ***bar)
-{
-  const constfn *p1 = (const constfn *) bar;
-  const pintfn *p2 = (const pintfn *) bar;
-  const constfn **p3 = (const constfn **) bar;
-  const pintfn **p4 = (const pintfn **) bar;
-  const constfn * const *p5 = (const constfn * const *) bar;
-  const pintfn * const *p6 = (const pintfn * const *) bar;
-  constfn * const *p7 = (constfn * const *) bar;
-  pintfn * const *p8 = (pintfn * const *) bar;
-  const constfn ***p9 = (const constfn ***) bar; /* { dg-warning "cast" } */
-  const pintfn ***p10 = (const pintfn ***) bar; /* { dg-warning "cast" } */
-  constfn * const **p11 = (constfn * const **) bar; /* { dg-warning "cast" } */
-  pintfn * const **p12 = (pintfn * const **) bar; /* { dg-warning "cast" } */
-  constfn ** const *p13 = (constfn ** const *) bar;
-  pintfn ** const *p14 = (pintfn ** const *) bar;
-  const constfn * const **p15 = (const constfn * const **) bar; /* { dg-warning "cast" } */
-  const pintfn * const **p16 = (const pintfn * const **) bar; /* { dg-warning "cast" } */
-  const constfn ** const *p17 = (const constfn ** const *) bar; /* { dg-warning "cast" } */
-  const pintfn ** const *p18 = (const pintfn ** const *) bar; /* { dg-warning "cast" } */
-  constfn * const * const * p19 = (constfn * const * const *) bar;
-  pintfn * const * const * p20 = (pintfn * const * const *) bar;
-  const constfn * const * const *p21 = (const constfn * const * const *) bar;
-  const pintfn * const * const *p22 = (const pintfn * const * const *) bar;
-}
commit dae42c0509e234d0cbcb148d35b9aeb35b3cf5fa
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Apr 5 14:31:54 2011 -0400

    	PR c++/48450
    	* call.c (resolve_args): Take complain.
    	(build_new_function_call, build_operator_new_call): Pass it.
    	(build_op_call, build_new_op, build_new_method_call): Pass it.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index c273027..f283bd1 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -155,7 +155,6 @@ static tree convert_like_real (conversion *, tree, tree, int, int, bool,
 			       bool, tsubst_flags_t);
 static void op_error (enum tree_code, enum tree_code, tree, tree,
 		      tree, bool);
-static VEC(tree,gc) *resolve_args (VEC(tree,gc) *);
 static struct z_candidate *build_user_type_conversion_1 (tree, tree, int);
 static void print_z_candidate (const char *, struct z_candidate *);
 static void print_z_candidates (location_t, struct z_candidate *);
@@ -3525,7 +3524,7 @@ build_integral_nontype_arg_conv (tree type, tree expr, tsubst_flags_t complain)
 /* Do any initial processing on the arguments to a function call.  */
 
 static VEC(tree,gc) *
-resolve_args (VEC(tree,gc) *args)
+resolve_args (VEC(tree,gc) *args, tsubst_flags_t complain)
 {
   unsigned int ix;
   tree arg;
@@ -3536,7 +3535,8 @@ resolve_args (VEC(tree,gc) *args)
 	return NULL;
       else if (VOID_TYPE_P (TREE_TYPE (arg)))
 	{
-	  error ("invalid use of void expression");
+	  if (complain & tf_error)
+	    error ("invalid use of void expression");
 	  return NULL;
 	}
       else if (invalid_nonstatic_memfn_p (arg, tf_warning_or_error))
@@ -3636,7 +3636,7 @@ build_new_function_call (tree fn, VEC(tree,gc) **args, bool koenig_p,
 
   if (args != NULL && *args != NULL)
     {
-      *args = resolve_args (*args);
+      *args = resolve_args (*args, complain);
       if (*args == NULL)
 	return error_mark_node;
     }
@@ -3707,7 +3707,7 @@ build_operator_new_call (tree fnname, VEC(tree,gc) **args,
   if (fn)
     *fn = NULL_TREE;
   VEC_safe_insert (tree, gc, *args, 0, *size);
-  *args = resolve_args (*args);
+  *args = resolve_args (*args, tf_warning_or_error);
   if (*args == NULL)
     return error_mark_node;
 
@@ -3820,7 +3820,7 @@ build_op_call (tree obj, VEC(tree,gc) **args, tsubst_flags_t complain)
 
   if (args != NULL && *args != NULL)
     {
-      *args = resolve_args (*args);
+      *args = resolve_args (*args, complain);
       if (*args == NULL)
 	return error_mark_node;
     }
@@ -4864,7 +4864,7 @@ build_new_op (enum tree_code code, int flags, tree arg1, tree arg2, tree arg3,
 	  if (overloaded_p)
 	    *overloaded_p = true;
 
-	  if (resolve_args (arglist) == NULL)
+	  if (resolve_args (arglist, complain) == NULL)
 	    result = error_mark_node;
 	  else
 	    result = build_over_call (cand, LOOKUP_NORMAL, complain);
@@ -6850,7 +6850,7 @@ build_new_method_call (tree instance, tree fns, VEC(tree,gc) **args,
   /* Process the argument list.  */
   if (args != NULL && *args != NULL)
     {
-      *args = resolve_args (*args);
+      *args = resolve_args (*args, complain);
       if (*args == NULL)
 	return error_mark_node;
     }
commit 9690fccd312c95916260a0ba5f07e397af811b37
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Apr 5 22:02:35 2011 -0400

    	PR c++/48452
    	* typeck.c (build_x_compound_expr_from_list): Return error_mark_node
    	in SFINAE context.

diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 8e3796e..ecd7d41 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -5472,6 +5472,8 @@ build_x_compound_expr_from_list (tree list, expr_list_kind exp,
 	  default:
 	    gcc_unreachable ();
 	  }
+      else
+	return error_mark_node;
 
       for (list = TREE_CHAIN (list); list; list = TREE_CHAIN (list))
 	expr = build_x_compound_expr (expr, TREE_VALUE (list), 
diff --git a/gcc/testsuite/g++.dg/cpp0x/sfinae10.C b/gcc/testsuite/g++.dg/cpp0x/sfinae10.C
new file mode 100644
index 0000000..ede8b70
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/sfinae10.C
@@ -0,0 +1,18 @@
+// PR c++/48452
+// { dg-options -std=c++0x }
+namespace std {
+  template <class T> T&& declval();
+}
+
+template<class T, class... Args>
+decltype(T(std::declval<Args>()...), char()) f(int);
+
+template<class, class...>
+char (&f(...))[2];
+
+struct A { virtual ~A() = 0; };
+struct B {};
+
+static_assert(sizeof(f<A, int, int>(0)) != 1, "Error"); // a
+static_assert(sizeof(f<B, void, int>(0)) != 1, "Error"); // b
+static_assert(sizeof(f<void, int, int>(0)) != 1, "Error"); // c
commit 6248bb19d8ddf86496da79db44ec14f7cca6dcef
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Apr 6 09:39:28 2011 -0400

    	PR c++/48468
    	* except.c (build_noexcept_spec): Propagate error_mark_node.
    	(finish_noexcept_expr): Likewise.

diff --git a/gcc/cp/except.c b/gcc/cp/except.c
index a814d67..874f111 100644
--- a/gcc/cp/except.c
+++ b/gcc/cp/except.c
@@ -1125,6 +1125,9 @@ finish_noexcept_expr (tree expr, tsubst_flags_t complain)
 {
   tree fn;
 
+  if (expr == error_mark_node)
+    return error_mark_node;
+
   if (processing_template_decl)
     return build_min (NOEXCEPT_EXPR, boolean_type_node, expr);
 
@@ -1212,6 +1215,8 @@ build_noexcept_spec (tree expr, int complain)
     return noexcept_true_spec;
   else if (expr == boolean_false_node)
     return noexcept_false_spec;
+  else if (expr == error_mark_node)
+    return error_mark_node;
   else
     {
       gcc_assert (processing_template_decl || expr == error_mark_node);
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept02.C b/gcc/testsuite/g++.dg/cpp0x/noexcept02.C
index be6fa00..60015e7 100644
--- a/gcc/testsuite/g++.dg/cpp0x/noexcept02.C
+++ b/gcc/testsuite/g++.dg/cpp0x/noexcept02.C
@@ -46,7 +46,9 @@ SA(!noexcept(f3(A())));
 template <class T1, class T2>
 void f (T1, T2) noexcept(noexcept(T1(), T2()));
 
-SA(noexcept(f3(1,1)));
+struct B { };
+
+SA(noexcept(f3(1,B())));
 SA(!noexcept(f3(1,A())));
 SA(!noexcept(f3(A(),1)));
 SA(!noexcept(f3(A(),A())));
diff --git a/gcc/testsuite/g++.dg/cpp0x/sfinae11.C b/gcc/testsuite/g++.dg/cpp0x/sfinae11.C
new file mode 100644
index 0000000..a3ffc34
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/sfinae11.C
@@ -0,0 +1,56 @@
+// PR c++/48468
+// { dg-options -std=c++0x }
+// { dg-prune-output "note" }
+
+template<class T>
+T&& declval() noexcept;
+
+template< class T >
+inline void f1( T& x ) noexcept( noexcept( declval<T&>().foo() ) )
+{
+  x.foo();
+}
+
+template< class T,
+  bool Noexcept = noexcept( declval<T&>().foo() )
+>
+inline void f2( T& x ) noexcept( Noexcept )
+{
+  x.foo();
+}
+
+// a common and trivial mistake
+template< class T >
+inline void f3( T& x ) noexcept( declval<T&>().foo() )
+{
+  x.foo();
+}
+
+struct X
+{
+  void foo();
+};
+
+struct Y
+{
+  void foo() noexcept;
+};
+
+struct Z {};
+
+int main()
+{
+  X x; Y y; Z z;
+
+  static_assert( !noexcept( f1(x) ), "OK." );
+  static_assert( !noexcept( f2(x) ), "OK." );
+  // static_assert( !noexcept( f3(x) ), "shall be ill-formed(OK)." );
+
+  static_assert(  noexcept( f1(y) ), "OK." );
+  static_assert(  noexcept( f2(y) ), "OK." );
+  // static_assert(  noexcept( f3(y) ), "shall be ill-formed(OK)." );
+
+  static_assert(  noexcept( f1(z) ), "shall be ill-formed." ); // { dg-error "no match" }
+  static_assert(  noexcept( f2(z) ), "shall be ill-formed." ); // { dg-error "no match" }
+  static_assert( !noexcept( f3(z) ), "shall be ill-formed." ); // { dg-error "no match" }
+}

Comments

H.J. Lu April 9, 2011, 2 p.m. UTC | #1
n Thu, Apr 7, 2011 at 2:32 PM, Jason Merrill <jason@redhat.com> wrote:
> People have been finding other places where we haven't been handling SFINAE
> properly.  In many cases, there are two patches for each fix; the first one
> fixes the bug conservatively (for possible application to 4.6), and the
> second one reorganizes things to be cleaner.
>
> Tested x86_64-pc-linux-gnu, applying to trunk.
>



>
> commit 9690fccd312c95916260a0ba5f07e397af811b37
> Author: Jason Merrill <jason@redhat.com>
> Date:   Tue Apr 5 22:02:35 2011 -0400
>
>        PR c++/48452
>        * typeck.c (build_x_compound_expr_from_list): Return error_mark_node
>        in SFINAE context.
>
> diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
> index 8e3796e..ecd7d41 100644
> --- a/gcc/cp/typeck.c
> +++ b/gcc/cp/typeck.c
> @@ -5472,6 +5472,8 @@ build_x_compound_expr_from_list (tree list,
> expr_list_kind exp,
>          default:
>            gcc_unreachable ();
>          }
> +      else
> +       return error_mark_node;
>
>       for (list = TREE_CHAIN (list); list; list = TREE_CHAIN (list))
>        expr = build_x_compound_expr (expr, TREE_VALUE (list),
> diff --git a/gcc/testsuite/g++.dg/cpp0x/sfinae10.C
> b/gcc/testsuite/g++.dg/cpp0x/sfinae10.C
> new file mode 100644
> index 0000000..ede8b70
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp0x/sfinae10.C
> @@ -0,0 +1,18 @@
> +// PR c++/48452
> +// { dg-options -std=c++0x }
> +namespace std {
> +  template <class T> T&& declval();
> +}
> +
> +template<class T, class... Args>
> +decltype(T(std::declval<Args>()...), char()) f(int);
> +
> +template<class, class...>
> +char (&f(...))[2];
> +
> +struct A { virtual ~A() = 0; };
> +struct B {};
> +
> +static_assert(sizeof(f<A, int, int>(0)) != 1, "Error"); // a
> +static_assert(sizeof(f<B, void, int>(0)) != 1, "Error"); // b
> +static_assert(sizeof(f<void, int, int>(0)) != 1, "Error"); // c
>

This caused:

http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48528
diff mbox

Patch

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 6ef6e6e..06b0b3e 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5346,7 +5346,7 @@  extern tree baselink_for_fns                    (tree);
 extern void finish_static_assert                (tree, tree, location_t,
                                                  bool);
 extern tree describable_type			(tree);
-extern tree finish_decltype_type                (tree, bool);
+extern tree finish_decltype_type                (tree, bool, tsubst_flags_t);
 extern tree finish_trait_expr			(enum cp_trait_kind, tree, tree);
 extern tree build_lambda_expr                   (void);
 extern tree build_lambda_object			(tree);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 9ed3a1f..607e9b8 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -10197,7 +10197,8 @@  cp_parser_decltype (cp_parser *parser)
       return error_mark_node;
     }
 
-  return finish_decltype_type (expr, id_expression_or_member_access_p);
+  return finish_decltype_type (expr, id_expression_or_member_access_p,
+			       tf_warning_or_error);
 }
 
 /* Special member functions [gram.special] */
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 5960e46..66db880 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -11025,7 +11025,7 @@  tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	  type = lambda_return_type (type);
 	else
 	  type = finish_decltype_type
-	    (type, DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t));
+	    (type, DECLTYPE_TYPE_ID_EXPR_OR_MEMBER_ACCESS_P (t), complain);
 	return cp_build_qualified_type_real (type,
 					     cp_type_quals (t)
 					     | cp_type_quals (type),
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index a15740a..80ec028 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -4785,7 +4785,8 @@  describable_type (tree expr)
    a full expression.  */
 
 tree
-finish_decltype_type (tree expr, bool id_expression_or_member_access_p)
+finish_decltype_type (tree expr, bool id_expression_or_member_access_p,
+		      tsubst_flags_t complain)
 {
   tree orig_expr = expr;
   tree type = NULL_TREE;
@@ -4798,7 +4799,8 @@  finish_decltype_type (tree expr, bool id_expression_or_member_access_p)
       || (TREE_CODE (expr) == BIT_NOT_EXPR
 	  && TYPE_P (TREE_OPERAND (expr, 0))))
     {
-      error ("argument to decltype must be an expression");
+      if (complain & tf_error)
+	error ("argument to decltype must be an expression");
       return error_mark_node;
     }
 
@@ -4864,7 +4866,9 @@  finish_decltype_type (tree expr, bool id_expression_or_member_access_p)
           if (OVL_CHAIN (expr)
 	      || TREE_CODE (OVL_FUNCTION (expr)) == TEMPLATE_DECL)
             {
-              error ("%qE refers to a set of overloaded functions", orig_expr);
+	      if (complain & tf_error)
+		error ("%qE refers to a set of overloaded functions",
+		       orig_expr);
               return error_mark_node;
             }
           else
@@ -4916,7 +4920,8 @@  finish_decltype_type (tree expr, bool id_expression_or_member_access_p)
         default:
 	  gcc_assert (TYPE_P (expr) || DECL_P (expr)
 		      || TREE_CODE (expr) == SCOPE_REF);
-          error ("argument to decltype must be an expression");
+	  if (complain & tf_error)
+	    error ("argument to decltype must be an expression");
           return error_mark_node;
         }
     }
@@ -4954,7 +4959,8 @@  finish_decltype_type (tree expr, bool id_expression_or_member_access_p)
 
   if (!type || type == unknown_type_node)
     {
-      error ("type of %qE is unknown", expr);
+      if (complain & tf_error)
+	error ("type of %qE is unknown", expr);
       return error_mark_node;
     }
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/sfinae7.C b/gcc/testsuite/g++.dg/cpp0x/sfinae7.C
new file mode 100644
index 0000000..0a95a96
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/sfinae7.C
@@ -0,0 +1,20 @@ 
+// { dg-options -std=c++0x }
+
+struct A
+{
+  void f();
+  void f(int);
+  typedef int g;
+};
+
+template <class T> decltype (T::f) f();
+template <class T> void f();
+
+template <class T> decltype (T::g) g();
+template <class T> void g();
+
+int main()
+{
+  f<A>();
+  g<A>();
+}