I've updated my proposal for return type deduction for normal functions
in C++14 for the upcoming Bristol meeting:
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3582.html
and this patch implements the changes in the new proposal relative to
the previous revision. I started trying to change the parsing of
initializers and return expressions in order to implement
decltype(auto), but that got rather hairy, particularly for
initializers, so I ended up going a different way: determining whether
an expression is an identifier or member reference based on the trees
rather than directly in the parser. To make that work, I needed to make
sure that a parenthesized expression, or a folded ?: expression, could
be distinguished from a simpler expression; to that end, I've adopted
PAREN_EXPR from the middle end.
Tested x86_64-pc-linux-gnu, applying to trunk.
commit 81070bed26366c95a3115b902c333bd32a1c9df8
Author: Jason Merrill <ja...@redhat.com>
Date: Fri Mar 22 21:31:38 2013 -0400
N3582
* cp-tree.h (AUTO_IS_DECLTYPE): New.
* parser.c (cp_parser_decltype): Handle decltype(auto).
(cp_parser_type_id_1): Allow auto without a late-specified
return in C++1y.
(cp_parser_primary_expression): Use the return value of
finish_parenthesized_expr.
(cp_parser_transaction_expression): Likewise.
* semantics.c (force_paren_expr): New.
(finish_parenthesized_expr): Use it.
* call.c (build_conditional_expr_1): Likewise.
* pt.c (do_auto_deduction): Handle decltype(auto).
(tsubst_copy): Handle PAREN_EXPR.
(tsubst_copy_and_build): Likewise.
* error.c (dump_expr): Handle PAREN_EXPR.
* cxx-pretty-print.c (pp_cxx_expression): Likewise.
* mangle.c (write_expression): Ignore PAREN_EXPR.
* parser.c (cp_parser_decltype_expr): Split out...
(cp_parser_decltype): ...from here.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index d1777a0..62d6e15 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -4845,6 +4845,8 @@ build_conditional_expr_1 (tree arg1, tree arg2, tree arg3,
lvalue, we must add a NON_LVALUE_EXPR. */
result = rvalue (result);
}
+ else
+ result = force_paren_expr (result);
return result;
}
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index edf46d4..521da00 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -121,6 +121,7 @@ c-common.h, not after.
4: TYPE_HAS_NONTRIVIAL_DESTRUCTOR
5: CLASS_TYPE_P (in RECORD_TYPE and UNION_TYPE)
ENUM_FIXED_UNDERLYING_TYPE_P (in ENUMERAL_TYPE)
+ AUTO_IS_DECLTYPE (in TEMPLATE_TYPE_PARM)
6: TYPE_DEPENDENT_P_VALID
Usage of DECL_LANG_FLAG_?:
@@ -4604,6 +4605,10 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
#define TEMPLATE_TYPE_PARAMETER_PACK(NODE) \
(TEMPLATE_PARM_PARAMETER_PACK (TEMPLATE_TYPE_PARM_INDEX (NODE)))
+/* True iff this TEMPLATE_TYPE_PARM represents decltype(auto). */
+#define AUTO_IS_DECLTYPE(NODE) \
+ (TYPE_LANG_FLAG_5 (TEMPLATE_TYPE_PARM_CHECK (NODE)))
+
/* These constants can used as bit flags in the process of tree formatting.
TFF_PLAIN_IDENTIFIER: unqualified part of a name.
@@ -5659,6 +5664,7 @@ extern tree finish_asm_stmt (int, tree, tree, tree, tree,
extern tree finish_label_stmt (tree);
extern void finish_label_decl (tree);
extern tree finish_parenthesized_expr (tree);
+extern tree force_paren_expr (tree);
extern tree finish_non_static_data_member (tree, tree, tree);
extern tree begin_stmt_expr (void);
extern tree finish_stmt_expr_expr (tree, tree);
diff --git a/gcc/cp/cxx-pretty-print.c b/gcc/cp/cxx-pretty-print.c
index 45ad20c..4275b45 100644
--- a/gcc/cp/cxx-pretty-print.c
+++ b/gcc/cp/cxx-pretty-print.c
@@ -1167,6 +1167,12 @@ pp_cxx_expression (cxx_pretty_printer *pp, tree t)
pp_cxx_ws_string (pp, "<lambda>");
break;
+ case PAREN_EXPR:
+ pp_cxx_left_paren (pp);
+ pp_cxx_expression (pp, TREE_OPERAND (t, 0));
+ pp_cxx_right_paren (pp);
+ break;
+
default:
pp_c_expression (pp_c_base (pp), t);
break;
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 2af900d..dd27e6c 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -2506,6 +2506,12 @@ dump_expr (tree t, int flags)
pp_string (cxx_pp, M_("<lambda>"));
break;
+ case PAREN_EXPR:
+ pp_cxx_left_paren (cxx_pp);
+ dump_expr (TREE_OPERAND (t, 0), flags | TFF_EXPR_IN_PARENS);
+ pp_cxx_right_paren (cxx_pp);
+ break;
+
/* This list is incomplete, but should suffice for now.
It is very important that `sorry' does not call
`report_error_function'. That could cause an infinite loop. */
diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c
index 08bfa22..e303ea2 100644
--- a/gcc/cp/mangle.c
+++ b/gcc/cp/mangle.c
@@ -2555,6 +2555,8 @@ write_expression (tree expr)
is converted (via qualification conversions) to another
type. */
while (TREE_CODE (expr) == NOP_EXPR
+ /* Parentheses aren't mangled. */
+ || code == PAREN_EXPR
|| TREE_CODE (expr) == NON_LVALUE_EXPR)
{
expr = TREE_OPERAND (expr, 0);
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index d36c984..8ad877e 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -4106,7 +4106,7 @@ cp_parser_primary_expression (cp_parser *parser,
example, the expression is of the form `A::B', since
`&A::B' might be a pointer-to-member, but `&(A::B)' is
not. */
- finish_parenthesized_expr (expr);
+ expr = finish_parenthesized_expr (expr);
/* DR 705: Wrapping an unqualified name in parentheses
suppresses arg-dependent lookup. We want to pass back
CP_ID_KIND_QUALIFIED for suppressing vtable lookup
@@ -11399,7 +11399,9 @@ cp_parser_decltype_expr (cp_parser *parser,
/* Parse a `decltype' type. Returns the type.
simple-type-specifier:
- decltype ( expression ) */
+ decltype ( expression )
+ C++14 proposal:
+ decltype ( auto ) */
static tree
cp_parser_decltype (cp_parser *parser)
@@ -11427,6 +11429,18 @@ cp_parser_decltype (cp_parser *parser)
if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
return error_mark_node;
+ /* decltype (auto) */
+ if (cxx_dialect >= cxx1y
+ && cp_lexer_next_token_is_keyword (parser->lexer, RID_AUTO))
+ {
+ cp_lexer_consume_token (parser->lexer);
+ if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN))
+ return error_mark_node;
+ expr = make_auto ();
+ AUTO_IS_DECLTYPE (expr) = true;
+ goto rewrite;
+ }
+
/* Types cannot be defined in a `decltype' expression. Save away the
old message. */
saved_message = parser->type_definition_forbidden_message;
@@ -11485,6 +11499,7 @@ cp_parser_decltype (cp_parser *parser)
expr = finish_decltype_type (expr, id_expression_or_member_access_p,
tf_warning_or_error);
+ rewrite:
/* Replace the decltype with a CPP_DECLTYPE so we don't need to parse
it again. */
start_token->type = CPP_DECLTYPE;
@@ -17207,6 +17222,7 @@ cp_parser_type_id_1 (cp_parser* parser, bool is_template_arg,
abstract_declarator = NULL;
if (type_specifier_seq.type
+ && cxx_dialect < cxx1y
&& type_uses_auto (type_specifier_seq.type))
{
/* A type-id with type 'auto' is only ok if the abstract declarator
@@ -28003,7 +28019,7 @@ cp_parser_transaction_expression (cp_parser *parser, enum rid keyword)
cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN);
expr = cp_parser_expression (parser, /*cast_p=*/false, NULL);
- finish_parenthesized_expr (expr);
+ expr = finish_parenthesized_expr (expr);
cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN);
}
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 4ef4ede..2fc282e 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -12303,6 +12303,7 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
case TYPEID_EXPR:
case REALPART_EXPR:
case IMAGPART_EXPR:
+ case PAREN_EXPR:
return build1
(code, tsubst (TREE_TYPE (t), args, complain, in_decl),
tsubst_copy (TREE_OPERAND (t, 0), args, complain, in_decl));
@@ -14549,6 +14550,9 @@ tsubst_copy_and_build (tree t,
RETURN (tsubst_expr(t, args, complain, in_decl,
integral_constant_expression_p));
+ case PAREN_EXPR:
+ RETURN (finish_parenthesized_expr (RECUR (TREE_OPERAND (t, 0))));
+
default:
/* Handle Objective-C++ constructs, if appropriate. */
{
@@ -20593,9 +20597,7 @@ listify_autos (tree type, tree auto_node)
tree
do_auto_deduction (tree type, tree init, tree auto_node)
{
- tree parms, tparms, targs;
- tree args[1];
- int val;
+ tree targs;
if (init == error_mark_node)
return error_mark_node;
@@ -20614,32 +20616,42 @@ do_auto_deduction (tree type, tree init, tree auto_node)
init = resolve_nondeduced_context (init);
- parms = build_tree_list (NULL_TREE, type);
- args[0] = init;
- tparms = make_tree_vec (1);
targs = make_tree_vec (1);
- TREE_VEC_ELT (tparms, 0)
- = build_tree_list (NULL_TREE, TYPE_NAME (auto_node));
- val = type_unification_real (tparms, targs, parms, args, 1, 0,
- DEDUCE_CALL, LOOKUP_NORMAL,
- /*explain_p=*/false);
- if (val > 0)
- {
- if (processing_template_decl)
- /* Try again at instantiation time. */
- return type;
- if (type && type != error_mark_node)
- /* If type is error_mark_node a diagnostic must have been
- emitted by now. Also, having a mention to '<type error>'
- in the diagnostic is not really useful to the user. */
+ if (AUTO_IS_DECLTYPE (auto_node))
+ {
+ bool id = (DECL_P (init) || TREE_CODE (init) == COMPONENT_REF);
+ TREE_VEC_ELT (targs, 0)
+ = finish_decltype_type (init, id, tf_warning_or_error);
+ }
+ else
+ {
+ tree parms = build_tree_list (NULL_TREE, type);
+ tree tparms = make_tree_vec (1);
+ int val;
+
+ TREE_VEC_ELT (tparms, 0)
+ = build_tree_list (NULL_TREE, TYPE_NAME (auto_node));
+ val = type_unification_real (tparms, targs, parms, &init, 1, 0,
+ DEDUCE_CALL, LOOKUP_NORMAL,
+ /*explain_p=*/false);
+ if (val > 0)
{
- if (cfun && auto_node == current_function_auto_return_pattern
- && LAMBDA_FUNCTION_P (current_function_decl))
- error ("unable to deduce lambda return type from %qE", init);
- else
- error ("unable to deduce %qT from %qE", type, init);
+ if (processing_template_decl)
+ /* Try again at instantiation time. */
+ return type;
+ if (type && type != error_mark_node)
+ /* If type is error_mark_node a diagnostic must have been
+ emitted by now. Also, having a mention to '<type error>'
+ in the diagnostic is not really useful to the user. */
+ {
+ if (cfun && auto_node == current_function_auto_return_pattern
+ && LAMBDA_FUNCTION_P (current_function_decl))
+ error ("unable to deduce lambda return type from %qE", init);
+ else
+ error ("unable to deduce %qT from %qE", type, init);
+ }
+ return error_mark_node;
}
- return error_mark_node;
}
/* If the list of declarators contains more than one declarator, the type
diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c
index 8bd7612..63f18d0 100644
--- a/gcc/cp/semantics.c
+++ b/gcc/cp/semantics.c
@@ -1507,6 +1507,38 @@ finish_mem_initializers (tree mem_inits)
emit_mem_initializers (mem_inits);
}
+/* Obfuscate EXPR if it looks like an id-expression or member access so
+ that the call to finish_decltype in do_auto_deduction will give the
+ right result. */
+
+tree
+force_paren_expr (tree expr)
+{
+ /* This is only needed for decltype(auto) in C++14. */
+ if (cxx_dialect < cxx1y)
+ return expr;
+
+ if (!DECL_P (expr) && TREE_CODE (expr) != COMPONENT_REF
+ && TREE_CODE (expr) != SCOPE_REF)
+ return expr;
+
+ if (processing_template_decl)
+ expr = build1 (PAREN_EXPR, TREE_TYPE (expr), expr);
+ else
+ {
+ cp_lvalue_kind kind = lvalue_kind (expr);
+ if ((kind & ~clk_class) != clk_none)
+ {
+ tree type = unlowered_expr_type (expr);
+ bool rval = !!(kind & clk_rvalueref);
+ type = cp_build_reference_type (type, rval);
+ expr = build_static_cast (type, expr, tf_warning_or_error);
+ }
+ }
+
+ return expr;
+}
+
/* Finish a parenthesized expression EXPR. */
tree
@@ -1525,6 +1557,8 @@ finish_parenthesized_expr (tree expr)
if (TREE_CODE (expr) == STRING_CST)
PAREN_STRING_LITERAL_P (expr) = 1;
+ expr = force_paren_expr (expr);
+
return expr;
}
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn15.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn15.C
new file mode 100644
index 0000000..bab58a6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn15.C
@@ -0,0 +1,50 @@
+// { dg-options "-std=c++1y -Wno-return-local-addr" }
+
+template<class,class> struct same_type;
+template<class T> struct same_type<T,T> {};
+
+int& f();
+int i;
+
+decltype(auto) g() { return f(); }
+decltype(auto) h1() { return i; }
+decltype(auto) h2() { return (i); }
+decltype(auto) h2a() { return 0,i; }
+
+struct A { int i; };
+A a;
+
+decltype(auto) h3() { return a.i; }
+decltype(auto) h4() { return (a.i); }
+
+template <class T>
+decltype(auto) h5(T t) { return t.i; }
+template <class T>
+decltype(auto) h6(T t) { return (t.i); }
+
+int main()
+{
+ decltype(auto) i = f();
+ same_type<decltype(i),int&>();
+ decltype(auto) i2 = i;
+ same_type<decltype(i2),int&>();
+ decltype(auto) i3 = ::i;
+ same_type<decltype(i3),int>();
+ decltype(auto) i4 = (::i);
+ same_type<decltype(i4),int&>();
+ decltype(auto) i5 = a.i;
+ same_type<decltype(i5),int>();
+ decltype(auto) i6 = (a.i);
+ same_type<decltype(i6),int&>();
+ decltype(auto) i7 = true ? ::i : ::i;
+ same_type<decltype(i7),int&>();
+
+ same_type<decltype(g()),int&>();
+ same_type<decltype(h1()),int>();
+ same_type<decltype(h2()),int&>();
+ same_type<decltype(h2a()),int&>();
+ same_type<decltype(h3()),int>();
+ same_type<decltype(h4()),int&>();
+ same_type<decltype(h5(a)),int>();
+ same_type<decltype(h6(a)),int&>();
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn16.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn16.C
new file mode 100644
index 0000000..5caec52
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn16.C
@@ -0,0 +1,12 @@
+// { dg-options -std=c++1y }
+
+template<class,class> struct ST;
+template<class T> struct ST<T,T> {};
+
+int j;
+auto x3 = []()->auto&& { return j; }; // OK: return type is int&
+
+int main()
+{
+ ST<decltype(x3()),int&>();
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/auto-fn17.C b/gcc/testsuite/g++.dg/cpp1y/auto-fn17.C
new file mode 100644
index 0000000..8bc961e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/auto-fn17.C
@@ -0,0 +1,22 @@
+// { dg-options -std=c++1y }
+// { dg-do run }
+
+int c;
+int d;
+
+struct A
+{
+ A() { ++c; }
+ A(const A&) { ++c; }
+ ~A() { ++d; }
+};
+
+A g() { return A(); }
+decltype(auto) f() { return g(); }
+
+int main()
+{
+ f();
+ if (c < 1 || c != d)
+ __builtin_abort ();
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/mangle1.C b/gcc/testsuite/g++.dg/cpp1y/mangle1.C
new file mode 100644
index 0000000..b593a48
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/mangle1.C
@@ -0,0 +1,13 @@
+// Test that the parens don't show up in the mangling
+// { dg-options "-std=c++1y -Wno-return-local-addr" }
+// { dg-final { scan-assembler "_Z1gI1AEDTdtfp_1iET_" } }
+
+struct A { int i; };
+
+template <class T>
+auto g(T t)->decltype((t.i)) { return t.i; }
+
+int main()
+{
+ g(A());
+}
commit 280f9803ad351e07d3edddabb83c20c1e7d17b23
Author: Jason Merrill <ja...@redhat.com>
Date: Fri Mar 22 21:17:59 2013 -0400
* parser.c (cp_parser_decltype_expr): Split out...
(cp_parser_decltype): ...from here.
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 09c5dff..d36c984 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -11286,59 +11286,15 @@ cp_parser_static_assert(cp_parser *parser, bool member_p)
finish_static_assert (condition, message, saved_loc, member_p);
}
-/* Parse a `decltype' type. Returns the type.
-
- simple-type-specifier:
- decltype ( expression ) */
+/* Parse the expression in decltype ( expression ). */
static tree
-cp_parser_decltype (cp_parser *parser)
+cp_parser_decltype_expr (cp_parser *parser,
+ bool &id_expression_or_member_access_p)
{
- tree expr;
- bool id_expression_or_member_access_p = false;
- const char *saved_message;
- bool saved_integral_constant_expression_p;
- bool saved_non_integral_constant_expression_p;
cp_token *id_expr_start_token;
- cp_token *start_token = cp_lexer_peek_token (parser->lexer);
-
- if (start_token->type == CPP_DECLTYPE)
- {
- /* Already parsed. */
- cp_lexer_consume_token (parser->lexer);
- return start_token->u.value;
- }
-
- /* Look for the `decltype' token. */
- if (!cp_parser_require_keyword (parser, RID_DECLTYPE, RT_DECLTYPE))
- return error_mark_node;
-
- /* Types cannot be defined in a `decltype' expression. Save away the
- old message. */
- saved_message = parser->type_definition_forbidden_message;
-
- /* And create the new one. */
- parser->type_definition_forbidden_message
- = G_("types may not be defined in %<decltype%> expressions");
-
- /* The restrictions on constant-expressions do not apply inside
- decltype expressions. */
- saved_integral_constant_expression_p
- = parser->integral_constant_expression_p;
- saved_non_integral_constant_expression_p
- = parser->non_integral_constant_expression_p;
- parser->integral_constant_expression_p = false;
-
- /* Do not actually evaluate the expression. */
- ++cp_unevaluated_operand;
-
- /* Do not warn about problems with the expression. */
- ++c_inhibit_evaluation_warnings;
+ tree expr;
- /* Parse the opening `('. */
- if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
- return error_mark_node;
-
/* First, try parsing an id-expression. */
id_expr_start_token = cp_lexer_peek_token (parser->lexer);
cp_parser_parse_tentatively (parser);
@@ -11428,32 +11384,88 @@ cp_parser_decltype (cp_parser *parser)
cp_parser_parse_definitely (parser);
else
{
- bool saved_greater_than_is_operator_p;
-
/* Abort our attempt to parse an id-expression or member access
expression. */
cp_parser_abort_tentative_parse (parser);
- /* Within a parenthesized expression, a `>' token is always
- the greater-than operator. */
- saved_greater_than_is_operator_p
- = parser->greater_than_is_operator_p;
- parser->greater_than_is_operator_p = true;
-
/* Parse a full expression. */
expr = cp_parser_expression (parser, /*cast_p=*/false,
/*decltype*/true, NULL);
+ }
+
+ return expr;
+}
+
+/* Parse a `decltype' type. Returns the type.
+
+ simple-type-specifier:
+ decltype ( expression ) */
- /* The `>' token might be the end of a template-id or
- template-parameter-list now. */
- parser->greater_than_is_operator_p
- = saved_greater_than_is_operator_p;
+static tree
+cp_parser_decltype (cp_parser *parser)
+{
+ tree expr;
+ bool id_expression_or_member_access_p = false;
+ const char *saved_message;
+ bool saved_integral_constant_expression_p;
+ bool saved_non_integral_constant_expression_p;
+ bool saved_greater_than_is_operator_p;
+ cp_token *start_token = cp_lexer_peek_token (parser->lexer);
+
+ if (start_token->type == CPP_DECLTYPE)
+ {
+ /* Already parsed. */
+ cp_lexer_consume_token (parser->lexer);
+ return start_token->u.value;
}
+ /* Look for the `decltype' token. */
+ if (!cp_parser_require_keyword (parser, RID_DECLTYPE, RT_DECLTYPE))
+ return error_mark_node;
+
+ /* Parse the opening `('. */
+ if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN))
+ return error_mark_node;
+
+ /* Types cannot be defined in a `decltype' expression. Save away the
+ old message. */
+ saved_message = parser->type_definition_forbidden_message;
+
+ /* And create the new one. */
+ parser->type_definition_forbidden_message
+ = G_("types may not be defined in %<decltype%> expressions");
+
+ /* The restrictions on constant-expressions do not apply inside
+ decltype expressions. */
+ saved_integral_constant_expression_p
+ = parser->integral_constant_expression_p;
+ saved_non_integral_constant_expression_p
+ = parser->non_integral_constant_expression_p;
+ parser->integral_constant_expression_p = false;
+
+ /* Within a parenthesized expression, a `>' token is always
+ the greater-than operator. */
+ saved_greater_than_is_operator_p
+ = parser->greater_than_is_operator_p;
+ parser->greater_than_is_operator_p = true;
+
+ /* Do not actually evaluate the expression. */
+ ++cp_unevaluated_operand;
+
+ /* Do not warn about problems with the expression. */
+ ++c_inhibit_evaluation_warnings;
+
+ expr = cp_parser_decltype_expr (parser, id_expression_or_member_access_p);
+
/* Go back to evaluating expressions. */
--cp_unevaluated_operand;
--c_inhibit_evaluation_warnings;
+ /* The `>' token might be the end of a template-id or
+ template-parameter-list now. */
+ parser->greater_than_is_operator_p
+ = saved_greater_than_is_operator_p;
+
/* Restore the old message and the integral constant expression
flags. */
parser->type_definition_forbidden_message = saved_message;