Patchwork C++ PATCH for c++/52748 (N3276 change to decltype of function call)

login
register
mail settings
Submitter Jason Merrill
Date March 16, 2013, 7:40 p.m.
Message ID <5144CAB1.3050803@redhat.com>
Download mbox | patch
Permalink /patch/228248/
State New
Headers show

Comments

Jason Merrill - March 16, 2013, 7:40 p.m.
N3276 changed the semantics of decltype of a function call so that a 
call that returns by value doesn't result in the creation of a 
temporary, so that the type doesn't need to be complete, the destructor 
doesn't need to be accessible, etc.

Tested x86_64-pc-linux-gnu, applying to trunk.

Patch

commit 31b7112c73c33d1696b5025c347671b74886fd23
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Feb 26 23:00:42 2013 -0500

    	N3276
    	PR c++/52748
    	* cp-tree.h (tsubst_flags): Add tf_decltype.
    	* call.c (build_cxx_call): Don't build a temporary if it's set.
    	(build_over_call): Make sure it's only passed to build_cxx_call.
    	* parser.c (cp_parser_primary_expression): Add decltype_p parm.
    	(cp_parser_unary_expression): Likewise.
    	(cp_parser_cast_expression): Likewise.
    	(cp_parser_binary_expression): Likewise.
    	(cp_parser_assignment_expression): Likewise.
    	(cp_parser_postfix_expression): Likewise.  Pass tf_decltype.
    	(cp_parser_explicit_instantiation): Add decltype_p.  Force a
    	temporary for a call on the LHS of a comma.
    	(cp_parser_decltype): Pass true to decltype_p parms.
    	* pt.c (tsubst) [DECLTYPE_TYPE]: Pass tf_decltype.
    	(tsubst_copy_and_build): Pass tf_decltype down only for
    	CALL_EXPR and the RHS of COMPOUND_EXPR.
    	* tree.c (build_cplus_new): Call complete_type_or_maybe_complain.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index e40c45f..8362c75 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -6693,6 +6693,10 @@  build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
       /* else continue to get conversion error.  */
     }
 
+  /* N3276 magic doesn't apply to nested calls.  */
+  int decltype_flag = (complain & tf_decltype);
+  complain &= ~tf_decltype;
+
   /* Find maximum size of vector to hold converted arguments.  */
   parmlen = list_length (parm);
   nargs = vec_safe_length (args) + (first_arg != NULL_TREE ? 1 : 0);
@@ -7064,7 +7068,7 @@  build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
 	return error_mark_node;
     }
 
-  return build_cxx_call (fn, nargs, argarray, complain);
+  return build_cxx_call (fn, nargs, argarray, complain|decltype_flag);
 }
 
 /* Build and return a call to FN, using NARGS arguments in ARGARRAY.
@@ -7106,12 +7110,20 @@  build_cxx_call (tree fn, int nargs, tree *argarray,
   if (VOID_TYPE_P (TREE_TYPE (fn)))
     return fn;
 
-  fn = require_complete_type_sfinae (fn, complain);
-  if (fn == error_mark_node)
-    return error_mark_node;
+  /* 5.2.2/11: If a function call is a prvalue of object type: if the
+     function call is either the operand of a decltype-specifier or the
+     right operand of a comma operator that is the operand of a
+     decltype-specifier, a temporary object is not introduced for the
+     prvalue. The type of the prvalue may be incomplete.  */
+  if (!(complain & tf_decltype))
+    {
+      fn = require_complete_type_sfinae (fn, complain);
+      if (fn == error_mark_node)
+	return error_mark_node;
 
-  if (MAYBE_CLASS_TYPE_P (TREE_TYPE (fn)))
-    fn = build_cplus_new (TREE_TYPE (fn), fn, complain);
+      if (MAYBE_CLASS_TYPE_P (TREE_TYPE (fn)))
+	fn = build_cplus_new (TREE_TYPE (fn), fn, complain);
+    }
   return convert_from_reference (fn);
 }
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index d9496d2..e2a96a5 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -4190,6 +4190,9 @@  enum tsubst_flags {
 				    conversion might be permissible,
 				    not actually performing the
 				    conversion.  */
+  tf_decltype = 1 << 7,          /* We are the operand of decltype.
+				    Used to implement the special rules
+				    for calls in decltype (5.2.2/11).  */
   tf_partial = 1 << 8,		 /* Doing initial explicit argument
 				    substitution in fn_type_unification.  */
   /* Convenient substitution flags combinations.  */
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 8e0c964..5add0c4 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -1802,7 +1802,7 @@  static tree cp_parser_nested_name_specifier
 static tree cp_parser_qualifying_entity
   (cp_parser *, bool, bool, bool, bool, bool);
 static tree cp_parser_postfix_expression
-  (cp_parser *, bool, bool, bool, cp_id_kind *);
+  (cp_parser *, bool, bool, bool, bool, cp_id_kind *);
 static tree cp_parser_postfix_open_square_expression
   (cp_parser *, tree, bool);
 static tree cp_parser_postfix_dot_deref_expression
@@ -1832,7 +1832,7 @@  static vec<tree, va_gc> *cp_parser_new_initializer
 static tree cp_parser_delete_expression
   (cp_parser *);
 static tree cp_parser_cast_expression
-  (cp_parser *, bool, bool, cp_id_kind *);
+  (cp_parser *, bool, bool, bool, cp_id_kind *);
 static tree cp_parser_binary_expression
   (cp_parser *, bool, bool, enum cp_parser_prec, cp_id_kind *);
 static tree cp_parser_question_colon_clause
@@ -1843,6 +1843,8 @@  static enum tree_code cp_parser_assignment_operator_opt
   (cp_parser *);
 static tree cp_parser_expression
   (cp_parser *, bool, cp_id_kind *);
+static tree cp_parser_expression
+  (cp_parser *, bool, bool, cp_id_kind *);
 static tree cp_parser_constant_expression
   (cp_parser *, bool, bool *);
 static tree cp_parser_builtin_offsetof
@@ -3900,6 +3902,7 @@  cp_parser_primary_expression (cp_parser *parser,
 			      bool address_p,
 			      bool cast_p,
 			      bool template_arg_p,
+			      bool decltype_p,
 			      cp_id_kind *idk)
 {
   cp_token *token = NULL;
@@ -4051,7 +4054,7 @@  cp_parser_primary_expression (cp_parser *parser,
 	else
 	  {
 	    /* Parse the parenthesized expression.  */
-	    expr = cp_parser_expression (parser, cast_p, idk);
+	    expr = cp_parser_expression (parser, cast_p, decltype_p, idk);
 	    /* Let the front end know that this expression was
 	       enclosed in parentheses. This matters in case, for
 	       example, the expression is of the form `A::B', since
@@ -4403,6 +4406,17 @@  cp_parser_primary_expression (cp_parser *parser,
     }
 }
 
+static inline tree
+cp_parser_primary_expression (cp_parser *parser,
+			      bool address_p,
+			      bool cast_p,
+			      bool template_arg_p,
+			      cp_id_kind *idk)
+{
+  return cp_parser_primary_expression (parser, address_p, cast_p, template_arg_p,
+				       /*decltype*/false, idk);
+}
+
 /* Parse an id-expression.
 
    id-expression:
@@ -5364,7 +5378,7 @@  cp_parser_qualifying_entity (cp_parser *parser,
 
 static tree
 cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
-                              bool member_access_only_p,
+                              bool member_access_only_p, bool decltype_p,
 			      cp_id_kind * pidk_return)
 {
   cp_token *token;
@@ -5625,11 +5639,17 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	postfix_expression
 	  = cp_parser_primary_expression (parser, address_p, cast_p,
 					  /*template_arg_p=*/false,
+					  decltype_p,
 					  &idk);
       }
       break;
     }
 
+  /* Note that we don't need to worry about calling build_cplus_new on a
+     class-valued CALL_EXPR in decltype when it isn't the end of the
+     postfix-expression; unary_complex_lvalue will take care of that for
+     all these cases.  */
+
   /* Keep looping until the postfix-expression is complete.  */
   while (true)
     {
@@ -5668,8 +5688,12 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 	    bool is_builtin_constant_p;
 	    bool saved_integral_constant_expression_p = false;
 	    bool saved_non_integral_constant_expression_p = false;
+	    int complain = tf_warning_or_error;
 	    vec<tree, va_gc> *args;
 
+	    if (decltype_p)
+	      complain |= tf_decltype;
+
             is_member_access = false;
 
 	    is_builtin_constant_p
@@ -5726,7 +5750,7 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 			  postfix_expression
 			    = perform_koenig_lookup (postfix_expression, args,
 						     /*include_std=*/false,
-						     tf_warning_or_error);
+						     complain);
 		      }
 		    else
 		      postfix_expression
@@ -5752,7 +5776,7 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 			  postfix_expression
 			    = perform_koenig_lookup (postfix_expression, args,
 						     /*include_std=*/false,
-						     tf_warning_or_error);
+						     complain);
 		      }
 		  }
 	      }
@@ -5784,21 +5808,21 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 			 ? LOOKUP_NORMAL|LOOKUP_NONVIRTUAL
 			 : LOOKUP_NORMAL),
 			/*fn_p=*/NULL,
-			tf_warning_or_error));
+			complain));
 		  }
 		else
 		  postfix_expression
 		    = finish_call_expr (postfix_expression, &args,
 					/*disallow_virtual=*/false,
 					/*koenig_p=*/false,
-					tf_warning_or_error);
+					complain);
 	      }
 	    else if (TREE_CODE (postfix_expression) == OFFSET_REF
 		     || TREE_CODE (postfix_expression) == MEMBER_REF
 		     || TREE_CODE (postfix_expression) == DOTSTAR_EXPR)
 	      postfix_expression = (build_offset_ref_call_from_tree
 				    (postfix_expression, &args,
-				     tf_warning_or_error));
+				     complain));
 	    else if (idk == CP_ID_KIND_QUALIFIED)
 	      /* A call to a static class member, or a namespace-scope
 		 function.  */
@@ -5806,14 +5830,14 @@  cp_parser_postfix_expression (cp_parser *parser, bool address_p, bool cast_p,
 		= finish_call_expr (postfix_expression, &args,
 				    /*disallow_virtual=*/true,
 				    koenig_p,
-				    tf_warning_or_error);
+				    complain);
 	    else
 	      /* All other function calls.  */
 	      postfix_expression
 		= finish_call_expr (postfix_expression, &args,
 				    /*disallow_virtual=*/false,
 				    koenig_p,
-				    tf_warning_or_error);
+				    complain);
 
 	    /* The POSTFIX_EXPRESSION is certainly no longer an id.  */
 	    idk = CP_ID_KIND_NONE;
@@ -6414,7 +6438,7 @@  cp_parser_pseudo_destructor_name (cp_parser* parser,
 
 static tree
 cp_parser_unary_expression (cp_parser *parser, bool address_p, bool cast_p,
-			    cp_id_kind * pidk)
+			    bool decltype_p, cp_id_kind * pidk)
 {
   cp_token *token;
   enum tree_code unary_operator;
@@ -6635,7 +6659,9 @@  cp_parser_unary_expression (cp_parser *parser, bool address_p, bool cast_p,
       cast_expression
 	= cp_parser_cast_expression (parser,
 				     unary_operator == ADDR_EXPR,
-				     /*cast_p=*/false, pidk);
+				     /*cast_p=*/false,
+				     /*decltype*/false,
+				     pidk);
       /* Now, build an appropriate representation.  */
       switch (unary_operator)
 	{
@@ -6681,9 +6707,18 @@  cp_parser_unary_expression (cp_parser *parser, bool address_p, bool cast_p,
 
   return cp_parser_postfix_expression (parser, address_p, cast_p,
                                        /*member_access_only_p=*/false,
+				       decltype_p,
 				       pidk);
 }
 
+static inline tree
+cp_parser_unary_expression (cp_parser *parser, bool address_p, bool cast_p,
+			    cp_id_kind * pidk)
+{
+  return cp_parser_unary_expression (parser, address_p, cast_p,
+				     /*decltype*/false, pidk);
+}
+
 /* Returns ERROR_MARK if TOKEN is not a unary-operator.  If TOKEN is a
    unary-operator, the corresponding tree code is returned.  */
 
@@ -7162,7 +7197,7 @@  cp_parser_tokens_start_cast_expression (cp_parser *parser)
 
 static tree
 cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
-			   cp_id_kind * pidk)
+			   bool decltype_p, cp_id_kind * pidk)
 {
   /* If it's a `(', then we might be looking at a cast.  */
   if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
@@ -7236,7 +7271,9 @@  cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 	  cp_parser_parse_definitely (parser);
 	  expr = cp_parser_cast_expression (parser,
 					    /*address_p=*/false,
-					    /*cast_p=*/true, pidk);
+					    /*cast_p=*/true,
+					    /*decltype_p=*/false,
+					    pidk);
 
 	  /* Warn about old-style casts, if so requested.  */
 	  if (warn_old_style_cast
@@ -7262,7 +7299,8 @@  cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 
   /* If we get here, then it's not a cast, so it must be a
      unary-expression.  */
-  return cp_parser_unary_expression (parser, address_p, cast_p, pidk);
+  return cp_parser_unary_expression (parser, address_p, cast_p,
+				     decltype_p, pidk);
 }
 
 /* Parse a binary expression of the general form:
@@ -7347,6 +7385,7 @@  cp_parser_cast_expression (cp_parser *parser, bool address_p, bool cast_p,
 static tree
 cp_parser_binary_expression (cp_parser* parser, bool cast_p,
 			     bool no_toplevel_fold_p,
+			     bool decltype_p,
 			     enum cp_parser_prec prec,
 			     cp_id_kind * pidk)
 {
@@ -7361,7 +7400,7 @@  cp_parser_binary_expression (cp_parser* parser, bool cast_p,
 
   /* Parse the first expression.  */
   current.lhs = cp_parser_cast_expression (parser, /*address_p=*/false,
-					   cast_p, pidk);
+					   cast_p, decltype_p, pidk);
   current.lhs_type = ERROR_MARK;
   current.prec = prec;
 
@@ -7498,6 +7537,15 @@  cp_parser_binary_expression (cp_parser* parser, bool cast_p,
   return current.lhs;
 }
 
+static tree
+cp_parser_binary_expression (cp_parser* parser, bool cast_p,
+			     bool no_toplevel_fold_p,
+			     enum cp_parser_prec prec,
+			     cp_id_kind * pidk)
+{
+  return cp_parser_binary_expression (parser, cast_p, no_toplevel_fold_p,
+				      /*decltype*/false, prec, pidk);
+}
 
 /* Parse the `? expression : assignment-expression' part of a
    conditional-expression.  The LOGICAL_OR_EXPR is the
@@ -7567,12 +7615,13 @@  cp_parser_question_colon_clause (cp_parser* parser, tree logical_or_expr)
      throw-expression
 
    CAST_P is true if this expression is the target of a cast.
+   DECLTYPE_P is true if this expression is the operand of decltype.
 
    Returns a representation for the expression.  */
 
 static tree
 cp_parser_assignment_expression (cp_parser* parser, bool cast_p,
-				 cp_id_kind * pidk)
+				 bool decltype_p, cp_id_kind * pidk)
 {
   tree expr;
 
@@ -7586,6 +7635,7 @@  cp_parser_assignment_expression (cp_parser* parser, bool cast_p,
     {
       /* Parse the binary expressions (logical-or-expression).  */
       expr = cp_parser_binary_expression (parser, cast_p, false,
+					  decltype_p,
 					  PREC_NOT_OPERATOR, pidk);
       /* If the next token is a `?' then we're actually looking at a
 	 conditional-expression.  */
@@ -7631,6 +7681,14 @@  cp_parser_assignment_expression (cp_parser* parser, bool cast_p,
   return expr;
 }
 
+static tree
+cp_parser_assignment_expression (cp_parser* parser, bool cast_p,
+				 cp_id_kind * pidk)
+{
+  return cp_parser_assignment_expression (parser, cast_p,
+					  /*decltype*/false, pidk);
+}
+
 /* Parse an (optional) assignment-operator.
 
    assignment-operator: one of
@@ -7722,11 +7780,14 @@  cp_parser_assignment_operator_opt (cp_parser* parser)
      expression , assignment-expression
 
    CAST_P is true if this expression is the target of a cast.
+   DECLTYPE_P is true if this expression is the immediate operand of decltype,
+     except possibly parenthesized or on the RHS of a comma (N3276).
 
    Returns a representation of the expression.  */
 
 static tree
-cp_parser_expression (cp_parser* parser, bool cast_p, cp_id_kind * pidk)
+cp_parser_expression (cp_parser* parser, bool cast_p, bool decltype_p,
+		      cp_id_kind * pidk)
 {
   tree expression = NULL_TREE;
   location_t loc = UNKNOWN_LOCATION;
@@ -7737,7 +7798,19 @@  cp_parser_expression (cp_parser* parser, bool cast_p, cp_id_kind * pidk)
 
       /* Parse the next assignment-expression.  */
       assignment_expression
-	= cp_parser_assignment_expression (parser, cast_p, pidk);
+	= cp_parser_assignment_expression (parser, cast_p, decltype_p, pidk);
+
+      /* We don't create a temporary for a call that is the immediate operand
+	 of decltype or on the RHS of a comma.  But when we see a comma, we
+	 need to create a temporary for a call on the LHS.  */
+      if (decltype_p && !processing_template_decl
+	  && TREE_CODE (assignment_expression) == CALL_EXPR
+	  && CLASS_TYPE_P (TREE_TYPE (assignment_expression))
+	  && cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	assignment_expression
+	  = build_cplus_new (TREE_TYPE (assignment_expression),
+			     assignment_expression, tf_warning_or_error);
+
       /* If this is the first assignment-expression, we can just
 	 save it away.  */
       if (!expression)
@@ -7761,6 +7834,12 @@  cp_parser_expression (cp_parser* parser, bool cast_p, cp_id_kind * pidk)
   return expression;
 }
 
+static inline tree
+cp_parser_expression (cp_parser* parser, bool cast_p, cp_id_kind * pidk)
+{
+  return cp_parser_expression (parser, cast_p, /*decltype*/false, pidk);
+}
+
 /* Parse a constant-expression.
 
    constant-expression:
@@ -11287,7 +11366,7 @@  cp_parser_decltype (cp_parser *parser)
 
       /* Parse a class member access.  */
       expr = cp_parser_postfix_expression (parser, /*address_p=*/false,
-                                           /*cast_p=*/false,
+                                           /*cast_p=*/false, /*decltype*/true,
                                            /*member_access_only_p=*/true, NULL);
 
       if (expr 
@@ -11315,7 +11394,8 @@  cp_parser_decltype (cp_parser *parser)
       parser->greater_than_is_operator_p = true;
 
       /* Parse a full expression.  */
-      expr = cp_parser_expression (parser, /*cast_p=*/false, NULL);
+      expr = cp_parser_expression (parser, /*cast_p=*/false,
+				   /*decltype*/true, NULL);
 
       /* The `>' token might be the end of a template-id or
 	 template-parameter-list now.  */
@@ -22033,7 +22113,7 @@  static tree
 cp_parser_simple_cast_expression (cp_parser *parser)
 {
   return cp_parser_cast_expression (parser, /*address_p=*/false,
-				    /*cast_p=*/false, NULL);
+				    /*cast_p=*/false, /*decltype*/false, NULL);
 }
 
 /* Parse a functional cast to TYPE.  Returns an expression
@@ -26830,7 +26910,7 @@  cp_parser_omp_for_incr (cp_parser *parser, tree decl)
       op = (token->type == CPP_PLUS_PLUS
 	    ? PREINCREMENT_EXPR : PREDECREMENT_EXPR);
       cp_lexer_consume_token (parser->lexer);
-      lhs = cp_parser_cast_expression (parser, false, false, NULL);
+      lhs = cp_parser_simple_cast_expression (parser);
       if (lhs != decl)
 	return error_mark_node;
       return build2 (op, TREE_TYPE (decl), decl, NULL_TREE);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index ce07fa4..fd1ad9b 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -11781,7 +11781,7 @@  tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	++c_inhibit_evaluation_warnings;
 
 	type = tsubst_expr (DECLTYPE_TYPE_EXPR (t), args,
-			    complain, in_decl,
+			    complain|tf_decltype, in_decl,
 			    /*integral_constant_expression_p=*/false);
 
 	--cp_unevaluated_operand;
@@ -13417,6 +13417,12 @@  tsubst_copy_and_build (tree t,
   if (EXPR_HAS_LOCATION (t))
     input_location = EXPR_LOCATION (t);
 
+  /* N3276 decltype magic only applies to calls at the top level or on the
+     right side of a comma.  */
+  if (TREE_CODE (t) != CALL_EXPR
+      && TREE_CODE (t) != COMPOUND_EXPR)
+    complain &= ~tf_decltype;
+
   switch (TREE_CODE (t))
     {
     case USING_DECL:
@@ -13848,10 +13854,16 @@  tsubst_copy_and_build (tree t,
 	complain));
 
     case COMPOUND_EXPR:
-      RETURN (build_x_compound_expr (EXPR_LOCATION (t),
-				    RECUR (TREE_OPERAND (t, 0)),
-				    RECUR (TREE_OPERAND (t, 1)),
-                                    complain));
+      {
+	tree op0 = tsubst_copy_and_build (TREE_OPERAND (t, 0), args,
+					  complain & ~tf_decltype, in_decl,
+					  /*function_p=*/false,
+					  integral_constant_expression_p);
+	RETURN (build_x_compound_expr (EXPR_LOCATION (t),
+				       op0,
+				       RECUR (TREE_OPERAND (t, 1)),
+				       complain));
+      }
 
     case CALL_EXPR:
       {
@@ -13862,6 +13874,10 @@  tsubst_copy_and_build (tree t,
 	bool koenig_p;
 	tree ret;
 
+	/* Don't pass tf_decltype down to subexpressions.  */
+	tsubst_flags_t decltype_flag = (complain & tf_decltype);
+	complain &= ~tf_decltype;
+
 	function = CALL_EXPR_FN (t);
 	/* When we parsed the expression,  we determined whether or
 	   not Koenig lookup should be performed.  */
@@ -14028,6 +14044,9 @@  tsubst_copy_and_build (tree t,
 	if (DECL_P (function))
 	  mark_used (function);
 
+	/* Put back tf_decltype for the actual call.  */
+	complain |= decltype_flag;
+
 	if (TREE_CODE (function) == OFFSET_REF)
 	  ret = build_offset_ref_call_from_tree (function, &call_args,
 						 complain);
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 178b80a..9707825 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -469,6 +469,9 @@  build_cplus_new (tree type, tree init, tsubst_flags_t complain)
   tree rval = build_aggr_init_expr (type, init);
   tree slot;
 
+  if (!complete_type_or_maybe_complain (type, init, complain))
+    return error_mark_node;
+
   /* Make sure that we're not trying to create an instance of an
      abstract class.  */
   if (abstract_virtuals_error_sfinae (NULL_TREE, type, complain))
diff --git a/gcc/testsuite/g++.dg/cpp0x/decltype-call1.C b/gcc/testsuite/g++.dg/cpp0x/decltype-call1.C
new file mode 100644
index 0000000..2616bb0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/decltype-call1.C
@@ -0,0 +1,32 @@ 
+// PR c++/52748
+// N3276
+// { dg-do compile { target c++11 } }
+
+struct A;			// { dg-error "forward declaration" }
+A f();
+
+decltype(f()) g1();		 // OK
+decltype(((f()))) g2b();	 // OK
+decltype(42,f()) g3();		 // OK
+decltype(42,45,f()) g3b();	 // OK
+decltype(42,45,(f())) g3c();	 // OK
+decltype(42,((45,(f())))) g3c(); // OK
+
+decltype(f(),42) g4();		 // { dg-error "" }
+decltype(45,f(),42) g4b();	 // { dg-error "" }
+
+class B
+{
+  ~B();				// { dg-error "private" }
+public:
+  int i;
+  void operator[](int);
+};
+B h();
+
+void i(const B&);
+
+decltype(h()) g5a();		// OK
+decltype(h().i) g5();		// { dg-error "" }
+decltype(h()[0]) g6();		// { dg-error "" }
+decltype(i(h())) g7();		// { dg-error "" }