public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* New C parser [patch]
@ 2004-10-23  1:25 Joseph S. Myers
  2004-10-23  2:39 ` Steven Bosscher
                   ` (3 more replies)
  0 siblings, 4 replies; 42+ messages in thread
From: Joseph S. Myers @ 2004-10-23  1:25 UTC (permalink / raw)
  To: gcc-patches

Writing a C parser is quicker than reading, analysing and replying to
hundreds of list messages discussing how to implement OpenMP and what
should or should not be replaced to do so, so I just wrote one over
the past week.  This is a hand-written, recursive-descent parser for C
that parses the same GNU C language as the Bison parser, just
replacing the parser and not other code at the same time.  It does not
yet have Objective-C support, although that should not be hard to add
(the lexer support and Objective-C parts in actions for C syntax
productions are present, but the Objective-C productions are not
handled); it does however handle the complete C language the old
parser did; with the present patch C uses the new parser and
Objective-C the old one.  There are many ??? comments where some
aspect of the language as accepted by the old parser seemed doubtful
but was duly preserved so all language changes can be considered
independently of a parser change.

File sizes for comparison:
-rw-r--r--  1 jsm28 jsm28 100399 Oct 19 19:40 c-parse.in
-rw-rw-r--  1 jsm28 jsm28 143561 Oct 22 20:24 c-parser.c
-rw-r--r--  1 jsm28 jsm28 488242 Oct 21 13:12 cp/parser.c

Error recovery may also need more work; parts are copied from the C++
parser, but it needs testing and tuning.  Although various places
where error location differed from the Bison parser have been
adjusted, more may need adjusting (as shown by GCC and GDB testsuite
runs), and ultimately the right solution after this parser is in is to
define exactly which token gives the location for each message and
pass location_t values around as necessary, using %H in diagnostics,
to get messages at a well-defined place rather than relying at all on
input_location.  (I will however be changing the lookahead strategy so
it only looks at the next token when required - as is done for the
second lookahead token - rather than always automatically keeping one
lookahead token, which should reduce some of the differences in this
regard from the Bison parser, and is also needed to avoid handling
pragmas prematurely until C moves to lexing up front, which is a
change I want to keep separate from the parser replacement.)

This parser should be clearer and more maintainable than the Bison
parser (for example, many cases needing duplication of similar rules
in a Bison parser can be handled without such duplication using
appropriate flags); it should assist in the implementation of OpenMP
and other extensions which fit naturally into such a parser structure;
there are compile-time improvements (about 2% at -O0 on .i files of
cc1; subsequent changes such as lexing up front should improve things
further, but lexing up front could be done in principle with either
parser), but it isn't really ready to benchmark definitively until
Objective-C support (which will have runtime conditionals between
languages) is there, and lexer lookahead strategy and error recovery
are more nearly in their final state; and in future it might
facilitate better diagnostic location (e.g. fixing bug 8927).

Undoubtedly there is much scope for improvement in this parser; the
parsing structure and actions of the old parser are followed closely
to minimise the change involved in going to a new parser, but once the
old parser is superseded individual minimal changes to the new parser
can gradually clean it up and move away from aspects that aren't ideal
but were dictated by following the old parser.

Requirements to go on mainline (not yet met) are that performance is
no worse (and preferably better) on .i files of cc1, at -O0; that
there are tests in the testsuite giving 100% coverage of the old and
new parsers; that error recovery as indicated by the testsuite is no
worse than the old parser; that testcases are updated for changed
error locations (which should be avoided if possible until after the
merge) and error recovery, and as updated there are no GCC or GDB
testsuite regressions; that ObjC support is included; that any changes
not strictly needing to be part of this patch are merged separately.
I would expect the start of 4.1 development to be a likely time for
this to go on mainline (though if other people want this in 4.0 for
the performance improvements and it seems ready before 4.0 is ready to
branch, I won't rule that out altogether; it just seems too
potentially destabilising to me for this to be likely to make sense).

This patch serves to provide a recursive-descent parser (facilitating
OpenMP, etc.) within reasonably forseeable time, unlike other
alternatives such as "merge the C and C++ front ends".

Bootstrapped (with some regressions), C only, on i686-pc-linux-gnu.
An earlier version was bootstrapped covering other languages as well;
some easy regressions found from the C testsuite were fixed to produce
this version.

The next development stage of this patch will be adding Objective-C
support and adequate testcases.  Once that is done it may be ready for
wider testing (e.g. distribution builds; and I'll test it with Plum
Hall, but can't report the results).

As regards the suggestion of working on gomp-branch, I don't want to
mix these changes with any not really required by them, as the aim is
for a minimal patch to apply to mainline; and before there is full
testsuite coverage with no regressions, it would destabilise
gomp-branch, but once there is full coverage with no regressions, it
is ready for mainline.  It is intended that all testcases will go on
mainline as developed (testing the old parser's messages on mainline),
so the final patch will add the new parser, remove the old and make
corresponding build system, documentation and testsuite adjustments
but make no other changes to the compiler.  (The new flags in
c_declspecs would go in separately just before the parser itself, as
they are independent of it but aren't actually useful until the parser
is ready to go in so if they went in now they'd just be dead code.)

Specific ObjC changes that would be helpful: if some patch along the
lines of that in
<http://gcc.gnu.org/ml/gcc-patches/2004-10/msg00951.html> or the
relevant part thereof went in to get rid of RID_ID that would simplify
the Objective-C grammar that needs to be implemented.  Is there some
reason why CLASSNAME isn't included as an alternative in
after_type_declarator and parm_declarator_starttypename?  If it should
be included, then that also simplifies the grammar.  Additionally,
testcases covering all the existing Objective-C syntax productions /
alternatives / actions would be helpful to ensure that their handling
remains the same under the new parser.

-- 
Joseph S. Myers               http://www.srcf.ucam.org/~jsm28/gcc/
    jsm@polyomino.org.uk (personal mail)
    joseph@codesourcery.com (CodeSourcery mail)
    jsm28@gcc.gnu.org (Bugzilla assignments and CCs)

diff -rupN GCC.orig/gcc/Makefile.in GCC/gcc/Makefile.in
--- GCC.orig/gcc/Makefile.in	2004-10-15 10:05:02.000000000 +0000
+++ GCC/gcc/Makefile.in	2004-10-16 18:31:46.000000000 +0000
@@ -191,7 +191,6 @@ gcc.o-warn = -Wno-error
 build/insn-conditions.o-warn = -Wno-error
 # Bison-1.75 output often yields (harmless) -Wtraditional warnings
 build/gengtype-yacc.o-warn = -Wno-error
-c-parse.o-warn = -Wno-error
 # flex output may yield harmless "no previous prototype" warnings
 build/gengtype-lex.o-warn = -Wno-error
 # SYSCALLS.c misses prototypes
@@ -882,7 +881,7 @@ C_AND_OBJC_OBJS = attribs.o c-errors.o c
   c-gimplify.o tree-mudflap.o c-pretty-print.o
 
 # Language-specific object files for C.
-C_OBJS = c-parse.o c-lang.o stub-objc.o $(C_AND_OBJC_OBJS)
+C_OBJS = c-parser.o c-lang.o stub-objc.o $(C_AND_OBJC_OBJS)
 
 # Language-independent object files.
 OBJS-common = \
@@ -1348,24 +1347,15 @@ s-crt0:	$(CRT0_S) $(MCRT0_S) $(GCC_PASSE
 
 c-errors.o: c-errors.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
     $(C_TREE_H) $(FLAGS_H) $(DIAGNOSTIC_H) $(TM_P_H)
-c-parse.o : c-parse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
+c-parser.o : c-parser.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
     $(GGC_H) intl.h $(C_TREE_H) input.h $(FLAGS_H) toplev.h output.h \
-    $(CPPLIB_H) varray.h gt-c-parse.h langhooks.h $(C_COMMON_H) $(C_PRAGMA_H)
+    $(CPPLIB_H) varray.h gt-c-parser.h langhooks.h $(C_COMMON_H) $(C_PRAGMA_H)
 
 srcextra: gcc.srcextra lang.srcextra
 
-gcc.srcextra: c-parse.y c-parse.c gengtype-lex.c gengtype-yacc.c gengtype-yacc.h
+gcc.srcextra: gengtype-lex.c gengtype-yacc.c gengtype-yacc.h
 	-cp -p $^ $(srcdir)
 
-c-parse.c: c-parse.y
-	-$(BISON) $(BISONFLAGS) -o $@ $<
-
-c-parse.y: c-parse.in
-	echo '/*WARNING: This file is automatically generated!*/' >tmp-c-parse.y
-	sed -e "/^@@ifobjc.*/,/^@@end_ifobjc.*/d" \
-	    -e "/^@@ifc.*/d" -e "/^@@end_ifc.*/d" $< >>tmp-c-parse.y
-	$(SHELL) $(srcdir)/../move-if-change tmp-c-parse.y $@
-
 c-incpath.o: c-incpath.c c-incpath.h $(CONFIG_H) $(SYSTEM_H) $(CPPLIB_H) \
 		intl.h prefix.h coretypes.h $(TM_H) cppdefault.h $(TARGET_H) \
 		$(MACHMODE_H)
@@ -2417,7 +2407,7 @@ GTFILES = $(srcdir)/input.h $(srcdir)/co
   $(srcdir)/sdbout.c $(srcdir)/stor-layout.c \
   $(srcdir)/stringpool.c $(srcdir)/tree.c $(srcdir)/varasm.c \
   $(srcdir)/tree-mudflap.c $(srcdir)/tree-flow.h \
-  $(srcdir)/c-objc-common.c $(srcdir)/c-common.c $(srcdir)/c-parse.in \
+  $(srcdir)/c-objc-common.c $(srcdir)/c-common.c $(srcdir)/c-parser.c \
   $(srcdir)/tree-ssanames.c $(srcdir)/tree-eh.c \
   $(srcdir)/tree-phinodes.c $(srcdir)/tree-cfg.c \
   $(srcdir)/tree-dfa.c $(srcdir)/tree-ssa-propagate.c \
@@ -2439,7 +2429,7 @@ gt-emit-rtl.h gt-explow.h gt-stor-layout
 gt-lists.h gt-alias.h gt-cselib.h gt-fold-const.h gt-gcse.h \
 gt-expr.h gt-sdbout.h gt-optabs.h gt-bitmap.h gt-dojump.h \
 gt-dwarf2out.h gt-ra-build.h gt-reg-stack.h gt-dwarf2asm.h \
-gt-dbxout.h gt-c-common.h gt-c-decl.h gt-c-parse.h \
+gt-dbxout.h gt-c-common.h gt-c-decl.h gt-c-parser.h \
 gt-c-pragma.h gtype-c.h gt-input.h gt-cfglayout.h \
 gt-tree-mudflap.h \
 gt-tree-ssa-ccp.h gt-tree-eh.h \
@@ -3109,7 +3099,7 @@ distclean: clean lang.distclean
 	-rm -f Makefile *.oaux
 	-rm -f gthr-default.h
 	-rm -f */stage1 */stage2 */stage3 */stage4 */include */stageprofile */stagefeedback
-	-rm -f c-parse.y c-parse.c c-parse.output TAGS */TAGS
+	-rm -f TAGS */TAGS
 	-rm -f *.asm
 	-rm -f site.exp site.bak testsuite/site.exp testsuite/site.bak
 	-rm -f testsuite/*.log testsuite/*.sum
@@ -3130,7 +3120,6 @@ maintainer-clean:
 	@echo 'This command is intended for maintainers to use; it'
 	@echo 'deletes files that may need special tools to rebuild.'
 	$(MAKE) lang.maintainer-clean distclean
-	-rm -f $(srcdir)/c-parse.y $(srcdir)/c-parse.c
 	-rm -f cpp.??s cpp.*aux
 	-rm -f gcc.??s gcc.*aux
 	-rm -f $(docdir)/*.info $(docdir)/*.1 $(docdir)/*.7 $(docdir)/*.dvi
@@ -3604,7 +3593,7 @@ TAGS: lang.tags
 	    incs="$$incs --include $$dir/TAGS.sub";	\
 	  fi;						\
 	done;						\
-	etags -o TAGS.sub *.y *.h *.c -l yacc c-parse.in; \
+	etags -o TAGS.sub *.y *.h *.c; \
 	etags --include TAGS.sub $$incs)
 
 # ------------------------------------------------------
diff -rupN GCC.orig/gcc/c-config-lang.in GCC/gcc/c-config-lang.in
--- GCC.orig/gcc/c-config-lang.in	2002-12-27 19:38:52.000000000 +0000
+++ GCC/gcc/c-config-lang.in	2004-10-21 23:47:38.000000000 +0000
@@ -23,4 +23,4 @@
 # files used by C that have garbage collection GTY macros in them
 # which therefore need to be scanned by gengtype.c.
 
-gtfiles="\$(srcdir)/c-lang.c \$(srcdir)/c-parse.in \$(srcdir)/c-tree.h \$(srcdir)/c-decl.c \$(srcdir)/c-common.c \$(srcdir)/c-common.h \$(srcdir)/c-pragma.c \$(srcdir)/c-objc-common.c"
+gtfiles="\$(srcdir)/c-lang.c \$(srcdir)/c-tree.h \$(srcdir)/c-decl.c \$(srcdir)/c-common.c \$(srcdir)/c-common.h \$(srcdir)/c-pragma.c \$(srcdir)/c-objc-common.c \$(srcdir)/c-parser.c"
diff -rupN GCC.orig/gcc/c-decl.c GCC/gcc/c-decl.c
--- GCC.orig/gcc/c-decl.c	2004-10-15 10:05:02.000000000 +0000
+++ GCC/gcc/c-decl.c	2004-10-16 21:51:00.000000000 +0000
@@ -6680,6 +6680,8 @@ build_null_declspecs (void)
   ret->attrs = 0;
   ret->typespec_word = cts_none;
   ret->storage_class = csc_none;
+  ret->declspecs_seen_p = false;
+  ret->type_seen_p = false;
   ret->non_sc_seen_p = false;
   ret->typedef_p = false;
   ret->tag_defined_p = false;
@@ -6709,6 +6711,7 @@ declspecs_add_qual (struct c_declspecs *
   enum rid i;
   bool dupe = false;
   specs->non_sc_seen_p = true;
+  specs->declspecs_seen_p = true;
   gcc_assert (TREE_CODE (qual) == IDENTIFIER_NODE
 	      && C_IS_RESERVED_WORD (qual));
   i = C_RID_CODE (qual);
@@ -6742,6 +6745,8 @@ declspecs_add_type (struct c_declspecs *
 {
   tree type = spec.spec;
   specs->non_sc_seen_p = true;
+  specs->declspecs_seen_p = true;
+  specs->type_seen_p = true;
   if (TREE_DEPRECATED (type))
     specs->deprecated_p = true;
 
@@ -7036,6 +7041,7 @@ declspecs_add_scspec (struct c_declspecs
   enum rid i;
   enum c_storage_class n = csc_none;
   bool dupe = false;
+  specs->declspecs_seen_p = true;
   gcc_assert (TREE_CODE (scspec) == IDENTIFIER_NODE
 	      && C_IS_RESERVED_WORD (scspec));
   i = C_RID_CODE (scspec);
@@ -7118,6 +7124,7 @@ struct c_declspecs *
 declspecs_add_attrs (struct c_declspecs *specs, tree attrs)
 {
   specs->attrs = chainon (attrs, specs->attrs);
+  specs->declspecs_seen_p = true;
   return specs;
 }
 
diff -rupN GCC.orig/gcc/c-parser.c GCC/gcc/c-parser.c
--- GCC.orig/gcc/c-parser.c	1970-01-01 00:00:00.000000000 +0000
+++ GCC/gcc/c-parser.c	2004-10-22 20:24:05.000000000 +0000
@@ -0,0 +1,4775 @@
+/* Parser for C and Objective-C.
+   Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997,
+   1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+   Parser actions based on the old Bison parser; structure somewhat
+   influenced by and fragments based on the C++ parser.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+/* TODO:
+
+   Support Objective-C.
+
+   Make sure all relevant comments, and all relevant code from all
+   actions, brought over from old parser.
+
+   Include full syntax for GNU C, including erroneous cases accepted
+   with error messages, in syntax productions in comments.
+
+   Avoid needing to adjust input_location when calling parser actions
+   by making more diagnostics in the front end generally take an
+   explicit location rather than implicitly using input_location.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "langhooks.h"
+#include "input.h"
+#include "cpplib.h"
+#include "timevar.h"
+#include "c-pragma.h"
+#include "c-tree.h"
+#include "flags.h"
+#include "output.h"
+#include "toplev.h"
+#include "ggc.h"
+#include "c-common.h"
+
+\f
+/* Miscellaneous data and functions needed for lexer and parser.  */
+
+int yydebug;
+
+/* Objective-C specific parser/lexer information.  */
+
+static int objc_pq_context = 0;
+
+/* The following flag is needed to contextualize ObjC lexical
+   analysis.  In some cases (e.g., 'int NSObject;'), it is undesirable
+   to bind an identifier to an ObjC class, even if a class with that
+   name exists.  */
+static int objc_need_raw_identifier = 0;
+#define OBJC_NEED_RAW_IDENTIFIER(VAL)		\
+  do {						\
+    if (c_dialect_objc ())			\
+      objc_need_raw_identifier = VAL;		\
+  } while (0)
+
+/* The reserved keyword table.  */
+struct resword
+{
+  const char *word;
+  ENUM_BITFIELD(rid) rid : 16;
+  unsigned int disable   : 16;
+};
+
+/* Disable mask.  Keywords are disabled if (reswords[i].disable &
+   mask) is _true_.  */
+#define D_C89	0x01	/* not in C89 */
+#define D_EXT	0x02	/* GCC extension */
+#define D_EXT89	0x04	/* GCC extension incorporated in C99 */
+#define D_OBJC	0x08	/* Objective C only */
+
+static const struct resword reswords[] =
+{
+  { "_Bool",		RID_BOOL,	0 },
+  { "_Complex",		RID_COMPLEX,	0 },
+  { "__FUNCTION__",	RID_FUNCTION_NAME, 0 },
+  { "__PRETTY_FUNCTION__", RID_PRETTY_FUNCTION_NAME, 0 },
+  { "__alignof",	RID_ALIGNOF,	0 },
+  { "__alignof__",	RID_ALIGNOF,	0 },
+  { "__asm",		RID_ASM,	0 },
+  { "__asm__",		RID_ASM,	0 },
+  { "__attribute",	RID_ATTRIBUTE,	0 },
+  { "__attribute__",	RID_ATTRIBUTE,	0 },
+  { "__builtin_choose_expr", RID_CHOOSE_EXPR, 0 },
+  { "__builtin_offsetof", RID_OFFSETOF, 0 },
+  { "__builtin_types_compatible_p", RID_TYPES_COMPATIBLE_P, 0 },
+  { "__builtin_va_arg",	RID_VA_ARG,	0 },
+  { "__complex",	RID_COMPLEX,	0 },
+  { "__complex__",	RID_COMPLEX,	0 },
+  { "__const",		RID_CONST,	0 },
+  { "__const__",	RID_CONST,	0 },
+  { "__extension__",	RID_EXTENSION,	0 },
+  { "__func__",		RID_C99_FUNCTION_NAME, 0 },
+  { "__imag",		RID_IMAGPART,	0 },
+  { "__imag__",		RID_IMAGPART,	0 },
+  { "__inline",		RID_INLINE,	0 },
+  { "__inline__",	RID_INLINE,	0 },
+  { "__label__",	RID_LABEL,	0 },
+  { "__real",		RID_REALPART,	0 },
+  { "__real__",		RID_REALPART,	0 },
+  { "__restrict",	RID_RESTRICT,	0 },
+  { "__restrict__",	RID_RESTRICT,	0 },
+  { "__signed",		RID_SIGNED,	0 },
+  { "__signed__",	RID_SIGNED,	0 },
+  { "__thread",		RID_THREAD,	0 },
+  { "__typeof",		RID_TYPEOF,	0 },
+  { "__typeof__",	RID_TYPEOF,	0 },
+  { "__volatile",	RID_VOLATILE,	0 },
+  { "__volatile__",	RID_VOLATILE,	0 },
+  { "asm",		RID_ASM,	D_EXT },
+  { "auto",		RID_AUTO,	0 },
+  { "break",		RID_BREAK,	0 },
+  { "case",		RID_CASE,	0 },
+  { "char",		RID_CHAR,	0 },
+  { "const",		RID_CONST,	0 },
+  { "continue",		RID_CONTINUE,	0 },
+  { "default",		RID_DEFAULT,	0 },
+  { "do",		RID_DO,		0 },
+  { "double",		RID_DOUBLE,	0 },
+  { "else",		RID_ELSE,	0 },
+  { "enum",		RID_ENUM,	0 },
+  { "extern",		RID_EXTERN,	0 },
+  { "float",		RID_FLOAT,	0 },
+  { "for",		RID_FOR,	0 },
+  { "goto",		RID_GOTO,	0 },
+  { "if",		RID_IF,		0 },
+  { "inline",		RID_INLINE,	D_EXT89 },
+  { "int",		RID_INT,	0 },
+  { "long",		RID_LONG,	0 },
+  { "register",		RID_REGISTER,	0 },
+  { "restrict",		RID_RESTRICT,	D_C89 },
+  { "return",		RID_RETURN,	0 },
+  { "short",		RID_SHORT,	0 },
+  { "signed",		RID_SIGNED,	0 },
+  { "sizeof",		RID_SIZEOF,	0 },
+  { "static",		RID_STATIC,	0 },
+  { "struct",		RID_STRUCT,	0 },
+  { "switch",		RID_SWITCH,	0 },
+  { "typedef",		RID_TYPEDEF,	0 },
+  { "typeof",		RID_TYPEOF,	D_EXT },
+  { "union",		RID_UNION,	0 },
+  { "unsigned",		RID_UNSIGNED,	0 },
+  { "void",		RID_VOID,	0 },
+  { "volatile",		RID_VOLATILE,	0 },
+  { "while",		RID_WHILE,	0 },
+  /* Objective-C keyword.  */
+  { "id",		RID_ID,			D_OBJC },
+  /* These objc keywords are recognized only immediately after
+     an '@'.  */
+  { "class",		RID_AT_CLASS,		D_OBJC },
+  { "compatibility_alias", RID_AT_ALIAS,	D_OBJC },
+  { "defs",		RID_AT_DEFS,		D_OBJC },
+  { "encode",		RID_AT_ENCODE,		D_OBJC },
+  { "end",		RID_AT_END,		D_OBJC },
+  { "implementation",	RID_AT_IMPLEMENTATION,	D_OBJC },
+  { "interface",	RID_AT_INTERFACE,	D_OBJC },
+  { "private",		RID_AT_PRIVATE,		D_OBJC },
+  { "protected",	RID_AT_PROTECTED,	D_OBJC },
+  { "protocol",		RID_AT_PROTOCOL,	D_OBJC },
+  { "public",		RID_AT_PUBLIC,		D_OBJC },
+  { "selector",		RID_AT_SELECTOR,	D_OBJC },
+  { "throw",		RID_AT_THROW,		D_OBJC },
+  { "try",		RID_AT_TRY,		D_OBJC },
+  { "catch",		RID_AT_CATCH,		D_OBJC },
+  { "finally",		RID_AT_FINALLY,		D_OBJC },
+  { "synchronized",	RID_AT_SYNCHRONIZED,	D_OBJC },
+  /* These are recognized only in protocol-qualifier context
+     (see above) */
+  { "bycopy",		RID_BYCOPY,		D_OBJC },
+  { "byref",		RID_BYREF,		D_OBJC },
+  { "in",		RID_IN,			D_OBJC },
+  { "inout",		RID_INOUT,		D_OBJC },
+  { "oneway",		RID_ONEWAY,		D_OBJC },
+  { "out",		RID_OUT,		D_OBJC },
+};
+#define N_reswords (sizeof reswords / sizeof (struct resword))
+
+/* Initialize the reserved word identifiers.  */
+
+static void
+init_reswords (void)
+{
+  unsigned int i;
+  tree id;
+  int mask = (flag_isoc99 ? 0 : D_C89)
+	      | (flag_no_asm ? (flag_isoc99 ? D_EXT : D_EXT|D_EXT89) : 0);
+
+  if (!c_dialect_objc ())
+     mask |= D_OBJC;
+
+  ridpointers = GGC_CNEWVEC (tree, (int) RID_MAX);
+  for (i = 0; i < N_reswords; i++)
+    {
+      /* If a keyword is disabled, do not enter it into the table
+	 and so create a canonical spelling that isn't a keyword.  */
+      if (reswords[i].disable & mask)
+	continue;
+
+      id = get_identifier (reswords[i].word);
+      C_RID_CODE (id) = reswords[i].rid;
+      C_IS_RESERVED_WORD (id) = 1;
+      ridpointers [(int) reswords[i].rid] = id;
+    }
+}
+
+/* Initialization routine for this file.  */
+
+void
+c_parse_init (void)
+{
+  init_reswords ();
+}
+\f
+/* The C lexer intermediates between the lexer in cpplib and c-lex.c
+   and the C parser.  Identifiers are separated into ordinary
+   identifiers, type names, keywords and some other Objective-C types
+   of identifiers, and some look-ahead is maintained.
+
+   ??? It might be a good idea to lex the whole file up front (as for
+   C++).  It should then be possible to share more of the C and C++
+   lexer code.  */
+
+/* The following local token type is used.  */
+
+/* A keyword.  */
+#define CPP_KEYWORD ((enum cpp_ttype) (N_TTYPES + 1))
+
+/* The number of token types, including C-specific ones.  */
+#define N_C_TTYPES ((int) (CPP_KEYWORD + 1))
+
+/* More information about the type of a CPP_NAME token.  */
+typedef enum c_id_kind {
+  /* An ordinary identifier.  */
+  C_ID_ID,
+  /* An identifier declared as a typedef name.  */
+  C_ID_TYPENAME,
+  /* An identifier declared as an Objective-C class name.  */
+  C_ID_CLASSNAME,
+  /* Not an identifier.  */
+  C_ID_NONE
+} c_id_kind;
+
+/* A single C token after string literal concatenation and conversion
+   of preprocessing tokens to tokens.  */
+typedef struct c_token GTY (())
+{
+  /* The kind of token.  */
+  ENUM_BITFIELD (cpp_ttype) type : 8;
+  /* If this token is a CPP_NAME, this value indicates whether also
+     declared as some kind of type.  Otherwise, it is C_ID_NONE.  */
+  ENUM_BITFIELD (c_id_kind) id_kind : 8;
+  /* If this token is a keyword, this value indicates which keyword.
+     Otherwise, this value is RID_MAX.  */
+  ENUM_BITFIELD (rid) keyword : 8;
+  /* True if this token is from a system header.  */
+  BOOL_BITFIELD in_system_header : 1;
+  /* The value associated with this token, if any.  */
+  tree value;
+  /* The location at which this token was found.  */
+  location_t location;
+} c_token;
+
+/* A lexer with up to two tokens of look-ahead; more are not needed
+   for C.  */
+typedef struct c_lexer GTY (())
+{
+  /* The look-ahead tokens.  */
+  c_token tokens[2];
+  /* How many look-ahead tokens are available (1 or 2).  */
+  int tokens_avail;
+} c_lexer;
+
+/* Read in and lex a single token, storing it in *TOKEN.  */
+
+static void
+c_lex_one_token (c_token *token)
+{
+  timevar_push (TV_LEX);
+  token->type = c_lex (&token->value);
+  token->location = input_location;
+  token->in_system_header = in_system_header;
+  switch (token->type)
+    {
+    case CPP_NAME:
+      token->id_kind = C_ID_NONE;
+      token->keyword = RID_MAX;
+      {
+	tree decl;
+
+	int objc_force_identifier = objc_need_raw_identifier;
+	OBJC_NEED_RAW_IDENTIFIER (0);
+
+	if (C_IS_RESERVED_WORD (token->value))
+	  {
+	    enum rid rid_code = C_RID_CODE (token->value);
+
+	    if (c_dialect_objc ())
+	      {
+		/* Turn non-typedefed refs to "id" into plain identifiers; this
+		   allows constructs like "void foo(id id);" to work.  */
+		if (rid_code == RID_ID)
+		  {
+		    decl = lookup_name (token->value);
+		    if (decl == NULL_TREE || TREE_CODE (decl) != TYPE_DECL)
+		      {
+			token->id_kind = C_ID_ID;
+			break;
+		      }
+		  }
+
+		if (!OBJC_IS_AT_KEYWORD (rid_code)
+		    && (!OBJC_IS_PQ_KEYWORD (rid_code) || objc_pq_context))
+		  {
+		    /* Return the canonical spelling for this keyword.  */
+		    token->value = ridpointers[(int) rid_code];
+		    token->type = CPP_KEYWORD;
+		    token->keyword = rid_code;
+		    break;
+		  }
+	      }
+	    else
+	      {
+		/* Return the canonical spelling for this keyword.  */
+		token->value = ridpointers[(int) rid_code];
+		token->type = CPP_KEYWORD;
+		token->keyword = rid_code;
+		break;
+	      }
+	  }
+
+	decl = lookup_name (token->value);
+	if (decl)
+	  {
+	    if (TREE_CODE (decl) == TYPE_DECL)
+	      {
+		token->id_kind = C_ID_TYPENAME;
+		break;
+	      }
+	  }
+	else if (c_dialect_objc ())
+	  {
+	    tree objc_interface_decl = objc_is_class_name (token->value);
+	    /* ObjC class names are in the same namespace as variables and
+	       typedefs, and hence are shadowed by local declarations.  */
+	    if (objc_interface_decl
+		&& (global_bindings_p ()
+		    || (!objc_force_identifier && !decl)))
+	      {
+		token->value = objc_interface_decl;
+		token->id_kind = C_ID_CLASSNAME;
+		break;
+	      }
+	  }
+      }
+      token->id_kind = C_ID_ID;
+      break;
+    case CPP_AT_NAME:
+      /* This only happens in Objective-C; it must be a keyword.  */
+      token->type = CPP_KEYWORD;
+      token->id_kind = C_ID_NONE;
+      token->keyword = C_RID_CODE (token->value);
+      break;
+    case CPP_COLON:
+    case CPP_COMMA:
+    case CPP_CLOSE_PAREN:
+    case CPP_SEMICOLON:
+      /* These tokens may affect the interpretation of any identifiers
+	 following, if doing Objective-C.  */
+      OBJC_NEED_RAW_IDENTIFIER (0);
+      token->id_kind = C_ID_NONE;
+      token->keyword = RID_MAX;
+      break;
+    default:
+      token->id_kind = C_ID_NONE;
+      token->keyword = RID_MAX;
+      break;
+    }
+  timevar_pop (TV_LEX);
+}
+
+/* Allocate a new lexer.  */
+
+static c_lexer *
+c_lexer_new (void)
+{
+  c_lexer *ret;
+  ret = GGC_CNEW (c_lexer);
+  return ret;
+}
+
+/* Initialize LEXER by reading in the first token.  */
+
+static void
+c_lexer_init (c_lexer *lexer)
+{
+  gcc_assert (lexer->tokens_avail == 0);
+  c_lex_one_token (&lexer->tokens[0]);
+  lexer->tokens_avail = 1;
+}
+
+/* Return a pointer to the next token from LEXER.  */
+
+static inline c_token *
+c_lexer_peek_token (c_lexer *lexer)
+{
+  return &lexer->tokens[0];
+}
+
+/* Return true if the next token from LEXER has the indicated TYPE.  */
+
+static inline bool
+c_lexer_next_token_is (c_lexer *lexer, enum cpp_ttype type)
+{
+  return c_lexer_peek_token (lexer)->type == type;
+}
+
+/* Return true if the next token from LEXER does not have the
+   indicated TYPE.  */
+
+static inline bool
+c_lexer_next_token_is_not (c_lexer *lexer, enum cpp_ttype type)
+{
+  return !c_lexer_next_token_is (lexer, type);
+}
+
+/* Return true if the next token from LEXER is the indicated
+   KEYWORD.  */
+
+static inline bool
+c_lexer_next_token_is_keyword (c_lexer *lexer, enum rid keyword)
+{
+  c_token *token;
+
+  /* Peek at the next token.  */
+  token = c_lexer_peek_token (lexer);
+  /* Check to see if it is the indicated keyword.  */
+  return token->keyword == keyword;
+}
+
+/* Return true if TOKEN can start a type name,
+   false otherwise.  */
+static bool
+c_token_starts_typename (c_token *token)
+{
+  switch (token->type)
+    {
+    case CPP_NAME:
+      return (token->id_kind == C_ID_TYPENAME);
+    case CPP_KEYWORD:
+      switch (token->keyword)
+	{
+	case RID_UNSIGNED:
+	case RID_LONG:
+	case RID_SHORT:
+	case RID_SIGNED:
+	case RID_COMPLEX:
+	case RID_INT:
+	case RID_CHAR:
+	case RID_FLOAT:
+	case RID_DOUBLE:
+	case RID_VOID:
+	case RID_BOOL:
+	case RID_ENUM:
+	case RID_STRUCT:
+	case RID_UNION:
+	case RID_TYPEOF:
+	case RID_CONST:
+	case RID_VOLATILE:
+	case RID_RESTRICT:
+	case RID_ATTRIBUTE:
+	  return true;
+	default:
+	  return false;
+	}
+    default:
+      return false;
+    }
+}
+
+/* Return true if the next token from LEXER can start a type name,
+   false otherwise.  */
+static inline bool
+c_lexer_next_token_starts_typename (c_lexer *lexer)
+{
+  c_token *token = c_lexer_peek_token (lexer);
+  return c_token_starts_typename (token);
+}
+
+/* Return true if TOKEN can start declaration specifiers, false
+   otherwise.  */
+static bool
+c_token_starts_declspecs (c_token *token)
+{
+  switch (token->type)
+    {
+    case CPP_NAME:
+      return (token->id_kind == C_ID_TYPENAME);
+    case CPP_KEYWORD:
+      switch (token->keyword)
+	{
+	case RID_STATIC:
+	case RID_EXTERN:
+	case RID_REGISTER:
+	case RID_TYPEDEF:
+	case RID_INLINE:
+	case RID_AUTO:
+	case RID_THREAD:
+	case RID_UNSIGNED:
+	case RID_LONG:
+	case RID_SHORT:
+	case RID_SIGNED:
+	case RID_COMPLEX:
+	case RID_INT:
+	case RID_CHAR:
+	case RID_FLOAT:
+	case RID_DOUBLE:
+	case RID_VOID:
+	case RID_BOOL:
+	case RID_ENUM:
+	case RID_STRUCT:
+	case RID_UNION:
+	case RID_TYPEOF:
+	case RID_CONST:
+	case RID_VOLATILE:
+	case RID_RESTRICT:
+	case RID_ATTRIBUTE:
+	  return true;
+	default:
+	  return false;
+	}
+    default:
+      return false;
+    }
+}
+
+/* Return true if the next token from LEXER can start declaration
+   specifiers, false otherwise.  */
+static inline bool
+c_lexer_next_token_starts_declspecs (c_lexer *lexer)
+{
+  c_token *token = c_lexer_peek_token (lexer);
+  return c_token_starts_declspecs (token);
+}
+
+/* Return a pointer to the next-but-one token from LEXER, reading it
+   in if necessary.  */
+
+static c_token *
+c_lexer_peek_2nd_token (c_lexer *lexer)
+{
+  if (lexer->tokens_avail >= 2)
+    return &lexer->tokens[1];
+  gcc_assert (lexer->tokens_avail == 1);
+  gcc_assert (lexer->tokens[0].type != CPP_EOF);
+  c_lex_one_token (&lexer->tokens[1]);
+  lexer->tokens_avail = 2;
+  return &lexer->tokens[1];
+}
+
+/* Consume the next token from LEXER, reading in another if
+   necessary.  */
+
+static void
+c_lexer_consume_token (c_lexer *lexer)
+{
+  if (lexer->tokens_avail == 2)
+    {
+      lexer->tokens[0] = lexer->tokens[1];
+      lexer->tokens_avail--;
+    }
+  else
+    {
+      gcc_assert (lexer->tokens_avail == 1);
+      gcc_assert (lexer->tokens[0].type != CPP_EOF);
+      c_lex_one_token (&lexer->tokens[0]);
+    }
+}
+
+/* Update the globals input_location and in_system_header from TOKEN.   */
+static inline void
+c_lexer_set_source_position_from_token (c_token *token)
+{
+  if (token->type != CPP_EOF)
+    {
+      input_location = token->location;
+      in_system_header = token->in_system_header;
+    }
+}
+\f
+/* A parser structure recording information about the state and
+   context of parsing.  */
+typedef struct c_parser GTY(())
+{
+  /* The lexer.  */
+  c_lexer *lexer;
+  /* True if a syntax error is being recovered from; false otherwise.
+     c_parser_error sets this flag.  It should clear this flag when
+     enough tokens have been consumed to recover from the error.  */
+  BOOL_BITFIELD error : 1;
+} c_parser;
+
+/* Allocate a new parser.  */
+
+static c_parser *
+c_parser_new (void)
+{
+  /* Use local storage to lex the first token because loading a PCH
+     file may cause garbage collection.  */
+  struct c_lexer tlexer;
+  c_parser *ret;
+  memset (&tlexer, 0, sizeof tlexer);
+  c_lexer_init (&tlexer);
+  ret = GGC_NEW (c_parser);
+  ret->lexer = c_lexer_new ();
+  memcpy (ret->lexer, &tlexer, sizeof tlexer);
+  ret->error = false;
+  return ret;
+}
+
+/* Issue a diagnostic of the form
+      FILE:LINE: MESSAGE before TOKEN
+   where TOKEN is the next token in the input stream of PARSER.
+   MESSAGE (specified by the caller) is usually of the form "expected
+   OTHER-TOKEN".
+
+   Do not issue a diagnostic if still recovering from an error.
+
+   ??? This is taken from the C++ parser, but building up messages in
+   this way is not i18n-friendly and some other approach should be
+   used.  */
+
+static void
+c_parser_error (c_parser *parser, const char *msgid)
+{
+  c_token *token = c_lexer_peek_token (parser->lexer);
+  if (parser->error)
+    return;
+  parser->error = true;
+  if (!msgid)
+    return;
+  /* This diagnostic makes more sense if it is tagged to the line of
+     the token we just peeked at.  */
+  c_lexer_set_source_position_from_token (token);
+  c_parse_error (msgid,
+		 /* Because c_parse_error does not understand
+		    CPP_KEYWORD, keywords are treated like
+		    identifiers.  */
+		 (token->type == CPP_KEYWORD ? CPP_NAME : token->type),
+		 token->value);
+}
+
+/* If the next token is of the indicated TYPE, consume it.  Otherwise,
+   issue the error MSGID.  If MSGID is NULL then a message has already
+   been produced and no message will be produced this time.  Returns
+   true if found, false otherwise.  */
+
+static bool
+c_parser_require (c_parser *parser,
+		  enum cpp_ttype type,
+		  const char *msgid)
+{
+  if (c_lexer_next_token_is (parser->lexer, type))
+    {
+      c_lexer_consume_token (parser->lexer);
+      return true;
+    }
+  else
+    {
+      c_parser_error (parser, msgid);
+      return false;
+    }
+}
+
+/* Like c_parser_require, except that tokens will be skipped until the
+   desired token is found.  An error message is still produced if the
+   next token is not as expected.  If MSGID is NULL then a message has
+   already been produced and no message will be produced this
+   time.  */
+
+static void
+c_parser_skip_until_found (c_parser *parser,
+			   enum cpp_ttype type,
+			   const char *msgid)
+{
+  unsigned nesting_depth = 0;
+
+  if (c_parser_require (parser, type, msgid))
+    return;
+
+  /* Skip tokens until the desired token is found.  */
+  while (true)
+    {
+      /* Peek at the next token.  */
+      c_token *token = c_lexer_peek_token (parser->lexer);
+      /* If we've reached the token we want, consume it and stop.  */
+      if (token->type == type && !nesting_depth)
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  return;
+	}
+      /* If we've run out of tokens, stop.  */
+      if (token->type == CPP_EOF)
+	return;
+      if (token->type == CPP_OPEN_BRACE
+	  || token->type == CPP_OPEN_PAREN
+	  || token->type == CPP_OPEN_SQUARE)
+	++nesting_depth;
+      else if (token->type == CPP_CLOSE_BRACE
+	       || token->type == CPP_CLOSE_PAREN
+	       || token->type == CPP_CLOSE_SQUARE)
+	{
+	  if (nesting_depth-- == 0)
+	    {
+	      parser->error = false;
+	      return;
+	    }
+	}
+      /* Consume this token.  */
+      c_lexer_consume_token (parser->lexer);
+      parser->error = false;
+    }
+}
+
+/* Skip tokens until we have consumed an entire block, or until we
+   have consumed a non-nested ';'.  */
+
+static void
+c_parser_skip_to_end_of_block_or_statement (c_parser *parser)
+{
+  unsigned nesting_depth = 0;
+
+  while (true)
+    {
+      c_token *token;
+
+      /* Peek at the next token.  */
+      token = c_lexer_peek_token (parser->lexer);
+      /* If we've run out of tokens, stop.  */
+      if (token->type == CPP_EOF)
+	break;
+      /* If the next token is a ';', we have reached the end of the
+	 statement.  */
+      if (token->type == CPP_SEMICOLON && !nesting_depth)
+	{
+	  /* Consume the ';'.  */
+	  c_lexer_consume_token (parser->lexer);
+	  break;
+	}
+      /* If the next token is a non-nested '}', then we have reached
+	 the end of the current block.  */
+      if (token->type == CPP_CLOSE_BRACE
+	  && (nesting_depth == 0 || --nesting_depth == 0))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  break;
+	}
+      /* If it the next token is a '{', then we are entering a new
+	 block.  Consume the entire block.  */
+      if (token->type == CPP_OPEN_BRACE)
+	++nesting_depth;
+      c_lexer_consume_token (parser->lexer);
+    }
+  parser->error = false;
+}
+
+/* If the next token is the indicated keyword, consume it.  Otherwise,
+   issue the error MSGID.  Returns true if found, false otherwise.  */
+
+static bool
+c_parser_require_keyword (c_parser *parser,
+			  enum rid keyword,
+			  const char *msgid)
+{
+  if (c_lexer_next_token_is_keyword (parser->lexer, keyword))
+    {
+      c_lexer_consume_token (parser->lexer);
+      return true;
+    }
+  else
+    {
+      c_parser_error (parser, msgid);
+      return false;
+    }
+}
+
+
+/* For __extension__, save/restore the warning flags which are
+   controlled by __extension__.  */
+#define SAVE_EXT_FLAGS(var)			\
+  do {						\
+    var = (pedantic				\
+	   | (warn_pointer_arith << 1)		\
+	   | (warn_traditional << 2)		\
+	   | (flag_iso << 3));			\
+     pedantic = 0;				\
+     warn_pointer_arith = 0;			\
+     warn_traditional = 0;			\
+     flag_iso = 0;				\
+  } while (0)
+
+#define RESTORE_EXT_FLAGS(val)			\
+  do {						\
+    pedantic = val & 1;				\
+    warn_pointer_arith = (val >> 1) & 1;	\
+    warn_traditional = (val >> 2) & 1;		\
+    flag_iso = (val >> 3) & 1;			\
+  } while (0)
+
+/* Possibly kinds of declarator to parse.  */
+typedef enum c_dtr_syn {
+  /* A normal declarator with an identifier.  */
+  C_DTR_NORMAL,
+  /* An abstract declarator (maybe empty).  */
+  C_DTR_ABSTRACT,
+  /* A parameter declarator: may be either, but after a type name does
+     not redeclare a typedef name as an identifier if it can
+     alternatively be interpreted as a typedef name.  For example,
+     given a typedef T, "int T" and "int *T" are valid parameter
+     declarations redeclaring T, while "int (T)" and "int * (T)" and
+     "int (T[])" and "int (T (int))" are abstract declarators rather
+     than involving redundant parentheses; the same applies with
+     attributes inside the parentheses before "T".  */
+  C_DTR_PARM
+} c_dtr_syn;
+
+static void c_parser_external_declaration (c_parser *);
+static void c_parser_asm_definition (c_parser *);
+static void c_parser_declaration_or_fndef (c_parser *, bool, bool, bool, bool);
+static void c_parser_declspecs (c_parser *, struct c_declspecs *, bool, bool,
+				bool);
+static struct c_typespec c_parser_enum_specifier (c_parser *);
+static struct c_typespec c_parser_struct_or_union_specifier (c_parser *);
+static tree c_parser_struct_declaration (c_parser *);
+static struct c_typespec c_parser_typeof_specifier (c_parser *);
+static struct c_declarator *c_parser_declarator (c_parser *, bool, c_dtr_syn,
+						 bool *);
+static struct c_declarator *c_parser_direct_declarator (c_parser *, bool,
+							c_dtr_syn, bool *);
+static struct c_declarator *c_parser_direct_declarator_inner (c_parser *,
+							      bool,
+							      struct c_declarator *);
+static struct c_arg_info *c_parser_parms_declarator (c_parser *, bool, tree);
+static struct c_arg_info *c_parser_parms_list_declarator (c_parser *, tree);
+static tree c_parser_simple_asm_expr (c_parser *);
+static tree c_parser_attributes (c_parser *);
+static struct c_type_name *c_parser_type_name (c_parser *);
+static struct c_expr c_parser_initializer (c_parser *);
+static struct c_expr c_parser_braced_init (c_parser *, tree, bool);
+static void c_parser_initelt (c_parser *);
+static void c_parser_initval (c_parser *);
+static tree c_parser_compound_statement (c_parser *, location_t *);
+static void c_parser_compound_statement_nostart (c_parser *, location_t *);
+static void c_parser_label (c_parser *);
+static void c_parser_statement (c_parser *);
+static void c_parser_statement_after_labels (c_parser *);
+static void c_parser_if_statement (c_parser *);
+static void c_parser_switch_statement (c_parser *);
+static void c_parser_while_statement (c_parser *);
+static void c_parser_do_statement (c_parser *);
+static void c_parser_for_statement (c_parser *);
+static tree c_parser_asm_statement (c_parser *);
+static tree c_parser_asm_operands (c_parser *);
+static tree c_parser_asm_clobbers (c_parser *);
+static struct c_expr c_parser_expr_no_commas (c_parser *);
+static struct c_expr c_parser_conditional_expression (c_parser *);
+static struct c_expr c_parser_binary_expression (c_parser *);
+static struct c_expr c_parser_cast_expression (c_parser *);
+static struct c_expr c_parser_unary_expression (c_parser *);
+static struct c_expr c_parser_sizeof_expression (c_parser *);
+static struct c_expr c_parser_alignof_expression (c_parser *);
+static struct c_expr c_parser_postfix_expression (c_parser *);
+static struct c_expr c_parser_postfix_expression_after_paren_type (c_parser *,
+								   struct c_type_name *);
+static struct c_expr c_parser_postfix_expression_after_primary (c_parser *,
+								struct c_expr);
+static struct c_expr c_parser_expression (c_parser *);
+static tree c_parser_expr_list (c_parser *);
+
+/* Parse a translation unit (C90 6.7, C99 6.9).
+
+   translation-unit:
+     external-declaration
+     translation-unit external-declaration
+
+   GNU extension: empty translation units.  */
+
+static void
+c_parser_translation_unit (c_parser *parser)
+{
+  if (c_lexer_next_token_is (parser->lexer, CPP_EOF))
+    {
+      if (pedantic)
+	pedwarn ("ISO C forbids an empty source file");
+    }
+  else
+    {
+      while (c_lexer_next_token_is_not (parser->lexer, CPP_EOF))
+	{
+	  void *obstack_position = obstack_alloc (&parser_obstack, 0);
+	  ggc_collect ();
+	  c_parser_external_declaration (parser);
+	  obstack_free (&parser_obstack, obstack_position);
+	}
+    }
+}
+
+/* Parse an external declaration (C90 6.7, C99 6.9).
+
+   external-declaration:
+     function-definition
+     declaration
+
+   GNU extensions:
+     asm-definition
+     __extension__ external-declaration
+     TODO: Objective-C.  */
+
+static void
+c_parser_external_declaration (c_parser *parser)
+{
+  if (c_lexer_next_token_is_keyword (parser->lexer, RID_EXTENSION))
+    {
+      int ext;
+      SAVE_EXT_FLAGS (ext);
+      c_lexer_consume_token (parser->lexer);
+      c_parser_external_declaration (parser);
+      RESTORE_EXT_FLAGS (ext);
+    }
+  else if (c_lexer_next_token_is_keyword (parser->lexer, RID_ASM))
+    {
+      c_parser_asm_definition (parser);
+    }
+  else
+    {
+      /* A declaration or a function definition.  We can only tell
+	 which after parsing the declaration specifiers, if any, and
+	 the first declarator.  */
+      c_parser_declaration_or_fndef (parser, true, true, false, true);
+    }
+}
+
+/* Parse a declaration or function definition (C90 6.5, 6.7.1, C99
+   6.7, 6.9.1).  If FNDEF_OK is true, a function definition is
+   accepted; otherwise (old-style parameter declarations) only other
+   declarations are accepted.  If NESTED is true, we are inside a
+   function or parsing old-style parameter declarations; any functions
+   encountered are nested functions and declaration specifiers are
+   required; otherwise we are at top level and functions are normal
+   functions and declaration specifiers may be optional.  If EMPTY_OK
+   is true, empty declarations are OK; otherwise (old-style parameter
+   declarations) they are diagnosed.  If START_ATTR_OK is true, the
+   declaration specifiers may start with attributes; otherwise they
+   may not.
+
+   declaration:
+     declaration-specifiers init-declarator-list[opt] ;
+
+   function-definition:
+     declaration-specifiers[opt] declarator declaration-list[opt]
+       compound-statement
+
+   init-declarator-list:
+     init-declarator
+     init-declarator-list , init-declarator
+
+   init-declarator:
+     declarator simple-asm-expr[opt] attributes[opt]
+     declarator simple-asm-expr[opt] attributes[opt] = initializer
+
+   The simple-asm-expr and attributes are GNU extensions.
+
+   This function does not handle __extension__; that is handled in its
+   callers.  ??? Following the old parser, __extension__ may start
+   extenal declarations, declarations in functions and declarations at
+   the start of "for" loops, but not old-style parameter declarations.
+
+   C99 requires declaration specifiers in a function definition; the
+   absence is diagnosed through the diagnosis of implicit int.  In GNU
+   C we also allow but diagnose declarations without declaration
+   specifiers, but only at top level (elsewhere they conflict with
+   other syntax).  At top level in GNU C we also allow stray ';'
+   outside a function.  */
+
+static void
+c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, bool empty_ok,
+			       bool nested, bool start_attr_ok)
+{
+  struct c_declspecs *specs;
+  tree prefix_attrs;
+  tree all_prefix_attrs;
+  bool diagnosed_no_specs = false;
+  if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+    {
+      if (pedantic)
+	pedwarn ("ISO C does not allow extra %<;%> outside of a function");
+      c_lexer_consume_token (parser->lexer);
+      return;
+    }
+  specs = build_null_declspecs ();
+  c_parser_declspecs (parser, specs, true, true, start_attr_ok);
+  if (parser->error)
+    {
+      c_parser_skip_to_end_of_block_or_statement (parser);
+      return;
+    }
+  finish_declspecs (specs);
+  if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+    {
+      if (empty_ok)
+	shadow_tag (specs);
+      else
+	{
+	  shadow_tag_warned (specs, 1);
+	  pedwarn ("empty declaration");
+	}
+      c_lexer_consume_token (parser->lexer);
+      return;
+    }
+  pending_xref_error ();
+  prefix_attrs = specs->attrs;
+  all_prefix_attrs = prefix_attrs;
+  specs->attrs = NULL_TREE;
+  while (true)
+    {
+      struct c_declarator *declarator;
+      bool dummy = false;
+      tree fnbody;
+      location_t fnend_loc, save_loc;
+      /* Declaring either one or more declarators (in which case we
+	 should diagnose if there were no declaration specifiers) or a
+	 function definition (in which case the diagnostic for
+	 implicit int suffices).  */
+      declarator = c_parser_declarator (parser, specs->type_seen_p,
+					C_DTR_NORMAL, &dummy);
+      if (declarator == NULL)
+	{
+	  c_parser_skip_to_end_of_block_or_statement (parser);
+	  return;
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_EQ)
+	  || c_lexer_next_token_is (parser->lexer, CPP_COMMA)
+	  || c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)
+	  || c_lexer_next_token_is_keyword (parser->lexer, RID_ASM)
+	  || c_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))
+	{
+	  tree asm_name = NULL_TREE;
+	  tree postfix_attrs = NULL_TREE;
+	  if (!diagnosed_no_specs && !specs->declspecs_seen_p)
+	    {
+	      diagnosed_no_specs = true;
+	      /* ??? This is what the old parser did but is not a proper
+		 use of pedantic.  */
+	      if (pedantic)
+		error ("ISO C forbids data definition with no type "
+		       "or storage class");
+	      else
+		warning ("data definition has no type or storage class");
+	    }
+	  /* Having seen a data definition, there cannot now be a
+	     function definition.  */
+	  fndef_ok = false;
+	  if (c_lexer_next_token_is_keyword (parser->lexer, RID_ASM))
+	    asm_name = c_parser_simple_asm_expr (parser);
+	  if (c_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))
+	    postfix_attrs = c_parser_attributes (parser);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_EQ))
+	    {
+	      tree d;
+	      struct c_expr init;
+	      c_lexer_consume_token (parser->lexer);
+	      /* The declaration of the variable is in effect while
+		 its initializer is parsed.  */
+	      d = start_decl (declarator, specs, true,
+			      chainon (postfix_attrs, all_prefix_attrs));
+	      start_init (d, asm_name, global_bindings_p ());
+	      init = c_parser_initializer (parser);
+	      finish_init ();
+	      maybe_warn_string_init (TREE_TYPE (d), init);
+	      finish_decl (d, init.value, asm_name);
+	    }
+	  else
+	    {
+	      tree d = start_decl (declarator, specs, false,
+				   chainon (postfix_attrs,
+					    all_prefix_attrs));
+	      finish_decl (d, NULL_TREE, asm_name);
+	    }
+	  if (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      if (c_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))
+		all_prefix_attrs = chainon (c_parser_attributes (parser),
+					    prefix_attrs);
+	      else
+		all_prefix_attrs = prefix_attrs;
+	      continue;
+	    }
+	  else if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      return;
+	    }
+	  else
+	    {
+	      c_parser_error (parser, "expected ',' or ';'");
+	      c_parser_skip_to_end_of_block_or_statement (parser);
+	      return;
+	    }
+	}
+      else if (!fndef_ok)
+	{
+	  c_parser_error (parser,
+			  "expected '=', ',', ';', 'asm' or '__attribute__'");
+	  c_parser_skip_to_end_of_block_or_statement (parser);
+	  return;
+	}
+      /* Function definition (nested or otherwise).  */
+      if (nested)
+	{
+	  if (pedantic)
+	    pedwarn ("ISO C forbids nested functions");
+	  push_function_context ();
+	}
+      if (!start_function (specs, declarator, all_prefix_attrs))
+	{
+	  /* This can appear in many cases looking nothing like a
+	     function definition, so we don't give a more specific
+	     error suggesting there was one.  */
+	  c_parser_error (parser,
+			  "expected '=', ',', ';', 'asm' or '__attribute__'");
+	  if (nested)
+	    pop_function_context ();
+	  break;
+	}
+      /* Parse old-style parameter declarations.  */
+      while (c_lexer_next_token_is_not (parser->lexer, CPP_EOF)
+	     && c_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE))
+	c_parser_declaration_or_fndef (parser, false, false, true, false);
+      DECL_SOURCE_LOCATION (current_function_decl) = input_location;
+      store_parm_decls ();
+      fnbody = c_parser_compound_statement (parser, &fnend_loc);
+      save_loc = input_location;
+      input_location = fnend_loc;
+      if (nested)
+	{
+	  tree decl = current_function_decl;
+	  add_stmt (fnbody);
+	  finish_function ();
+	  pop_function_context ();
+	  add_stmt (build_stmt (DECL_EXPR, decl));
+	}
+      else
+	{
+	  add_stmt (fnbody);
+	  finish_function ();
+	}
+      input_location = save_loc;
+      break;
+    }
+}
+
+/* Parse an asm-definition (asm() outside a function body).  This is a
+   GNU extension.
+
+   asm-definition:
+     simple-asm-expr ;
+*/
+
+static void
+c_parser_asm_definition (c_parser *parser)
+{
+  tree asm_str = c_parser_simple_asm_expr (parser);
+  if (asm_str)
+    assemble_asm (asm_str);
+  c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected ';'");
+}
+
+/* Parse some declaration specifiers (possibly none) (C90 6.5, C99
+   6.7), adding them to SPECS (which may already include some).
+   Storage class specifiers are accepted iff SCSPEC_OK; type
+   specifiers are accepted iff TYPESPEC_OK; attributes are accepted at
+   the start iff START_ATTR_OK.
+
+   declaration-specifiers:
+     storage-class-specifier declaration-specifiers[opt]
+     type-specifier declaration-specifiers[opt]
+     type-qualifier declaration-specifiers[opt]
+     function-specifier declaration-specifiers[opt]
+
+   Function specifiers (inline) are from C99, and are currently
+   handled as storage class specifiers, as is __thread.
+
+   GNU extension:
+     attributes declaration-specifiers[opt]
+     TODO: Objective-C.
+
+   C90 6.5.1, C99 6.7.1:
+   storage-class-specifier:
+     typedef
+     extern
+     static
+     auto
+     register
+
+   GNU extension:
+     __thread
+
+   C99 6.7.4:
+   function-specifier:
+     inline
+
+   C90 6.5.2, C99 6.7.2:
+   type-specifier:
+     void
+     char
+     short
+     int
+     long
+     float
+     double
+     signed
+     unsigned
+     _Bool
+     _Complex
+     [_Imaginary removed in C99 TC2]
+     struct-or-union-specifier
+     enum-specifier
+     typedef-name
+
+   (_Bool and _Complex are new in C99.)
+
+   GNU extension:
+     typeof-specifier
+
+   C90 6.5.3, C99 6.7.3:
+
+   type-qualifier:
+     const
+     restrict
+     volatile
+
+   (restrict is new in C99.)
+*/
+
+static void
+c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
+		    bool scspec_ok, bool typespec_ok, bool start_attr_ok)
+{
+  bool attrs_ok = start_attr_ok;
+  bool seen_type = specs->type_seen_p;
+  while (c_lexer_next_token_is (parser->lexer, CPP_NAME)
+	 || c_lexer_next_token_is (parser->lexer, CPP_KEYWORD))
+    {
+      struct c_typespec t;
+      tree attrs;
+      if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+	{
+	  /* This finishes the specifiers unless a type name is OK, it
+	     is declared as a type name and a type name hasn't yet
+	     been seen.  */
+	  if (!typespec_ok || seen_type
+	      || c_lexer_peek_token (parser->lexer)->id_kind != C_ID_TYPENAME)
+	    break;
+	  seen_type = true;
+	  attrs_ok = true;
+	  t.kind = ctsk_typedef;
+	  /* For a typedef name, record the meaning, not the name.
+	     In case of 'foo foo, bar;'.  */
+	  t.spec = lookup_name (c_lexer_peek_token (parser->lexer)->value);
+	  declspecs_add_type (specs, t);
+	  c_lexer_consume_token (parser->lexer);
+	  continue;
+	}
+      switch (c_lexer_peek_token (parser->lexer)->keyword)
+	{
+	case RID_STATIC:
+	case RID_EXTERN:
+	case RID_REGISTER:
+	case RID_TYPEDEF:
+	case RID_INLINE:
+	case RID_AUTO:
+	case RID_THREAD:
+	  if (!scspec_ok)
+	    goto out;
+	  attrs_ok = true;
+	  declspecs_add_scspec (specs,
+				c_lexer_peek_token (parser->lexer)->value);
+	  c_lexer_consume_token (parser->lexer);
+	  break;
+	case RID_UNSIGNED:
+	case RID_LONG:
+	case RID_SHORT:
+	case RID_SIGNED:
+	case RID_COMPLEX:
+	case RID_INT:
+	case RID_CHAR:
+	case RID_FLOAT:
+	case RID_DOUBLE:
+	case RID_VOID:
+	case RID_BOOL:
+	  if (!typespec_ok)
+	    goto out;
+	  attrs_ok = true;
+	  seen_type = true;
+	  OBJC_NEED_RAW_IDENTIFIER (1);
+	  t.kind = ctsk_resword;
+	  t.spec = c_lexer_peek_token (parser->lexer)->value;
+	  declspecs_add_type (specs, t);
+	  c_lexer_consume_token (parser->lexer);
+	  break;
+	case RID_ENUM:
+	  if (!typespec_ok)
+	    goto out;
+	  attrs_ok = true;
+	  seen_type = true;
+	  t = c_parser_enum_specifier (parser);
+	  declspecs_add_type (specs, t);
+	  break;
+	case RID_STRUCT:
+	case RID_UNION:
+	  if (!typespec_ok)
+	    goto out;
+	  attrs_ok = true;
+	  seen_type = true;
+	  t = c_parser_struct_or_union_specifier (parser);
+	  declspecs_add_type (specs, t);
+	  break;
+	case RID_TYPEOF:
+	  /* ??? The old parser rejected typeof after other type
+	     specifiers, but is a syntax error the best way of
+	     handling this?  */
+	  if (!typespec_ok || seen_type)
+	    goto out;
+	  attrs_ok = true;
+	  seen_type = true;
+	  t = c_parser_typeof_specifier (parser);
+	  declspecs_add_type (specs, t);
+	  break;
+	case RID_CONST:
+	case RID_VOLATILE:
+	case RID_RESTRICT:
+	  attrs_ok = true;
+	  declspecs_add_qual (specs,
+			      c_lexer_peek_token (parser->lexer)->value);
+	  c_lexer_consume_token (parser->lexer);
+	  break;
+	case RID_ATTRIBUTE:
+	  if (!attrs_ok)
+	    goto out;
+	  attrs = c_parser_attributes (parser);
+	  declspecs_add_attrs (specs, attrs);
+	  break;
+	default:
+	  goto out;
+	}
+    }
+ out: ;
+}
+
+/* Parse an enum specifier (C90 6.5.2.2, C99 6.7.2.2).
+
+   enum-specifier:
+     enum attributes[opt] identifier[opt] { enumerator-list } attributes[opt]
+     enum attributes[opt] identifier[opt] { enumerator-list , } attributes[opt]
+     enum attributes[opt] identifier
+
+   The form with trailing comma is new in C99.  The forms with
+   attributes are GNU extensions.  In GNU C, we accept any expression
+   without commas in the syntax (assignment expressions, not just
+   conditional expressions); assignment expressions will be diagnosed
+   as non-constant.
+
+   enumerator-list:
+     enumerator
+     enumerator-list , enumerator
+
+   enumerator:
+     enumeration-constant
+     enumeration-constant = constant-expression
+*/
+
+static struct c_typespec
+c_parser_enum_specifier (c_parser *parser)
+{
+  struct c_typespec ret;
+  tree attrs;
+  tree ident = NULL_TREE;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_ENUM));
+  c_lexer_consume_token (parser->lexer);
+  attrs = c_parser_attributes (parser);
+  if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+    {
+      ident = c_lexer_peek_token (parser->lexer)->value;
+      c_lexer_consume_token (parser->lexer);
+    }
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+    {
+      /* Parse an enum definition.  */
+      tree type = start_enum (ident);
+      tree postfix_attrs;
+      /* We chain the enumerators in reverse order, then put them in
+	 forward order at the end.  */
+      tree values = NULL_TREE;
+      c_lexer_consume_token (parser->lexer);
+      while (true)
+	{
+	  tree enum_id;
+	  tree enum_value;
+	  tree enum_decl;
+	  bool seen_comma;
+	  if (c_lexer_next_token_is_not (parser->lexer, CPP_NAME))
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL);
+	      values = error_mark_node;
+	      break;
+	    }
+	  enum_id = c_lexer_peek_token (parser->lexer)->value;
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_EQ))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      enum_value = c_parser_expr_no_commas (parser).value;
+	    }
+	  else
+	    enum_value = NULL_TREE;
+	  enum_decl = build_enumerator (enum_id, enum_value);
+	  TREE_CHAIN (enum_decl) = values;
+	  values = enum_decl;
+	  seen_comma = false;
+	  if (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	    {
+	      seen_comma = true;
+	      c_lexer_consume_token (parser->lexer);
+	    }
+	  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE))
+	    {
+	      if (seen_comma && pedantic && !flag_isoc99)
+		pedwarn ("comma at end of enumerator list");
+	      c_lexer_consume_token (parser->lexer);
+	      break;
+	    }
+	  if (!seen_comma)
+	    {
+	      c_parser_error (parser, "expected ',' or '}'");
+	      c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL);
+	      values = error_mark_node;
+	      break;
+	    }
+	}
+      postfix_attrs = c_parser_attributes (parser);
+      ret.spec = finish_enum (type, nreverse (values),
+			      chainon (attrs, postfix_attrs));
+      ret.kind = ctsk_tagdef;
+      return ret;
+    }
+  else if (!ident)
+    {
+      c_parser_error (parser, "expected '{'");
+      ret.spec = error_mark_node;
+      ret.kind = ctsk_tagref;
+      return ret;
+    }
+  ret = parser_xref_tag (ENUMERAL_TYPE, ident);
+  /* In ISO C, enumerated types can be referred to only if already
+     defined.  */
+  if (pedantic && !COMPLETE_TYPE_P (ret.spec))
+    pedwarn ("ISO C forbids forward references to %<enum%> types");
+  return ret;
+}
+
+/* Parse a struct or union specifier (C90 6.5.2.1, C99 6.7.2.1).
+
+   struct-or-union-specifier:
+     struct-or-union attributes[opt] identifier[opt]
+       { struct-declaration-list } attributes[opt]
+     struct-or-union attributes[opt] identifier
+
+   struct-declaration-list:
+     struct-declaration
+     struct-declaration-list struct-declaration
+
+   TODO: Objective-C.
+
+   GNU extensions: the semicolon at the end may be omitted; extra
+   semicolons may be included between, before or after
+   struct-declarations.  */
+
+static struct c_typespec
+c_parser_struct_or_union_specifier (c_parser *parser)
+{
+  struct c_typespec ret;
+  tree attrs;
+  tree ident = NULL_TREE;
+  enum tree_code code;
+  switch (c_lexer_peek_token (parser->lexer)->keyword)
+    {
+    case RID_STRUCT:
+      code = RECORD_TYPE;
+      break;
+    case RID_UNION:
+      code = UNION_TYPE;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+  c_lexer_consume_token (parser->lexer);
+  attrs = c_parser_attributes (parser);
+  if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+    {
+      ident = c_lexer_peek_token (parser->lexer)->value;
+      c_lexer_consume_token (parser->lexer);
+    }
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+    {
+      /* Parse a struct or union definition.  */
+      tree type = start_struct (code, ident);
+      tree postfix_attrs;
+      /* We chain the components in reverse order, then put them in
+	 forward order at the end.  Each struct-declaration may
+	 declare multiple components (comma-separated), so we must use
+	 chainon to join them, although when parsing each
+	 struct-declaration we can use TREE_CHAIN directly.
+
+	 The theory behind all this is that there will be more
+	 semicolon separated fields than comma separated fields, and
+	 so we'll be minimizing the number of node traversals required
+	 by chainon.  */
+      tree contents = NULL_TREE;
+      c_lexer_consume_token (parser->lexer);
+      /* Parse the struct-declarations and semicolons.  Problems with
+	 semicolons are diagnosed here; empty structures are diagnosed
+	 elsewhere.  */
+      while (true)
+	{
+	  tree decls;
+	  /* Parse any stray semicolon.  */
+	  if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	    {
+	      if (pedantic)
+		pedwarn ("extra semicolon in struct or union specified");
+	      c_lexer_consume_token (parser->lexer);
+	      continue;
+	    }
+	  /* Stop if at the end of the struct or union contents.  */
+	  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      break;
+	    }
+	  /* Parse some comma-separated declarations, but not the
+	     trailing semicolon if any.  */
+	  decls = c_parser_struct_declaration (parser);
+	  contents = chainon (decls, contents);
+	  /* If no semicolon follows, either we have a parse error or
+	     are at the end of the struct or union and should
+	     pedwarn.  */
+	  if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	    c_lexer_consume_token (parser->lexer);
+	  else
+	    {
+	      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE))
+		pedwarn ("no semicolon at end of struct or union");
+	      else
+		{
+		  c_parser_error (parser, "expected ';'");
+		  c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL);
+		  break;
+		}
+	    }
+	}
+      postfix_attrs = c_parser_attributes (parser);
+      ret.spec = finish_struct (type, nreverse (contents),
+				chainon (attrs, postfix_attrs));
+      ret.kind = ctsk_tagdef;
+      return ret;
+    }
+  else if (!ident)
+    {
+      c_parser_error (parser, "expected '{'");
+      ret.spec = error_mark_node;
+      ret.kind = ctsk_tagref;
+    }
+  ret = parser_xref_tag (code, ident);
+  return ret;
+}
+
+/* Parse a struct-declaration (C90 6.5.2.1, C99 6.7.2.1), *without*
+   the trailing semicolon.
+
+   struct-declaration:
+     specifier-qualifier-list struct-declarator-list
+
+   specifier-qualifier-list:
+     type-specifier specifier-qualifier-list[opt]
+     type-qualifier specifier-qualifier-list[opt]
+     attributes specifier-qualifier-list[opt]
+
+   struct-declarator-list:
+     struct-declarator
+     struct-declarator-list , attributes[opt] struct-declarator
+
+   struct-declarator:
+     declarator attributes[opt]
+     declarator[opt] : constant-expression attributes[opt]
+
+   GNU extensions: semicolons are handled elsewhere; attributes may be
+   used where shown; a struct-declarator-list may be empty;
+   __extension__ may be used at the start of a struct-declaration.  In
+   GNU C, we accept any expression without commas in the syntax
+   (assignment expressions, not just conditional expressions);
+   assignment expressions will be diagnosed as non-constant.  */
+
+static tree
+c_parser_struct_declaration (c_parser *parser)
+{
+  struct c_declspecs *specs;
+  tree prefix_attrs;
+  tree all_prefix_attrs;
+  tree decls;
+  if (c_lexer_next_token_is_keyword (parser->lexer, RID_EXTENSION))
+    {
+      int ext;
+      tree decl;
+      SAVE_EXT_FLAGS (ext);
+      c_lexer_consume_token (parser->lexer);
+      decl = c_parser_struct_declaration (parser);
+      RESTORE_EXT_FLAGS (ext);
+      return decl;
+    }
+  specs = build_null_declspecs ();
+  c_parser_declspecs (parser, specs, false, true, true);
+  if (parser->error)
+    return error_mark_node;
+  if (!specs->declspecs_seen_p)
+    {
+      c_parser_error (parser, "expected specifier-qualifier-list");
+      return NULL_TREE;
+    }
+  finish_declspecs (specs);
+  if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+    {
+      tree ret;
+      if (!specs->type_seen_p)
+	{
+	  if (pedantic)
+	    pedwarn ("ISO C forbids member declarations with no members");
+	  shadow_tag_warned (specs, pedantic);
+	  ret = NULL_TREE;
+	}
+      else
+	{
+	  /* Support for unnamed structs or unions as members of
+	     structs or unions (which is [a] useful and [b] supports
+	     MS P-SDK).  */
+	  ret = grokfield (build_id_declarator (NULL_TREE), specs, NULL_TREE);
+	}
+      return ret;
+    }
+  pending_xref_error ();
+  prefix_attrs = specs->attrs;
+  all_prefix_attrs = prefix_attrs;
+  specs->attrs = NULL_TREE;
+  decls = NULL_TREE;
+  while (true)
+    {
+      /* Declaring one or more declarators or un-named bit-fields.  */
+      struct c_declarator *declarator;
+      bool dummy = false;
+      if (c_lexer_next_token_is (parser->lexer, CPP_COLON))
+	declarator = build_id_declarator (NULL_TREE);
+      else
+	declarator = c_parser_declarator (parser, specs->type_seen_p,
+					  C_DTR_NORMAL, &dummy);
+      if (declarator == NULL)
+	{
+	  c_parser_skip_to_end_of_block_or_statement (parser);
+	  break;
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_COLON)
+	  || c_lexer_next_token_is (parser->lexer, CPP_COMMA)
+	  || c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)
+	  || c_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))
+	{
+	  tree postfix_attrs = NULL_TREE;
+	  tree width = NULL_TREE;
+	  tree d;
+	  if (c_lexer_next_token_is (parser->lexer, CPP_COLON))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      width = c_parser_expr_no_commas (parser).value;
+	    }
+	  if (c_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))
+	    postfix_attrs = c_parser_attributes (parser);
+	  d = grokfield (declarator, specs, width);
+	  decl_attributes (&d, chainon (postfix_attrs,
+					all_prefix_attrs), 0);
+	  TREE_CHAIN (d) = decls;
+	  decls = d;
+	  if (c_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))
+	    all_prefix_attrs = chainon (c_parser_attributes (parser),
+					prefix_attrs);
+	  else
+	    all_prefix_attrs = prefix_attrs;
+	  if (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	    c_lexer_consume_token (parser->lexer);
+	  else if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	    {
+	      /* Semicolon consumed in caller.  */
+	      break;
+	    }
+	  else
+	    {
+	      c_parser_error (parser, "expected ',' or ';'");
+	      break;
+	    }
+	}
+      else
+	{
+	  c_parser_error (parser,
+			  "expected ':', ',', ';' or '__attribute__'");
+	  break;
+	}
+    }
+  return decls;
+}
+
+/* Parse a typeof specifier (a GNU extension).
+
+   typeof-specifier:
+     typeof ( expression )
+     typeof ( type-name )
+*/
+
+static struct c_typespec
+c_parser_typeof_specifier (c_parser *parser)
+{
+  struct c_typespec ret;
+  ret.kind = ctsk_typeof;
+  ret.spec = error_mark_node;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_TYPEOF));
+  c_lexer_consume_token (parser->lexer);
+  skip_evaluation++;
+  in_typeof++;
+  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    {
+      skip_evaluation--;
+      in_typeof--;
+      return ret;
+    }
+  if (c_lexer_next_token_starts_typename (parser->lexer))
+    {
+      struct c_type_name *type = c_parser_type_name (parser);
+      skip_evaluation--;
+      in_typeof--;
+      if (type != NULL)
+	{
+	  ret.spec = groktypename (type);
+	  pop_maybe_used (variably_modified_type_p (ret.spec, NULL_TREE));
+	}
+    }
+  else
+    {
+      struct c_expr expr = c_parser_expression (parser);
+      skip_evaluation--;
+      in_typeof--;
+      if (TREE_CODE (expr.value) == COMPONENT_REF
+	  && DECL_C_BIT_FIELD (TREE_OPERAND (expr.value, 1)))
+	error ("%<typeof%> applied to a bit-field");
+      ret.spec = TREE_TYPE (expr.value);
+      pop_maybe_used (variably_modified_type_p (ret.spec, NULL_TREE));
+    }
+  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+  return ret;
+}
+
+/* Parse a declarator, possibly an abstract declarator (C90 6.5.4,
+   6.5.5, C99 6.7.5, 6.7.6).  If TYPE_SEEN_P then a typedef name may
+   be redeclared; otherwise it may not.  KIND indicates which kind of
+   declarator is wanted.  Returns a valid declarator except in the
+   case of a syntax error in which case NULL is returned.  *SEEN_ID is
+   set to true if an identifier being declared is seen; this is used
+   to diagnose bad forms of abstract array declarators and to
+   determine whether an identifer list is syntactically permitted.
+
+   declarator:
+     pointer[opt] direct-declarator
+
+   direct-declarator:
+     identifier
+     ( attributes[opt] declarator )
+     direct-declarator array-declarator
+     direct-declarator ( parameter-type-list )
+     direct-declarator ( identifier-list[opt] )
+
+   pointer:
+     * type-qualifier-list[opt]
+     * type-qualifier-list[opt] pointer
+
+   type-qualifier-list:
+     type-qualifier
+     attributes
+     type-qualifier-list type-qualifier
+     type-qualifier-list attributes
+
+   parameter-type-list:
+     parameter-list
+     parameter-list , ...
+
+   parameter-list:
+     parameter-declaration
+     parameter-list , parameter-declaration
+
+   parameter-declaration:
+     declaration-specifiers declarator attributes[opt]
+     declaration-specifiers abstract-declarator[opt] attributes[opt]
+
+   identifier-list:
+     identifier
+     identifier-list , identifier
+
+   abstract-declarator:
+     pointer
+     pointer[opt] direct-abstract-declarator
+
+   direct-abstract-declarator:
+     ( attributes[opt] abstract-declarator )
+     direct-abstract-declarator[opt] array-declarator
+     direct-abstract-declarator[opt] ( parameter-type-list[opt] )
+
+   Some forms of array declarator are not included in C99 in the
+   syntax for abstract declarators; these are disallowed elsewhere.
+   This may be a defect (DR#289).
+
+   This function also accepts an omitted abstract declarator as being
+   an abstract declarator, although not part of the formal syntax.
+
+   GNU extensions: the uses of attributes shown above; forward
+   declarations of parameters (not shown in syntax above).  */
+
+static struct c_declarator *
+c_parser_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
+		     bool *seen_id)
+{
+  /* Parse any initial pointer part.  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_MULT))
+    {
+      struct c_declspecs *quals_attrs = build_null_declspecs ();
+      struct c_declarator *inner;
+      c_lexer_consume_token (parser->lexer);
+      c_parser_declspecs (parser, quals_attrs, false, false, true);
+      inner = c_parser_declarator (parser, type_seen_p, kind, seen_id);
+      if (inner == NULL)
+	return NULL;
+      else
+	return make_pointer_declarator (quals_attrs, inner);
+    }
+  /* Now we have a direct declarator, direct abstract declarator or
+     nothing (which counts as a direct abstract declarator here).  */
+  return c_parser_direct_declarator (parser, type_seen_p, kind, seen_id);
+}
+
+/* Parse a direct declarator or direct abstract declarator; arguments
+   as c_parser_declarator.  */
+
+static struct c_declarator *
+c_parser_direct_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
+			    bool *seen_id)
+{
+  /* The direct declarator must start with an identifier (possibly
+     omitted) or a parenthesized declarator (possibly abstract).  In
+     an ordinary declarator, initial parentheses must start a
+     parenthesized declarator.  In an abstract declarator or parameter
+     declarator, they could start a parenthesized declarator or a
+     parameter list.  To tell which, the open parenthesis and any
+     following attributes must be read.  If a declaration specifier
+     follows, then it is a parameter list; if the specifier is a
+     typedef name, there might be an ambiguity about redeclaring it,
+     which is resolved in the direction of treating it as a typedef
+     name.  If a close parenthesis follows, it is also an empty
+     parameter list, as the syntax does not permit empty abstract
+     declarators.  Otherwise, it is a parenthesised declarator (in
+     which case the analysis may be repeated inside it, recursively).
+
+     ??? There is an ambiguity in a parameter declaration "int
+     (__attribute__((foo)) x)", where x is not a typedef name: it
+     could be an abstract declarator for a function, or declare x with
+     parentheses.  The proper resolution of this ambiguity needs
+     documenting.  At present we follow an accident of the old
+     parser's implementation, whereby the first parameter must have
+     some declaration specifiers other than just attributes.  Thus as
+     a parameter declaration it is treated as a parenthesised
+     parameter named x, and as an abstract declarator it is
+     rejected.
+
+     ??? Also following the old parser, attributes inside an empty
+     parameter list are ignored, making it a list not yielding a
+     prototype, rather than giving an error or making it have one
+     parameter with implicit type int.  */
+
+  if (kind != C_DTR_ABSTRACT
+      && c_lexer_next_token_is (parser->lexer, CPP_NAME)
+      && (type_seen_p
+	  || c_lexer_peek_token (parser->lexer)->id_kind == C_ID_ID))
+    {
+      struct c_declarator *inner
+	= build_id_declarator (c_lexer_peek_token (parser->lexer)->value);
+      *seen_id = true;
+      c_lexer_consume_token (parser->lexer);
+      return c_parser_direct_declarator_inner (parser, *seen_id, inner);
+    }
+
+  if (kind != C_DTR_NORMAL
+      && c_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
+    {
+      struct c_declarator *inner = build_id_declarator (NULL_TREE);
+      return c_parser_direct_declarator_inner (parser, *seen_id, inner);
+    }
+
+  /* Either we are at the end of an abstract declarator, or we have
+     parentheses.  */
+
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+    {
+      tree attrs;
+      struct c_declarator *inner;
+      c_lexer_consume_token (parser->lexer);
+      attrs = c_parser_attributes (parser);
+      if (kind != C_DTR_NORMAL
+	  && (c_lexer_next_token_starts_declspecs (parser->lexer)
+	      || c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN)))
+	{
+	  struct c_arg_info *args
+	    = c_parser_parms_declarator (parser, kind == C_DTR_NORMAL,
+					 attrs);
+	  if (args == NULL)
+	    return NULL;
+	  else
+	    {
+	      inner
+		= build_function_declarator (args,
+					     build_id_declarator (NULL_TREE));
+	      return c_parser_direct_declarator_inner (parser, *seen_id,
+						       inner);
+	    }
+	}
+      /* A parenthesized declarator.  */
+      inner = c_parser_declarator (parser, type_seen_p, kind, seen_id);
+      if (inner != NULL && attrs != NULL)
+	inner = build_attrs_declarator (attrs, inner);
+      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  if (inner == NULL)
+	    return NULL;
+	  else
+	    return c_parser_direct_declarator_inner (parser, *seen_id, inner);
+	}
+      else
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  return NULL;
+	}
+    }
+  else
+    {
+      if (kind == C_DTR_NORMAL)
+	{
+	  c_parser_error (parser, "expected identifier or '('");
+	  return NULL;
+	}
+      else
+	return build_id_declarator (NULL_TREE);
+    }
+}
+
+/* Parse part of a direct declarator or direct abstract declarator,
+   given that some (in INNER) has already been parsed; ID_PRESENT is
+   true if an identifier is present, false for an abstract
+   declarator.  */
+
+static struct c_declarator *
+c_parser_direct_declarator_inner (c_parser *parser, bool id_present,
+				  struct c_declarator *inner)
+{
+  /* Parse a sequence of array declarators and parameter lists.  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
+    {
+      struct c_declarator *declarator;
+      struct c_declspecs *quals_attrs = build_null_declspecs ();
+      bool static_seen;
+      bool star_seen;
+      tree dimen;
+      c_lexer_consume_token (parser->lexer);
+      c_parser_declspecs (parser, quals_attrs, false, false, true);
+      static_seen = c_lexer_next_token_is_keyword (parser->lexer, RID_STATIC);
+      if (static_seen)
+	c_lexer_consume_token (parser->lexer);
+      if (static_seen && !quals_attrs->declspecs_seen_p)
+	c_parser_declspecs (parser, quals_attrs, false, false, true);
+      if (!quals_attrs->declspecs_seen_p)
+	quals_attrs = NULL;
+      /* If "static" is present, there must be an array dimension.
+	 Otherwise, there may be a dimension, "*", or no
+	 dimension.  */
+      if (static_seen)
+	{
+	  star_seen = false;
+	  dimen = c_parser_expr_no_commas (parser).value;
+	}
+      else
+	{
+	  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE))
+	    {
+	      dimen = NULL_TREE;
+	      star_seen = false;
+	    }
+	  else if (c_lexer_next_token_is (parser->lexer, CPP_MULT))
+	    {
+	      if (c_lexer_peek_2nd_token (parser->lexer)->type
+		  == CPP_CLOSE_SQUARE)
+		{
+		  dimen = NULL_TREE;
+		  star_seen = true;
+		  c_lexer_consume_token (parser->lexer);
+		}
+	      else
+		{
+		  star_seen = false;
+		  dimen = c_parser_expr_no_commas (parser).value;
+		}
+	    }
+	  else
+	    {
+	      star_seen = false;
+	      dimen = c_parser_expr_no_commas (parser).value;
+	    }
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE))
+	c_lexer_consume_token (parser->lexer);
+      else
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, "expected ']'");
+	  return NULL;
+	}
+      declarator = build_array_declarator (dimen, quals_attrs, static_seen,
+					   star_seen);
+      inner = set_array_declarator_inner (declarator, inner, !id_present);
+      return c_parser_direct_declarator_inner (parser, id_present, inner);
+    }
+  else if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+    {
+      tree attrs;
+      struct c_arg_info *args;
+      c_lexer_consume_token (parser->lexer);
+      attrs = c_parser_attributes (parser);
+      args = c_parser_parms_declarator (parser, id_present, attrs);
+      if (args == NULL)
+	return NULL;
+      else
+	{
+	  inner = build_function_declarator (args, inner);
+	  return c_parser_direct_declarator_inner (parser, id_present, inner);
+	}
+    }
+  return inner;
+}
+
+/* Parse a parameter list or identifier list, including the closing
+   parenthesis but not the opening one.  ATTRS are the attributes at
+   the start of the list.  ID_LIST_OK is true if an identifier list is
+   acceptable; such a list must not have attributes at the start.  */
+
+static struct c_arg_info *
+c_parser_parms_declarator (c_parser *parser, bool id_list_ok, tree attrs)
+{
+  push_scope ();
+  declare_parm_level ();
+  /* If the list starts with an identifier, it is an identifier list.
+     Otherwise, it is either a prototype list or an empty list.  */
+  if (id_list_ok
+      && !attrs
+      && c_lexer_next_token_is (parser->lexer, CPP_NAME)
+      && c_lexer_peek_token (parser->lexer)->id_kind == C_ID_ID)
+    {
+      tree list = NULL_TREE;
+      while (c_lexer_next_token_is (parser->lexer, CPP_NAME)
+	     && c_lexer_peek_token (parser->lexer)->id_kind == C_ID_ID)
+	{
+	  list = chainon (list, build_tree_list (NULL_TREE,
+						 c_lexer_peek_token (parser->lexer)->value));
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is_not (parser->lexer, CPP_COMMA))
+	    break;
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      break;
+	    }
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	{
+	  struct c_arg_info *ret = XOBNEW (&parser_obstack, struct c_arg_info);
+	  ret->parms = 0;
+	  ret->tags = 0;
+	  ret->types = list;
+	  ret->others = 0;
+	  c_lexer_consume_token (parser->lexer);
+	  pop_scope ();
+	  return ret;
+	}
+      else
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  pop_scope ();
+	  return NULL;
+	}
+    }
+  else
+    {
+      struct c_arg_info *ret = c_parser_parms_list_declarator (parser, attrs);
+      pop_scope ();
+      return ret;
+    }
+}
+
+/* Parse a parameter list (possibly empty), including the closing
+   parenthesis but not the opening one.  ATTRS are the attributes at
+   the start of the list.  */
+
+static struct c_arg_info *
+c_parser_parms_list_declarator (c_parser *parser, tree attrs)
+{
+  /* ??? Following the old parser, forward parameter declarations may
+     use abstract declarators, and if no real parameter declarations
+     follow the forward declarations then this is not diagnosed.  Also
+     note as above that attributes are ignored as the only contents of
+     the parentheses, or as the only contents after forward
+     declarations.  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+    {
+      struct c_arg_info *ret = XOBNEW (&parser_obstack, struct c_arg_info);
+      ret->parms = 0;
+      ret->tags = 0;
+      ret->types = 0;
+      ret->others = 0;
+      c_lexer_consume_token (parser->lexer);
+      return ret;
+    }
+  if (c_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
+    {
+      struct c_arg_info *ret = XOBNEW (&parser_obstack, struct c_arg_info);
+      ret->parms = 0;
+      ret->tags = 0;
+      ret->others = 0;
+      /* Suppress -Wold-style-definition for this case.  */
+      ret->types = error_mark_node;
+      error ("ISO C requires a named argument before %<...%>");
+      c_lexer_consume_token (parser->lexer);
+      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  return ret;
+	}
+      else
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  return NULL;
+	}
+    }
+  /* Nonempty list of parameters, either terminated with semicolon
+     (forward declarations; recurse) or with close parenthesis (normal
+     function) or with ", ... )" (variadic function).  */
+  while (true)
+    {
+      /* Parse a parameter.  */
+      struct c_declspecs *specs;
+      struct c_declarator *declarator;
+      tree prefix_attrs;
+      tree postfix_attrs = NULL_TREE;
+      bool dummy = false;
+      if (!c_lexer_next_token_starts_declspecs (parser->lexer))
+	{
+	  c_parser_error (parser, "expected declaration specifiers or '...'");
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  return NULL;
+	}
+      specs = build_null_declspecs ();
+      if (attrs)
+	{
+	  declspecs_add_attrs (specs, attrs);
+	  attrs = NULL_TREE;
+	}
+      c_parser_declspecs (parser, specs, true, true, true);
+      finish_declspecs (specs);
+      pending_xref_error ();
+      prefix_attrs = specs->attrs;
+      specs->attrs = NULL_TREE;
+      declarator = c_parser_declarator (parser, specs->type_seen_p,
+					C_DTR_PARM, &dummy);
+      if (declarator == NULL)
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  return NULL;
+	}
+      if (c_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))
+	postfix_attrs = c_parser_attributes (parser);
+      push_parm_decl (build_c_parm (specs,
+				    chainon (postfix_attrs,
+					     prefix_attrs), declarator));
+      if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	{
+	  tree new_attrs;
+	  c_lexer_consume_token (parser->lexer);
+	  new_attrs = c_parser_attributes (parser);
+	  return c_parser_parms_list_declarator (parser, new_attrs);
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  return get_parm_info (false);
+	}
+      if (!c_parser_require (parser, CPP_COMMA, "expected ';', ',' or ')'"))
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  return NULL;
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      return get_parm_info (true);
+	    }
+	  else
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+					 "expected ')'");
+	      return NULL;
+	    }
+	}
+    }
+}
+
+/* Parse a simple asm expression.  This is used in restricted
+   contexts, where a full expression with inputs and outputs does not
+   make sense.  This is a GNU extension.
+
+   simple-asm-expr:
+     asm ( string-literal )
+*/
+
+static tree
+c_parser_simple_asm_expr (c_parser *parser)
+{
+  tree str;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_ASM));
+  c_lex_string_translate = 0;
+  c_lexer_consume_token (parser->lexer);
+  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    {
+      c_lex_string_translate = 1;
+      return NULL_TREE;
+    }
+  /* ??? The old parser accepted wide string literals here, but do we
+     want to?  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_STRING)
+      || c_lexer_next_token_is (parser->lexer, CPP_WSTRING))
+    {
+      str = c_lexer_peek_token (parser->lexer)->value;
+      c_lexer_consume_token (parser->lexer);
+    }
+  else
+    {
+      c_lex_string_translate = 1;
+      c_parser_error (parser, "expected string literal");
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  c_lex_string_translate = 1;
+  if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected ')'"))
+    {
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  return str;
+}
+
+/* Parse (possibly empty) attributes.  This is a GNU extension.
+
+   attributes:
+     empty
+     attributes attribute
+
+   attribute:
+     __attribute__ ( ( attribute-list ) )
+
+   attribute-list:
+     attrib
+     attribute_list , attrib
+
+   attrib:
+     empty
+     any-word
+     any-word ( identifier )
+     any-word ( identifier , nonempty-expr-list )
+     any-word ( expr-list )
+
+   where the "identifier" must not be declared as a type, and
+   "any-word" may be any identifier (including one declared as a
+   type), a reserved word storage class specifier, type specifier or
+   type qualifier.  ??? This still leaves out most reserved keywords
+   (following the old parser), shouldn't we include them, and why not
+   allow identifiers declared as types to start the arguments?  */
+
+static tree
+c_parser_attributes (c_parser *parser)
+{
+  tree attrs = NULL_TREE;
+  while (c_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))
+    {
+      c_lex_string_translate = 0;
+      c_lexer_consume_token (parser->lexer);
+      if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	{
+	  c_lex_string_translate = 1;
+	  return attrs;
+	}
+      if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	{
+	  c_lex_string_translate = 1;
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  return attrs;
+	}
+      /* Parse the attribute list.  */
+      while (c_lexer_next_token_is (parser->lexer, CPP_COMMA)
+	     || c_lexer_next_token_is (parser->lexer, CPP_NAME)
+	     || c_lexer_next_token_is (parser->lexer, CPP_KEYWORD))
+	{
+	  tree attr, attr_name, attr_args;
+	  if (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      continue;
+	    }
+	  if (c_lexer_next_token_is (parser->lexer, CPP_KEYWORD))
+	    {
+	      /* ??? See comment above about what keywords are
+		 accepted here.  */
+	      bool ok;
+	      switch (c_lexer_peek_token (parser->lexer)->keyword)
+		{
+		case RID_STATIC:
+		case RID_UNSIGNED:
+		case RID_LONG:
+		case RID_CONST:
+		case RID_EXTERN:
+		case RID_REGISTER:
+		case RID_TYPEDEF:
+		case RID_SHORT:
+		case RID_INLINE:
+		case RID_VOLATILE:
+		case RID_SIGNED:
+		case RID_AUTO:
+		case RID_RESTRICT:
+		case RID_COMPLEX:
+		case RID_THREAD:
+		case RID_INT:
+		case RID_CHAR:
+		case RID_FLOAT:
+		case RID_DOUBLE:
+		case RID_VOID:
+		case RID_BOOL:
+		  ok = true;
+		  break;
+		default:
+		  ok = false;
+		  break;
+		}
+	      if (!ok)
+		break;
+	    }
+	  attr_name = c_lexer_peek_token (parser->lexer)->value;
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is_not (parser->lexer, CPP_OPEN_PAREN))
+	    {
+	      attr = build_tree_list (attr_name, NULL_TREE);
+	      attrs = chainon (attrs, attr);
+	      continue;
+	    }
+	  c_lexer_consume_token (parser->lexer);
+	  /* Parse the attribute contents.  If they start with an
+	     identifier which is followed by a comma or close
+	     parenthesis, then the arguments start with that
+	     identifier; otherwise they are an expression list.  */
+	  if (c_lexer_next_token_is (parser->lexer, CPP_NAME)
+	      && c_lexer_peek_token (parser->lexer)->id_kind == C_ID_ID
+	      && ((c_lexer_peek_2nd_token (parser->lexer)->type == CPP_COMMA)
+		  || (c_lexer_peek_2nd_token (parser->lexer)->type
+		      == CPP_CLOSE_PAREN)))
+	    {
+	      tree arg1 = c_lexer_peek_token (parser->lexer)->value;
+	      c_lexer_consume_token (parser->lexer);
+	      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+		attr_args = build_tree_list (NULL_TREE, arg1);
+	      else
+		{
+		  c_lexer_consume_token (parser->lexer);
+		  attr_args = tree_cons (NULL_TREE, arg1,
+					 c_parser_expr_list (parser));
+		}
+	    }
+	  else
+	    {
+	      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+		attr_args = NULL_TREE;
+	      else
+		attr_args = c_parser_expr_list (parser);
+	    }
+	  attr = build_tree_list (attr_name, attr_args);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	    c_lexer_consume_token (parser->lexer);
+	  else
+	    {
+	      c_lex_string_translate = 1;
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+					 "expected ')'");
+	      return attrs;
+	    }
+	  attrs = chainon (attrs, attr);
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	c_lexer_consume_token (parser->lexer);
+      else
+	{
+	  c_lex_string_translate = 1;
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  return attrs;
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	c_lexer_consume_token (parser->lexer);
+      else
+	{
+	  c_lex_string_translate = 1;
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  return attrs;
+	}
+      c_lex_string_translate = 1;
+    }
+  return attrs;
+}
+
+/* Parse a type name (C90 6.5.5, C99 6.7.6).
+
+   type-name:
+     specifier-qualifier-list abstract-declarator[opt]
+*/
+
+static struct c_type_name *
+c_parser_type_name (c_parser *parser)
+{
+  struct c_declspecs *specs = build_null_declspecs ();
+  struct c_declarator *declarator;
+  struct c_type_name *ret;
+  bool dummy = false;
+  c_parser_declspecs (parser, specs, false, true, true);
+  if (!specs->declspecs_seen_p)
+    {
+      c_parser_error (parser, "expected specifier-qualifier-list");
+      return NULL;
+    }
+  pending_xref_error ();
+  finish_declspecs (specs);
+  declarator = c_parser_declarator (parser, specs->type_seen_p,
+				    C_DTR_ABSTRACT, &dummy);
+  if (declarator == NULL)
+    return NULL;
+  ret = XOBNEW (&parser_obstack, struct c_type_name);
+  ret->specs = specs;
+  ret->declarator = declarator;
+  return ret;
+}
+
+/* Parse an initializer (C90 6.5.7, C99 6.7.8).
+
+   initializer:
+     assignment-expression
+     { initializer-list }
+     { initializer-list , }
+
+   initializer-list:
+     designation[opt] initializer
+     initializer-list , designation[opt] initializer
+
+   designation:
+     designator-list =
+
+   designator-list:
+     designator
+     designator-list designator
+
+   designator:
+     [ constant-expression ]
+     . identifier
+
+   GNU extensions:
+
+   initializer:
+     { }
+
+   designation:
+     designator
+     identifier :
+
+   designator:
+     [ constant-expression ... constant-expression ]
+
+   Any expression without commas is accepted in the syntax for the
+   constant-expressions, with non-constant expressions rejected later.
+
+   ??? Allowing old-style [n] designators has as a side-effect (copied
+   from the old parser) allowing ".member" without "=" as a
+   designator, but perhaps as this has never been documented (and only
+   worked from 2.95.x onwards) it could be freely removed.
+
+   This function is only used for top-level initializers; for nested
+   ones, see c_parser_initval.  */
+
+static struct c_expr
+c_parser_initializer (c_parser *parser)
+{
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+    return c_parser_braced_init (parser, NULL_TREE, false);
+  else
+    return c_parser_expr_no_commas (parser);
+}
+
+/* Parse a braced initializer list.  TYPE is the type specified for a
+   compound literal, and NULL_TREE for other initializers and for
+   nested braced lists.  NESTED_P is true for nested braced lists,
+   false for the list of a compound literal or the list that is the
+   top-level initializer in a declaration.  */
+
+static struct c_expr
+c_parser_braced_init (c_parser *parser, tree type, bool nested_p)
+{
+  gcc_assert (c_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE));
+  c_lexer_consume_token (parser->lexer);
+  if (nested_p)
+    push_init_level (0);
+  else
+    really_start_incremental_init (type);
+  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE))
+    {
+      if (pedantic)
+	pedwarn ("ISO C forbids empty initializer braces");
+    }
+  else
+    {
+      /* Parse a non-empty initializer list, possibly with a trailing
+	 comma.  */
+      while (true)
+	{
+	  c_parser_initelt (parser);
+	  if (parser->error)
+	    break;
+	  if (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	    c_lexer_consume_token (parser->lexer);
+	  else
+	    break;
+	  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE))
+	    break;
+	}
+    }
+  if (c_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_BRACE))
+    {
+      struct c_expr ret;
+      ret.value = error_mark_node;
+      ret.original_code = ERROR_MARK;
+      c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, "expected '}'");
+      return ret;
+    }
+  c_lexer_consume_token (parser->lexer);
+  return pop_init_level (0);
+}
+
+/* Parse a nested initializer, including designators.  */
+
+static void
+c_parser_initelt (c_parser *parser)
+{
+  /* Parse any designator or designator list.  A single designator may
+     have the subsequent "=" omitted in GNU C, but a longer list may
+     not.  See ??? comment above about ".foo" case without "=".  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_NAME)
+      && c_lexer_peek_2nd_token (parser->lexer)->type == CPP_COLON)
+    {
+      /* Old-style structure member designator.  */
+      set_init_label (c_lexer_peek_token (parser->lexer)->value);
+      if (pedantic)
+	pedwarn ("obsolete use of designated initializer with %<:%>");
+      c_lexer_consume_token (parser->lexer);
+      c_lexer_consume_token (parser->lexer);
+    }
+  else
+    {
+      int des_seen = 0;
+      while (c_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE)
+	     || c_lexer_next_token_is (parser->lexer, CPP_DOT))
+	{
+	  if (des_seen < 2)
+	    des_seen++;
+	  if (c_lexer_next_token_is (parser->lexer, CPP_DOT))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+		{
+		  set_init_label (c_lexer_peek_token (parser->lexer)->value);
+		  c_lexer_consume_token (parser->lexer);
+		}
+	      else
+		{
+		  c_parser_error (parser, "expected identifier");
+		  return;
+		}
+	    }
+	  else
+	    {
+	      tree first, second;
+	      c_lexer_consume_token (parser->lexer);
+	      first = c_parser_expr_no_commas (parser).value;
+	      if (c_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
+		{
+		  c_lexer_consume_token (parser->lexer);
+		  second = c_parser_expr_no_commas (parser).value;
+		}
+	      else
+		second = NULL_TREE;
+	      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE))
+		{
+		  c_lexer_consume_token (parser->lexer);
+		  set_init_index (first, second);
+		  if (pedantic && second)
+		    pedwarn ("ISO C forbids specifying range of "
+			     "elements to initialize");
+		}
+	      else
+		c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE,
+					   "expected ']'");
+	    }
+	}
+      if (des_seen >= 1)
+	{
+	  if (c_lexer_next_token_is (parser->lexer, CPP_EQ))
+	    {
+	      if (pedantic && !flag_isoc99)
+		pedwarn ("ISO C90 forbids specifying subobject to initialize");
+	      c_lexer_consume_token (parser->lexer);
+	    }
+	  else
+	    {
+	      if (des_seen == 1)
+		{
+		  if (pedantic)
+		    pedwarn ("obsolete use of designated initializer "
+			     "without %<=%>");
+		}
+	      else
+		{
+		  c_parser_error (parser, "expected '='");
+		  return;
+		}
+	    }
+	}
+    }
+  c_parser_initval (parser);
+}
+
+/* Parse a nested initializer; as c_parser_initializer but parses
+   initializers within braced lists, after any designators have been
+   applied.  */
+
+static void
+c_parser_initval (c_parser *parser)
+{
+  struct c_expr init;
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+    init = c_parser_braced_init (parser, NULL_TREE, true);
+  else
+    init = c_parser_expr_no_commas (parser);
+  process_init_element (init);
+}
+
+/* Parse a compound statement (possibly a function body) (C90 6.6.2,
+   C99 6.8.2).  The location of the closing brace is stored in
+   *END_LOC if that is not NULL.
+
+   compound-statement:
+     { block-item-list[opt] }
+     { label-decls block-item-list }
+
+   label-decls:
+     label-decl
+     label-decls label-decl
+
+   label-decl:
+     __label__ identifier-list ;
+
+   block-item-list:
+     block-item
+     block-item-list block-item
+
+   block-item:
+     declaration
+     statement
+
+   Label-decls are a GNU extension.  Allowing the mixing of
+   declarations and code is new in C99.  The GNU syntax also permits
+   (not shown above) labels at the end of compound statements, which
+   yield an error.  ??? The syntax follows the old parser in requiring
+   something after label declarations.  Although they are erroneous if
+   the labels declared aren't defined, is it useful for the syntax to
+   be this way?  */
+
+static tree
+c_parser_compound_statement (c_parser *parser, location_t *end_loc)
+{
+  tree stmt;
+  if (!c_parser_require (parser, CPP_OPEN_BRACE, "expected '{'"))
+    return NULL_TREE;
+  stmt = c_begin_compound_stmt (true);
+  c_parser_compound_statement_nostart (parser, end_loc);
+  return c_end_compound_stmt (stmt, true);
+}
+
+/* Parse a compound statement except for the opening brace.  This is
+   used for parsing both compound statements and statement expressions
+   (which follow different paths to handling the opening).  */
+
+static void
+c_parser_compound_statement_nostart (c_parser *parser, location_t *end_loc)
+{
+  bool last_stmt = false;
+  bool last_label = false;
+  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE))
+    {
+      if (end_loc)
+	*end_loc = c_lexer_peek_token (parser->lexer)->location;
+      c_lexer_consume_token (parser->lexer);
+      return;
+    }
+  if (c_lexer_next_token_is_keyword (parser->lexer, RID_LABEL))
+    {
+      while (c_lexer_next_token_is_keyword (parser->lexer, RID_LABEL))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  /* Any identifiers, including those declared as type names,
+	     are OK here.  */
+	  if (c_lexer_next_token_is_not (parser->lexer, CPP_NAME))
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      c_parser_skip_until_found (parser, CPP_SEMICOLON, NULL);
+	      continue;
+	    }
+	  while (true)
+	    {
+	      tree label;
+	      if (c_lexer_next_token_is_not (parser->lexer, CPP_NAME))
+		{
+		  c_parser_error (parser, "expected identifier");
+		  break;
+		}
+	      label
+		= declare_label (c_lexer_peek_token (parser->lexer)->value);
+	      C_DECLARED_LABEL_FLAG (label) = 1;
+	      add_stmt (build_stmt (DECL_EXPR, label));
+	      c_lexer_consume_token (parser->lexer);
+	      if (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+		c_lexer_consume_token (parser->lexer);
+	      else
+		break;
+	    }
+	  c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected ';'");
+	}
+      if (pedantic)
+	pedwarn ("ISO C forbids label declarations");
+    }
+  /* We must now have at least one statement, label or declaration.  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE))
+    {
+      c_parser_error (parser, "expected declaration or statement");
+      if (end_loc)
+	*end_loc = c_lexer_peek_token (parser->lexer)->location;
+      c_lexer_consume_token (parser->lexer);
+      return;
+    }
+  while (c_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_BRACE))
+    {
+      location_t loc = c_lexer_peek_token (parser->lexer)->location;
+      if (c_lexer_next_token_is_keyword (parser->lexer, RID_CASE)
+	  || c_lexer_next_token_is_keyword (parser->lexer, RID_DEFAULT)
+	  || (c_lexer_next_token_is (parser->lexer, CPP_NAME)
+	      && c_lexer_peek_2nd_token (parser->lexer)->type == CPP_COLON))
+	{
+	  last_label = true;
+	  last_stmt = false;
+	  c_parser_label (parser);
+	}
+      else if (!last_label
+	       && c_lexer_next_token_starts_declspecs (parser->lexer))
+	{
+	  last_label = false;
+	  c_parser_declaration_or_fndef (parser, true, true, true, true);
+	  if (last_stmt
+	      && ((pedantic && !flag_isoc99)
+		  || warn_declaration_after_statement))
+	    pedwarn_c90 ("%HISO C90 forbids mixed declarations and code",
+			 &loc);
+	  last_stmt = false;
+	}
+      else if (!last_label
+	       && c_lexer_next_token_is_keyword (parser->lexer, RID_EXTENSION))
+	{
+	  /* __extension__ can start a declaration, but is also an
+	     unary operator that can start an expression.  Consume all
+	     but the last of a possible series of __extension__ to
+	     determine which.  */
+	  while (c_lexer_peek_2nd_token (parser->lexer)->type == CPP_KEYWORD
+		 && (c_lexer_peek_2nd_token (parser->lexer)->keyword
+		     == RID_EXTENSION))
+	    c_lexer_consume_token (parser->lexer);
+	  if (c_token_starts_declspecs (c_lexer_peek_2nd_token (parser->lexer)))
+	    {
+	      int ext;
+	      SAVE_EXT_FLAGS (ext);
+	      c_lexer_consume_token (parser->lexer);
+	      last_label = false;
+	      c_parser_declaration_or_fndef (parser, true, true, true, true);
+	      /* Following the old parser, __extension__ does not
+		 disable this diagnostic.  */
+	      RESTORE_EXT_FLAGS (ext);
+	      if (last_stmt
+		  && ((pedantic && !flag_isoc99)
+		      || warn_declaration_after_statement))
+		pedwarn_c90 ("%HISO C90 forbids mixed declarations and code",
+			     &loc);
+	      last_stmt = false;
+	    }
+	  else
+	    goto statement;
+	}
+      else
+	{
+	statement:
+	  last_label = false;
+	  last_stmt = true;
+	  c_parser_statement_after_labels (parser);
+	}
+    }
+  if (last_label)
+    error ("label at end of compound statement");
+  if (end_loc)
+    *end_loc = c_lexer_peek_token (parser->lexer)->location;
+  c_lexer_consume_token (parser->lexer);
+}
+
+/* Parse a label (C90 6.6.1, C99 6.8.1).
+
+   label:
+     identifier : attributes[opt]
+     case constant-expression :
+     default :
+
+   GNU extension:
+     case constant-expression ... constant-expression :
+
+   The use of attributes on labels is a GNU extension.  The syntax in
+   GNU C accepts any expressions without commas, non-constant
+   expressions being rejected later.  */
+
+static void
+c_parser_label (c_parser *parser)
+{
+  location_t loc1 = c_lexer_peek_token (parser->lexer)->location;
+  tree label = NULL_TREE;
+  if (c_lexer_next_token_is_keyword (parser->lexer, RID_CASE))
+    {
+      tree exp1, exp2;
+      c_lexer_consume_token (parser->lexer);
+      exp1 = c_parser_expr_no_commas (parser).value;
+      if (c_lexer_next_token_is (parser->lexer, CPP_COLON))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  label = do_case (exp1, NULL_TREE);
+	}
+      else if (c_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  exp2 = c_parser_expr_no_commas (parser).value;
+	  if (c_parser_require (parser, CPP_COLON, "expected ':'"))
+	    label = do_case (exp1, exp2);
+	}
+      else
+	c_parser_error (parser, "expected ':' or '...'");
+    }
+  else if (c_lexer_next_token_is_keyword (parser->lexer, RID_DEFAULT))
+    {
+      c_lexer_consume_token (parser->lexer);
+      if (c_parser_require (parser, CPP_COLON, "expected ':'"))
+	label = do_case (NULL_TREE, NULL_TREE);
+    }
+  else
+    {
+      tree name = c_lexer_peek_token (parser->lexer)->value;
+      tree tlab;
+      location_t loc2;
+      tree attrs;
+      gcc_assert (c_lexer_next_token_is (parser->lexer, CPP_NAME));
+      c_lexer_consume_token (parser->lexer);
+      gcc_assert (c_lexer_next_token_is (parser->lexer, CPP_COLON));
+      loc2 = c_lexer_peek_token (parser->lexer)->location;
+      c_lexer_consume_token (parser->lexer);
+      attrs = c_parser_attributes (parser);
+      tlab = define_label (loc2, name);
+      if (tlab)
+	{
+	  decl_attributes (&tlab, attrs, 0);
+	  label = add_stmt (build_stmt (LABEL_EXPR, tlab));
+	}
+    }
+  if (label)
+    SET_EXPR_LOCATION (label, loc1);
+}
+
+/* Parse a statement (C90 6.6, C99 6.8).
+
+   statement:
+     labeled-statement
+     compound-statement
+     expression-statement
+     selection-statement
+     iteration-statement
+     jump-statement
+
+   GNU extension:
+     asm-statement
+
+   GNU extension to jump-statement:
+     goto * expression ;
+
+   TODO: Objective-C.  */
+
+static void
+c_parser_statement (c_parser *parser)
+{
+  while (c_lexer_next_token_is_keyword (parser->lexer, RID_CASE)
+	 || c_lexer_next_token_is_keyword (parser->lexer, RID_DEFAULT)
+	 || (c_lexer_next_token_is (parser->lexer, CPP_NAME)
+	     && c_lexer_peek_2nd_token (parser->lexer)->type == CPP_COLON))
+    c_parser_label (parser);
+  c_parser_statement_after_labels (parser);
+}
+
+/* Parse a statement, other than a labeled statement.  */
+
+static void
+c_parser_statement_after_labels (c_parser *parser)
+{
+  location_t loc = c_lexer_peek_token (parser->lexer)->location;
+  tree stmt = NULL_TREE;
+  switch (c_lexer_peek_token (parser->lexer)->type)
+    {
+    case CPP_OPEN_BRACE:
+      add_stmt (c_parser_compound_statement (parser, NULL));
+      break;
+    case CPP_KEYWORD:
+      switch (c_lexer_peek_token (parser->lexer)->keyword)
+	{
+	case RID_IF:
+	  c_parser_if_statement (parser);
+	  break;
+	case RID_SWITCH:
+	  c_parser_switch_statement (parser);
+	  break;
+	case RID_WHILE:
+	  c_parser_while_statement (parser);
+	  break;
+	case RID_DO:
+	  c_parser_do_statement (parser);
+	  break;
+	case RID_FOR:
+	  c_parser_for_statement (parser);
+	  break;
+	case RID_GOTO:
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+	    {
+	      stmt = c_finish_goto_label (c_lexer_peek_token (parser->lexer)->value);
+	      c_lexer_consume_token (parser->lexer);
+	    }
+	  else if (c_lexer_next_token_is (parser->lexer, CPP_MULT))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      stmt = c_finish_goto_ptr (c_parser_expression (parser).value);
+	    }
+	  else
+	    c_parser_error (parser, "expected identifier or '*'");
+	  goto expect_semicolon;
+	case RID_CONTINUE:
+	  c_lexer_consume_token (parser->lexer);
+	  stmt = c_finish_bc_stmt (&c_cont_label, false);
+	  goto expect_semicolon;
+	case RID_BREAK:
+	  c_lexer_consume_token (parser->lexer);
+	  stmt = c_finish_bc_stmt (&c_break_label, true);
+	  goto expect_semicolon;
+	case RID_RETURN:
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	    {
+	      stmt = c_finish_return (NULL_TREE);
+	      c_lexer_consume_token (parser->lexer);
+	    }
+	  else
+	    {
+	      stmt = c_finish_return (c_parser_expression (parser).value);
+	      goto expect_semicolon;
+	    }
+	  break;
+	case RID_ASM:
+	  stmt = c_parser_asm_statement (parser);
+	  break;
+	default:
+	  goto expr_stmt;
+	}
+      break;
+    case CPP_SEMICOLON:
+      c_lexer_consume_token (parser->lexer);
+      break;
+    default:
+    expr_stmt:
+      stmt = c_finish_expr_stmt (c_parser_expression (parser).value);
+    expect_semicolon:
+      c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected ';'");
+      break;
+    }
+  /* Two cases cannot and do not have line numbers associated: If stmt
+     is degenerate, such as "2;", then stmt is an INTEGER_CST, which
+     cannot hold line numbers.  But that's ok because the statement
+     will either be changed to a MODIFY_EXPR during gimplification of
+     the statement expr, or discarded.  If stmt was compound, but
+     without new variables, we will have skipped the creation of a
+     BIND and will have a bare STATEMENT_LIST.  But that's ok because
+     (recursively) all of the component statments should already have
+     line numbers assigned.  */
+  if (stmt && EXPR_P (stmt))
+    SET_EXPR_LOCATION (stmt, loc);
+}
+
+/* Parse a parenthesized condition from an if, do or while statement.
+
+   condition:
+     ( expression )
+*/
+static tree
+c_parser_paren_condition (c_parser *parser)
+{
+  location_t loc;
+  tree cond;
+  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    return error_mark_node;
+  loc = c_lexer_peek_token (parser->lexer)->location;
+  cond = lang_hooks.truthvalue_conversion (c_parser_expression (parser).value);
+  if (EXPR_P (cond))
+    SET_EXPR_LOCATION (cond, loc);
+  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+  return cond;
+}
+
+/* Parse a statement which is a block in C99.  */
+
+static tree
+c_parser_c99_block_statement (c_parser *parser)
+{
+  tree block = c_begin_compound_stmt (flag_isoc99);
+  c_parser_statement (parser);
+  return c_end_compound_stmt (block, flag_isoc99);
+}
+
+/* Parse the body of an if statement or the else half thereof.  This
+   is just parsing a statement but (a) it is a block in C99, (b) we
+   track whether the body is an if statement for the sake of
+   -Wparentheses warnings, (c) we handle an empty body specially for
+   the sake of -Wextra warnings.  */
+
+static tree
+c_parser_if_body (c_parser *parser, bool *if_p)
+{
+  tree block = c_begin_compound_stmt (flag_isoc99);
+  while (c_lexer_next_token_is_keyword (parser->lexer, RID_CASE)
+	 || c_lexer_next_token_is_keyword (parser->lexer, RID_DEFAULT)
+	 || (c_lexer_next_token_is (parser->lexer, CPP_NAME)
+	     && c_lexer_peek_2nd_token (parser->lexer)->type == CPP_COLON))
+    c_parser_label (parser);
+  *if_p = c_lexer_next_token_is_keyword (parser->lexer, RID_IF);
+  if (extra_warnings && c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+    add_stmt (build (NOP_EXPR, NULL_TREE, NULL_TREE));
+  c_parser_statement_after_labels (parser);
+  return c_end_compound_stmt (block, flag_isoc99);
+}
+
+/* Parse an if statement (C90 6.6.4, C99 6.8.4).
+
+   if-statement:
+     if ( expression ) statement
+     if ( expression ) statement else statement
+*/
+
+static void
+c_parser_if_statement (c_parser *parser)
+{
+  tree block;
+  location_t loc;
+  tree cond;
+  bool first_if = false, second_if = false;
+  tree first_body, second_body;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_IF));
+  c_lexer_consume_token (parser->lexer);
+  block = c_begin_compound_stmt (flag_isoc99);
+  loc = c_lexer_peek_token (parser->lexer)->location;
+  cond = c_parser_paren_condition (parser);
+  first_body = c_parser_if_body (parser, &first_if);
+  if (c_lexer_next_token_is_keyword (parser->lexer, RID_ELSE))
+    {
+      c_lexer_consume_token (parser->lexer);
+      second_body = c_parser_if_body (parser, &second_if);
+    }
+  else
+    second_body = NULL_TREE;
+  c_finish_if_stmt (loc, cond, first_body, second_body, first_if);
+  add_stmt (c_end_compound_stmt (block, flag_isoc99));
+}
+
+/* Parse a switch statement (C90 6.6.4, C99 6.8.4).
+
+   switch-statement:
+     switch (expression) statement
+*/
+
+static void
+c_parser_switch_statement (c_parser *parser)
+{
+  tree block, expr, body, save_break;
+  location_t loc, save_loc;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_SWITCH));
+  c_lexer_consume_token (parser->lexer);
+  block = c_begin_compound_stmt (flag_isoc99);
+  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    {
+      expr = c_parser_expression (parser).value;
+      /* ??? Following the old parser, the location at the time of
+	 c_start_case is the location of the close parenthesis.  But
+	 the location of the "switch" keyword might make more sense,
+	 and it would be a better interface to pass a location_t to
+	 c_start_case rather than saving and restoring
+	 input_location.  */
+      loc = c_lexer_peek_token (parser->lexer)->location;
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+    }
+  else
+    {
+      loc = input_location;
+      expr = error_mark_node;
+    }
+  save_loc = input_location;
+  input_location = loc;
+  c_start_case (expr);
+  input_location = save_loc;
+  save_break = c_break_label;
+  c_break_label = NULL_TREE;
+  body = c_parser_c99_block_statement (parser);
+  c_finish_case (body);
+  if (c_break_label)
+    add_stmt (build (LABEL_EXPR, void_type_node, c_break_label));
+  c_break_label = save_break;
+  add_stmt (c_end_compound_stmt (block, flag_isoc99));
+}
+
+/* Parse a while statement (C90 6.6.5, C99 6.8.5).
+
+   while-statement:
+      while (expression) statement
+*/
+
+static void
+c_parser_while_statement (c_parser *parser)
+{
+  tree block, cond, body, save_break, save_cont;
+  location_t loc;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_WHILE));
+  c_lexer_consume_token (parser->lexer);
+  block = c_begin_compound_stmt (flag_isoc99);
+  loc = c_lexer_peek_token (parser->lexer)->location;
+  cond = c_parser_paren_condition (parser);
+  save_break = c_break_label;
+  c_break_label = NULL_TREE;
+  save_cont = c_cont_label;
+  c_cont_label = NULL_TREE;
+  body = c_parser_c99_block_statement (parser);
+  c_finish_loop (loc, cond, NULL, body, c_break_label, c_cont_label, true);
+  add_stmt (c_end_compound_stmt (block, flag_isoc99));
+  c_break_label = save_break;
+  c_cont_label = save_cont;
+}
+
+/* Parse a do statement (C90 6.6.5, C99 6.8.5).
+
+   do-statement:
+     do statement while ( expression ) ;
+*/
+
+static void
+c_parser_do_statement (c_parser *parser)
+{
+  tree block, cond, body, save_break, save_cont, new_break, new_cont;
+  location_t loc;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_DO));
+  c_lexer_consume_token (parser->lexer);
+  block = c_begin_compound_stmt (flag_isoc99);
+  loc = c_lexer_peek_token (parser->lexer)->location;
+  save_break = c_break_label;
+  c_break_label = NULL_TREE;
+  save_cont = c_cont_label;
+  c_cont_label = NULL_TREE;
+  body = c_parser_c99_block_statement (parser);
+  c_parser_require_keyword (parser, RID_WHILE, "expected 'while'");
+  new_break = c_break_label;
+  c_break_label = save_break;
+  new_cont = c_cont_label;
+  c_cont_label = save_cont;
+  cond = c_parser_paren_condition (parser);
+  if (!c_parser_require (parser, CPP_SEMICOLON, "expected ';'"))
+    c_parser_skip_to_end_of_block_or_statement (parser);
+  c_finish_loop (loc, cond, NULL, body, new_break, new_cont, false);
+  add_stmt (c_end_compound_stmt (block, flag_isoc99));
+}
+
+/* Parse a for statement (C90 6.6.5, C99 6.8.5).
+
+   for-statement:
+     for ( expression[opt] ; expression[opt] ; expression[opt] ) statement
+     for ( declaration expression[opt] ; expression[opt] ) statement
+
+   The form with a declaration is new in C99.
+
+   ??? In accordance with the old parser, the declaration may be a
+   nested function, which is then rejected in check_for_loop_decls,
+   but does it make any sense for this to be included in the
+   grammar?  */
+
+static void
+c_parser_for_statement (c_parser *parser)
+{
+  tree block, cond, incr, save_break, save_cont, body;
+  location_t loc;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_FOR));
+  c_lexer_consume_token (parser->lexer);
+  block = c_begin_compound_stmt (flag_isoc99);
+  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    {
+      /* Parse the initialization declaration or expression.  */
+      if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  c_finish_expr_stmt (NULL_TREE);
+	}
+      else if (c_lexer_next_token_starts_declspecs (parser->lexer))
+	{
+	  c_parser_declaration_or_fndef (parser, true, true, true, true);
+	  check_for_loop_decls ();
+	}
+      else if (c_lexer_next_token_is_keyword (parser->lexer, RID_EXTENSION))
+	{
+	  /* __extension__ can start a declaration, but is also an
+	     unary operator that can start an expression.  Consume all
+	     but the last of a possible series of __extension__ to
+	     determine which.  */
+	  while (c_lexer_peek_2nd_token (parser->lexer)->type == CPP_KEYWORD
+		 && (c_lexer_peek_2nd_token (parser->lexer)->keyword
+		     == RID_EXTENSION))
+	    c_lexer_consume_token (parser->lexer);
+	  if (c_token_starts_declspecs (c_lexer_peek_2nd_token (parser->lexer)))
+	    {
+	      int ext;
+	      SAVE_EXT_FLAGS (ext);
+	      c_lexer_consume_token (parser->lexer);
+	      c_parser_declaration_or_fndef (parser, true, true, true, true);
+	      RESTORE_EXT_FLAGS (ext);
+	      check_for_loop_decls ();
+	    }
+	  else
+	    goto init_expr;
+	}
+      else
+	{
+	init_expr:
+	  c_finish_expr_stmt (c_parser_expression (parser).value);
+	  c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected ';'");
+	}
+      /* Parse the loop condition.  */
+      loc = input_location;
+      if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  cond = NULL_TREE;
+	}
+      else
+	{
+	  tree ocond = c_parser_expression (parser).value;
+	  cond = lang_hooks.truthvalue_conversion (ocond);
+	  if (EXPR_P (cond))
+	    SET_EXPR_LOCATION (cond, loc);
+	  c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected ';'");
+	}
+      /* Parse the increment expression.  */
+      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	incr = c_process_expr_stmt (NULL_TREE);
+      else
+	incr = c_process_expr_stmt (c_parser_expression (parser).value);
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+    }
+  else
+    {
+      cond = error_mark_node;
+      incr = error_mark_node;
+    }
+  save_break = c_break_label;
+  c_break_label = NULL_TREE;
+  save_cont = c_cont_label;
+  c_cont_label = NULL_TREE;
+  body = c_parser_c99_block_statement (parser);
+  c_finish_loop (loc, cond, incr, body, c_break_label, c_cont_label, true);
+  add_stmt (c_end_compound_stmt (block, flag_isoc99));
+  c_break_label = save_break;
+  c_cont_label = save_cont;
+}
+
+/* Parse an asm statement, a GNU extension.
+
+   asm-statement:
+     asm type-qualifier[opt] ( asm-argument ) ;
+
+   asm-argument:
+     string-literal
+     string-literal : asm-operands[opt]
+     string-literal : asm-operands[opt] : asm-operands[opt]
+     string-literal : asm-operands[opt] : asm-operands[opt] : asm-clobbers
+
+   asm-clobbers:
+     string-literal
+     asm-clobbers , string-literal
+
+   Qualifiers other than volatile are accepted in the syntax but
+   warned for.
+
+   ??? The old parser accepted wide strings here, but do we want
+   to?  */
+
+static tree
+c_parser_asm_statement (c_parser *parser)
+{
+  tree quals, str, outputs, inputs, clobbers, ret;
+  bool simple;
+  location_t loc_end, save_loc;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_ASM));
+  c_lexer_consume_token (parser->lexer);
+  if (c_lexer_next_token_is_keyword (parser->lexer, RID_VOLATILE))
+    {
+      quals = c_lexer_peek_token (parser->lexer)->value;
+      c_lexer_consume_token (parser->lexer);
+    }
+  else if (c_lexer_next_token_is_keyword (parser->lexer, RID_CONST)
+	   || c_lexer_next_token_is_keyword (parser->lexer, RID_RESTRICT))
+    {
+      warning ("%E qualifier ignored on asm",
+	       c_lexer_peek_token (parser->lexer)->value);
+      quals = NULL_TREE;
+      c_lexer_consume_token (parser->lexer);
+    }
+  else
+    quals = NULL_TREE;
+  c_lex_string_translate = 0;
+  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    {
+      c_lex_string_translate = 1;
+      return NULL_TREE;
+    }
+  if (c_lexer_next_token_is (parser->lexer, CPP_STRING)
+      || c_lexer_next_token_is (parser->lexer, CPP_WSTRING))
+    {
+      str = c_lexer_peek_token (parser->lexer)->value;
+      c_lexer_consume_token (parser->lexer);
+    }
+  else
+    {
+      c_lex_string_translate = 1;
+      c_parser_error (parser, "expected string literal");
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+    {
+      simple = true;
+      outputs = NULL_TREE;
+      inputs = NULL_TREE;
+      clobbers = NULL_TREE;
+      goto done_asm;
+    }
+  if (!c_parser_require (parser, CPP_COLON, "expected ':' or ')'"))
+    {
+      c_lex_string_translate = 1;
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  simple = false;
+  /* Parse outputs.  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_COLON)
+      || c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+    outputs = NULL_TREE;
+  else
+    outputs = c_parser_asm_operands (parser);
+  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+    {
+      inputs = NULL_TREE;
+      clobbers = NULL_TREE;
+      goto done_asm;
+    }
+  if (!c_parser_require (parser, CPP_COLON, "expected ':' or ')'"))
+    {
+      c_lex_string_translate = 1;
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  /* Parse inputs.  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_COLON)
+      || c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+    inputs = NULL_TREE;
+  else
+    inputs = c_parser_asm_operands (parser);
+  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+    {
+      clobbers = NULL_TREE;
+      goto done_asm;
+    }
+  if (!c_parser_require (parser, CPP_COLON, "expected ':' or ')'"))
+    {
+      c_lex_string_translate = 1;
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  /* Parse clobbers.  */
+  clobbers = c_parser_asm_clobbers (parser);
+ done_asm:
+  c_lex_string_translate = 1;
+  if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected ')'"))
+    {
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  loc_end = c_lexer_peek_token (parser->lexer)->location;
+  if (!c_parser_require (parser, CPP_SEMICOLON, "expected ';'"))
+    c_parser_skip_to_end_of_block_or_statement (parser);
+  save_loc = input_location;
+  input_location = loc_end;
+  ret = build_asm_stmt (quals, build_asm_expr (str, outputs, inputs,
+					       clobbers, simple));
+  input_location = save_loc;
+  return ret;
+}
+
+/* Parse asm operands, a GNU extension.
+
+   asm-operands:
+     asm-operand
+     asm-operands , asm-operand
+
+   asm-operand:
+     string-literal ( expression )
+     [ identifier ] string-literal ( expression )
+
+   ??? The old parser accepted wide strings here, but do we want
+   to?  */
+
+static tree
+c_parser_asm_operands (c_parser *parser)
+{
+  tree list = NULL_TREE;
+  while (true)
+    {
+      tree name, str, expr;
+      if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+	    {
+	      tree id = c_lexer_peek_token (parser->lexer)->value;
+	      c_lexer_consume_token (parser->lexer);
+	      name = build_string (IDENTIFIER_LENGTH (id),
+				   IDENTIFIER_POINTER (id));
+	    }
+	  else
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, NULL);
+	      return NULL_TREE;
+	    }
+	  c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, "expected ']'");
+	}
+      else
+	name = NULL_TREE;
+      if (c_lexer_next_token_is (parser->lexer, CPP_STRING)
+	  || c_lexer_next_token_is (parser->lexer, CPP_WSTRING))
+	{
+	  str = c_lexer_peek_token (parser->lexer)->value;
+	  c_lexer_consume_token (parser->lexer);
+	}
+      else
+	{
+	  c_parser_error (parser, "expected string literal");
+	  return NULL_TREE;
+	}
+      c_lex_string_translate = 1;
+      if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	{
+	  c_lex_string_translate = 0;
+	  return NULL_TREE;
+	}
+      expr = c_parser_expression (parser).value;
+      c_lex_string_translate = 0;
+      if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected ')'"))
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  return NULL_TREE;
+	}
+      list = chainon (list, build_tree_list (build_tree_list (name, str),
+					     expr));
+      if (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	c_lexer_consume_token (parser->lexer);
+      else
+	break;
+    }
+  return list;
+}
+
+/* Parse asm clobbers, a GNU extension.
+
+   asm-clobbers:
+     string-literal
+     asm-clobbers , string-literal
+
+   ??? The old parser accepted wide strings here, but do we want
+   to?  */
+
+static tree
+c_parser_asm_clobbers (c_parser *parser)
+{
+  tree list = NULL_TREE;
+  while (true)
+    {
+      if (c_lexer_next_token_is (parser->lexer, CPP_STRING)
+	  || c_lexer_next_token_is (parser->lexer, CPP_WSTRING))
+	{
+	  tree str = c_lexer_peek_token (parser->lexer)->value;
+	  c_lexer_consume_token (parser->lexer);
+	  list = tree_cons (NULL_TREE, str, list);
+	}
+      else
+	{
+	  c_parser_error (parser, "expected string literal");
+	  return NULL_TREE;
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	c_lexer_consume_token (parser->lexer);
+      else
+	break;
+    }
+  return list;
+}
+
+/* Parse an expression other than a compound expression; that is, an
+   assignment expression (C90 6.3.16, C99 6.5.16).
+
+   assignment-expression:
+     conditional-expression
+     unary-expression assignment-operator assignment-expression
+
+   assignment-operator: one of
+     = *= /= %= += -= <<= >>= &= ^= |=
+
+   In GNU C we accept any conditional expression on the LHS and
+   diagnose the invalid lvalue rather than producing a syntax
+   error.  */
+
+static struct c_expr
+c_parser_expr_no_commas (c_parser *parser)
+{
+  struct c_expr lhs, rhs, ret;
+  enum tree_code code;
+  lhs = c_parser_conditional_expression (parser);
+  switch (c_lexer_peek_token (parser->lexer)->type)
+    {
+    case CPP_EQ:
+      code = NOP_EXPR;
+      break;
+    case CPP_MULT_EQ:
+      code = MULT_EXPR;
+      break;
+    case CPP_DIV_EQ:
+      code = TRUNC_DIV_EXPR;
+      break;
+    case CPP_MOD_EQ:
+      code = TRUNC_MOD_EXPR;
+      break;
+    case CPP_PLUS_EQ:
+      code = PLUS_EXPR;
+      break;
+    case CPP_MINUS_EQ:
+      code = MINUS_EXPR;
+      break;
+    case CPP_LSHIFT_EQ:
+      code = LSHIFT_EXPR;
+      break;
+    case CPP_RSHIFT_EQ:
+      code = RSHIFT_EXPR;
+      break;
+    case CPP_AND_EQ:
+      code = BIT_AND_EXPR;
+      break;
+    case CPP_XOR_EQ:
+      code = BIT_XOR_EXPR;
+      break;
+    case CPP_OR_EQ:
+      code = BIT_IOR_EXPR;
+      break;
+    default:
+      return lhs;
+    }
+  c_lexer_consume_token (parser->lexer);
+  rhs = c_parser_expr_no_commas (parser);
+  ret.value = build_modify_expr (lhs.value, code, rhs.value);
+  if (code == NOP_EXPR)
+    ret.original_code = MODIFY_EXPR;
+  else
+    {
+      TREE_NO_WARNING (ret.value) = 1;
+      ret.original_code = ERROR_MARK;
+    }
+  return ret;
+}
+
+/* Parse a conditional expression (C90 6.3.15, C99 6.5.15).
+
+   conditional-expression:
+     logical-OR-expression
+     logical-OR-expression ? expression : conditional-expression
+
+   GNU extension:
+     logical-OR-expression ? : conditional-expression
+*/
+
+static struct c_expr
+c_parser_conditional_expression (c_parser *parser)
+{
+  struct c_expr cond, exp1, exp2, ret;
+  cond = c_parser_binary_expression (parser);
+  if (c_lexer_next_token_is_not (parser->lexer, CPP_QUERY))
+    return cond;
+  c_lexer_consume_token (parser->lexer);
+  if (c_lexer_next_token_is (parser->lexer, CPP_COLON))
+    {
+      if (pedantic)
+	pedwarn ("ISO C forbids omitting the middle term of a ?: expression");
+      /* Make sure first operand is calculated only once.  */
+      exp1.value = save_expr (default_conversion (cond.value));
+      cond.value = lang_hooks.truthvalue_conversion (exp1.value);
+      skip_evaluation += cond.value == truthvalue_true_node;
+    }
+  else
+    {
+      cond.value
+	= lang_hooks.truthvalue_conversion (default_conversion (cond.value));
+      skip_evaluation += cond.value == truthvalue_false_node;
+      exp1 = c_parser_expression (parser);
+      skip_evaluation += ((cond.value == truthvalue_true_node)
+			  - (cond.value == truthvalue_false_node));
+    }
+  if (!c_parser_require (parser, CPP_COLON, "expected ':'"))
+    {
+      skip_evaluation -= cond.value == truthvalue_true_node;
+      ret.value = error_mark_node;
+      ret.original_code = ERROR_MARK;
+      return ret;
+    }
+  exp2 = c_parser_conditional_expression (parser);
+  skip_evaluation -= cond.value == truthvalue_true_node;
+  ret.value = build_conditional_expr (cond.value, exp1.value, exp2.value);
+  ret.original_code = ERROR_MARK;
+  return ret;
+}
+
+/* Parse a binary expression; that is, a logical-OR-expression (C90
+   6.3.5-6.3.14, C99 6.5.5-6.5.14).
+
+   multiplicative-expression:
+     cast-expression
+     multiplicative-expression * cast-expression
+     multiplicative-expression / cast-expression
+     multiplicative-expression % cast-expression
+
+   additive-expression:
+     multiplicative-expression
+     additive-expression + multiplicative-expression
+     additive-expression - multiplicative-expression
+
+   shift-expression:
+     additive-expression
+     shift-expression << additive-expression
+     shift-expression >> additive-expression
+
+   relational-expression:
+     shift-expression
+     relational-expression < shift-expression
+     relational-expression > shift-expression
+     relational-expression <= shift-expression
+     relational-expression >= shift-expression
+
+   equality-expression:
+     relational-expression
+     equality-expression == relational-expression
+     equality-expression != relational-expression
+
+   AND-expression:
+     equality-expression
+     AND-expression & equality-expression
+
+   exclusive-OR-expression:
+     AND-expression
+     exclusive-OR-expression ^ AND-expression
+
+   inclusive-OR-expression:
+     exclusive-OR-expression
+     inclusive-OR-expression | exclusive-OR-expression
+
+   logical-AND-expression:
+     inclusive-OR-expression
+     logical-AND-expression && inclusive-OR-expression
+
+   logical-OR-expression:
+     logical-AND-expression
+     logical-OR-expression || logical-AND-expression
+*/
+
+static struct c_expr
+c_parser_binary_expression (c_parser *parser)
+{
+  /* A binary expression is parsed using operator-precedence parsing,
+     with the operands being cast expressions.  All the binary
+     operators are left-associative.  Thus a binary expression is of
+     form:
+
+     E0 op1 E1 op2 E2 ...
+
+     which we represent on a stack.  On the stack, the precedence
+     levels are strictly increasing.  When a new operator is
+     encountered of higher precedence than that at the top of the
+     stack, it is pushed; its LHS is the top expression, and its RHS
+     is everything parsed until it is popped.  When a new operator is
+     encountered with precedence less than or equal to that at the top
+     of the stack, triples E[i-1] op[i] E[i] are popped and replaced
+     by the result of the operation until the operator at the top of
+     the stack has lower precedence than the new operator or there is
+     only one element on the stack; then the top expression is the LHS
+     of the new operator.  In the case of logical AND and OR
+     expressions, we also need to adjust skip_evaluation as
+     appropriate when the operators are pushed and popped.  */
+
+  /* The precedence levels, where 0 is a dummy lowest level used for
+     the bottom of the stack.  */
+  enum prec {
+    PREC_NONE,
+    PREC_LOGOR,
+    PREC_LOGAND,
+    PREC_BITOR,
+    PREC_BITXOR,
+    PREC_BITAND,
+    PREC_EQ,
+    PREC_REL,
+    PREC_SHIFT,
+    PREC_ADD,
+    PREC_MULT,
+    NUM_PRECS
+  };
+  struct {
+    /* The expression at this stack level.  */
+    struct c_expr expr;
+    /* The precedence of the operator on its left, PREC_NONE at the
+       bottom of the stack.  */
+    enum prec prec;
+    /* The operation on its left.  */
+    enum tree_code op;
+  } stack[NUM_PRECS];
+  int sp;
+#define POP								      \
+  do {									      \
+    switch (stack[sp].op)						      \
+      {									      \
+      case TRUTH_ANDIF_EXPR:						      \
+	skip_evaluation -= stack[sp - 1].expr.value == truthvalue_false_node; \
+	break;								      \
+      case TRUTH_ORIF_EXPR:						      \
+	skip_evaluation -= stack[sp - 1].expr.value == truthvalue_true_node;  \
+	break;								      \
+      default:								      \
+	break;								      \
+      }									      \
+    stack[sp - 1].expr = parser_build_binary_op (stack[sp].op,		      \
+						 stack[sp - 1].expr,	      \
+						 stack[sp].expr);	      \
+    sp--;								      \
+  } while (0)
+  stack[0].expr = c_parser_cast_expression (parser);
+  stack[0].prec = PREC_NONE;
+  sp = 0;
+  while (true)
+    {
+      enum prec oprec;
+      enum tree_code ocode;
+      if (parser->error)
+	goto out;
+      switch (c_lexer_peek_token (parser->lexer)->type)
+	{
+	case CPP_MULT:
+	  oprec = PREC_MULT;
+	  ocode = MULT_EXPR;
+	  break;
+	case CPP_DIV:
+	  oprec = PREC_MULT;
+	  ocode = TRUNC_DIV_EXPR;
+	  break;
+	case CPP_MOD:
+	  oprec = PREC_MULT;
+	  ocode = TRUNC_MOD_EXPR;
+	  break;
+	case CPP_PLUS:
+	  oprec = PREC_ADD;
+	  ocode = PLUS_EXPR;
+	  break;
+	case CPP_MINUS:
+	  oprec = PREC_ADD;
+	  ocode = MINUS_EXPR;
+	  break;
+	case CPP_LSHIFT:
+	  oprec = PREC_SHIFT;
+	  ocode = LSHIFT_EXPR;
+	  break;
+	case CPP_RSHIFT:
+	  oprec = PREC_SHIFT;
+	  ocode = RSHIFT_EXPR;
+	  break;
+	case CPP_LESS:
+	  oprec = PREC_REL;
+	  ocode = LT_EXPR;
+	  break;
+	case CPP_GREATER:
+	  oprec = PREC_REL;
+	  ocode = GT_EXPR;
+	  break;
+	case CPP_LESS_EQ:
+	  oprec = PREC_REL;
+	  ocode = LE_EXPR;
+	  break;
+	case CPP_GREATER_EQ:
+	  oprec = PREC_REL;
+	  ocode = GE_EXPR;
+	  break;
+	case CPP_EQ_EQ:
+	  oprec = PREC_EQ;
+	  ocode = EQ_EXPR;
+	  break;
+	case CPP_NOT_EQ:
+	  oprec = PREC_EQ;
+	  ocode = NE_EXPR;
+	  break;
+	case CPP_AND:
+	  oprec = PREC_BITAND;
+	  ocode = BIT_AND_EXPR;
+	  break;
+	case CPP_XOR:
+	  oprec = PREC_BITXOR;
+	  ocode = BIT_XOR_EXPR;
+	  break;
+	case CPP_OR:
+	  oprec = PREC_BITOR;
+	  ocode = BIT_IOR_EXPR;
+	  break;
+	case CPP_AND_AND:
+	  oprec = PREC_LOGAND;
+	  ocode = TRUTH_ANDIF_EXPR;
+	  break;
+	case CPP_OR_OR:
+	  oprec = PREC_LOGOR;
+	  ocode = TRUTH_ORIF_EXPR;
+	  break;
+	default:
+	  /* Not a binary operator, so end of the binary
+	     expression.  */
+	  goto out;
+	}
+      c_lexer_consume_token (parser->lexer);
+      while (oprec <= stack[sp].prec)
+	POP;
+      switch (ocode)
+	{
+	case TRUTH_ANDIF_EXPR:
+	  stack[sp].expr.value = lang_hooks.truthvalue_conversion
+	    (default_conversion (stack[sp].expr.value));
+	  skip_evaluation += stack[sp].expr.value == truthvalue_false_node;
+	  break;
+	case TRUTH_ORIF_EXPR:
+	  stack[sp].expr.value = lang_hooks.truthvalue_conversion
+	    (default_conversion (stack[sp].expr.value));
+	  skip_evaluation += stack[sp].expr.value == truthvalue_true_node;
+	  break;
+	default:
+	  break;
+	}
+      sp++;
+      stack[sp].expr = c_parser_cast_expression (parser);
+      stack[sp].prec = oprec;
+      stack[sp].op = ocode;
+    }
+ out:
+  while (sp > 0)
+    POP;
+  return stack[0].expr;
+#undef POP
+}
+
+/* Parse a cast expression (C90 6.3.4, C99 6.5.4).
+
+   cast-expression:
+     unary-expression
+     ( type-name ) unary-expression
+*/
+
+static struct c_expr
+c_parser_cast_expression (c_parser *parser)
+{
+  /* If the expression begins with a parenthesized type name, it may
+     be either a cast or a compound literal; we need to see whether
+     the next character is '{' to tell the difference.  If not, it is
+     an unary expression.  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)
+      && c_token_starts_typename (c_lexer_peek_2nd_token (parser->lexer)))
+    {
+      struct c_type_name *type_name;
+      struct c_expr ret;
+      tree expr;
+      c_lexer_consume_token (parser->lexer);
+      type_name = c_parser_type_name (parser);
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+      if (type_name == NULL)
+	{
+	  ret.value = error_mark_node;
+	  ret.original_code = ERROR_MARK;
+	  return ret;
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+	return c_parser_postfix_expression_after_paren_type (parser,
+							     type_name);
+      expr = c_parser_cast_expression (parser).value;
+      ret.value = c_cast_expr (type_name, expr);
+      ret.original_code = ERROR_MARK;
+      return ret;
+    }
+  else
+    return c_parser_unary_expression (parser);
+}
+
+/* Parse an unary expression (C90 6.3.3, C99 6.5.3).
+
+   unary-expression:
+     postfix-expression
+     ++ unary-expression
+     -- unary-expression
+     unary-operator cast-expression
+     sizeof unary-expression
+     sizeof ( type-name )
+
+   unary-operator: one of
+     & * + - ~ !
+
+   GNU extensions:
+
+   unary-expression:
+     __alignof__ unary-expression
+     __alignof__ ( type-name )
+     && identifier
+
+   unary-operator: one of
+     __extension__ __real__ __imag__
+
+   In addition, the GNU syntax treats ++ and -- as unary operators, so
+   they may be applied to cast expressions with errors for non-lvalues
+   given later.  */
+
+static struct c_expr
+c_parser_unary_expression (c_parser *parser)
+{
+  int ext;
+  struct c_expr ret;
+  ret.original_code = ERROR_MARK;
+  switch (c_lexer_peek_token (parser->lexer)->type)
+    {
+    case CPP_PLUS_PLUS:
+      c_lexer_consume_token (parser->lexer);
+      ret.value = build_unary_op (PREINCREMENT_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_MINUS_MINUS:
+      c_lexer_consume_token (parser->lexer);
+      ret.value = build_unary_op (PREDECREMENT_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_AND:
+      c_lexer_consume_token (parser->lexer);
+      ret.value = build_unary_op (ADDR_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_MULT:
+      c_lexer_consume_token (parser->lexer);
+      ret.value = build_indirect_ref (c_parser_cast_expression (parser).value,
+				      "unary *");
+      return ret;
+    case CPP_PLUS:
+      c_lexer_consume_token (parser->lexer);
+      if (!c_dialect_objc () && warn_traditional && !in_system_header)
+	warning ("traditional C rejects the unary plus operator");
+      ret.value = build_unary_op (CONVERT_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_MINUS:
+      c_lexer_consume_token (parser->lexer);
+      ret.value = build_unary_op (NEGATE_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_COMPL:
+      c_lexer_consume_token (parser->lexer);
+      ret.value = build_unary_op (BIT_NOT_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_NOT:
+      c_lexer_consume_token (parser->lexer);
+      ret.value = build_unary_op (TRUTH_NOT_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_AND_AND:
+      c_lexer_consume_token (parser->lexer);
+      if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+	{
+	  ret.value = finish_label_address_expr
+	    (c_lexer_peek_token (parser->lexer)->value);
+	  c_lexer_consume_token (parser->lexer);
+	  return ret;
+	}
+      else
+	{
+	  c_parser_error (parser, "expected identifier");
+	  ret.value = error_mark_node;
+	  return ret;
+	}
+    case CPP_KEYWORD:
+      switch (c_lexer_peek_token (parser->lexer)->keyword)
+	{
+	case RID_SIZEOF:
+	  return c_parser_sizeof_expression (parser);
+	case RID_ALIGNOF:
+	  return c_parser_alignof_expression (parser);
+	case RID_EXTENSION:
+	  c_lexer_consume_token (parser->lexer);
+	  SAVE_EXT_FLAGS (ext);
+	  ret = c_parser_cast_expression (parser);
+	  RESTORE_EXT_FLAGS (ext);
+	  return ret;
+	case RID_REALPART:
+	  c_lexer_consume_token (parser->lexer);
+	  ret.value = build_unary_op (REALPART_EXPR,
+				      c_parser_cast_expression (parser).value,
+				      0);
+	  return ret;
+	case RID_IMAGPART:
+	  c_lexer_consume_token (parser->lexer);
+	  ret.value = build_unary_op (IMAGPART_EXPR,
+				      c_parser_cast_expression (parser).value,
+				      0);
+	  return ret;
+	default:
+	  return c_parser_postfix_expression (parser);
+	}
+    default:
+      return c_parser_postfix_expression (parser);
+    }
+}
+
+/* Parse a sizeof expression.  */
+
+static struct c_expr
+c_parser_sizeof_expression (c_parser *parser)
+{
+  struct c_expr expr;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_SIZEOF));
+  c_lexer_consume_token (parser->lexer);
+  skip_evaluation++;
+  in_sizeof++;
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)
+      && c_token_starts_typename (c_lexer_peek_2nd_token (parser->lexer)))
+    {
+      /* Either sizeof ( type-name ) or sizeof unary-expression
+	 starting with a compound literal.  */
+      struct c_type_name *type_name;
+      c_lexer_consume_token (parser->lexer);
+      type_name = c_parser_type_name (parser);
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+      if (type_name == NULL)
+	{
+	  struct c_expr ret;
+	  skip_evaluation--;
+	  in_sizeof--;
+	  ret.value = error_mark_node;
+	  ret.original_code = ERROR_MARK;
+	  return ret;
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+	{
+	  expr = c_parser_postfix_expression_after_paren_type (parser,
+							       type_name);
+	  goto sizeof_expr;
+	}
+      /* sizeof ( type-name ).  */
+      skip_evaluation--;
+      in_sizeof--;
+      return c_expr_sizeof_type (type_name);
+    }
+  else
+    {
+      expr = c_parser_unary_expression (parser);
+    sizeof_expr:
+      skip_evaluation--;
+      in_sizeof--;
+      if (TREE_CODE (expr.value) == COMPONENT_REF
+	  && DECL_C_BIT_FIELD (TREE_OPERAND (expr.value, 1)))
+	error ("%<sizeof%> applied to a bit-field");
+      return c_expr_sizeof_expr (expr);
+    }
+}
+
+/* Parse an alignof expression.  */
+
+static struct c_expr
+c_parser_alignof_expression (c_parser *parser)
+{
+  struct c_expr expr;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_ALIGNOF));
+  c_lexer_consume_token (parser->lexer);
+  skip_evaluation++;
+  in_alignof++;
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)
+      && c_token_starts_typename (c_lexer_peek_2nd_token (parser->lexer)))
+    {
+      /* Either __alignof__ ( type-name ) or __alignof__
+	 unary-expression starting with a compound literal.  */
+      struct c_type_name *type_name;
+      struct c_expr ret;
+      c_lexer_consume_token (parser->lexer);
+      type_name = c_parser_type_name (parser);
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+      if (type_name == NULL)
+	{
+	  struct c_expr ret;
+	  skip_evaluation--;
+	  in_alignof--;
+	  ret.value = error_mark_node;
+	  ret.original_code = ERROR_MARK;
+	  return ret;
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+	{
+	  expr = c_parser_postfix_expression_after_paren_type (parser,
+							       type_name);
+	  goto alignof_expr;
+	}
+      /* alignof ( type-name ).  */
+      skip_evaluation--;
+      in_alignof--;
+      ret.value = c_alignof (groktypename (type_name));
+      ret.original_code = ERROR_MARK;
+      return ret;
+    }
+  else
+    {
+      struct c_expr ret;
+      expr = c_parser_unary_expression (parser);
+    alignof_expr:
+      skip_evaluation--;
+      in_alignof--;
+      ret.value = c_alignof_expr (expr.value);
+      ret.original_code = ERROR_MARK;
+      return ret;
+    }
+}
+
+/* Parse a postfix expression (C90 6.3.1-6.3.2, C99 6.5.1-6.5.2).
+
+   postfix-expression:
+     primary-expression
+     postfix-expression [ expression ]
+     postfix-expression ( argument-expression-list[opt] )
+     postfix-expression . identifier
+     postfix-expression -> identifier
+     postfix-expression ++
+     postfix-expression --
+     ( type-name ) { initializer-list }
+     ( type-name ) { initializer-list , }
+
+   argument-expression-list:
+     argument-expression
+     argument-expression-list , argument-expression
+
+   primary-expression:
+     identifier
+     constant
+     string-literal
+     ( expression )
+
+   GNU extensions to primary-expression:
+     __func__
+       (treated as a keyword in GNU C)
+     __FUNCTION__
+     __PRETTY_FUNCTION__
+     ( compound-statement )
+     __builtin_va_arg ( assignment-expression , type-name )
+     __builtin_offsetof ( type-name , offsetof-member-designator )
+     __builtin_choose_expr ( assignment-expression ,
+			     assignment-expression ,
+			     assignment-expression )
+     __builtin_types_compatible_p ( type-name , type-name )
+
+   offsetof-member-designator:
+     identifier
+     offsetof-member-designator . identifier
+     offsetof-member-designator [ expression ]
+
+   TODO: Objective-C.  */
+
+static struct c_expr
+c_parser_postfix_expression (c_parser *parser)
+{
+  struct c_expr expr, e1, e2, e3;
+  struct c_type_name *t1, *t2;
+  switch (c_lexer_peek_token (parser->lexer)->type)
+    {
+    case CPP_NUMBER:
+    case CPP_CHAR:
+    case CPP_WCHAR:
+      expr.value = c_lexer_peek_token (parser->lexer)->value;
+      expr.original_code = ERROR_MARK;
+      c_lexer_consume_token (parser->lexer);
+      break;
+    case CPP_STRING:
+    case CPP_WSTRING:
+      expr.value = c_lexer_peek_token (parser->lexer)->value;
+      expr.original_code = STRING_CST;
+      c_lexer_consume_token (parser->lexer);
+      break;
+    case CPP_NAME:
+      if (c_lexer_peek_token (parser->lexer)->id_kind != C_ID_ID)
+	{
+	  c_parser_error (parser, "expected expression");
+	  expr.value = error_mark_node;
+	  expr.original_code = ERROR_MARK;
+	  break;
+	}
+      expr.value = build_external_ref
+	(c_lexer_peek_token (parser->lexer)->value,
+	 c_lexer_peek_2nd_token (parser->lexer)->type == CPP_OPEN_PAREN);
+      expr.original_code = ERROR_MARK;
+      c_lexer_consume_token (parser->lexer);
+      break;
+    case CPP_OPEN_PAREN:
+      /* A parenthesized expression, statement expression or compound
+	 literal.  */
+      if (c_lexer_peek_2nd_token (parser->lexer)->type == CPP_OPEN_BRACE)
+	{
+	  /* A statement expression.  */
+	  tree stmt;
+	  c_lexer_consume_token (parser->lexer);
+	  c_lexer_consume_token (parser->lexer);
+	  if (cur_stmt_list == NULL)
+	    {
+	      error ("braced-group within expression allowed "
+		     "only inside a function");
+	      parser->error = true;
+	      c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL);
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  stmt = c_begin_stmt_expr ();
+	  c_parser_compound_statement_nostart (parser, NULL);
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  if (pedantic)
+	    pedwarn ("ISO C forbids braced-groups within expressions");
+	  expr.value = c_finish_stmt_expr (stmt);
+	  expr.original_code = ERROR_MARK;
+	}
+      else if (c_token_starts_typename (c_lexer_peek_2nd_token (parser->lexer)))
+	{
+	  /* A compound literal.  ??? Can we actually get here rather
+	     than going directly to
+	     c_parser_postfix_expression_after_paren_type from
+	     elsewhere?  */
+	  struct c_type_name *type_name;
+	  c_lexer_consume_token (parser->lexer);
+	  type_name = c_parser_type_name (parser);
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  if (type_name == NULL)
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	    }
+	  else
+	    expr = c_parser_postfix_expression_after_paren_type (parser,
+								 type_name);
+	}
+      else
+	{
+	  /* A parenthesized expression.  */
+	  c_lexer_consume_token (parser->lexer);
+	  expr = c_parser_expression (parser);
+	  if (TREE_CODE (expr.value) == MODIFY_EXPR)
+	    TREE_NO_WARNING (expr.value) = 1;
+	  expr.original_code = ERROR_MARK;
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	}
+      break;
+    case CPP_KEYWORD:
+      switch (c_lexer_peek_token (parser->lexer)->keyword)
+	{
+	case RID_FUNCTION_NAME:
+	case RID_PRETTY_FUNCTION_NAME:
+	case RID_C99_FUNCTION_NAME:
+	  expr.value = fname_decl (c_lexer_peek_token (parser->lexer)->keyword,
+				   c_lexer_peek_token (parser->lexer)->value);
+	  expr.original_code = ERROR_MARK;
+	  c_lexer_consume_token (parser->lexer);
+	  break;
+	case RID_VA_ARG:
+	  c_lexer_consume_token (parser->lexer);
+	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  e1 = c_parser_expr_no_commas (parser);
+	  if (!c_parser_require (parser, CPP_COMMA, "expected ','"))
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  t1 = c_parser_type_name (parser);
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  if (t1 == NULL)
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	    }
+	  else
+	    {
+	      expr.value = build_va_arg (e1.value, groktypename (t1));
+	      expr.original_code = ERROR_MARK;
+	    }
+	  break;
+	case RID_OFFSETOF:
+	  c_lexer_consume_token (parser->lexer);
+	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  t1 = c_parser_type_name (parser);
+	  if (t1 == NULL)
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  if (!c_parser_require (parser, CPP_COMMA, "expected ','"))
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  {
+	    tree type = groktypename (t1);
+	    tree offsetof_ref;
+	    if (type == error_mark_node)
+	      offsetof_ref = error_mark_node;
+	    else
+	      offsetof_ref = build1 (INDIRECT_REF, type, NULL);
+	    /* Parse the second argument to __builtin_offsetof.  We
+	       must have one identifier, and beyond that we want to
+	       accept sub structure and sub array references.  */
+	    if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+	      {
+		offsetof_ref = build_component_ref
+		  (offsetof_ref, c_lexer_peek_token (parser->lexer)->value);
+		c_lexer_consume_token (parser->lexer);
+		while (c_lexer_next_token_is (parser->lexer, CPP_DOT)
+		       || c_lexer_next_token_is (parser->lexer,
+						 CPP_OPEN_SQUARE))
+		  {
+		    if (c_lexer_next_token_is (parser->lexer, CPP_DOT))
+		      {
+			c_lexer_consume_token (parser->lexer);
+			if (c_lexer_next_token_is_not (parser->lexer,
+						       CPP_NAME))
+			  {
+			    c_parser_error (parser, "expected identifier");
+			    break;
+			  }
+			offsetof_ref = build_component_ref
+			  (offsetof_ref,
+			   c_lexer_peek_token (parser->lexer)->value);
+			c_lexer_consume_token (parser->lexer);
+		      }
+		    else
+		      {
+			tree idx;
+			c_lexer_consume_token (parser->lexer);
+			idx = c_parser_expression (parser).value;
+			c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE,
+						   "expected ']'");
+			offsetof_ref = build_array_ref (offsetof_ref, idx);
+		      }
+		  }
+	      }
+	    else
+	      c_parser_error (parser, "expected identifier");
+	    c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+				       "expected ')'");
+	    expr.value = fold_offsetof (offsetof_ref);
+	    expr.original_code = ERROR_MARK;
+	  }
+	  break;
+	case RID_CHOOSE_EXPR:
+	  c_lexer_consume_token (parser->lexer);
+	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  e1 = c_parser_expr_no_commas (parser);
+	  if (!c_parser_require (parser, CPP_COMMA, "expected ','"))
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  e2 = c_parser_expr_no_commas (parser);
+	  if (!c_parser_require (parser, CPP_COMMA, "expected ','"))
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  e3 = c_parser_expr_no_commas (parser);
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  {
+	    tree c;
+
+	    c = fold (e1.value);
+	    STRIP_NOPS (c);
+	    if (TREE_CODE (c) != INTEGER_CST)
+	      error ("first argument to __builtin_choose_expr not"
+		     " a constant");
+	    expr = integer_zerop (c) ? e3 : e2;
+	  }
+	  break;
+	case RID_TYPES_COMPATIBLE_P:
+	  c_lexer_consume_token (parser->lexer);
+	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  t1 = c_parser_type_name (parser);
+	  if (t1 == NULL)
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  if (!c_parser_require (parser, CPP_COMMA, "expected ','"))
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  t2 = c_parser_type_name (parser);
+	  if (t2 == NULL)
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  {
+	    tree e1, e2;
+
+	    e1 = TYPE_MAIN_VARIANT (groktypename (t1));
+	    e2 = TYPE_MAIN_VARIANT (groktypename (t2));
+
+	    expr.value = comptypes (e1, e2)
+	      ? build_int_cst (NULL_TREE, 1)
+	      : build_int_cst (NULL_TREE, 0);
+	    expr.original_code = ERROR_MARK;
+	  }
+	  break;
+	default:
+	  c_parser_error (parser, "expected expression");
+	  expr.value = error_mark_node;
+	  expr.original_code = ERROR_MARK;
+	  break;
+	}
+      break;
+    default:
+      c_parser_error (parser, "expected expression");
+      expr.value = error_mark_node;
+      expr.original_code = ERROR_MARK;
+      break;
+    }
+  return c_parser_postfix_expression_after_primary (parser, expr);
+}
+
+/* Parse a postfix expression after a parenthesized type name: the
+   brace-enclosed initializer of a compound literal, possibly followed
+   by some postfix operators.  This is separate because it is not
+   possible to tell until after the type name whether a cast
+   expression has a cast or a compound literal, or whether the operand
+   of sizeof is a parenthesized type name or starts with a compound
+   literal.  */
+
+static struct c_expr
+c_parser_postfix_expression_after_paren_type (c_parser *parser,
+					      struct c_type_name *type_name)
+{
+  tree type;
+  struct c_expr init;
+  struct c_expr expr;
+  start_init (NULL_TREE, NULL, 0);
+  type = groktypename (type_name);
+  if (C_TYPE_VARIABLE_SIZE (type))
+    {
+      error ("compound literal has variable size");
+      type = error_mark_node;
+    }
+  init = c_parser_braced_init (parser, type, false);
+  finish_init ();
+  maybe_warn_string_init (type, init);
+
+  if (pedantic && !flag_isoc99)
+    pedwarn ("ISO C90 forbids compound literals");
+  expr.value = build_compound_literal (type, init.value);
+  expr.original_code = ERROR_MARK;
+  return c_parser_postfix_expression_after_primary (parser, expr);
+}
+
+/* Parse a postfix expression after the initial primary or compound
+   literal; that is, parse a series of postfix operators.  */
+
+static struct c_expr
+c_parser_postfix_expression_after_primary (c_parser *parser,
+					   struct c_expr expr)
+{
+  tree ident, idx, exprlist;
+  location_t loc, save_loc;
+  while (true)
+    {
+      switch (c_lexer_peek_token (parser->lexer)->type)
+	{
+	case CPP_OPEN_SQUARE:
+	  /* Array reference.  */
+	  c_lexer_consume_token (parser->lexer);
+	  idx = c_parser_expression (parser).value;
+	  c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, "expected ']'");
+	  expr.value = build_array_ref (expr.value, idx);
+	  expr.original_code = ERROR_MARK;
+	  break;
+	case CPP_OPEN_PAREN:
+	  /* Function call.  */
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	    exprlist = NULL_TREE;
+	  else
+	    exprlist = c_parser_expr_list (parser);
+	  loc = c_lexer_peek_token (parser->lexer)->location;
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  save_loc = input_location;
+	  input_location = loc;
+	  expr.value = build_function_call (expr.value, exprlist);
+	  expr.original_code = ERROR_MARK;
+	  input_location = save_loc;
+	  break;
+	case CPP_DOT:
+	  /* Structure element reference.  */
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+	    ident = c_lexer_peek_token (parser->lexer)->value;
+	  else
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      return expr;
+	    }
+	  c_lexer_consume_token (parser->lexer);
+	  expr.value = build_component_ref (expr.value, ident);
+	  expr.original_code = ERROR_MARK;
+	  break;
+	case CPP_DEREF:
+	  /* Structure element reference.  */
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+	    ident = c_lexer_peek_token (parser->lexer)->value;
+	  else
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      return expr;
+	    }
+	  c_lexer_consume_token (parser->lexer);
+	  expr.value = build_component_ref (build_indirect_ref (expr.value,
+								"->"), ident);
+	  expr.original_code = ERROR_MARK;
+	  break;
+	case CPP_PLUS_PLUS:
+	  /* Postincrement.  */
+	  c_lexer_consume_token (parser->lexer);
+	  expr.value = build_unary_op (POSTINCREMENT_EXPR, expr.value, 0);
+	  expr.original_code = ERROR_MARK;
+	  break;
+	case CPP_MINUS_MINUS:
+	  /* Postdecrement.  */
+	  c_lexer_consume_token (parser->lexer);
+	  expr.value = build_unary_op (POSTDECREMENT_EXPR, expr.value, 0);
+	  expr.original_code = ERROR_MARK;
+	  break;
+	default:
+	  return expr;
+	}
+    }
+}
+
+/* Parse an expression (C90 6.3.17, C99 6.5.17).
+
+   expression:
+     assignment-expression
+     expression , assignment-expression
+*/
+
+static struct c_expr
+c_parser_expression (c_parser *parser)
+{
+  struct c_expr expr;
+  expr = c_parser_expr_no_commas (parser);
+  while (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+    {
+      struct c_expr next;
+      c_lexer_consume_token (parser->lexer);
+      next = c_parser_expr_no_commas (parser);
+      expr.value = build_compound_expr (expr.value, next.value);
+      expr.original_code = COMPOUND_EXPR;
+    }
+  return expr;
+}
+
+/* Parse a non-empty list of expressions.
+
+   nonempty-expr-list:
+     assignment-expression
+     nonempty-expr-list , assignment-expression
+*/
+
+static tree
+c_parser_expr_list (c_parser *parser)
+{
+  struct c_expr expr;
+  tree ret;
+  expr = c_parser_expr_no_commas (parser);
+  ret = build_tree_list (NULL_TREE, expr.value);
+  while (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+    {
+      c_lexer_consume_token (parser->lexer);
+      expr = c_parser_expr_no_commas (parser);
+      ret = chainon (ret, build_tree_list (NULL_TREE, expr.value));
+    }
+  return ret;
+}
+
+\f
+/* The actual parser and external interface.  */
+
+static GTY (()) c_parser *the_parser;
+
+/* Parse a single source file.  */
+
+void
+c_parse_file (void)
+{
+  the_parser = c_parser_new ();
+  c_parser_translation_unit (the_parser);
+  the_parser = NULL;
+}
+
+#include "gt-c-parser.h"
diff -rupN GCC.orig/gcc/c-tree.h GCC/gcc/c-tree.h
--- GCC.orig/gcc/c-tree.h	2004-10-14 00:31:11.000000000 +0000
+++ GCC/gcc/c-tree.h	2004-10-16 21:50:02.000000000 +0000
@@ -204,6 +204,10 @@ struct c_declspecs {
   enum c_typespec_keyword typespec_word;
   /* The storage class specifier, or csc_none if none.  */
   enum c_storage_class storage_class;
+  /* Whether any declaration specifiers have been seen at all.  */
+  BOOL_BITFIELD declspecs_seen_p : 1;
+  /* Whether a type specifier has been seen.  */
+  BOOL_BITFIELD type_seen_p : 1;
   /* Whether something other than a storage class specifier or
      attribute has been seen.  This is used to warn for the
      obsolescent usage of storage class specifiers other than at the

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-23  1:25 New C parser [patch] Joseph S. Myers
@ 2004-10-23  2:39 ` Steven Bosscher
  2004-10-23  4:15   ` Joseph S. Myers
  2004-10-23  5:44 ` Scott Robert Ladd
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 42+ messages in thread
From: Steven Bosscher @ 2004-10-23  2:39 UTC (permalink / raw)
  To: Joseph S. Myers, gcc-patches

On Saturday 23 October 2004 01:56, Joseph S. Myers wrote:
> Writing a C parser is quicker than reading, analysing and replying to
> hundreds of list messages discussing how to implement OpenMP and what
> should or should not be replaced to do so, so I just wrote one over
> the past week.  This is a hand-written, recursive-descent parser for C
> that parses the same GNU C language as the Bison parser, just
> replacing the parser and not other code at the same time.

Very, very cool.

Quickly looked over it, only one remark so far:

> +      /* Parse old-style parameter declarations.  */
> +      while (c_lexer_next_token_is_not (parser->lexer, CPP_EOF)
> +            && c_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE))
> +       c_parser_declaration_or_fndef (parser, false, false, true, false);
> +      DECL_SOURCE_LOCATION (current_function_decl) = input_location;

Don't you want to make the location of current_function_decl the first
line where the function definition starts (or the location of the first
token even)?

Gr.
Steven

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-23  2:39 ` Steven Bosscher
@ 2004-10-23  4:15   ` Joseph S. Myers
  0 siblings, 0 replies; 42+ messages in thread
From: Joseph S. Myers @ 2004-10-23  4:15 UTC (permalink / raw)
  To: Steven Bosscher; +Cc: gcc-patches

On Sat, 23 Oct 2004, Steven Bosscher wrote:

> > +      /* Parse old-style parameter declarations.  */
> > +      while (c_lexer_next_token_is_not (parser->lexer, CPP_EOF)
> > +            && c_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE))
> > +       c_parser_declaration_or_fndef (parser, false, false, true, false);
> > +      DECL_SOURCE_LOCATION (current_function_decl) = input_location;
> 
> Don't you want to make the location of current_function_decl the first
> line where the function definition starts (or the location of the first
> token even)?

This does make it the location of the open brace.  This is a direct 
translation of

          old_style_parm_decls save_location
                { DECL_SOURCE_LOCATION (current_function_decl) = $6;

from the old grammar - and unlike on some places, both the old and new 
parsers have done the one token lookahead at this point so there isn't a 
difference arising and I didn't need to fix this place up to get 
diagnostic locations to agree between the parsers.

Yes, referring to the location of a token explicitly is better than using 
input_location to get it implicitly when location handling is cleaned up 
generally later; just not needed to replace one parser with another.

-- 
Joseph S. Myers               http://www.srcf.ucam.org/~jsm28/gcc/
    jsm@polyomino.org.uk (personal mail)
    joseph@codesourcery.com (CodeSourcery mail)
    jsm28@gcc.gnu.org (Bugzilla assignments and CCs)

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-23  1:25 New C parser [patch] Joseph S. Myers
  2004-10-23  2:39 ` Steven Bosscher
@ 2004-10-23  5:44 ` Scott Robert Ladd
  2004-10-24 22:49 ` Joseph S. Myers
  2004-10-25 22:33 ` Ziemowit Laski
  3 siblings, 0 replies; 42+ messages in thread
From: Scott Robert Ladd @ 2004-10-23  5:44 UTC (permalink / raw)
  To: Joseph S. Myers; +Cc: gcc-patches

Joseph S. Myers wrote:
> Writing a C parser is quicker than reading, analysing and replying to
> hundreds of list messages discussing how to implement OpenMP and what
> should or should not be replaced to do so, so I just wrote one over
> the past week.

Fascinating.

I suspect your C parser will generate far more comment than has OpenMP. 
;). It would certainly would make gomp's work easier (in the C front 
end, at least.)

I'll skip much of your discussion to focus on the OpenMP-specific part.

> As regards the suggestion of working on gomp-branch, I don't want to
> mix these changes with any not really required by them, as the aim is
> for a minimal patch to apply to mainline; and before there is full
> testsuite coverage with no regressions, it would destabilise
> gomp-branch, but once there is full coverage with no regressions, it
> is ready for mainline.

Assuming this moves forward, I don't see why gomp would implement code 
for the current Bison parser. Given that C++ has a recursive descent 
parser all ready, my initial thought is to proceed there, and port to C 
when your work is ready. Fortran will, of course, be handled separately.

Good stuff.

..Scott

-- 
Scott Robert Ladd
site: http://www.coyotegulch.com
blog: http://chaoticcoyote.blogspot.com

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-23  1:25 New C parser [patch] Joseph S. Myers
  2004-10-23  2:39 ` Steven Bosscher
  2004-10-23  5:44 ` Scott Robert Ladd
@ 2004-10-24 22:49 ` Joseph S. Myers
  2004-10-26  0:32   ` Zack Weinberg
  2004-10-27 20:25   ` Joseph S. Myers
  2004-10-25 22:33 ` Ziemowit Laski
  3 siblings, 2 replies; 42+ messages in thread
From: Joseph S. Myers @ 2004-10-24 22:49 UTC (permalink / raw)
  To: gcc-patches

This second slightly revised patch version fixes the lexer lookahead
strategy not to look ahead one token until required, so improving the
correspondence of diagnostic locations with the new parser to those
with the old parser.  In addition it has various improvements to
commentary and makes certain bad old-style parameter declarations,
stray semicolons and those without declaration specifiers, be rejected
as the old parser did.  It remains 2% faster than the old parser
(timing -O0 compilation of .i files of cc1, --disable-checking).

Bootstrapped on i686-pc-linux-gnu, the only regressions being for
testcases containing syntax errors.

The next significant development stage remains adding ObjC support;
the same notes as before about ObjC changes which would simplify the
grammar that needs reverse-engineering and reimplementing apply:

* Some patch along the lines of that in
  <http://gcc.gnu.org/ml/gcc-patches/2004-10/msg00951.html> or the
  relevant part thereof to get rid of RID_ID that would simplify the
  grammar.

* Is there some reason why CLASSNAME isn't included as an alternative
  in after_type_declarator and parm_declarator_starttypename?  If it
  should be included, then that also simplifies the grammar.

-- 
Joseph S. Myers               http://www.srcf.ucam.org/~jsm28/gcc/
    jsm@polyomino.org.uk (personal mail)
    joseph@codesourcery.com (CodeSourcery mail)
    jsm28@gcc.gnu.org (Bugzilla assignments and CCs)

diff -rupN GCC.orig/gcc/Makefile.in GCC/gcc/Makefile.in
--- GCC.orig/gcc/Makefile.in	2004-10-15 10:05:02.000000000 +0000
+++ GCC/gcc/Makefile.in	2004-10-16 18:31:46.000000000 +0000
@@ -191,7 +191,6 @@ gcc.o-warn = -Wno-error
 build/insn-conditions.o-warn = -Wno-error
 # Bison-1.75 output often yields (harmless) -Wtraditional warnings
 build/gengtype-yacc.o-warn = -Wno-error
-c-parse.o-warn = -Wno-error
 # flex output may yield harmless "no previous prototype" warnings
 build/gengtype-lex.o-warn = -Wno-error
 # SYSCALLS.c misses prototypes
@@ -882,7 +881,7 @@ C_AND_OBJC_OBJS = attribs.o c-errors.o c
   c-gimplify.o tree-mudflap.o c-pretty-print.o
 
 # Language-specific object files for C.
-C_OBJS = c-parse.o c-lang.o stub-objc.o $(C_AND_OBJC_OBJS)
+C_OBJS = c-parser.o c-lang.o stub-objc.o $(C_AND_OBJC_OBJS)
 
 # Language-independent object files.
 OBJS-common = \
@@ -1348,24 +1347,15 @@ s-crt0:	$(CRT0_S) $(MCRT0_S) $(GCC_PASSE
 
 c-errors.o: c-errors.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
     $(C_TREE_H) $(FLAGS_H) $(DIAGNOSTIC_H) $(TM_P_H)
-c-parse.o : c-parse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
+c-parser.o : c-parser.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
     $(GGC_H) intl.h $(C_TREE_H) input.h $(FLAGS_H) toplev.h output.h \
-    $(CPPLIB_H) varray.h gt-c-parse.h langhooks.h $(C_COMMON_H) $(C_PRAGMA_H)
+    $(CPPLIB_H) varray.h gt-c-parser.h langhooks.h $(C_COMMON_H) $(C_PRAGMA_H)
 
 srcextra: gcc.srcextra lang.srcextra
 
-gcc.srcextra: c-parse.y c-parse.c gengtype-lex.c gengtype-yacc.c gengtype-yacc.h
+gcc.srcextra: gengtype-lex.c gengtype-yacc.c gengtype-yacc.h
 	-cp -p $^ $(srcdir)
 
-c-parse.c: c-parse.y
-	-$(BISON) $(BISONFLAGS) -o $@ $<
-
-c-parse.y: c-parse.in
-	echo '/*WARNING: This file is automatically generated!*/' >tmp-c-parse.y
-	sed -e "/^@@ifobjc.*/,/^@@end_ifobjc.*/d" \
-	    -e "/^@@ifc.*/d" -e "/^@@end_ifc.*/d" $< >>tmp-c-parse.y
-	$(SHELL) $(srcdir)/../move-if-change tmp-c-parse.y $@
-
 c-incpath.o: c-incpath.c c-incpath.h $(CONFIG_H) $(SYSTEM_H) $(CPPLIB_H) \
 		intl.h prefix.h coretypes.h $(TM_H) cppdefault.h $(TARGET_H) \
 		$(MACHMODE_H)
@@ -2417,7 +2407,7 @@ GTFILES = $(srcdir)/input.h $(srcdir)/co
   $(srcdir)/sdbout.c $(srcdir)/stor-layout.c \
   $(srcdir)/stringpool.c $(srcdir)/tree.c $(srcdir)/varasm.c \
   $(srcdir)/tree-mudflap.c $(srcdir)/tree-flow.h \
-  $(srcdir)/c-objc-common.c $(srcdir)/c-common.c $(srcdir)/c-parse.in \
+  $(srcdir)/c-objc-common.c $(srcdir)/c-common.c $(srcdir)/c-parser.c \
   $(srcdir)/tree-ssanames.c $(srcdir)/tree-eh.c \
   $(srcdir)/tree-phinodes.c $(srcdir)/tree-cfg.c \
   $(srcdir)/tree-dfa.c $(srcdir)/tree-ssa-propagate.c \
@@ -2439,7 +2429,7 @@ gt-emit-rtl.h gt-explow.h gt-stor-layout
 gt-lists.h gt-alias.h gt-cselib.h gt-fold-const.h gt-gcse.h \
 gt-expr.h gt-sdbout.h gt-optabs.h gt-bitmap.h gt-dojump.h \
 gt-dwarf2out.h gt-ra-build.h gt-reg-stack.h gt-dwarf2asm.h \
-gt-dbxout.h gt-c-common.h gt-c-decl.h gt-c-parse.h \
+gt-dbxout.h gt-c-common.h gt-c-decl.h gt-c-parser.h \
 gt-c-pragma.h gtype-c.h gt-input.h gt-cfglayout.h \
 gt-tree-mudflap.h \
 gt-tree-ssa-ccp.h gt-tree-eh.h \
@@ -3109,7 +3099,7 @@ distclean: clean lang.distclean
 	-rm -f Makefile *.oaux
 	-rm -f gthr-default.h
 	-rm -f */stage1 */stage2 */stage3 */stage4 */include */stageprofile */stagefeedback
-	-rm -f c-parse.y c-parse.c c-parse.output TAGS */TAGS
+	-rm -f TAGS */TAGS
 	-rm -f *.asm
 	-rm -f site.exp site.bak testsuite/site.exp testsuite/site.bak
 	-rm -f testsuite/*.log testsuite/*.sum
@@ -3130,7 +3120,6 @@ maintainer-clean:
 	@echo 'This command is intended for maintainers to use; it'
 	@echo 'deletes files that may need special tools to rebuild.'
 	$(MAKE) lang.maintainer-clean distclean
-	-rm -f $(srcdir)/c-parse.y $(srcdir)/c-parse.c
 	-rm -f cpp.??s cpp.*aux
 	-rm -f gcc.??s gcc.*aux
 	-rm -f $(docdir)/*.info $(docdir)/*.1 $(docdir)/*.7 $(docdir)/*.dvi
@@ -3604,7 +3593,7 @@ TAGS: lang.tags
 	    incs="$$incs --include $$dir/TAGS.sub";	\
 	  fi;						\
 	done;						\
-	etags -o TAGS.sub *.y *.h *.c -l yacc c-parse.in; \
+	etags -o TAGS.sub *.y *.h *.c; \
 	etags --include TAGS.sub $$incs)
 
 # ------------------------------------------------------
diff -rupN GCC.orig/gcc/c-config-lang.in GCC/gcc/c-config-lang.in
--- GCC.orig/gcc/c-config-lang.in	2002-12-27 19:38:52.000000000 +0000
+++ GCC/gcc/c-config-lang.in	2004-10-21 23:47:38.000000000 +0000
@@ -23,4 +23,4 @@
 # files used by C that have garbage collection GTY macros in them
 # which therefore need to be scanned by gengtype.c.
 
-gtfiles="\$(srcdir)/c-lang.c \$(srcdir)/c-parse.in \$(srcdir)/c-tree.h \$(srcdir)/c-decl.c \$(srcdir)/c-common.c \$(srcdir)/c-common.h \$(srcdir)/c-pragma.c \$(srcdir)/c-objc-common.c"
+gtfiles="\$(srcdir)/c-lang.c \$(srcdir)/c-tree.h \$(srcdir)/c-decl.c \$(srcdir)/c-common.c \$(srcdir)/c-common.h \$(srcdir)/c-pragma.c \$(srcdir)/c-objc-common.c \$(srcdir)/c-parser.c"
diff -rupN GCC.orig/gcc/c-decl.c GCC/gcc/c-decl.c
--- GCC.orig/gcc/c-decl.c	2004-10-23 23:24:39.000000000 +0000
+++ GCC/gcc/c-decl.c	2004-10-24 00:43:07.000000000 +0000
@@ -6679,6 +6679,8 @@ build_null_declspecs (void)
   ret->attrs = 0;
   ret->typespec_word = cts_none;
   ret->storage_class = csc_none;
+  ret->declspecs_seen_p = false;
+  ret->type_seen_p = false;
   ret->non_sc_seen_p = false;
   ret->typedef_p = false;
   ret->tag_defined_p = false;
@@ -6708,6 +6710,7 @@ declspecs_add_qual (struct c_declspecs *
   enum rid i;
   bool dupe = false;
   specs->non_sc_seen_p = true;
+  specs->declspecs_seen_p = true;
   gcc_assert (TREE_CODE (qual) == IDENTIFIER_NODE
 	      && C_IS_RESERVED_WORD (qual));
   i = C_RID_CODE (qual);
@@ -6741,6 +6744,8 @@ declspecs_add_type (struct c_declspecs *
 {
   tree type = spec.spec;
   specs->non_sc_seen_p = true;
+  specs->declspecs_seen_p = true;
+  specs->type_seen_p = true;
   if (TREE_DEPRECATED (type))
     specs->deprecated_p = true;
 
@@ -7035,6 +7040,7 @@ declspecs_add_scspec (struct c_declspecs
   enum rid i;
   enum c_storage_class n = csc_none;
   bool dupe = false;
+  specs->declspecs_seen_p = true;
   gcc_assert (TREE_CODE (scspec) == IDENTIFIER_NODE
 	      && C_IS_RESERVED_WORD (scspec));
   i = C_RID_CODE (scspec);
@@ -7117,6 +7123,7 @@ struct c_declspecs *
 declspecs_add_attrs (struct c_declspecs *specs, tree attrs)
 {
   specs->attrs = chainon (attrs, specs->attrs);
+  specs->declspecs_seen_p = true;
   return specs;
 }
 
diff -rupN GCC.orig/gcc/c-parser.c GCC/gcc/c-parser.c
--- GCC.orig/gcc/c-parser.c	1970-01-01 00:00:00.000000000 +0000
+++ GCC/gcc/c-parser.c	2004-10-24 15:37:39.000000000 +0000
@@ -0,0 +1,4860 @@
+/* Parser for C and Objective-C.
+   Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997,
+   1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+   Parser actions based on the old Bison parser; structure somewhat
+   influenced by and fragments based on the C++ parser.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+/* TODO:
+
+   Support Objective-C.
+
+   Make sure all relevant comments, and all relevant code from all
+   actions, brought over from old parser.  Verify exact correspondence
+   of syntax accepted.
+
+   Add testcases covering every input symbol in every state in old and
+   new parsers.
+
+   Include full syntax for GNU C, including erroneous cases accepted
+   with error messages, in syntax productions in comments.
+
+   Make more diagnostics in the front end generally take an explicit
+   location rather than implicitly using input_location.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "langhooks.h"
+#include "input.h"
+#include "cpplib.h"
+#include "timevar.h"
+#include "c-pragma.h"
+#include "c-tree.h"
+#include "flags.h"
+#include "output.h"
+#include "toplev.h"
+#include "ggc.h"
+#include "c-common.h"
+
+\f
+/* Miscellaneous data and functions needed for lexer and parser.  */
+
+int yydebug;
+
+/* Objective-C specific parser/lexer information.  */
+
+static int objc_pq_context = 0;
+
+/* The following flag is needed to contextualize Objective-C lexical
+   analysis.  In some cases (e.g., 'int NSObject;'), it is undesirable
+   to bind an identifier to an Objective-C class, even if a class with
+   that name exists.  */
+static int objc_need_raw_identifier = 0;
+#define OBJC_NEED_RAW_IDENTIFIER(VAL)		\
+  do {						\
+    if (c_dialect_objc ())			\
+      objc_need_raw_identifier = VAL;		\
+  } while (0)
+
+/* The reserved keyword table.  */
+struct resword
+{
+  const char *word;
+  ENUM_BITFIELD(rid) rid : 16;
+  unsigned int disable   : 16;
+};
+
+/* Disable mask.  Keywords are disabled if (reswords[i].disable &
+   mask) is _true_.  */
+#define D_C89	0x01	/* not in C89 */
+#define D_EXT	0x02	/* GCC extension */
+#define D_EXT89	0x04	/* GCC extension incorporated in C99 */
+#define D_OBJC	0x08	/* Objective C only */
+
+static const struct resword reswords[] =
+{
+  { "_Bool",		RID_BOOL,	0 },
+  { "_Complex",		RID_COMPLEX,	0 },
+  { "__FUNCTION__",	RID_FUNCTION_NAME, 0 },
+  { "__PRETTY_FUNCTION__", RID_PRETTY_FUNCTION_NAME, 0 },
+  { "__alignof",	RID_ALIGNOF,	0 },
+  { "__alignof__",	RID_ALIGNOF,	0 },
+  { "__asm",		RID_ASM,	0 },
+  { "__asm__",		RID_ASM,	0 },
+  { "__attribute",	RID_ATTRIBUTE,	0 },
+  { "__attribute__",	RID_ATTRIBUTE,	0 },
+  { "__builtin_choose_expr", RID_CHOOSE_EXPR, 0 },
+  { "__builtin_offsetof", RID_OFFSETOF, 0 },
+  { "__builtin_types_compatible_p", RID_TYPES_COMPATIBLE_P, 0 },
+  { "__builtin_va_arg",	RID_VA_ARG,	0 },
+  { "__complex",	RID_COMPLEX,	0 },
+  { "__complex__",	RID_COMPLEX,	0 },
+  { "__const",		RID_CONST,	0 },
+  { "__const__",	RID_CONST,	0 },
+  { "__extension__",	RID_EXTENSION,	0 },
+  { "__func__",		RID_C99_FUNCTION_NAME, 0 },
+  { "__imag",		RID_IMAGPART,	0 },
+  { "__imag__",		RID_IMAGPART,	0 },
+  { "__inline",		RID_INLINE,	0 },
+  { "__inline__",	RID_INLINE,	0 },
+  { "__label__",	RID_LABEL,	0 },
+  { "__real",		RID_REALPART,	0 },
+  { "__real__",		RID_REALPART,	0 },
+  { "__restrict",	RID_RESTRICT,	0 },
+  { "__restrict__",	RID_RESTRICT,	0 },
+  { "__signed",		RID_SIGNED,	0 },
+  { "__signed__",	RID_SIGNED,	0 },
+  { "__thread",		RID_THREAD,	0 },
+  { "__typeof",		RID_TYPEOF,	0 },
+  { "__typeof__",	RID_TYPEOF,	0 },
+  { "__volatile",	RID_VOLATILE,	0 },
+  { "__volatile__",	RID_VOLATILE,	0 },
+  { "asm",		RID_ASM,	D_EXT },
+  { "auto",		RID_AUTO,	0 },
+  { "break",		RID_BREAK,	0 },
+  { "case",		RID_CASE,	0 },
+  { "char",		RID_CHAR,	0 },
+  { "const",		RID_CONST,	0 },
+  { "continue",		RID_CONTINUE,	0 },
+  { "default",		RID_DEFAULT,	0 },
+  { "do",		RID_DO,		0 },
+  { "double",		RID_DOUBLE,	0 },
+  { "else",		RID_ELSE,	0 },
+  { "enum",		RID_ENUM,	0 },
+  { "extern",		RID_EXTERN,	0 },
+  { "float",		RID_FLOAT,	0 },
+  { "for",		RID_FOR,	0 },
+  { "goto",		RID_GOTO,	0 },
+  { "if",		RID_IF,		0 },
+  { "inline",		RID_INLINE,	D_EXT89 },
+  { "int",		RID_INT,	0 },
+  { "long",		RID_LONG,	0 },
+  { "register",		RID_REGISTER,	0 },
+  { "restrict",		RID_RESTRICT,	D_C89 },
+  { "return",		RID_RETURN,	0 },
+  { "short",		RID_SHORT,	0 },
+  { "signed",		RID_SIGNED,	0 },
+  { "sizeof",		RID_SIZEOF,	0 },
+  { "static",		RID_STATIC,	0 },
+  { "struct",		RID_STRUCT,	0 },
+  { "switch",		RID_SWITCH,	0 },
+  { "typedef",		RID_TYPEDEF,	0 },
+  { "typeof",		RID_TYPEOF,	D_EXT },
+  { "union",		RID_UNION,	0 },
+  { "unsigned",		RID_UNSIGNED,	0 },
+  { "void",		RID_VOID,	0 },
+  { "volatile",		RID_VOLATILE,	0 },
+  { "while",		RID_WHILE,	0 },
+  /* Objective-C keyword.  */
+  { "id",		RID_ID,			D_OBJC },
+  /* These Objective-C keywords are recognized only immediately after
+     an '@'.  */
+  { "class",		RID_AT_CLASS,		D_OBJC },
+  { "compatibility_alias", RID_AT_ALIAS,	D_OBJC },
+  { "defs",		RID_AT_DEFS,		D_OBJC },
+  { "encode",		RID_AT_ENCODE,		D_OBJC },
+  { "end",		RID_AT_END,		D_OBJC },
+  { "implementation",	RID_AT_IMPLEMENTATION,	D_OBJC },
+  { "interface",	RID_AT_INTERFACE,	D_OBJC },
+  { "private",		RID_AT_PRIVATE,		D_OBJC },
+  { "protected",	RID_AT_PROTECTED,	D_OBJC },
+  { "protocol",		RID_AT_PROTOCOL,	D_OBJC },
+  { "public",		RID_AT_PUBLIC,		D_OBJC },
+  { "selector",		RID_AT_SELECTOR,	D_OBJC },
+  { "throw",		RID_AT_THROW,		D_OBJC },
+  { "try",		RID_AT_TRY,		D_OBJC },
+  { "catch",		RID_AT_CATCH,		D_OBJC },
+  { "finally",		RID_AT_FINALLY,		D_OBJC },
+  { "synchronized",	RID_AT_SYNCHRONIZED,	D_OBJC },
+  /* These are recognized only in protocol-qualifier context
+     (see above) */
+  { "bycopy",		RID_BYCOPY,		D_OBJC },
+  { "byref",		RID_BYREF,		D_OBJC },
+  { "in",		RID_IN,			D_OBJC },
+  { "inout",		RID_INOUT,		D_OBJC },
+  { "oneway",		RID_ONEWAY,		D_OBJC },
+  { "out",		RID_OUT,		D_OBJC },
+};
+#define N_reswords (sizeof reswords / sizeof (struct resword))
+
+/* Initialize the reserved word identifiers.  */
+
+static void
+init_reswords (void)
+{
+  unsigned int i;
+  tree id;
+  int mask = (flag_isoc99 ? 0 : D_C89)
+	      | (flag_no_asm ? (flag_isoc99 ? D_EXT : D_EXT|D_EXT89) : 0);
+
+  if (!c_dialect_objc ())
+     mask |= D_OBJC;
+
+  ridpointers = GGC_CNEWVEC (tree, (int) RID_MAX);
+  for (i = 0; i < N_reswords; i++)
+    {
+      /* If a keyword is disabled, do not enter it into the table
+	 and so create a canonical spelling that isn't a keyword.  */
+      if (reswords[i].disable & mask)
+	continue;
+
+      id = get_identifier (reswords[i].word);
+      C_RID_CODE (id) = reswords[i].rid;
+      C_IS_RESERVED_WORD (id) = 1;
+      ridpointers [(int) reswords[i].rid] = id;
+    }
+}
+
+/* Initialization routine for this file.  */
+
+void
+c_parse_init (void)
+{
+  init_reswords ();
+}
+\f
+/* The C lexer intermediates between the lexer in cpplib and c-lex.c
+   and the C parser.  Identifiers are separated into ordinary
+   identifiers, type names, keywords and some other Objective-C types
+   of identifiers, and some look-ahead is maintained.
+
+   ??? It might be a good idea to lex the whole file up front (as for
+   C++).  It should then be possible to share more of the C and C++
+   lexer code.  */
+
+/* The following local token type is used.  */
+
+/* A keyword.  */
+#define CPP_KEYWORD ((enum cpp_ttype) (N_TTYPES + 1))
+
+/* The number of token types, including C-specific ones.  */
+#define N_C_TTYPES ((int) (CPP_KEYWORD + 1))
+
+/* More information about the type of a CPP_NAME token.  */
+typedef enum c_id_kind {
+  /* An ordinary identifier.  */
+  C_ID_ID,
+  /* An identifier declared as a typedef name.  */
+  C_ID_TYPENAME,
+  /* An identifier declared as an Objective-C class name.  */
+  C_ID_CLASSNAME,
+  /* Not an identifier.  */
+  C_ID_NONE
+} c_id_kind;
+
+/* A single C token after string literal concatenation and conversion
+   of preprocessing tokens to tokens.  */
+typedef struct c_token GTY (())
+{
+  /* The kind of token.  */
+  ENUM_BITFIELD (cpp_ttype) type : 8;
+  /* If this token is a CPP_NAME, this value indicates whether also
+     declared as some kind of type.  Otherwise, it is C_ID_NONE.  */
+  ENUM_BITFIELD (c_id_kind) id_kind : 8;
+  /* If this token is a keyword, this value indicates which keyword.
+     Otherwise, this value is RID_MAX.  */
+  ENUM_BITFIELD (rid) keyword : 8;
+  /* True if this token is from a system header.  */
+  BOOL_BITFIELD in_system_header : 1;
+  /* The value associated with this token, if any.  */
+  tree value;
+  /* The location at which this token was found.  */
+  location_t location;
+} c_token;
+
+/* A lexer with up to two tokens of look-ahead; more are not needed
+   for C.  */
+typedef struct c_lexer GTY (())
+{
+  /* The look-ahead tokens.  */
+  c_token tokens[2];
+  /* How many look-ahead tokens are available (0, 1 or 2).  */
+  int tokens_avail;
+} c_lexer;
+
+/* Read in and lex a single token, storing it in *TOKEN.  */
+
+static void
+c_lex_one_token (c_token *token)
+{
+  timevar_push (TV_LEX);
+  token->type = c_lex (&token->value);
+  token->location = input_location;
+  token->in_system_header = in_system_header;
+  switch (token->type)
+    {
+    case CPP_NAME:
+      token->id_kind = C_ID_NONE;
+      token->keyword = RID_MAX;
+      {
+	tree decl;
+
+	int objc_force_identifier = objc_need_raw_identifier;
+	OBJC_NEED_RAW_IDENTIFIER (0);
+
+	if (C_IS_RESERVED_WORD (token->value))
+	  {
+	    enum rid rid_code = C_RID_CODE (token->value);
+
+	    if (c_dialect_objc ())
+	      {
+		/* Turn non-typedefed references to "id" into plain
+		   identifiers; this allows constructs like "void
+		   foo(id id);" to work.  */
+		if (rid_code == RID_ID)
+		  {
+		    decl = lookup_name (token->value);
+		    if (decl == NULL_TREE || TREE_CODE (decl) != TYPE_DECL)
+		      {
+			token->id_kind = C_ID_ID;
+			break;
+		      }
+		  }
+
+		if (!OBJC_IS_AT_KEYWORD (rid_code)
+		    && (!OBJC_IS_PQ_KEYWORD (rid_code) || objc_pq_context))
+		  {
+		    /* Return the canonical spelling for this keyword.  */
+		    token->value = ridpointers[(int) rid_code];
+		    token->type = CPP_KEYWORD;
+		    token->keyword = rid_code;
+		    break;
+		  }
+	      }
+	    else
+	      {
+		/* Return the canonical spelling for this keyword.  */
+		token->value = ridpointers[(int) rid_code];
+		token->type = CPP_KEYWORD;
+		token->keyword = rid_code;
+		break;
+	      }
+	  }
+
+	decl = lookup_name (token->value);
+	if (decl)
+	  {
+	    if (TREE_CODE (decl) == TYPE_DECL)
+	      {
+		token->id_kind = C_ID_TYPENAME;
+		break;
+	      }
+	  }
+	else if (c_dialect_objc ())
+	  {
+	    tree objc_interface_decl = objc_is_class_name (token->value);
+	    /* Objective-C class names are in the same namespace as
+	       variables and typedefs, and hence are shadowed by local
+	       declarations.  */
+	    if (objc_interface_decl
+		&& (global_bindings_p ()
+		    || (!objc_force_identifier && !decl)))
+	      {
+		token->value = objc_interface_decl;
+		token->id_kind = C_ID_CLASSNAME;
+		break;
+	      }
+	  }
+      }
+      token->id_kind = C_ID_ID;
+      break;
+    case CPP_AT_NAME:
+      /* This only happens in Objective-C; it must be a keyword.  */
+      token->type = CPP_KEYWORD;
+      token->id_kind = C_ID_NONE;
+      token->keyword = C_RID_CODE (token->value);
+      break;
+    case CPP_COLON:
+    case CPP_COMMA:
+    case CPP_CLOSE_PAREN:
+    case CPP_SEMICOLON:
+      /* These tokens may affect the interpretation of any identifiers
+	 following, if doing Objective-C.  */
+      OBJC_NEED_RAW_IDENTIFIER (0);
+      token->id_kind = C_ID_NONE;
+      token->keyword = RID_MAX;
+      break;
+    default:
+      token->id_kind = C_ID_NONE;
+      token->keyword = RID_MAX;
+      break;
+    }
+  timevar_pop (TV_LEX);
+}
+
+/* Allocate a new lexer.  */
+
+static c_lexer *
+c_lexer_new (void)
+{
+  c_lexer *ret;
+  ret = GGC_CNEW (c_lexer);
+  return ret;
+}
+
+/* Initialize LEXER by reading in the first token.  */
+
+static void
+c_lexer_init (c_lexer *lexer)
+{
+  gcc_assert (lexer->tokens_avail == 0);
+  c_lex_one_token (&lexer->tokens[0]);
+  lexer->tokens_avail = 1;
+}
+
+/* Return a pointer to the next token from LEXER, reading it in if
+   necessary.  */
+
+static inline c_token *
+c_lexer_peek_token (c_lexer *lexer)
+{
+  if (lexer->tokens_avail == 0)
+    {
+      c_lex_one_token (&lexer->tokens[0]);
+      lexer->tokens_avail = 1;
+    }
+  return &lexer->tokens[0];
+}
+
+/* Return true if the next token from LEXER has the indicated TYPE.  */
+
+static inline bool
+c_lexer_next_token_is (c_lexer *lexer, enum cpp_ttype type)
+{
+  return c_lexer_peek_token (lexer)->type == type;
+}
+
+/* Return true if the next token from LEXER does not have the
+   indicated TYPE.  */
+
+static inline bool
+c_lexer_next_token_is_not (c_lexer *lexer, enum cpp_ttype type)
+{
+  return !c_lexer_next_token_is (lexer, type);
+}
+
+/* Return true if the next token from LEXER is the indicated
+   KEYWORD.  */
+
+static inline bool
+c_lexer_next_token_is_keyword (c_lexer *lexer, enum rid keyword)
+{
+  c_token *token;
+
+  /* Peek at the next token.  */
+  token = c_lexer_peek_token (lexer);
+  /* Check to see if it is the indicated keyword.  */
+  return token->keyword == keyword;
+}
+
+/* Return true if TOKEN can start a type name,
+   false otherwise.  */
+static bool
+c_token_starts_typename (c_token *token)
+{
+  switch (token->type)
+    {
+    case CPP_NAME:
+      return (token->id_kind == C_ID_TYPENAME);
+    case CPP_KEYWORD:
+      switch (token->keyword)
+	{
+	case RID_UNSIGNED:
+	case RID_LONG:
+	case RID_SHORT:
+	case RID_SIGNED:
+	case RID_COMPLEX:
+	case RID_INT:
+	case RID_CHAR:
+	case RID_FLOAT:
+	case RID_DOUBLE:
+	case RID_VOID:
+	case RID_BOOL:
+	case RID_ENUM:
+	case RID_STRUCT:
+	case RID_UNION:
+	case RID_TYPEOF:
+	case RID_CONST:
+	case RID_VOLATILE:
+	case RID_RESTRICT:
+	case RID_ATTRIBUTE:
+	  return true;
+	default:
+	  return false;
+	}
+    default:
+      return false;
+    }
+}
+
+/* Return true if the next token from LEXER can start a type name,
+   false otherwise.  */
+static inline bool
+c_lexer_next_token_starts_typename (c_lexer *lexer)
+{
+  c_token *token = c_lexer_peek_token (lexer);
+  return c_token_starts_typename (token);
+}
+
+/* Return true if TOKEN can start declaration specifiers, false
+   otherwise.  */
+static bool
+c_token_starts_declspecs (c_token *token)
+{
+  switch (token->type)
+    {
+    case CPP_NAME:
+      return (token->id_kind == C_ID_TYPENAME);
+    case CPP_KEYWORD:
+      switch (token->keyword)
+	{
+	case RID_STATIC:
+	case RID_EXTERN:
+	case RID_REGISTER:
+	case RID_TYPEDEF:
+	case RID_INLINE:
+	case RID_AUTO:
+	case RID_THREAD:
+	case RID_UNSIGNED:
+	case RID_LONG:
+	case RID_SHORT:
+	case RID_SIGNED:
+	case RID_COMPLEX:
+	case RID_INT:
+	case RID_CHAR:
+	case RID_FLOAT:
+	case RID_DOUBLE:
+	case RID_VOID:
+	case RID_BOOL:
+	case RID_ENUM:
+	case RID_STRUCT:
+	case RID_UNION:
+	case RID_TYPEOF:
+	case RID_CONST:
+	case RID_VOLATILE:
+	case RID_RESTRICT:
+	case RID_ATTRIBUTE:
+	  return true;
+	default:
+	  return false;
+	}
+    default:
+      return false;
+    }
+}
+
+/* Return true if the next token from LEXER can start declaration
+   specifiers, false otherwise.  */
+static inline bool
+c_lexer_next_token_starts_declspecs (c_lexer *lexer)
+{
+  c_token *token = c_lexer_peek_token (lexer);
+  return c_token_starts_declspecs (token);
+}
+
+/* Return a pointer to the next-but-one token from LEXER, reading it
+   in if necessary.  The next token is already read in.  */
+
+static c_token *
+c_lexer_peek_2nd_token (c_lexer *lexer)
+{
+  if (lexer->tokens_avail >= 2)
+    return &lexer->tokens[1];
+  gcc_assert (lexer->tokens_avail == 1);
+  gcc_assert (lexer->tokens[0].type != CPP_EOF);
+  c_lex_one_token (&lexer->tokens[1]);
+  lexer->tokens_avail = 2;
+  return &lexer->tokens[1];
+}
+
+/* Consume the next token from LEXER.  */
+
+static void
+c_lexer_consume_token (c_lexer *lexer)
+{
+  if (lexer->tokens_avail == 2)
+    lexer->tokens[0] = lexer->tokens[1];
+  else
+    {
+      gcc_assert (lexer->tokens_avail == 1);
+      gcc_assert (lexer->tokens[0].type != CPP_EOF);
+    }
+  lexer->tokens_avail--;
+}
+
+/* Update the globals input_location and in_system_header from TOKEN.   */
+static inline void
+c_lexer_set_source_position_from_token (c_token *token)
+{
+  if (token->type != CPP_EOF)
+    {
+      input_location = token->location;
+      in_system_header = token->in_system_header;
+    }
+}
+\f
+/* A parser structure recording information about the state and
+   context of parsing.  */
+typedef struct c_parser GTY(())
+{
+  /* The lexer.  */
+  c_lexer *lexer;
+  /* True if a syntax error is being recovered from; false otherwise.
+     c_parser_error sets this flag.  It should clear this flag when
+     enough tokens have been consumed to recover from the error.  */
+  BOOL_BITFIELD error : 1;
+} c_parser;
+
+/* Allocate a new parser.  */
+
+static c_parser *
+c_parser_new (void)
+{
+  /* Use local storage to lex the first token because loading a PCH
+     file may cause garbage collection.  */
+  struct c_lexer tlexer;
+  c_parser *ret;
+  memset (&tlexer, 0, sizeof tlexer);
+  c_lexer_init (&tlexer);
+  ret = GGC_NEW (c_parser);
+  ret->lexer = c_lexer_new ();
+  memcpy (ret->lexer, &tlexer, sizeof tlexer);
+  ret->error = false;
+  return ret;
+}
+
+/* Issue a diagnostic of the form
+      FILE:LINE: MESSAGE before TOKEN
+   where TOKEN is the next token in the input stream of PARSER.
+   MESSAGE (specified by the caller) is usually of the form "expected
+   OTHER-TOKEN".
+
+   Do not issue a diagnostic if still recovering from an error.
+
+   ??? This is taken from the C++ parser, but building up messages in
+   this way is not i18n-friendly and some other approach should be
+   used.  */
+
+static void
+c_parser_error (c_parser *parser, const char *msgid)
+{
+  c_token *token = c_lexer_peek_token (parser->lexer);
+  if (parser->error)
+    return;
+  parser->error = true;
+  if (!msgid)
+    return;
+  /* This diagnostic makes more sense if it is tagged to the line of
+     the token we just peeked at.  */
+  c_lexer_set_source_position_from_token (token);
+  c_parse_error (msgid,
+		 /* Because c_parse_error does not understand
+		    CPP_KEYWORD, keywords are treated like
+		    identifiers.  */
+		 (token->type == CPP_KEYWORD ? CPP_NAME : token->type),
+		 token->value);
+}
+
+/* If the next token is of the indicated TYPE, consume it.  Otherwise,
+   issue the error MSGID.  If MSGID is NULL then a message has already
+   been produced and no message will be produced this time.  Returns
+   true if found, false otherwise.  */
+
+static bool
+c_parser_require (c_parser *parser,
+		  enum cpp_ttype type,
+		  const char *msgid)
+{
+  if (c_lexer_next_token_is (parser->lexer, type))
+    {
+      c_lexer_consume_token (parser->lexer);
+      return true;
+    }
+  else
+    {
+      c_parser_error (parser, msgid);
+      return false;
+    }
+}
+
+/* Like c_parser_require, except that tokens will be skipped until the
+   desired token is found.  An error message is still produced if the
+   next token is not as expected.  If MSGID is NULL then a message has
+   already been produced and no message will be produced this
+   time.  */
+
+static void
+c_parser_skip_until_found (c_parser *parser,
+			   enum cpp_ttype type,
+			   const char *msgid)
+{
+  unsigned nesting_depth = 0;
+
+  if (c_parser_require (parser, type, msgid))
+    return;
+
+  /* Skip tokens until the desired token is found.  */
+  while (true)
+    {
+      /* Peek at the next token.  */
+      c_token *token = c_lexer_peek_token (parser->lexer);
+      /* If we've reached the token we want, consume it and stop.  */
+      if (token->type == type && !nesting_depth)
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  return;
+	}
+      /* If we've run out of tokens, stop.  */
+      if (token->type == CPP_EOF)
+	return;
+      if (token->type == CPP_OPEN_BRACE
+	  || token->type == CPP_OPEN_PAREN
+	  || token->type == CPP_OPEN_SQUARE)
+	++nesting_depth;
+      else if (token->type == CPP_CLOSE_BRACE
+	       || token->type == CPP_CLOSE_PAREN
+	       || token->type == CPP_CLOSE_SQUARE)
+	{
+	  if (nesting_depth-- == 0)
+	    {
+	      parser->error = false;
+	      return;
+	    }
+	}
+      /* Consume this token.  */
+      c_lexer_consume_token (parser->lexer);
+      parser->error = false;
+    }
+}
+
+/* Skip tokens until we have consumed an entire block, or until we
+   have consumed a non-nested ';'.  */
+
+static void
+c_parser_skip_to_end_of_block_or_statement (c_parser *parser)
+{
+  unsigned nesting_depth = 0;
+
+  while (true)
+    {
+      c_token *token;
+
+      /* Peek at the next token.  */
+      token = c_lexer_peek_token (parser->lexer);
+      /* If we've run out of tokens, stop.  */
+      if (token->type == CPP_EOF)
+	break;
+      /* If the next token is a ';', we have reached the end of the
+	 statement.  */
+      if (token->type == CPP_SEMICOLON && !nesting_depth)
+	{
+	  /* Consume the ';'.  */
+	  c_lexer_consume_token (parser->lexer);
+	  break;
+	}
+      /* If the next token is a non-nested '}', then we have reached
+	 the end of the current block.  */
+      if (token->type == CPP_CLOSE_BRACE
+	  && (nesting_depth == 0 || --nesting_depth == 0))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  break;
+	}
+      /* If it the next token is a '{', then we are entering a new
+	 block.  Consume the entire block.  */
+      if (token->type == CPP_OPEN_BRACE)
+	++nesting_depth;
+      c_lexer_consume_token (parser->lexer);
+    }
+  parser->error = false;
+}
+
+/* If the next token is the indicated keyword, consume it.  Otherwise,
+   issue the error MSGID.  Returns true if found, false otherwise.  */
+
+static bool
+c_parser_require_keyword (c_parser *parser,
+			  enum rid keyword,
+			  const char *msgid)
+{
+  if (c_lexer_next_token_is_keyword (parser->lexer, keyword))
+    {
+      c_lexer_consume_token (parser->lexer);
+      return true;
+    }
+  else
+    {
+      c_parser_error (parser, msgid);
+      return false;
+    }
+}
+
+
+/* For __extension__, save/restore the warning flags which are
+   controlled by __extension__.  */
+#define SAVE_EXT_FLAGS(var)			\
+  do {						\
+    var = (pedantic				\
+	   | (warn_pointer_arith << 1)		\
+	   | (warn_traditional << 2)		\
+	   | (flag_iso << 3));			\
+     pedantic = 0;				\
+     warn_pointer_arith = 0;			\
+     warn_traditional = 0;			\
+     flag_iso = 0;				\
+  } while (0)
+
+#define RESTORE_EXT_FLAGS(val)			\
+  do {						\
+    pedantic = val & 1;				\
+    warn_pointer_arith = (val >> 1) & 1;	\
+    warn_traditional = (val >> 2) & 1;		\
+    flag_iso = (val >> 3) & 1;			\
+  } while (0)
+
+/* Possibly kinds of declarator to parse.  */
+typedef enum c_dtr_syn {
+  /* A normal declarator with an identifier.  */
+  C_DTR_NORMAL,
+  /* An abstract declarator (maybe empty).  */
+  C_DTR_ABSTRACT,
+  /* A parameter declarator: may be either, but after a type name does
+     not redeclare a typedef name as an identifier if it can
+     alternatively be interpreted as a typedef name; see DR#009,
+     applied in C90 TC1, omitted from C99 and reapplied in C99 TC2
+     following DR#249.  For example, given a typedef T, "int T" and
+     "int *T" are valid parameter declarations redeclaring T, while
+     "int (T)" and "int * (T)" and "int (T[])" and "int (T (int))" are
+     abstract declarators rather than involving redundant parentheses;
+     the same applies with attributes inside the parentheses before
+     "T".  */
+  C_DTR_PARM
+} c_dtr_syn;
+
+static void c_parser_external_declaration (c_parser *);
+static void c_parser_asm_definition (c_parser *);
+static void c_parser_declaration_or_fndef (c_parser *, bool, bool, bool, bool);
+static void c_parser_declspecs (c_parser *, struct c_declspecs *, bool, bool,
+				bool);
+static struct c_typespec c_parser_enum_specifier (c_parser *);
+static struct c_typespec c_parser_struct_or_union_specifier (c_parser *);
+static tree c_parser_struct_declaration (c_parser *);
+static struct c_typespec c_parser_typeof_specifier (c_parser *);
+static struct c_declarator *c_parser_declarator (c_parser *, bool, c_dtr_syn,
+						 bool *);
+static struct c_declarator *c_parser_direct_declarator (c_parser *, bool,
+							c_dtr_syn, bool *);
+static struct c_declarator *c_parser_direct_declarator_inner (c_parser *,
+							      bool,
+							      struct c_declarator *);
+static struct c_arg_info *c_parser_parms_declarator (c_parser *, bool, tree);
+static struct c_arg_info *c_parser_parms_list_declarator (c_parser *, tree);
+static tree c_parser_simple_asm_expr (c_parser *);
+static tree c_parser_attributes (c_parser *);
+static struct c_type_name *c_parser_type_name (c_parser *);
+static struct c_expr c_parser_initializer (c_parser *);
+static struct c_expr c_parser_braced_init (c_parser *, tree, bool);
+static void c_parser_initelt (c_parser *);
+static void c_parser_initval (c_parser *);
+static tree c_parser_compound_statement (c_parser *);
+static void c_parser_compound_statement_nostart (c_parser *);
+static void c_parser_label (c_parser *);
+static void c_parser_statement (c_parser *);
+static void c_parser_statement_after_labels (c_parser *);
+static void c_parser_if_statement (c_parser *);
+static void c_parser_switch_statement (c_parser *);
+static void c_parser_while_statement (c_parser *);
+static void c_parser_do_statement (c_parser *);
+static void c_parser_for_statement (c_parser *);
+static tree c_parser_asm_statement (c_parser *);
+static tree c_parser_asm_operands (c_parser *);
+static tree c_parser_asm_clobbers (c_parser *);
+static struct c_expr c_parser_expr_no_commas (c_parser *);
+static struct c_expr c_parser_conditional_expression (c_parser *);
+static struct c_expr c_parser_binary_expression (c_parser *);
+static struct c_expr c_parser_cast_expression (c_parser *);
+static struct c_expr c_parser_unary_expression (c_parser *);
+static struct c_expr c_parser_sizeof_expression (c_parser *);
+static struct c_expr c_parser_alignof_expression (c_parser *);
+static struct c_expr c_parser_postfix_expression (c_parser *);
+static struct c_expr c_parser_postfix_expression_after_paren_type (c_parser *,
+								   struct c_type_name *);
+static struct c_expr c_parser_postfix_expression_after_primary (c_parser *,
+								struct c_expr);
+static struct c_expr c_parser_expression (c_parser *);
+static tree c_parser_expr_list (c_parser *);
+
+/* Parse a translation unit (C90 6.7, C99 6.9).
+
+   translation-unit:
+     external-declarations
+
+   external-declarations:
+     external-declaration
+     external-declarations external-declaration
+
+   GNU extensions:
+
+   translation-unit:
+     empty
+*/
+
+static void
+c_parser_translation_unit (c_parser *parser)
+{
+  if (c_lexer_next_token_is (parser->lexer, CPP_EOF))
+    {
+      if (pedantic)
+	pedwarn ("ISO C forbids an empty source file");
+    }
+  else
+    {
+      while (c_lexer_next_token_is_not (parser->lexer, CPP_EOF))
+	{
+	  void *obstack_position = obstack_alloc (&parser_obstack, 0);
+	  ggc_collect ();
+	  c_parser_external_declaration (parser);
+	  obstack_free (&parser_obstack, obstack_position);
+	}
+    }
+}
+
+/* Parse an external declaration (C90 6.7, C99 6.9).
+
+   external-declaration:
+     function-definition
+     declaration
+
+   GNU extensions:
+
+   external-declaration:
+     asm-definition
+     ;
+     __extension__ external-declaration
+
+   TODO: Objective-C.
+*/
+
+static void
+c_parser_external_declaration (c_parser *parser)
+{
+  if (c_lexer_next_token_is_keyword (parser->lexer, RID_EXTENSION))
+    {
+      int ext;
+      SAVE_EXT_FLAGS (ext);
+      c_lexer_consume_token (parser->lexer);
+      c_parser_external_declaration (parser);
+      RESTORE_EXT_FLAGS (ext);
+    }
+  else if (c_lexer_next_token_is_keyword (parser->lexer, RID_ASM))
+    {
+      c_parser_asm_definition (parser);
+    }
+  else if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+    {
+      if (pedantic)
+	pedwarn ("ISO C does not allow extra %<;%> outside of a function");
+      c_lexer_consume_token (parser->lexer);
+    }
+  else
+    {
+      /* A declaration or a function definition.  We can only tell
+	 which after parsing the declaration specifiers, if any, and
+	 the first declarator.  */
+      c_parser_declaration_or_fndef (parser, true, true, false, true);
+    }
+}
+
+/* Parse a declaration or function definition (C90 6.5, 6.7.1, C99
+   6.7, 6.9.1).  If FNDEF_OK is true, a function definition is
+   accepted; otherwise (old-style parameter declarations) only other
+   declarations are accepted.  If NESTED is true, we are inside a
+   function or parsing old-style parameter declarations; any functions
+   encountered are nested functions and declaration specifiers are
+   required; otherwise we are at top level and functions are normal
+   functions and declaration specifiers may be optional.  If EMPTY_OK
+   is true, empty declarations are OK (subject to all other
+   constraints); otherwise (old-style parameter declarations) they are
+   diagnosed.  If START_ATTR_OK is true, the declaration specifiers
+   may start with attributes; otherwise they may not.
+
+   declaration:
+     declaration-specifiers init-declarator-list[opt] ;
+
+   function-definition:
+     declaration-specifiers[opt] declarator declaration-list[opt]
+       compound-statement
+
+   declaration-list:
+     declaration
+     declaration-list declaration
+
+   init-declarator-list:
+     init-declarator
+     init-declarator-list , init-declarator
+
+   init-declarator:
+     declarator simple-asm-expr[opt] attributes[opt]
+     declarator simple-asm-expr[opt] attributes[opt] = initializer
+
+   GNU extensions:
+
+   nested-function-definition:
+     declaration-specifiers declarator declaration-list[opt]
+       compound-statement
+
+   The simple-asm-expr and attributes are GNU extensions.
+
+   This function does not handle __extension__; that is handled in its
+   callers.  ??? Following the old parser, __extension__ may start
+   external declarations, declarations in functions and declarations
+   at the start of "for" loops, but not old-style parameter
+   declarations.
+
+   C99 requires declaration specifiers in a function definition; the
+   absence is diagnosed through the diagnosis of implicit int.  In GNU
+   C we also allow but diagnose declarations without declaration
+   specifiers, but only at top level (elsewhere they conflict with
+   other syntax).  */
+
+static void
+c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, bool empty_ok,
+			       bool nested, bool start_attr_ok)
+{
+  struct c_declspecs *specs;
+  tree prefix_attrs;
+  tree all_prefix_attrs;
+  bool diagnosed_no_specs = false;
+  specs = build_null_declspecs ();
+  c_parser_declspecs (parser, specs, true, true, start_attr_ok);
+  if (parser->error)
+    {
+      c_parser_skip_to_end_of_block_or_statement (parser);
+      return;
+    }
+  if (nested && !specs->declspecs_seen_p)
+    {
+      c_parser_error (parser, "expected declaration specifiers");
+      c_parser_skip_to_end_of_block_or_statement (parser);
+      return;
+    }
+  finish_declspecs (specs);
+  if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+    {
+      if (empty_ok)
+	shadow_tag (specs);
+      else
+	{
+	  shadow_tag_warned (specs, 1);
+	  pedwarn ("empty declaration");
+	}
+      c_lexer_consume_token (parser->lexer);
+      return;
+    }
+  pending_xref_error ();
+  prefix_attrs = specs->attrs;
+  all_prefix_attrs = prefix_attrs;
+  specs->attrs = NULL_TREE;
+  while (true)
+    {
+      struct c_declarator *declarator;
+      bool dummy = false;
+      tree fnbody;
+      /* Declaring either one or more declarators (in which case we
+	 should diagnose if there were no declaration specifiers) or a
+	 function definition (in which case the diagnostic for
+	 implicit int suffices).  */
+      declarator = c_parser_declarator (parser, specs->type_seen_p,
+					C_DTR_NORMAL, &dummy);
+      if (declarator == NULL)
+	{
+	  c_parser_skip_to_end_of_block_or_statement (parser);
+	  return;
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_EQ)
+	  || c_lexer_next_token_is (parser->lexer, CPP_COMMA)
+	  || c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)
+	  || c_lexer_next_token_is_keyword (parser->lexer, RID_ASM)
+	  || c_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))
+	{
+	  tree asm_name = NULL_TREE;
+	  tree postfix_attrs = NULL_TREE;
+	  if (!diagnosed_no_specs && !specs->declspecs_seen_p)
+	    {
+	      diagnosed_no_specs = true;
+	      /* ??? This is what the old parser did but is not a proper
+		 use of pedantic.  */
+	      if (pedantic)
+		error ("ISO C forbids data definition with no type "
+		       "or storage class");
+	      else
+		warning ("data definition has no type or storage class");
+	    }
+	  /* Having seen a data definition, there cannot now be a
+	     function definition.  */
+	  fndef_ok = false;
+	  if (c_lexer_next_token_is_keyword (parser->lexer, RID_ASM))
+	    asm_name = c_parser_simple_asm_expr (parser);
+	  if (c_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))
+	    postfix_attrs = c_parser_attributes (parser);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_EQ))
+	    {
+	      tree d;
+	      struct c_expr init;
+	      c_lexer_consume_token (parser->lexer);
+	      /* The declaration of the variable is in effect while
+		 its initializer is parsed.  */
+	      d = start_decl (declarator, specs, true,
+			      chainon (postfix_attrs, all_prefix_attrs));
+	      start_init (d, asm_name, global_bindings_p ());
+	      init = c_parser_initializer (parser);
+	      finish_init ();
+	      maybe_warn_string_init (TREE_TYPE (d), init);
+	      finish_decl (d, init.value, asm_name);
+	    }
+	  else
+	    {
+	      tree d = start_decl (declarator, specs, false,
+				   chainon (postfix_attrs,
+					    all_prefix_attrs));
+	      finish_decl (d, NULL_TREE, asm_name);
+	    }
+	  if (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      if (c_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))
+		all_prefix_attrs = chainon (c_parser_attributes (parser),
+					    prefix_attrs);
+	      else
+		all_prefix_attrs = prefix_attrs;
+	      continue;
+	    }
+	  else if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      return;
+	    }
+	  else
+	    {
+	      c_parser_error (parser, "expected ',' or ';'");
+	      c_parser_skip_to_end_of_block_or_statement (parser);
+	      return;
+	    }
+	}
+      else if (!fndef_ok)
+	{
+	  c_parser_error (parser,
+			  "expected '=', ',', ';', 'asm' or '__attribute__'");
+	  c_parser_skip_to_end_of_block_or_statement (parser);
+	  return;
+	}
+      /* Function definition (nested or otherwise).  */
+      if (nested)
+	{
+	  if (pedantic)
+	    pedwarn ("ISO C forbids nested functions");
+	  push_function_context ();
+	}
+      if (!start_function (specs, declarator, all_prefix_attrs))
+	{
+	  /* This can appear in many cases looking nothing like a
+	     function definition, so we don't give a more specific
+	     error suggesting there was one.  */
+	  c_parser_error (parser,
+			  "expected '=', ',', ';', 'asm' or '__attribute__'");
+	  if (nested)
+	    pop_function_context ();
+	  break;
+	}
+      /* Parse old-style parameter declarations.  ??? Attributes are
+	 not allowed to start declaration specifiers here because of a
+	 syntax conflict between a function declaration with attribute
+	 suffix and a function definition with an attribute prefix on
+	 first old-style parameter declaration.  Following the old
+	 parser, they are not accepted on subsequent old-style
+	 parameter declarations either.  However, there is no
+	 ambiguity after the first declaration, nor indeed on the
+	 first as long as we don't allow postfix attributes after a
+	 declarator with a nonempty identifier list in a definition;
+	 and postfix attributes have never been accepted here in
+	 function definitions either.  */
+      while (c_lexer_next_token_is_not (parser->lexer, CPP_EOF)
+	     && c_lexer_next_token_is_not (parser->lexer, CPP_OPEN_BRACE))
+	c_parser_declaration_or_fndef (parser, false, false, true, false);
+      DECL_SOURCE_LOCATION (current_function_decl)
+	= c_lexer_peek_token (parser->lexer)->location;
+      store_parm_decls ();
+      fnbody = c_parser_compound_statement (parser);
+      if (nested)
+	{
+	  tree decl = current_function_decl;
+	  add_stmt (fnbody);
+	  finish_function ();
+	  pop_function_context ();
+	  add_stmt (build_stmt (DECL_EXPR, decl));
+	}
+      else
+	{
+	  add_stmt (fnbody);
+	  finish_function ();
+	}
+      break;
+    }
+}
+
+/* Parse an asm-definition (asm() outside a function body).  This is a
+   GNU extension.
+
+   asm-definition:
+     simple-asm-expr ;
+*/
+
+static void
+c_parser_asm_definition (c_parser *parser)
+{
+  tree asm_str = c_parser_simple_asm_expr (parser);
+  if (asm_str)
+    assemble_asm (asm_str);
+  c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected ';'");
+}
+
+/* Parse some declaration specifiers (possibly none) (C90 6.5, C99
+   6.7), adding them to SPECS (which may already include some).
+   Storage class specifiers are accepted iff SCSPEC_OK; type
+   specifiers are accepted iff TYPESPEC_OK; attributes are accepted at
+   the start iff START_ATTR_OK.
+
+   declaration-specifiers:
+     storage-class-specifier declaration-specifiers[opt]
+     type-specifier declaration-specifiers[opt]
+     type-qualifier declaration-specifiers[opt]
+     function-specifier declaration-specifiers[opt]
+
+   Function specifiers (inline) are from C99, and are currently
+   handled as storage class specifiers, as is __thread.
+
+   C90 6.5.1, C99 6.7.1:
+   storage-class-specifier:
+     typedef
+     extern
+     static
+     auto
+     register
+
+   C99 6.7.4:
+   function-specifier:
+     inline
+
+   C90 6.5.2, C99 6.7.2:
+   type-specifier:
+     void
+     char
+     short
+     int
+     long
+     float
+     double
+     signed
+     unsigned
+     _Bool
+     _Complex
+     [_Imaginary removed in C99 TC2]
+     struct-or-union-specifier
+     enum-specifier
+     typedef-name
+
+   (_Bool and _Complex are new in C99.)
+
+   C90 6.5.3, C99 6.7.3:
+
+   type-qualifier:
+     const
+     restrict
+     volatile
+
+   (restrict is new in C99.)
+
+   GNU extensions:
+
+   declaration-specifiers:
+     attributes declaration-specifiers[opt]
+
+   storage-class-specifier:
+     __thread
+
+   type-specifier:
+     typeof-specifier
+
+   TODO: Objective-C.
+*/
+
+static void
+c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
+		    bool scspec_ok, bool typespec_ok, bool start_attr_ok)
+{
+  bool attrs_ok = start_attr_ok;
+  bool seen_type = specs->type_seen_p;
+  while (c_lexer_next_token_is (parser->lexer, CPP_NAME)
+	 || c_lexer_next_token_is (parser->lexer, CPP_KEYWORD))
+    {
+      struct c_typespec t;
+      tree attrs;
+      if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+	{
+	  /* This finishes the specifiers unless a type name is OK, it
+	     is declared as a type name and a type name hasn't yet
+	     been seen.  */
+	  if (!typespec_ok || seen_type
+	      || c_lexer_peek_token (parser->lexer)->id_kind != C_ID_TYPENAME)
+	    break;
+	  seen_type = true;
+	  attrs_ok = true;
+	  t.kind = ctsk_typedef;
+	  /* For a typedef name, record the meaning, not the name.
+	     In case of 'foo foo, bar;'.  */
+	  t.spec = lookup_name (c_lexer_peek_token (parser->lexer)->value);
+	  declspecs_add_type (specs, t);
+	  c_lexer_consume_token (parser->lexer);
+	  continue;
+	}
+      switch (c_lexer_peek_token (parser->lexer)->keyword)
+	{
+	case RID_STATIC:
+	case RID_EXTERN:
+	case RID_REGISTER:
+	case RID_TYPEDEF:
+	case RID_INLINE:
+	case RID_AUTO:
+	case RID_THREAD:
+	  if (!scspec_ok)
+	    goto out;
+	  attrs_ok = true;
+	  /* TODO: Distinguish between function specifiers (inline)
+	     and storage class specifiers, either here or in
+	     declspecs_add_scspec.  */
+	  declspecs_add_scspec (specs,
+				c_lexer_peek_token (parser->lexer)->value);
+	  c_lexer_consume_token (parser->lexer);
+	  break;
+	case RID_UNSIGNED:
+	case RID_LONG:
+	case RID_SHORT:
+	case RID_SIGNED:
+	case RID_COMPLEX:
+	case RID_INT:
+	case RID_CHAR:
+	case RID_FLOAT:
+	case RID_DOUBLE:
+	case RID_VOID:
+	case RID_BOOL:
+	  if (!typespec_ok)
+	    goto out;
+	  attrs_ok = true;
+	  seen_type = true;
+	  OBJC_NEED_RAW_IDENTIFIER (1);
+	  t.kind = ctsk_resword;
+	  t.spec = c_lexer_peek_token (parser->lexer)->value;
+	  declspecs_add_type (specs, t);
+	  c_lexer_consume_token (parser->lexer);
+	  break;
+	case RID_ENUM:
+	  if (!typespec_ok)
+	    goto out;
+	  attrs_ok = true;
+	  seen_type = true;
+	  t = c_parser_enum_specifier (parser);
+	  declspecs_add_type (specs, t);
+	  break;
+	case RID_STRUCT:
+	case RID_UNION:
+	  if (!typespec_ok)
+	    goto out;
+	  attrs_ok = true;
+	  seen_type = true;
+	  t = c_parser_struct_or_union_specifier (parser);
+	  declspecs_add_type (specs, t);
+	  break;
+	case RID_TYPEOF:
+	  /* ??? The old parser rejected typeof after other type
+	     specifiers, but is a syntax error the best way of
+	     handling this?  */
+	  if (!typespec_ok || seen_type)
+	    goto out;
+	  attrs_ok = true;
+	  seen_type = true;
+	  t = c_parser_typeof_specifier (parser);
+	  declspecs_add_type (specs, t);
+	  break;
+	case RID_CONST:
+	case RID_VOLATILE:
+	case RID_RESTRICT:
+	  attrs_ok = true;
+	  declspecs_add_qual (specs,
+			      c_lexer_peek_token (parser->lexer)->value);
+	  c_lexer_consume_token (parser->lexer);
+	  break;
+	case RID_ATTRIBUTE:
+	  if (!attrs_ok)
+	    goto out;
+	  attrs = c_parser_attributes (parser);
+	  declspecs_add_attrs (specs, attrs);
+	  break;
+	default:
+	  goto out;
+	}
+    }
+ out: ;
+}
+
+/* Parse an enum specifier (C90 6.5.2.2, C99 6.7.2.2).
+
+   enum-specifier:
+     enum attributes[opt] identifier[opt] { enumerator-list } attributes[opt]
+     enum attributes[opt] identifier[opt] { enumerator-list , } attributes[opt]
+     enum attributes[opt] identifier
+
+   The form with trailing comma is new in C99.  The forms with
+   attributes are GNU extensions.  In GNU C, we accept any expression
+   without commas in the syntax (assignment expressions, not just
+   conditional expressions); assignment expressions will be diagnosed
+   as non-constant.
+
+   enumerator-list:
+     enumerator
+     enumerator-list , enumerator
+
+   enumerator:
+     enumeration-constant
+     enumeration-constant = constant-expression
+*/
+
+static struct c_typespec
+c_parser_enum_specifier (c_parser *parser)
+{
+  struct c_typespec ret;
+  tree attrs;
+  tree ident = NULL_TREE;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_ENUM));
+  c_lexer_consume_token (parser->lexer);
+  attrs = c_parser_attributes (parser);
+  if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+    {
+      ident = c_lexer_peek_token (parser->lexer)->value;
+      c_lexer_consume_token (parser->lexer);
+    }
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+    {
+      /* Parse an enum definition.  */
+      tree type = start_enum (ident);
+      tree postfix_attrs;
+      /* We chain the enumerators in reverse order, then put them in
+	 forward order at the end.  */
+      tree values = NULL_TREE;
+      c_lexer_consume_token (parser->lexer);
+      while (true)
+	{
+	  tree enum_id;
+	  tree enum_value;
+	  tree enum_decl;
+	  bool seen_comma;
+	  if (c_lexer_next_token_is_not (parser->lexer, CPP_NAME))
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL);
+	      values = error_mark_node;
+	      break;
+	    }
+	  enum_id = c_lexer_peek_token (parser->lexer)->value;
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_EQ))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      enum_value = c_parser_expr_no_commas (parser).value;
+	    }
+	  else
+	    enum_value = NULL_TREE;
+	  enum_decl = build_enumerator (enum_id, enum_value);
+	  TREE_CHAIN (enum_decl) = values;
+	  values = enum_decl;
+	  seen_comma = false;
+	  if (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	    {
+	      seen_comma = true;
+	      c_lexer_consume_token (parser->lexer);
+	    }
+	  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE))
+	    {
+	      if (seen_comma && pedantic && !flag_isoc99)
+		pedwarn ("comma at end of enumerator list");
+	      c_lexer_consume_token (parser->lexer);
+	      break;
+	    }
+	  if (!seen_comma)
+	    {
+	      c_parser_error (parser, "expected ',' or '}'");
+	      c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL);
+	      values = error_mark_node;
+	      break;
+	    }
+	}
+      postfix_attrs = c_parser_attributes (parser);
+      ret.spec = finish_enum (type, nreverse (values),
+			      chainon (attrs, postfix_attrs));
+      ret.kind = ctsk_tagdef;
+      return ret;
+    }
+  else if (!ident)
+    {
+      c_parser_error (parser, "expected '{'");
+      ret.spec = error_mark_node;
+      ret.kind = ctsk_tagref;
+      return ret;
+    }
+  ret = parser_xref_tag (ENUMERAL_TYPE, ident);
+  /* In ISO C, enumerated types can be referred to only if already
+     defined.  */
+  if (pedantic && !COMPLETE_TYPE_P (ret.spec))
+    pedwarn ("ISO C forbids forward references to %<enum%> types");
+  return ret;
+}
+
+/* Parse a struct or union specifier (C90 6.5.2.1, C99 6.7.2.1).
+
+   struct-or-union-specifier:
+     struct-or-union attributes[opt] identifier[opt]
+       { struct-declaration-list } attributes[opt]
+     struct-or-union attributes[opt] identifier
+
+   struct-declaration-list:
+     struct-declaration
+     struct-declaration-list struct-declaration
+
+   TODO: Objective-C.
+
+   GNU extensions: the semicolon at the end may be omitted; extra
+   semicolons may be included between, before or after
+   struct-declarations.  */
+
+static struct c_typespec
+c_parser_struct_or_union_specifier (c_parser *parser)
+{
+  struct c_typespec ret;
+  tree attrs;
+  tree ident = NULL_TREE;
+  enum tree_code code;
+  switch (c_lexer_peek_token (parser->lexer)->keyword)
+    {
+    case RID_STRUCT:
+      code = RECORD_TYPE;
+      break;
+    case RID_UNION:
+      code = UNION_TYPE;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+  c_lexer_consume_token (parser->lexer);
+  attrs = c_parser_attributes (parser);
+  if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+    {
+      ident = c_lexer_peek_token (parser->lexer)->value;
+      c_lexer_consume_token (parser->lexer);
+    }
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+    {
+      /* Parse a struct or union definition.  Start the scope of the
+	 tag before parsing components.  */
+      tree type = start_struct (code, ident);
+      tree postfix_attrs;
+      /* We chain the components in reverse order, then put them in
+	 forward order at the end.  Each struct-declaration may
+	 declare multiple components (comma-separated), so we must use
+	 chainon to join them, although when parsing each
+	 struct-declaration we can use TREE_CHAIN directly.
+
+	 The theory behind all this is that there will be more
+	 semicolon separated fields than comma separated fields, and
+	 so we'll be minimizing the number of node traversals required
+	 by chainon.  */
+      tree contents = NULL_TREE;
+      c_lexer_consume_token (parser->lexer);
+      /* Parse the struct-declarations and semicolons.  Problems with
+	 semicolons are diagnosed here; empty structures are diagnosed
+	 elsewhere.  */
+      while (true)
+	{
+	  tree decls;
+	  /* Parse any stray semicolon.  */
+	  if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	    {
+	      if (pedantic)
+		pedwarn ("extra semicolon in struct or union specified");
+	      c_lexer_consume_token (parser->lexer);
+	      continue;
+	    }
+	  /* Stop if at the end of the struct or union contents.  */
+	  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      break;
+	    }
+	  /* Parse some comma-separated declarations, but not the
+	     trailing semicolon if any.  */
+	  decls = c_parser_struct_declaration (parser);
+	  contents = chainon (decls, contents);
+	  /* If no semicolon follows, either we have a parse error or
+	     are at the end of the struct or union and should
+	     pedwarn.  */
+	  if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	    c_lexer_consume_token (parser->lexer);
+	  else
+	    {
+	      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE))
+		pedwarn ("no semicolon at end of struct or union");
+	      else
+		{
+		  c_parser_error (parser, "expected ';'");
+		  c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL);
+		  break;
+		}
+	    }
+	}
+      postfix_attrs = c_parser_attributes (parser);
+      ret.spec = finish_struct (type, nreverse (contents),
+				chainon (attrs, postfix_attrs));
+      ret.kind = ctsk_tagdef;
+      return ret;
+    }
+  else if (!ident)
+    {
+      c_parser_error (parser, "expected '{'");
+      ret.spec = error_mark_node;
+      ret.kind = ctsk_tagref;
+    }
+  ret = parser_xref_tag (code, ident);
+  return ret;
+}
+
+/* Parse a struct-declaration (C90 6.5.2.1, C99 6.7.2.1), *without*
+   the trailing semicolon.
+
+   struct-declaration:
+     specifier-qualifier-list struct-declarator-list
+
+   specifier-qualifier-list:
+     type-specifier specifier-qualifier-list[opt]
+     type-qualifier specifier-qualifier-list[opt]
+     attributes specifier-qualifier-list[opt]
+
+   struct-declarator-list:
+     struct-declarator
+     struct-declarator-list , attributes[opt] struct-declarator
+
+   struct-declarator:
+     declarator attributes[opt]
+     declarator[opt] : constant-expression attributes[opt]
+
+   GNU extensions: semicolons are handled elsewhere; attributes may be
+   used where shown; a struct-declarator-list may be empty;
+   __extension__ may be used at the start of a struct-declaration.  In
+   GNU C, we accept any expression without commas in the syntax
+   (assignment expressions, not just conditional expressions);
+   assignment expressions will be diagnosed as non-constant.  */
+
+static tree
+c_parser_struct_declaration (c_parser *parser)
+{
+  struct c_declspecs *specs;
+  tree prefix_attrs;
+  tree all_prefix_attrs;
+  tree decls;
+  if (c_lexer_next_token_is_keyword (parser->lexer, RID_EXTENSION))
+    {
+      int ext;
+      tree decl;
+      SAVE_EXT_FLAGS (ext);
+      c_lexer_consume_token (parser->lexer);
+      decl = c_parser_struct_declaration (parser);
+      RESTORE_EXT_FLAGS (ext);
+      return decl;
+    }
+  specs = build_null_declspecs ();
+  c_parser_declspecs (parser, specs, false, true, true);
+  if (parser->error)
+    return error_mark_node;
+  if (!specs->declspecs_seen_p)
+    {
+      c_parser_error (parser, "expected specifier-qualifier-list");
+      return NULL_TREE;
+    }
+  finish_declspecs (specs);
+  if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+    {
+      tree ret;
+      if (!specs->type_seen_p)
+	{
+	  if (pedantic)
+	    pedwarn ("ISO C forbids member declarations with no members");
+	  shadow_tag_warned (specs, pedantic);
+	  ret = NULL_TREE;
+	}
+      else
+	{
+	  /* Support for unnamed structs or unions as members of
+	     structs or unions (which is [a] useful and [b] supports
+	     MS P-SDK).  */
+	  ret = grokfield (build_id_declarator (NULL_TREE), specs, NULL_TREE);
+	}
+      return ret;
+    }
+  pending_xref_error ();
+  prefix_attrs = specs->attrs;
+  all_prefix_attrs = prefix_attrs;
+  specs->attrs = NULL_TREE;
+  decls = NULL_TREE;
+  while (true)
+    {
+      /* Declaring one or more declarators or un-named bit-fields.  */
+      struct c_declarator *declarator;
+      bool dummy = false;
+      if (c_lexer_next_token_is (parser->lexer, CPP_COLON))
+	declarator = build_id_declarator (NULL_TREE);
+      else
+	declarator = c_parser_declarator (parser, specs->type_seen_p,
+					  C_DTR_NORMAL, &dummy);
+      if (declarator == NULL)
+	{
+	  c_parser_skip_to_end_of_block_or_statement (parser);
+	  break;
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_COLON)
+	  || c_lexer_next_token_is (parser->lexer, CPP_COMMA)
+	  || c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON)
+	  || c_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))
+	{
+	  tree postfix_attrs = NULL_TREE;
+	  tree width = NULL_TREE;
+	  tree d;
+	  if (c_lexer_next_token_is (parser->lexer, CPP_COLON))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      width = c_parser_expr_no_commas (parser).value;
+	    }
+	  if (c_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))
+	    postfix_attrs = c_parser_attributes (parser);
+	  d = grokfield (declarator, specs, width);
+	  decl_attributes (&d, chainon (postfix_attrs,
+					all_prefix_attrs), 0);
+	  TREE_CHAIN (d) = decls;
+	  decls = d;
+	  if (c_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))
+	    all_prefix_attrs = chainon (c_parser_attributes (parser),
+					prefix_attrs);
+	  else
+	    all_prefix_attrs = prefix_attrs;
+	  if (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	    c_lexer_consume_token (parser->lexer);
+	  else if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	    {
+	      /* Semicolon consumed in caller.  */
+	      break;
+	    }
+	  else
+	    {
+	      c_parser_error (parser, "expected ',' or ';'");
+	      break;
+	    }
+	}
+      else
+	{
+	  c_parser_error (parser,
+			  "expected ':', ',', ';' or '__attribute__'");
+	  break;
+	}
+    }
+  return decls;
+}
+
+/* Parse a typeof specifier (a GNU extension).
+
+   typeof-specifier:
+     typeof ( expression )
+     typeof ( type-name )
+*/
+
+static struct c_typespec
+c_parser_typeof_specifier (c_parser *parser)
+{
+  struct c_typespec ret;
+  ret.kind = ctsk_typeof;
+  ret.spec = error_mark_node;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_TYPEOF));
+  c_lexer_consume_token (parser->lexer);
+  skip_evaluation++;
+  in_typeof++;
+  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    {
+      skip_evaluation--;
+      in_typeof--;
+      return ret;
+    }
+  if (c_lexer_next_token_starts_typename (parser->lexer))
+    {
+      struct c_type_name *type = c_parser_type_name (parser);
+      skip_evaluation--;
+      in_typeof--;
+      if (type != NULL)
+	{
+	  ret.spec = groktypename (type);
+	  pop_maybe_used (variably_modified_type_p (ret.spec, NULL_TREE));
+	}
+    }
+  else
+    {
+      struct c_expr expr = c_parser_expression (parser);
+      skip_evaluation--;
+      in_typeof--;
+      if (TREE_CODE (expr.value) == COMPONENT_REF
+	  && DECL_C_BIT_FIELD (TREE_OPERAND (expr.value, 1)))
+	error ("%<typeof%> applied to a bit-field");
+      ret.spec = TREE_TYPE (expr.value);
+      pop_maybe_used (variably_modified_type_p (ret.spec, NULL_TREE));
+    }
+  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+  return ret;
+}
+
+/* Parse a declarator, possibly an abstract declarator (C90 6.5.4,
+   6.5.5, C99 6.7.5, 6.7.6).  If TYPE_SEEN_P then a typedef name may
+   be redeclared; otherwise it may not.  KIND indicates which kind of
+   declarator is wanted.  Returns a valid declarator except in the
+   case of a syntax error in which case NULL is returned.  *SEEN_ID is
+   set to true if an identifier being declared is seen; this is used
+   to diagnose bad forms of abstract array declarators and to
+   determine whether an identifier list is syntactically permitted.
+
+   declarator:
+     pointer[opt] direct-declarator
+
+   direct-declarator:
+     identifier
+     ( attributes[opt] declarator )
+     direct-declarator array-declarator
+     direct-declarator ( parameter-type-list )
+     direct-declarator ( identifier-list[opt] )
+
+   pointer:
+     * type-qualifier-list[opt]
+     * type-qualifier-list[opt] pointer
+
+   type-qualifier-list:
+     type-qualifier
+     attributes
+     type-qualifier-list type-qualifier
+     type-qualifier-list attributes
+
+   parameter-type-list:
+     parameter-list
+     parameter-list , ...
+
+   parameter-list:
+     parameter-declaration
+     parameter-list , parameter-declaration
+
+   parameter-declaration:
+     declaration-specifiers declarator attributes[opt]
+     declaration-specifiers abstract-declarator[opt] attributes[opt]
+
+   identifier-list:
+     identifier
+     identifier-list , identifier
+
+   abstract-declarator:
+     pointer
+     pointer[opt] direct-abstract-declarator
+
+   direct-abstract-declarator:
+     ( attributes[opt] abstract-declarator )
+     direct-abstract-declarator[opt] array-declarator
+     direct-abstract-declarator[opt] ( parameter-type-list[opt] )
+
+   GNU extensions:
+
+   direct-declarator:
+     direct-declarator ( parameter-forward-declarations
+			 parameter-type-list[opt] )
+
+   direct-abstract-declarator:
+     direct-abstract-declarator[opt] ( parameter-forward-declarations 
+				       parameter-type-list[opt] )
+
+   parameter-forward-declarations:
+     parameter-list ;
+     parameter-forward-declarations parameter-list ;
+
+   The uses of attributes shown above are GNU extensions.
+
+   Some forms of array declarator are not included in C99 in the
+   syntax for abstract declarators; these are disallowed elsewhere.
+   This may be a defect (DR#289).
+
+   This function also accepts an omitted abstract declarator as being
+   an abstract declarator, although not part of the formal syntax.  */
+
+static struct c_declarator *
+c_parser_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
+		     bool *seen_id)
+{
+  /* Parse any initial pointer part.  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_MULT))
+    {
+      struct c_declspecs *quals_attrs = build_null_declspecs ();
+      struct c_declarator *inner;
+      c_lexer_consume_token (parser->lexer);
+      c_parser_declspecs (parser, quals_attrs, false, false, true);
+      inner = c_parser_declarator (parser, type_seen_p, kind, seen_id);
+      if (inner == NULL)
+	return NULL;
+      else
+	return make_pointer_declarator (quals_attrs, inner);
+    }
+  /* Now we have a direct declarator, direct abstract declarator or
+     nothing (which counts as a direct abstract declarator here).  */
+  return c_parser_direct_declarator (parser, type_seen_p, kind, seen_id);
+}
+
+/* Parse a direct declarator or direct abstract declarator; arguments
+   as c_parser_declarator.  */
+
+static struct c_declarator *
+c_parser_direct_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
+			    bool *seen_id)
+{
+  /* The direct declarator must start with an identifier (possibly
+     omitted) or a parenthesized declarator (possibly abstract).  In
+     an ordinary declarator, initial parentheses must start a
+     parenthesized declarator.  In an abstract declarator or parameter
+     declarator, they could start a parenthesized declarator or a
+     parameter list.  To tell which, the open parenthesis and any
+     following attributes must be read.  If a declaration specifier
+     follows, then it is a parameter list; if the specifier is a
+     typedef name, there might be an ambiguity about redeclaring it,
+     which is resolved in the direction of treating it as a typedef
+     name.  If a close parenthesis follows, it is also an empty
+     parameter list, as the syntax does not permit empty abstract
+     declarators.  Otherwise, it is a parenthesised declarator (in
+     which case the analysis may be repeated inside it, recursively).
+
+     ??? There is an ambiguity in a parameter declaration "int
+     (__attribute__((foo)) x)", where x is not a typedef name: it
+     could be an abstract declarator for a function, or declare x with
+     parentheses.  The proper resolution of this ambiguity needs
+     documenting.  At present we follow an accident of the old
+     parser's implementation, whereby the first parameter must have
+     some declaration specifiers other than just attributes.  Thus as
+     a parameter declaration it is treated as a parenthesised
+     parameter named x, and as an abstract declarator it is
+     rejected.
+
+     ??? Also following the old parser, attributes inside an empty
+     parameter list are ignored, making it a list not yielding a
+     prototype, rather than giving an error or making it have one
+     parameter with implicit type int.  */
+
+  if (kind != C_DTR_ABSTRACT
+      && c_lexer_next_token_is (parser->lexer, CPP_NAME)
+      && (type_seen_p
+	  || c_lexer_peek_token (parser->lexer)->id_kind == C_ID_ID))
+    {
+      struct c_declarator *inner
+	= build_id_declarator (c_lexer_peek_token (parser->lexer)->value);
+      *seen_id = true;
+      c_lexer_consume_token (parser->lexer);
+      return c_parser_direct_declarator_inner (parser, *seen_id, inner);
+    }
+
+  if (kind != C_DTR_NORMAL
+      && c_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
+    {
+      struct c_declarator *inner = build_id_declarator (NULL_TREE);
+      return c_parser_direct_declarator_inner (parser, *seen_id, inner);
+    }
+
+  /* Either we are at the end of an abstract declarator, or we have
+     parentheses.  */
+
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+    {
+      tree attrs;
+      struct c_declarator *inner;
+      c_lexer_consume_token (parser->lexer);
+      attrs = c_parser_attributes (parser);
+      if (kind != C_DTR_NORMAL
+	  && (c_lexer_next_token_starts_declspecs (parser->lexer)
+	      || c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN)))
+	{
+	  struct c_arg_info *args
+	    = c_parser_parms_declarator (parser, kind == C_DTR_NORMAL,
+					 attrs);
+	  if (args == NULL)
+	    return NULL;
+	  else
+	    {
+	      inner
+		= build_function_declarator (args,
+					     build_id_declarator (NULL_TREE));
+	      return c_parser_direct_declarator_inner (parser, *seen_id,
+						       inner);
+	    }
+	}
+      /* A parenthesized declarator.  */
+      inner = c_parser_declarator (parser, type_seen_p, kind, seen_id);
+      if (inner != NULL && attrs != NULL)
+	inner = build_attrs_declarator (attrs, inner);
+      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  if (inner == NULL)
+	    return NULL;
+	  else
+	    return c_parser_direct_declarator_inner (parser, *seen_id, inner);
+	}
+      else
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  return NULL;
+	}
+    }
+  else
+    {
+      if (kind == C_DTR_NORMAL)
+	{
+	  c_parser_error (parser, "expected identifier or '('");
+	  return NULL;
+	}
+      else
+	return build_id_declarator (NULL_TREE);
+    }
+}
+
+/* Parse part of a direct declarator or direct abstract declarator,
+   given that some (in INNER) has already been parsed; ID_PRESENT is
+   true if an identifier is present, false for an abstract
+   declarator.  */
+
+static struct c_declarator *
+c_parser_direct_declarator_inner (c_parser *parser, bool id_present,
+				  struct c_declarator *inner)
+{
+  /* Parse a sequence of array declarators and parameter lists.  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
+    {
+      struct c_declarator *declarator;
+      struct c_declspecs *quals_attrs = build_null_declspecs ();
+      bool static_seen;
+      bool star_seen;
+      tree dimen;
+      c_lexer_consume_token (parser->lexer);
+      c_parser_declspecs (parser, quals_attrs, false, false, true);
+      static_seen = c_lexer_next_token_is_keyword (parser->lexer, RID_STATIC);
+      if (static_seen)
+	c_lexer_consume_token (parser->lexer);
+      if (static_seen && !quals_attrs->declspecs_seen_p)
+	c_parser_declspecs (parser, quals_attrs, false, false, true);
+      if (!quals_attrs->declspecs_seen_p)
+	quals_attrs = NULL;
+      /* If "static" is present, there must be an array dimension.
+	 Otherwise, there may be a dimension, "*", or no
+	 dimension.  */
+      if (static_seen)
+	{
+	  star_seen = false;
+	  dimen = c_parser_expr_no_commas (parser).value;
+	}
+      else
+	{
+	  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE))
+	    {
+	      dimen = NULL_TREE;
+	      star_seen = false;
+	    }
+	  else if (c_lexer_next_token_is (parser->lexer, CPP_MULT))
+	    {
+	      if (c_lexer_peek_2nd_token (parser->lexer)->type
+		  == CPP_CLOSE_SQUARE)
+		{
+		  dimen = NULL_TREE;
+		  star_seen = true;
+		  c_lexer_consume_token (parser->lexer);
+		}
+	      else
+		{
+		  star_seen = false;
+		  dimen = c_parser_expr_no_commas (parser).value;
+		}
+	    }
+	  else
+	    {
+	      star_seen = false;
+	      dimen = c_parser_expr_no_commas (parser).value;
+	    }
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE))
+	c_lexer_consume_token (parser->lexer);
+      else
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, "expected ']'");
+	  return NULL;
+	}
+      declarator = build_array_declarator (dimen, quals_attrs, static_seen,
+					   star_seen);
+      inner = set_array_declarator_inner (declarator, inner, !id_present);
+      return c_parser_direct_declarator_inner (parser, id_present, inner);
+    }
+  else if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN))
+    {
+      tree attrs;
+      struct c_arg_info *args;
+      c_lexer_consume_token (parser->lexer);
+      attrs = c_parser_attributes (parser);
+      args = c_parser_parms_declarator (parser, id_present, attrs);
+      if (args == NULL)
+	return NULL;
+      else
+	{
+	  inner = build_function_declarator (args, inner);
+	  return c_parser_direct_declarator_inner (parser, id_present, inner);
+	}
+    }
+  return inner;
+}
+
+/* Parse a parameter list or identifier list, including the closing
+   parenthesis but not the opening one.  ATTRS are the attributes at
+   the start of the list.  ID_LIST_OK is true if an identifier list is
+   acceptable; such a list must not have attributes at the start.  */
+
+static struct c_arg_info *
+c_parser_parms_declarator (c_parser *parser, bool id_list_ok, tree attrs)
+{
+  push_scope ();
+  declare_parm_level ();
+  /* If the list starts with an identifier, it is an identifier list.
+     Otherwise, it is either a prototype list or an empty list.  */
+  if (id_list_ok
+      && !attrs
+      && c_lexer_next_token_is (parser->lexer, CPP_NAME)
+      && c_lexer_peek_token (parser->lexer)->id_kind == C_ID_ID)
+    {
+      tree list = NULL_TREE;
+      while (c_lexer_next_token_is (parser->lexer, CPP_NAME)
+	     && c_lexer_peek_token (parser->lexer)->id_kind == C_ID_ID)
+	{
+	  list = chainon (list, build_tree_list (NULL_TREE,
+						 c_lexer_peek_token (parser->lexer)->value));
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is_not (parser->lexer, CPP_COMMA))
+	    break;
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      break;
+	    }
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	{
+	  struct c_arg_info *ret = XOBNEW (&parser_obstack, struct c_arg_info);
+	  ret->parms = 0;
+	  ret->tags = 0;
+	  ret->types = list;
+	  ret->others = 0;
+	  c_lexer_consume_token (parser->lexer);
+	  pop_scope ();
+	  return ret;
+	}
+      else
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  pop_scope ();
+	  return NULL;
+	}
+    }
+  else
+    {
+      struct c_arg_info *ret = c_parser_parms_list_declarator (parser, attrs);
+      pop_scope ();
+      return ret;
+    }
+}
+
+/* Parse a parameter list (possibly empty), including the closing
+   parenthesis but not the opening one.  ATTRS are the attributes at
+   the start of the list.  */
+
+static struct c_arg_info *
+c_parser_parms_list_declarator (c_parser *parser, tree attrs)
+{
+  /* ??? Following the old parser, forward parameter declarations may
+     use abstract declarators, and if no real parameter declarations
+     follow the forward declarations then this is not diagnosed.  Also
+     note as above that attributes are ignored as the only contents of
+     the parentheses, or as the only contents after forward
+     declarations.  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+    {
+      struct c_arg_info *ret = XOBNEW (&parser_obstack, struct c_arg_info);
+      ret->parms = 0;
+      ret->tags = 0;
+      ret->types = 0;
+      ret->others = 0;
+      c_lexer_consume_token (parser->lexer);
+      return ret;
+    }
+  if (c_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
+    {
+      struct c_arg_info *ret = XOBNEW (&parser_obstack, struct c_arg_info);
+      ret->parms = 0;
+      ret->tags = 0;
+      ret->others = 0;
+      /* Suppress -Wold-style-definition for this case.  */
+      ret->types = error_mark_node;
+      error ("ISO C requires a named argument before %<...%>");
+      c_lexer_consume_token (parser->lexer);
+      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  return ret;
+	}
+      else
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  return NULL;
+	}
+    }
+  /* Nonempty list of parameters, either terminated with semicolon
+     (forward declarations; recurse) or with close parenthesis (normal
+     function) or with ", ... )" (variadic function).  */
+  while (true)
+    {
+      /* Parse a parameter.  */
+      struct c_declspecs *specs;
+      struct c_declarator *declarator;
+      tree prefix_attrs;
+      tree postfix_attrs = NULL_TREE;
+      bool dummy = false;
+      if (!c_lexer_next_token_starts_declspecs (parser->lexer))
+	{
+	  c_parser_error (parser, "expected declaration specifiers or '...'");
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  return NULL;
+	}
+      specs = build_null_declspecs ();
+      if (attrs)
+	{
+	  declspecs_add_attrs (specs, attrs);
+	  attrs = NULL_TREE;
+	}
+      c_parser_declspecs (parser, specs, true, true, true);
+      finish_declspecs (specs);
+      pending_xref_error ();
+      prefix_attrs = specs->attrs;
+      specs->attrs = NULL_TREE;
+      declarator = c_parser_declarator (parser, specs->type_seen_p,
+					C_DTR_PARM, &dummy);
+      if (declarator == NULL)
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  return NULL;
+	}
+      if (c_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))
+	postfix_attrs = c_parser_attributes (parser);
+      push_parm_decl (build_c_parm (specs,
+				    chainon (postfix_attrs,
+					     prefix_attrs), declarator));
+      if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	{
+	  tree new_attrs;
+	  c_lexer_consume_token (parser->lexer);
+	  new_attrs = c_parser_attributes (parser);
+	  return c_parser_parms_list_declarator (parser, new_attrs);
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  return get_parm_info (false);
+	}
+      if (!c_parser_require (parser, CPP_COMMA, "expected ';', ',' or ')'"))
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  return NULL;
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      return get_parm_info (true);
+	    }
+	  else
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+					 "expected ')'");
+	      return NULL;
+	    }
+	}
+    }
+}
+
+/* Parse a simple asm expression.  This is used in restricted
+   contexts, where a full expression with inputs and outputs does not
+   make sense.  This is a GNU extension.
+
+   simple-asm-expr:
+     asm ( string-literal )
+*/
+
+static tree
+c_parser_simple_asm_expr (c_parser *parser)
+{
+  tree str;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_ASM));
+  c_lex_string_translate = 0;
+  c_lexer_consume_token (parser->lexer);
+  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    {
+      c_lex_string_translate = 1;
+      return NULL_TREE;
+    }
+  /* ??? The old parser accepted wide string literals here, but do we
+     want to?  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_STRING)
+      || c_lexer_next_token_is (parser->lexer, CPP_WSTRING))
+    {
+      str = c_lexer_peek_token (parser->lexer)->value;
+      c_lexer_consume_token (parser->lexer);
+    }
+  else
+    {
+      c_lex_string_translate = 1;
+      c_parser_error (parser, "expected string literal");
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  c_lex_string_translate = 1;
+  if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected ')'"))
+    {
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  return str;
+}
+
+/* Parse (possibly empty) attributes.  This is a GNU extension.
+
+   attributes:
+     empty
+     attributes attribute
+
+   attribute:
+     __attribute__ ( ( attribute-list ) )
+
+   attribute-list:
+     attrib
+     attribute_list , attrib
+
+   attrib:
+     empty
+     any-word
+     any-word ( identifier )
+     any-word ( identifier , nonempty-expr-list )
+     any-word ( expr-list )
+
+   where the "identifier" must not be declared as a type, and
+   "any-word" may be any identifier (including one declared as a
+   type), a reserved word storage class specifier, type specifier or
+   type qualifier.  ??? This still leaves out most reserved keywords
+   (following the old parser), shouldn't we include them, and why not
+   allow identifiers declared as types to start the arguments?  */
+
+static tree
+c_parser_attributes (c_parser *parser)
+{
+  tree attrs = NULL_TREE;
+  while (c_lexer_next_token_is_keyword (parser->lexer, RID_ATTRIBUTE))
+    {
+      c_lex_string_translate = 0;
+      c_lexer_consume_token (parser->lexer);
+      if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	{
+	  c_lex_string_translate = 1;
+	  return attrs;
+	}
+      if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	{
+	  c_lex_string_translate = 1;
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  return attrs;
+	}
+      /* Parse the attribute list.  */
+      while (c_lexer_next_token_is (parser->lexer, CPP_COMMA)
+	     || c_lexer_next_token_is (parser->lexer, CPP_NAME)
+	     || c_lexer_next_token_is (parser->lexer, CPP_KEYWORD))
+	{
+	  tree attr, attr_name, attr_args;
+	  if (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      continue;
+	    }
+	  if (c_lexer_next_token_is (parser->lexer, CPP_KEYWORD))
+	    {
+	      /* ??? See comment above about what keywords are
+		 accepted here.  */
+	      bool ok;
+	      switch (c_lexer_peek_token (parser->lexer)->keyword)
+		{
+		case RID_STATIC:
+		case RID_UNSIGNED:
+		case RID_LONG:
+		case RID_CONST:
+		case RID_EXTERN:
+		case RID_REGISTER:
+		case RID_TYPEDEF:
+		case RID_SHORT:
+		case RID_INLINE:
+		case RID_VOLATILE:
+		case RID_SIGNED:
+		case RID_AUTO:
+		case RID_RESTRICT:
+		case RID_COMPLEX:
+		case RID_THREAD:
+		case RID_INT:
+		case RID_CHAR:
+		case RID_FLOAT:
+		case RID_DOUBLE:
+		case RID_VOID:
+		case RID_BOOL:
+		  ok = true;
+		  break;
+		default:
+		  ok = false;
+		  break;
+		}
+	      if (!ok)
+		break;
+	    }
+	  attr_name = c_lexer_peek_token (parser->lexer)->value;
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is_not (parser->lexer, CPP_OPEN_PAREN))
+	    {
+	      attr = build_tree_list (attr_name, NULL_TREE);
+	      attrs = chainon (attrs, attr);
+	      continue;
+	    }
+	  c_lexer_consume_token (parser->lexer);
+	  /* Parse the attribute contents.  If they start with an
+	     identifier which is followed by a comma or close
+	     parenthesis, then the arguments start with that
+	     identifier; otherwise they are an expression list.  */
+	  if (c_lexer_next_token_is (parser->lexer, CPP_NAME)
+	      && c_lexer_peek_token (parser->lexer)->id_kind == C_ID_ID
+	      && ((c_lexer_peek_2nd_token (parser->lexer)->type == CPP_COMMA)
+		  || (c_lexer_peek_2nd_token (parser->lexer)->type
+		      == CPP_CLOSE_PAREN)))
+	    {
+	      tree arg1 = c_lexer_peek_token (parser->lexer)->value;
+	      c_lexer_consume_token (parser->lexer);
+	      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+		attr_args = build_tree_list (NULL_TREE, arg1);
+	      else
+		{
+		  c_lexer_consume_token (parser->lexer);
+		  attr_args = tree_cons (NULL_TREE, arg1,
+					 c_parser_expr_list (parser));
+		}
+	    }
+	  else
+	    {
+	      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+		attr_args = NULL_TREE;
+	      else
+		attr_args = c_parser_expr_list (parser);
+	    }
+	  attr = build_tree_list (attr_name, attr_args);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	    c_lexer_consume_token (parser->lexer);
+	  else
+	    {
+	      c_lex_string_translate = 1;
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+					 "expected ')'");
+	      return attrs;
+	    }
+	  attrs = chainon (attrs, attr);
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	c_lexer_consume_token (parser->lexer);
+      else
+	{
+	  c_lex_string_translate = 1;
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  return attrs;
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	c_lexer_consume_token (parser->lexer);
+      else
+	{
+	  c_lex_string_translate = 1;
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  return attrs;
+	}
+      c_lex_string_translate = 1;
+    }
+  return attrs;
+}
+
+/* Parse a type name (C90 6.5.5, C99 6.7.6).
+
+   type-name:
+     specifier-qualifier-list abstract-declarator[opt]
+*/
+
+static struct c_type_name *
+c_parser_type_name (c_parser *parser)
+{
+  struct c_declspecs *specs = build_null_declspecs ();
+  struct c_declarator *declarator;
+  struct c_type_name *ret;
+  bool dummy = false;
+  c_parser_declspecs (parser, specs, false, true, true);
+  if (!specs->declspecs_seen_p)
+    {
+      c_parser_error (parser, "expected specifier-qualifier-list");
+      return NULL;
+    }
+  pending_xref_error ();
+  finish_declspecs (specs);
+  declarator = c_parser_declarator (parser, specs->type_seen_p,
+				    C_DTR_ABSTRACT, &dummy);
+  if (declarator == NULL)
+    return NULL;
+  ret = XOBNEW (&parser_obstack, struct c_type_name);
+  ret->specs = specs;
+  ret->declarator = declarator;
+  return ret;
+}
+
+/* Parse an initializer (C90 6.5.7, C99 6.7.8).
+
+   initializer:
+     assignment-expression
+     { initializer-list }
+     { initializer-list , }
+
+   initializer-list:
+     designation[opt] initializer
+     initializer-list , designation[opt] initializer
+
+   designation:
+     designator-list =
+
+   designator-list:
+     designator
+     designator-list designator
+
+   designator:
+     [ constant-expression ]
+     . identifier
+
+   GNU extensions:
+
+   initializer:
+     { }
+
+   designation:
+     designator
+     identifier :
+
+   designator:
+     [ constant-expression ... constant-expression ]
+
+   Any expression without commas is accepted in the syntax for the
+   constant-expressions, with non-constant expressions rejected later.
+
+   ??? Allowing old-style [n] designators has as a side-effect (copied
+   from the old parser) allowing ".member" without "=" as a
+   designator, but perhaps as this has never been documented (and only
+   worked from 2.95.x onwards) it could be freely removed.
+
+   This function is only used for top-level initializers; for nested
+   ones, see c_parser_initval.  */
+
+static struct c_expr
+c_parser_initializer (c_parser *parser)
+{
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+    return c_parser_braced_init (parser, NULL_TREE, false);
+  else
+    return c_parser_expr_no_commas (parser);
+}
+
+/* Parse a braced initializer list.  TYPE is the type specified for a
+   compound literal, and NULL_TREE for other initializers and for
+   nested braced lists.  NESTED_P is true for nested braced lists,
+   false for the list of a compound literal or the list that is the
+   top-level initializer in a declaration.  */
+
+static struct c_expr
+c_parser_braced_init (c_parser *parser, tree type, bool nested_p)
+{
+  gcc_assert (c_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE));
+  c_lexer_consume_token (parser->lexer);
+  if (nested_p)
+    push_init_level (0);
+  else
+    really_start_incremental_init (type);
+  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE))
+    {
+      if (pedantic)
+	pedwarn ("ISO C forbids empty initializer braces");
+    }
+  else
+    {
+      /* Parse a non-empty initializer list, possibly with a trailing
+	 comma.  */
+      while (true)
+	{
+	  c_parser_initelt (parser);
+	  if (parser->error)
+	    break;
+	  if (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	    c_lexer_consume_token (parser->lexer);
+	  else
+	    break;
+	  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE))
+	    break;
+	}
+    }
+  if (c_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_BRACE))
+    {
+      struct c_expr ret;
+      ret.value = error_mark_node;
+      ret.original_code = ERROR_MARK;
+      c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, "expected '}'");
+      return ret;
+    }
+  c_lexer_consume_token (parser->lexer);
+  return pop_init_level (0);
+}
+
+/* Parse a nested initializer, including designators.  */
+
+static void
+c_parser_initelt (c_parser *parser)
+{
+  /* Parse any designator or designator list.  A single designator may
+     have the subsequent "=" omitted in GNU C, but a longer list may
+     not.  See ??? comment above about ".foo" case without "=".  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_NAME)
+      && c_lexer_peek_2nd_token (parser->lexer)->type == CPP_COLON)
+    {
+      /* Old-style structure member designator.  */
+      set_init_label (c_lexer_peek_token (parser->lexer)->value);
+      if (pedantic)
+	pedwarn ("obsolete use of designated initializer with %<:%>");
+      c_lexer_consume_token (parser->lexer);
+      c_lexer_consume_token (parser->lexer);
+    }
+  else
+    {
+      int des_seen = 0;
+      while (c_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE)
+	     || c_lexer_next_token_is (parser->lexer, CPP_DOT))
+	{
+	  if (des_seen < 2)
+	    des_seen++;
+	  if (c_lexer_next_token_is (parser->lexer, CPP_DOT))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+		{
+		  set_init_label (c_lexer_peek_token (parser->lexer)->value);
+		  c_lexer_consume_token (parser->lexer);
+		}
+	      else
+		{
+		  c_parser_error (parser, "expected identifier");
+		  return;
+		}
+	    }
+	  else
+	    {
+	      tree first, second;
+	      c_lexer_consume_token (parser->lexer);
+	      first = c_parser_expr_no_commas (parser).value;
+	      if (c_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
+		{
+		  c_lexer_consume_token (parser->lexer);
+		  second = c_parser_expr_no_commas (parser).value;
+		}
+	      else
+		second = NULL_TREE;
+	      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_SQUARE))
+		{
+		  c_lexer_consume_token (parser->lexer);
+		  set_init_index (first, second);
+		  if (pedantic && second)
+		    pedwarn ("ISO C forbids specifying range of "
+			     "elements to initialize");
+		}
+	      else
+		c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE,
+					   "expected ']'");
+	    }
+	}
+      if (des_seen >= 1)
+	{
+	  if (c_lexer_next_token_is (parser->lexer, CPP_EQ))
+	    {
+	      if (pedantic && !flag_isoc99)
+		pedwarn ("ISO C90 forbids specifying subobject to initialize");
+	      c_lexer_consume_token (parser->lexer);
+	    }
+	  else
+	    {
+	      if (des_seen == 1)
+		{
+		  if (pedantic)
+		    pedwarn ("obsolete use of designated initializer "
+			     "without %<=%>");
+		}
+	      else
+		{
+		  c_parser_error (parser, "expected '='");
+		  return;
+		}
+	    }
+	}
+    }
+  c_parser_initval (parser);
+}
+
+/* Parse a nested initializer; as c_parser_initializer but parses
+   initializers within braced lists, after any designators have been
+   applied.  */
+
+static void
+c_parser_initval (c_parser *parser)
+{
+  struct c_expr init;
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+    init = c_parser_braced_init (parser, NULL_TREE, true);
+  else
+    init = c_parser_expr_no_commas (parser);
+  process_init_element (init);
+}
+
+/* Parse a compound statement (possibly a function body) (C90 6.6.2,
+   C99 6.8.2).
+
+   compound-statement:
+     { block-item-list[opt] }
+     { label-decls block-item-list }
+
+   block-item-list:
+     block-item
+     block-item-list block-item
+
+   block-item:
+     nested-declaration
+     statement
+
+   nested-declaration:
+     declaration
+
+   GNU extensions:
+
+   compound-statement:
+     { label-decls block-item-list }
+
+   nested-declaration:
+     __extension__ nested-declaration
+     nested-function-definition
+
+   label-decls:
+     label-decl
+     label-decls label-decl
+
+   label-decl:
+     __label__ identifier-list ;
+
+   Allowing the mixing of declarations and code is new in C99.  The
+   GNU syntax also permits (not shown above) labels at the end of
+   compound statements, which yield an error.  We don't allow labels
+   on declarations; this might seem like a natural extension, but
+   there would be a conflict between attributes on the label and
+   prefix attributes on the declaration.  ??? The syntax follows the
+   old parser in requiring something after label declarations.
+   Although they are erroneous if the labels declared aren't defined,
+   is it useful for the syntax to be this way?  */
+
+static tree
+c_parser_compound_statement (c_parser *parser)
+{
+  tree stmt;
+  if (!c_parser_require (parser, CPP_OPEN_BRACE, "expected '{'"))
+    return NULL_TREE;
+  stmt = c_begin_compound_stmt (true);
+  c_parser_compound_statement_nostart (parser);
+  return c_end_compound_stmt (stmt, true);
+}
+
+/* Parse a compound statement except for the opening brace.  This is
+   used for parsing both compound statements and statement expressions
+   (which follow different paths to handling the opening).  */
+
+static void
+c_parser_compound_statement_nostart (c_parser *parser)
+{
+  bool last_stmt = false;
+  bool last_label = false;
+  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE))
+    {
+      c_lexer_consume_token (parser->lexer);
+      return;
+    }
+  if (c_lexer_next_token_is_keyword (parser->lexer, RID_LABEL))
+    {
+      /* Read zero or more forward-declarations for labels that nested
+	 functions can jump to.  */
+      while (c_lexer_next_token_is_keyword (parser->lexer, RID_LABEL))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  /* Any identifiers, including those declared as type names,
+	     are OK here.  */
+	  if (c_lexer_next_token_is_not (parser->lexer, CPP_NAME))
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      c_parser_skip_until_found (parser, CPP_SEMICOLON, NULL);
+	      continue;
+	    }
+	  while (true)
+	    {
+	      tree label;
+	      if (c_lexer_next_token_is_not (parser->lexer, CPP_NAME))
+		{
+		  c_parser_error (parser, "expected identifier");
+		  break;
+		}
+	      label
+		= declare_label (c_lexer_peek_token (parser->lexer)->value);
+	      C_DECLARED_LABEL_FLAG (label) = 1;
+	      add_stmt (build_stmt (DECL_EXPR, label));
+	      c_lexer_consume_token (parser->lexer);
+	      if (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+		c_lexer_consume_token (parser->lexer);
+	      else
+		break;
+	    }
+	  c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected ';'");
+	}
+      if (pedantic)
+	pedwarn ("ISO C forbids label declarations");
+    }
+  /* We must now have at least one statement, label or declaration.  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_BRACE))
+    {
+      c_parser_error (parser, "expected declaration or statement");
+      c_lexer_consume_token (parser->lexer);
+      return;
+    }
+  while (c_lexer_next_token_is_not (parser->lexer, CPP_CLOSE_BRACE))
+    {
+      location_t loc = c_lexer_peek_token (parser->lexer)->location;
+      if (c_lexer_next_token_is_keyword (parser->lexer, RID_CASE)
+	  || c_lexer_next_token_is_keyword (parser->lexer, RID_DEFAULT)
+	  || (c_lexer_next_token_is (parser->lexer, CPP_NAME)
+	      && c_lexer_peek_2nd_token (parser->lexer)->type == CPP_COLON))
+	{
+	  last_label = true;
+	  last_stmt = false;
+	  c_parser_label (parser);
+	}
+      else if (!last_label
+	       && c_lexer_next_token_starts_declspecs (parser->lexer))
+	{
+	  last_label = false;
+	  c_parser_declaration_or_fndef (parser, true, true, true, true);
+	  if (last_stmt
+	      && ((pedantic && !flag_isoc99)
+		  || warn_declaration_after_statement))
+	    pedwarn_c90 ("%HISO C90 forbids mixed declarations and code",
+			 &loc);
+	  last_stmt = false;
+	}
+      else if (!last_label
+	       && c_lexer_next_token_is_keyword (parser->lexer, RID_EXTENSION))
+	{
+	  /* __extension__ can start a declaration, but is also an
+	     unary operator that can start an expression.  Consume all
+	     but the last of a possible series of __extension__ to
+	     determine which.  */
+	  while (c_lexer_peek_2nd_token (parser->lexer)->type == CPP_KEYWORD
+		 && (c_lexer_peek_2nd_token (parser->lexer)->keyword
+		     == RID_EXTENSION))
+	    c_lexer_consume_token (parser->lexer);
+	  if (c_token_starts_declspecs (c_lexer_peek_2nd_token (parser->lexer)))
+	    {
+	      int ext;
+	      SAVE_EXT_FLAGS (ext);
+	      c_lexer_consume_token (parser->lexer);
+	      last_label = false;
+	      c_parser_declaration_or_fndef (parser, true, true, true, true);
+	      /* Following the old parser, __extension__ does not
+		 disable this diagnostic.  */
+	      RESTORE_EXT_FLAGS (ext);
+	      if (last_stmt
+		  && ((pedantic && !flag_isoc99)
+		      || warn_declaration_after_statement))
+		pedwarn_c90 ("%HISO C90 forbids mixed declarations and code",
+			     &loc);
+	      last_stmt = false;
+	    }
+	  else
+	    goto statement;
+	}
+      else
+	{
+	statement:
+	  last_label = false;
+	  last_stmt = true;
+	  c_parser_statement_after_labels (parser);
+	}
+    }
+  if (last_label)
+    error ("label at end of compound statement");
+  c_lexer_consume_token (parser->lexer);
+}
+
+/* Parse a label (C90 6.6.1, C99 6.8.1).
+
+   label:
+     identifier : attributes[opt]
+     case constant-expression :
+     default :
+
+   GNU extensions:
+
+   label:
+     case constant-expression ... constant-expression :
+
+   The use of attributes on labels is a GNU extension.  The syntax in
+   GNU C accepts any expressions without commas, non-constant
+   expressions being rejected later.  */
+
+static void
+c_parser_label (c_parser *parser)
+{
+  location_t loc1 = c_lexer_peek_token (parser->lexer)->location;
+  tree label = NULL_TREE;
+  if (c_lexer_next_token_is_keyword (parser->lexer, RID_CASE))
+    {
+      tree exp1, exp2;
+      c_lexer_consume_token (parser->lexer);
+      exp1 = c_parser_expr_no_commas (parser).value;
+      if (c_lexer_next_token_is (parser->lexer, CPP_COLON))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  label = do_case (exp1, NULL_TREE);
+	}
+      else if (c_lexer_next_token_is (parser->lexer, CPP_ELLIPSIS))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  exp2 = c_parser_expr_no_commas (parser).value;
+	  if (c_parser_require (parser, CPP_COLON, "expected ':'"))
+	    label = do_case (exp1, exp2);
+	}
+      else
+	c_parser_error (parser, "expected ':' or '...'");
+    }
+  else if (c_lexer_next_token_is_keyword (parser->lexer, RID_DEFAULT))
+    {
+      c_lexer_consume_token (parser->lexer);
+      if (c_parser_require (parser, CPP_COLON, "expected ':'"))
+	label = do_case (NULL_TREE, NULL_TREE);
+    }
+  else
+    {
+      tree name = c_lexer_peek_token (parser->lexer)->value;
+      tree tlab;
+      location_t loc2;
+      tree attrs;
+      gcc_assert (c_lexer_next_token_is (parser->lexer, CPP_NAME));
+      c_lexer_consume_token (parser->lexer);
+      gcc_assert (c_lexer_next_token_is (parser->lexer, CPP_COLON));
+      loc2 = c_lexer_peek_token (parser->lexer)->location;
+      c_lexer_consume_token (parser->lexer);
+      attrs = c_parser_attributes (parser);
+      tlab = define_label (loc2, name);
+      if (tlab)
+	{
+	  decl_attributes (&tlab, attrs, 0);
+	  label = add_stmt (build_stmt (LABEL_EXPR, tlab));
+	}
+    }
+  if (label)
+    SET_EXPR_LOCATION (label, loc1);
+}
+
+/* Parse a statement (C90 6.6, C99 6.8).
+
+   statement:
+     labeled-statement
+     compound-statement
+     expression-statement
+     selection-statement
+     iteration-statement
+     jump-statement
+
+   labeled-statement:
+     label statement
+
+   expression-statement:
+     expression[opt] ;
+
+   selection-statement:
+     if-statement
+     switch-statement
+
+   iteration-statement:
+     while-statement
+     do-statement
+     for-statement
+
+   jump-statement:
+     goto identifier ;
+     continue ;
+     break ;
+     return expression[opt] ;
+
+   GNU extensions:
+
+   statement:
+     asm-statement
+
+   jump-statement:
+     goto * expression ;
+
+   TODO: Objective-C.
+*/
+
+static void
+c_parser_statement (c_parser *parser)
+{
+  while (c_lexer_next_token_is_keyword (parser->lexer, RID_CASE)
+	 || c_lexer_next_token_is_keyword (parser->lexer, RID_DEFAULT)
+	 || (c_lexer_next_token_is (parser->lexer, CPP_NAME)
+	     && c_lexer_peek_2nd_token (parser->lexer)->type == CPP_COLON))
+    c_parser_label (parser);
+  c_parser_statement_after_labels (parser);
+}
+
+/* Parse a statement, other than a labeled statement.  */
+
+static void
+c_parser_statement_after_labels (c_parser *parser)
+{
+  location_t loc = c_lexer_peek_token (parser->lexer)->location;
+  tree stmt = NULL_TREE;
+  switch (c_lexer_peek_token (parser->lexer)->type)
+    {
+    case CPP_OPEN_BRACE:
+      add_stmt (c_parser_compound_statement (parser));
+      break;
+    case CPP_KEYWORD:
+      switch (c_lexer_peek_token (parser->lexer)->keyword)
+	{
+	case RID_IF:
+	  c_parser_if_statement (parser);
+	  break;
+	case RID_SWITCH:
+	  c_parser_switch_statement (parser);
+	  break;
+	case RID_WHILE:
+	  c_parser_while_statement (parser);
+	  break;
+	case RID_DO:
+	  c_parser_do_statement (parser);
+	  break;
+	case RID_FOR:
+	  c_parser_for_statement (parser);
+	  break;
+	case RID_GOTO:
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+	    {
+	      stmt = c_finish_goto_label (c_lexer_peek_token (parser->lexer)->value);
+	      c_lexer_consume_token (parser->lexer);
+	    }
+	  else if (c_lexer_next_token_is (parser->lexer, CPP_MULT))
+	    {
+	      c_lexer_consume_token (parser->lexer);
+	      stmt = c_finish_goto_ptr (c_parser_expression (parser).value);
+	    }
+	  else
+	    c_parser_error (parser, "expected identifier or '*'");
+	  goto expect_semicolon;
+	case RID_CONTINUE:
+	  c_lexer_consume_token (parser->lexer);
+	  stmt = c_finish_bc_stmt (&c_cont_label, false);
+	  goto expect_semicolon;
+	case RID_BREAK:
+	  c_lexer_consume_token (parser->lexer);
+	  stmt = c_finish_bc_stmt (&c_break_label, true);
+	  goto expect_semicolon;
+	case RID_RETURN:
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	    {
+	      stmt = c_finish_return (NULL_TREE);
+	      c_lexer_consume_token (parser->lexer);
+	    }
+	  else
+	    {
+	      stmt = c_finish_return (c_parser_expression (parser).value);
+	      goto expect_semicolon;
+	    }
+	  break;
+	case RID_ASM:
+	  stmt = c_parser_asm_statement (parser);
+	  break;
+	default:
+	  goto expr_stmt;
+	}
+      break;
+    case CPP_SEMICOLON:
+      c_lexer_consume_token (parser->lexer);
+      break;
+    default:
+    expr_stmt:
+      stmt = c_finish_expr_stmt (c_parser_expression (parser).value);
+    expect_semicolon:
+      c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected ';'");
+      break;
+    }
+  /* Two cases cannot and do not have line numbers associated: If stmt
+     is degenerate, such as "2;", then stmt is an INTEGER_CST, which
+     cannot hold line numbers.  But that's OK because the statement
+     will either be changed to a MODIFY_EXPR during gimplification of
+     the statement expr, or discarded.  If stmt was compound, but
+     without new variables, we will have skipped the creation of a
+     BIND and will have a bare STATEMENT_LIST.  But that's OK because
+     (recursively) all of the component statements should already have
+     line numbers assigned.  */
+  if (stmt && EXPR_P (stmt))
+    SET_EXPR_LOCATION (stmt, loc);
+}
+
+/* Parse a parenthesized condition from an if, do or while statement.
+
+   condition:
+     ( expression )
+*/
+static tree
+c_parser_paren_condition (c_parser *parser)
+{
+  location_t loc;
+  tree cond;
+  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    return error_mark_node;
+  loc = c_lexer_peek_token (parser->lexer)->location;
+  cond = lang_hooks.truthvalue_conversion (c_parser_expression (parser).value);
+  if (EXPR_P (cond))
+    SET_EXPR_LOCATION (cond, loc);
+  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+  return cond;
+}
+
+/* Parse a statement which is a block in C99.  */
+
+static tree
+c_parser_c99_block_statement (c_parser *parser)
+{
+  tree block = c_begin_compound_stmt (flag_isoc99);
+  c_parser_statement (parser);
+  return c_end_compound_stmt (block, flag_isoc99);
+}
+
+/* Parse the body of an if statement or the else half thereof.  This
+   is just parsing a statement but (a) it is a block in C99, (b) we
+   track whether the body is an if statement for the sake of
+   -Wparentheses warnings, (c) we handle an empty body specially for
+   the sake of -Wextra warnings.  */
+
+static tree
+c_parser_if_body (c_parser *parser, bool *if_p)
+{
+  tree block = c_begin_compound_stmt (flag_isoc99);
+  while (c_lexer_next_token_is_keyword (parser->lexer, RID_CASE)
+	 || c_lexer_next_token_is_keyword (parser->lexer, RID_DEFAULT)
+	 || (c_lexer_next_token_is (parser->lexer, CPP_NAME)
+	     && c_lexer_peek_2nd_token (parser->lexer)->type == CPP_COLON))
+    c_parser_label (parser);
+  *if_p = c_lexer_next_token_is_keyword (parser->lexer, RID_IF);
+  if (extra_warnings && c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+    add_stmt (build (NOP_EXPR, NULL_TREE, NULL_TREE));
+  c_parser_statement_after_labels (parser);
+  return c_end_compound_stmt (block, flag_isoc99);
+}
+
+/* Parse an if statement (C90 6.6.4, C99 6.8.4).
+
+   if-statement:
+     if ( expression ) statement
+     if ( expression ) statement else statement
+*/
+
+static void
+c_parser_if_statement (c_parser *parser)
+{
+  tree block;
+  location_t loc;
+  tree cond;
+  bool first_if = false, second_if = false;
+  tree first_body, second_body;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_IF));
+  c_lexer_consume_token (parser->lexer);
+  block = c_begin_compound_stmt (flag_isoc99);
+  loc = c_lexer_peek_token (parser->lexer)->location;
+  cond = c_parser_paren_condition (parser);
+  first_body = c_parser_if_body (parser, &first_if);
+  if (c_lexer_next_token_is_keyword (parser->lexer, RID_ELSE))
+    {
+      c_lexer_consume_token (parser->lexer);
+      second_body = c_parser_if_body (parser, &second_if);
+    }
+  else
+    second_body = NULL_TREE;
+  c_finish_if_stmt (loc, cond, first_body, second_body, first_if);
+  add_stmt (c_end_compound_stmt (block, flag_isoc99));
+}
+
+/* Parse a switch statement (C90 6.6.4, C99 6.8.4).
+
+   switch-statement:
+     switch (expression) statement
+*/
+
+static void
+c_parser_switch_statement (c_parser *parser)
+{
+  tree block, expr, body, save_break;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_SWITCH));
+  c_lexer_consume_token (parser->lexer);
+  block = c_begin_compound_stmt (flag_isoc99);
+  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    {
+      expr = c_parser_expression (parser).value;
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+    }
+  else
+    expr = error_mark_node;
+  c_start_case (expr);
+  save_break = c_break_label;
+  c_break_label = NULL_TREE;
+  body = c_parser_c99_block_statement (parser);
+  c_finish_case (body);
+  if (c_break_label)
+    add_stmt (build (LABEL_EXPR, void_type_node, c_break_label));
+  c_break_label = save_break;
+  add_stmt (c_end_compound_stmt (block, flag_isoc99));
+}
+
+/* Parse a while statement (C90 6.6.5, C99 6.8.5).
+
+   while-statement:
+      while (expression) statement
+*/
+
+static void
+c_parser_while_statement (c_parser *parser)
+{
+  tree block, cond, body, save_break, save_cont;
+  location_t loc;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_WHILE));
+  c_lexer_consume_token (parser->lexer);
+  block = c_begin_compound_stmt (flag_isoc99);
+  loc = c_lexer_peek_token (parser->lexer)->location;
+  cond = c_parser_paren_condition (parser);
+  save_break = c_break_label;
+  c_break_label = NULL_TREE;
+  save_cont = c_cont_label;
+  c_cont_label = NULL_TREE;
+  body = c_parser_c99_block_statement (parser);
+  c_finish_loop (loc, cond, NULL, body, c_break_label, c_cont_label, true);
+  add_stmt (c_end_compound_stmt (block, flag_isoc99));
+  c_break_label = save_break;
+  c_cont_label = save_cont;
+}
+
+/* Parse a do statement (C90 6.6.5, C99 6.8.5).
+
+   do-statement:
+     do statement while ( expression ) ;
+*/
+
+static void
+c_parser_do_statement (c_parser *parser)
+{
+  tree block, cond, body, save_break, save_cont, new_break, new_cont;
+  location_t loc;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_DO));
+  c_lexer_consume_token (parser->lexer);
+  block = c_begin_compound_stmt (flag_isoc99);
+  loc = c_lexer_peek_token (parser->lexer)->location;
+  save_break = c_break_label;
+  c_break_label = NULL_TREE;
+  save_cont = c_cont_label;
+  c_cont_label = NULL_TREE;
+  body = c_parser_c99_block_statement (parser);
+  c_parser_require_keyword (parser, RID_WHILE, "expected 'while'");
+  new_break = c_break_label;
+  c_break_label = save_break;
+  new_cont = c_cont_label;
+  c_cont_label = save_cont;
+  cond = c_parser_paren_condition (parser);
+  if (!c_parser_require (parser, CPP_SEMICOLON, "expected ';'"))
+    c_parser_skip_to_end_of_block_or_statement (parser);
+  c_finish_loop (loc, cond, NULL, body, new_break, new_cont, false);
+  add_stmt (c_end_compound_stmt (block, flag_isoc99));
+}
+
+/* Parse a for statement (C90 6.6.5, C99 6.8.5).
+
+   for-statement:
+     for ( expression[opt] ; expression[opt] ; expression[opt] ) statement
+     for ( nested-declaration expression[opt] ; expression[opt] ) statement
+
+   The form with a declaration is new in C99.
+
+   ??? In accordance with the old parser, the declaration may be a
+   nested function, which is then rejected in check_for_loop_decls,
+   but does it make any sense for this to be included in the grammar?
+   Note in particular that the nested function does not include a
+   trailing ';', whereas the "declaration" production includes
+   one.  */
+
+static void
+c_parser_for_statement (c_parser *parser)
+{
+  tree block, cond, incr, save_break, save_cont, body;
+  location_t loc;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_FOR));
+  c_lexer_consume_token (parser->lexer);
+  block = c_begin_compound_stmt (flag_isoc99);
+  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    {
+      /* Parse the initialization declaration or expression.  */
+      if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  c_finish_expr_stmt (NULL_TREE);
+	}
+      else if (c_lexer_next_token_starts_declspecs (parser->lexer))
+	{
+	  c_parser_declaration_or_fndef (parser, true, true, true, true);
+	  check_for_loop_decls ();
+	}
+      else if (c_lexer_next_token_is_keyword (parser->lexer, RID_EXTENSION))
+	{
+	  /* __extension__ can start a declaration, but is also an
+	     unary operator that can start an expression.  Consume all
+	     but the last of a possible series of __extension__ to
+	     determine which.  */
+	  while (c_lexer_peek_2nd_token (parser->lexer)->type == CPP_KEYWORD
+		 && (c_lexer_peek_2nd_token (parser->lexer)->keyword
+		     == RID_EXTENSION))
+	    c_lexer_consume_token (parser->lexer);
+	  if (c_token_starts_declspecs (c_lexer_peek_2nd_token (parser->lexer)))
+	    {
+	      int ext;
+	      SAVE_EXT_FLAGS (ext);
+	      c_lexer_consume_token (parser->lexer);
+	      c_parser_declaration_or_fndef (parser, true, true, true, true);
+	      RESTORE_EXT_FLAGS (ext);
+	      check_for_loop_decls ();
+	    }
+	  else
+	    goto init_expr;
+	}
+      else
+	{
+	init_expr:
+	  c_finish_expr_stmt (c_parser_expression (parser).value);
+	  c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected ';'");
+	}
+      /* Parse the loop condition.  */
+      loc = c_lexer_peek_token (parser->lexer)->location;
+      if (c_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  cond = NULL_TREE;
+	}
+      else
+	{
+	  tree ocond = c_parser_expression (parser).value;
+	  cond = lang_hooks.truthvalue_conversion (ocond);
+	  if (EXPR_P (cond))
+	    SET_EXPR_LOCATION (cond, loc);
+	  c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected ';'");
+	}
+      /* Parse the increment expression.  */
+      if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	incr = c_process_expr_stmt (NULL_TREE);
+      else
+	incr = c_process_expr_stmt (c_parser_expression (parser).value);
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+    }
+  else
+    {
+      cond = error_mark_node;
+      incr = error_mark_node;
+    }
+  save_break = c_break_label;
+  c_break_label = NULL_TREE;
+  save_cont = c_cont_label;
+  c_cont_label = NULL_TREE;
+  body = c_parser_c99_block_statement (parser);
+  c_finish_loop (loc, cond, incr, body, c_break_label, c_cont_label, true);
+  add_stmt (c_end_compound_stmt (block, flag_isoc99));
+  c_break_label = save_break;
+  c_cont_label = save_cont;
+}
+
+/* Parse an asm statement, a GNU extension.  This is a full-blown asm
+   statement with inputs, outputs, clobbers, and volatile tag
+   allowed.
+
+   asm-statement:
+     asm type-qualifier[opt] ( asm-argument ) ;
+
+   asm-argument:
+     string-literal
+     string-literal : asm-operands[opt]
+     string-literal : asm-operands[opt] : asm-operands[opt]
+     string-literal : asm-operands[opt] : asm-operands[opt] : asm-clobbers
+
+   asm-clobbers:
+     string-literal
+     asm-clobbers , string-literal
+
+   Qualifiers other than volatile are accepted in the syntax but
+   warned for.
+
+   ??? The old parser accepted wide strings here, but do we want
+   to?  */
+
+static tree
+c_parser_asm_statement (c_parser *parser)
+{
+  tree quals, str, outputs, inputs, clobbers, ret;
+  bool simple;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_ASM));
+  c_lexer_consume_token (parser->lexer);
+  if (c_lexer_next_token_is_keyword (parser->lexer, RID_VOLATILE))
+    {
+      quals = c_lexer_peek_token (parser->lexer)->value;
+      c_lexer_consume_token (parser->lexer);
+    }
+  else if (c_lexer_next_token_is_keyword (parser->lexer, RID_CONST)
+	   || c_lexer_next_token_is_keyword (parser->lexer, RID_RESTRICT))
+    {
+      warning ("%E qualifier ignored on asm",
+	       c_lexer_peek_token (parser->lexer)->value);
+      quals = NULL_TREE;
+      c_lexer_consume_token (parser->lexer);
+    }
+  else
+    quals = NULL_TREE;
+  c_lex_string_translate = 0;
+  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    {
+      c_lex_string_translate = 1;
+      return NULL_TREE;
+    }
+  if (c_lexer_next_token_is (parser->lexer, CPP_STRING)
+      || c_lexer_next_token_is (parser->lexer, CPP_WSTRING))
+    {
+      str = c_lexer_peek_token (parser->lexer)->value;
+      c_lexer_consume_token (parser->lexer);
+    }
+  else
+    {
+      c_lex_string_translate = 1;
+      c_parser_error (parser, "expected string literal");
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+    {
+      simple = true;
+      outputs = NULL_TREE;
+      inputs = NULL_TREE;
+      clobbers = NULL_TREE;
+      goto done_asm;
+    }
+  if (!c_parser_require (parser, CPP_COLON, "expected ':' or ')'"))
+    {
+      c_lex_string_translate = 1;
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  simple = false;
+  /* Parse outputs.  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_COLON)
+      || c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+    outputs = NULL_TREE;
+  else
+    outputs = c_parser_asm_operands (parser);
+  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+    {
+      inputs = NULL_TREE;
+      clobbers = NULL_TREE;
+      goto done_asm;
+    }
+  if (!c_parser_require (parser, CPP_COLON, "expected ':' or ')'"))
+    {
+      c_lex_string_translate = 1;
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  /* Parse inputs.  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_COLON)
+      || c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+    inputs = NULL_TREE;
+  else
+    inputs = c_parser_asm_operands (parser);
+  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+    {
+      clobbers = NULL_TREE;
+      goto done_asm;
+    }
+  if (!c_parser_require (parser, CPP_COLON, "expected ':' or ')'"))
+    {
+      c_lex_string_translate = 1;
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  /* Parse clobbers.  */
+  clobbers = c_parser_asm_clobbers (parser);
+ done_asm:
+  c_lex_string_translate = 1;
+  if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected ')'"))
+    {
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  if (!c_parser_require (parser, CPP_SEMICOLON, "expected ';'"))
+    c_parser_skip_to_end_of_block_or_statement (parser);
+  ret = build_asm_stmt (quals, build_asm_expr (str, outputs, inputs,
+					       clobbers, simple));
+  return ret;
+}
+
+/* Parse asm operands, a GNU extension.
+
+   asm-operands:
+     asm-operand
+     asm-operands , asm-operand
+
+   asm-operand:
+     string-literal ( expression )
+     [ identifier ] string-literal ( expression )
+
+   ??? The old parser accepted wide strings here, but do we want
+   to?  */
+
+static tree
+c_parser_asm_operands (c_parser *parser)
+{
+  tree list = NULL_TREE;
+  while (true)
+    {
+      tree name, str, expr;
+      if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_SQUARE))
+	{
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+	    {
+	      tree id = c_lexer_peek_token (parser->lexer)->value;
+	      c_lexer_consume_token (parser->lexer);
+	      name = build_string (IDENTIFIER_LENGTH (id),
+				   IDENTIFIER_POINTER (id));
+	    }
+	  else
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, NULL);
+	      return NULL_TREE;
+	    }
+	  c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, "expected ']'");
+	}
+      else
+	name = NULL_TREE;
+      if (c_lexer_next_token_is (parser->lexer, CPP_STRING)
+	  || c_lexer_next_token_is (parser->lexer, CPP_WSTRING))
+	{
+	  str = c_lexer_peek_token (parser->lexer)->value;
+	  c_lexer_consume_token (parser->lexer);
+	}
+      else
+	{
+	  c_parser_error (parser, "expected string literal");
+	  return NULL_TREE;
+	}
+      c_lex_string_translate = 1;
+      if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	{
+	  c_lex_string_translate = 0;
+	  return NULL_TREE;
+	}
+      expr = c_parser_expression (parser).value;
+      c_lex_string_translate = 0;
+      if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected ')'"))
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  return NULL_TREE;
+	}
+      list = chainon (list, build_tree_list (build_tree_list (name, str),
+					     expr));
+      if (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	c_lexer_consume_token (parser->lexer);
+      else
+	break;
+    }
+  return list;
+}
+
+/* Parse asm clobbers, a GNU extension.
+
+   asm-clobbers:
+     string-literal
+     asm-clobbers , string-literal
+
+   ??? The old parser accepted wide strings here, but do we want
+   to?  */
+
+static tree
+c_parser_asm_clobbers (c_parser *parser)
+{
+  tree list = NULL_TREE;
+  while (true)
+    {
+      if (c_lexer_next_token_is (parser->lexer, CPP_STRING)
+	  || c_lexer_next_token_is (parser->lexer, CPP_WSTRING))
+	{
+	  tree str = c_lexer_peek_token (parser->lexer)->value;
+	  c_lexer_consume_token (parser->lexer);
+	  list = tree_cons (NULL_TREE, str, list);
+	}
+      else
+	{
+	  c_parser_error (parser, "expected string literal");
+	  return NULL_TREE;
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	c_lexer_consume_token (parser->lexer);
+      else
+	break;
+    }
+  return list;
+}
+
+/* Parse an expression other than a compound expression; that is, an
+   assignment expression (C90 6.3.16, C99 6.5.16).
+
+   assignment-expression:
+     conditional-expression
+     unary-expression assignment-operator assignment-expression
+
+   assignment-operator: one of
+     = *= /= %= += -= <<= >>= &= ^= |=
+
+   In GNU C we accept any conditional expression on the LHS and
+   diagnose the invalid lvalue rather than producing a syntax
+   error.  */
+
+static struct c_expr
+c_parser_expr_no_commas (c_parser *parser)
+{
+  struct c_expr lhs, rhs, ret;
+  enum tree_code code;
+  lhs = c_parser_conditional_expression (parser);
+  switch (c_lexer_peek_token (parser->lexer)->type)
+    {
+    case CPP_EQ:
+      code = NOP_EXPR;
+      break;
+    case CPP_MULT_EQ:
+      code = MULT_EXPR;
+      break;
+    case CPP_DIV_EQ:
+      code = TRUNC_DIV_EXPR;
+      break;
+    case CPP_MOD_EQ:
+      code = TRUNC_MOD_EXPR;
+      break;
+    case CPP_PLUS_EQ:
+      code = PLUS_EXPR;
+      break;
+    case CPP_MINUS_EQ:
+      code = MINUS_EXPR;
+      break;
+    case CPP_LSHIFT_EQ:
+      code = LSHIFT_EXPR;
+      break;
+    case CPP_RSHIFT_EQ:
+      code = RSHIFT_EXPR;
+      break;
+    case CPP_AND_EQ:
+      code = BIT_AND_EXPR;
+      break;
+    case CPP_XOR_EQ:
+      code = BIT_XOR_EXPR;
+      break;
+    case CPP_OR_EQ:
+      code = BIT_IOR_EXPR;
+      break;
+    default:
+      return lhs;
+    }
+  c_lexer_consume_token (parser->lexer);
+  rhs = c_parser_expr_no_commas (parser);
+  ret.value = build_modify_expr (lhs.value, code, rhs.value);
+  if (code == NOP_EXPR)
+    ret.original_code = MODIFY_EXPR;
+  else
+    {
+      TREE_NO_WARNING (ret.value) = 1;
+      ret.original_code = ERROR_MARK;
+    }
+  return ret;
+}
+
+/* Parse a conditional expression (C90 6.3.15, C99 6.5.15).
+
+   conditional-expression:
+     logical-OR-expression
+     logical-OR-expression ? expression : conditional-expression
+
+   GNU extensions:
+
+   conditional-expression:
+     logical-OR-expression ? : conditional-expression
+*/
+
+static struct c_expr
+c_parser_conditional_expression (c_parser *parser)
+{
+  struct c_expr cond, exp1, exp2, ret;
+  cond = c_parser_binary_expression (parser);
+  if (c_lexer_next_token_is_not (parser->lexer, CPP_QUERY))
+    return cond;
+  c_lexer_consume_token (parser->lexer);
+  if (c_lexer_next_token_is (parser->lexer, CPP_COLON))
+    {
+      if (pedantic)
+	pedwarn ("ISO C forbids omitting the middle term of a ?: expression");
+      /* Make sure first operand is calculated only once.  */
+      exp1.value = save_expr (default_conversion (cond.value));
+      cond.value = lang_hooks.truthvalue_conversion (exp1.value);
+      skip_evaluation += cond.value == truthvalue_true_node;
+    }
+  else
+    {
+      cond.value
+	= lang_hooks.truthvalue_conversion (default_conversion (cond.value));
+      skip_evaluation += cond.value == truthvalue_false_node;
+      exp1 = c_parser_expression (parser);
+      skip_evaluation += ((cond.value == truthvalue_true_node)
+			  - (cond.value == truthvalue_false_node));
+    }
+  if (!c_parser_require (parser, CPP_COLON, "expected ':'"))
+    {
+      skip_evaluation -= cond.value == truthvalue_true_node;
+      ret.value = error_mark_node;
+      ret.original_code = ERROR_MARK;
+      return ret;
+    }
+  exp2 = c_parser_conditional_expression (parser);
+  skip_evaluation -= cond.value == truthvalue_true_node;
+  ret.value = build_conditional_expr (cond.value, exp1.value, exp2.value);
+  ret.original_code = ERROR_MARK;
+  return ret;
+}
+
+/* Parse a binary expression; that is, a logical-OR-expression (C90
+   6.3.5-6.3.14, C99 6.5.5-6.5.14).
+
+   multiplicative-expression:
+     cast-expression
+     multiplicative-expression * cast-expression
+     multiplicative-expression / cast-expression
+     multiplicative-expression % cast-expression
+
+   additive-expression:
+     multiplicative-expression
+     additive-expression + multiplicative-expression
+     additive-expression - multiplicative-expression
+
+   shift-expression:
+     additive-expression
+     shift-expression << additive-expression
+     shift-expression >> additive-expression
+
+   relational-expression:
+     shift-expression
+     relational-expression < shift-expression
+     relational-expression > shift-expression
+     relational-expression <= shift-expression
+     relational-expression >= shift-expression
+
+   equality-expression:
+     relational-expression
+     equality-expression == relational-expression
+     equality-expression != relational-expression
+
+   AND-expression:
+     equality-expression
+     AND-expression & equality-expression
+
+   exclusive-OR-expression:
+     AND-expression
+     exclusive-OR-expression ^ AND-expression
+
+   inclusive-OR-expression:
+     exclusive-OR-expression
+     inclusive-OR-expression | exclusive-OR-expression
+
+   logical-AND-expression:
+     inclusive-OR-expression
+     logical-AND-expression && inclusive-OR-expression
+
+   logical-OR-expression:
+     logical-AND-expression
+     logical-OR-expression || logical-AND-expression
+*/
+
+static struct c_expr
+c_parser_binary_expression (c_parser *parser)
+{
+  /* A binary expression is parsed using operator-precedence parsing,
+     with the operands being cast expressions.  All the binary
+     operators are left-associative.  Thus a binary expression is of
+     form:
+
+     E0 op1 E1 op2 E2 ...
+
+     which we represent on a stack.  On the stack, the precedence
+     levels are strictly increasing.  When a new operator is
+     encountered of higher precedence than that at the top of the
+     stack, it is pushed; its LHS is the top expression, and its RHS
+     is everything parsed until it is popped.  When a new operator is
+     encountered with precedence less than or equal to that at the top
+     of the stack, triples E[i-1] op[i] E[i] are popped and replaced
+     by the result of the operation until the operator at the top of
+     the stack has lower precedence than the new operator or there is
+     only one element on the stack; then the top expression is the LHS
+     of the new operator.  In the case of logical AND and OR
+     expressions, we also need to adjust skip_evaluation as
+     appropriate when the operators are pushed and popped.  */
+
+  /* The precedence levels, where 0 is a dummy lowest level used for
+     the bottom of the stack.  */
+  enum prec {
+    PREC_NONE,
+    PREC_LOGOR,
+    PREC_LOGAND,
+    PREC_BITOR,
+    PREC_BITXOR,
+    PREC_BITAND,
+    PREC_EQ,
+    PREC_REL,
+    PREC_SHIFT,
+    PREC_ADD,
+    PREC_MULT,
+    NUM_PRECS
+  };
+  struct {
+    /* The expression at this stack level.  */
+    struct c_expr expr;
+    /* The precedence of the operator on its left, PREC_NONE at the
+       bottom of the stack.  */
+    enum prec prec;
+    /* The operation on its left.  */
+    enum tree_code op;
+  } stack[NUM_PRECS];
+  int sp;
+#define POP								      \
+  do {									      \
+    switch (stack[sp].op)						      \
+      {									      \
+      case TRUTH_ANDIF_EXPR:						      \
+	skip_evaluation -= stack[sp - 1].expr.value == truthvalue_false_node; \
+	break;								      \
+      case TRUTH_ORIF_EXPR:						      \
+	skip_evaluation -= stack[sp - 1].expr.value == truthvalue_true_node;  \
+	break;								      \
+      default:								      \
+	break;								      \
+      }									      \
+    stack[sp - 1].expr = parser_build_binary_op (stack[sp].op,		      \
+						 stack[sp - 1].expr,	      \
+						 stack[sp].expr);	      \
+    sp--;								      \
+  } while (0)
+  stack[0].expr = c_parser_cast_expression (parser);
+  stack[0].prec = PREC_NONE;
+  sp = 0;
+  while (true)
+    {
+      enum prec oprec;
+      enum tree_code ocode;
+      if (parser->error)
+	goto out;
+      switch (c_lexer_peek_token (parser->lexer)->type)
+	{
+	case CPP_MULT:
+	  oprec = PREC_MULT;
+	  ocode = MULT_EXPR;
+	  break;
+	case CPP_DIV:
+	  oprec = PREC_MULT;
+	  ocode = TRUNC_DIV_EXPR;
+	  break;
+	case CPP_MOD:
+	  oprec = PREC_MULT;
+	  ocode = TRUNC_MOD_EXPR;
+	  break;
+	case CPP_PLUS:
+	  oprec = PREC_ADD;
+	  ocode = PLUS_EXPR;
+	  break;
+	case CPP_MINUS:
+	  oprec = PREC_ADD;
+	  ocode = MINUS_EXPR;
+	  break;
+	case CPP_LSHIFT:
+	  oprec = PREC_SHIFT;
+	  ocode = LSHIFT_EXPR;
+	  break;
+	case CPP_RSHIFT:
+	  oprec = PREC_SHIFT;
+	  ocode = RSHIFT_EXPR;
+	  break;
+	case CPP_LESS:
+	  oprec = PREC_REL;
+	  ocode = LT_EXPR;
+	  break;
+	case CPP_GREATER:
+	  oprec = PREC_REL;
+	  ocode = GT_EXPR;
+	  break;
+	case CPP_LESS_EQ:
+	  oprec = PREC_REL;
+	  ocode = LE_EXPR;
+	  break;
+	case CPP_GREATER_EQ:
+	  oprec = PREC_REL;
+	  ocode = GE_EXPR;
+	  break;
+	case CPP_EQ_EQ:
+	  oprec = PREC_EQ;
+	  ocode = EQ_EXPR;
+	  break;
+	case CPP_NOT_EQ:
+	  oprec = PREC_EQ;
+	  ocode = NE_EXPR;
+	  break;
+	case CPP_AND:
+	  oprec = PREC_BITAND;
+	  ocode = BIT_AND_EXPR;
+	  break;
+	case CPP_XOR:
+	  oprec = PREC_BITXOR;
+	  ocode = BIT_XOR_EXPR;
+	  break;
+	case CPP_OR:
+	  oprec = PREC_BITOR;
+	  ocode = BIT_IOR_EXPR;
+	  break;
+	case CPP_AND_AND:
+	  oprec = PREC_LOGAND;
+	  ocode = TRUTH_ANDIF_EXPR;
+	  break;
+	case CPP_OR_OR:
+	  oprec = PREC_LOGOR;
+	  ocode = TRUTH_ORIF_EXPR;
+	  break;
+	default:
+	  /* Not a binary operator, so end of the binary
+	     expression.  */
+	  goto out;
+	}
+      c_lexer_consume_token (parser->lexer);
+      while (oprec <= stack[sp].prec)
+	POP;
+      switch (ocode)
+	{
+	case TRUTH_ANDIF_EXPR:
+	  stack[sp].expr.value = lang_hooks.truthvalue_conversion
+	    (default_conversion (stack[sp].expr.value));
+	  skip_evaluation += stack[sp].expr.value == truthvalue_false_node;
+	  break;
+	case TRUTH_ORIF_EXPR:
+	  stack[sp].expr.value = lang_hooks.truthvalue_conversion
+	    (default_conversion (stack[sp].expr.value));
+	  skip_evaluation += stack[sp].expr.value == truthvalue_true_node;
+	  break;
+	default:
+	  break;
+	}
+      sp++;
+      stack[sp].expr = c_parser_cast_expression (parser);
+      stack[sp].prec = oprec;
+      stack[sp].op = ocode;
+    }
+ out:
+  while (sp > 0)
+    POP;
+  return stack[0].expr;
+#undef POP
+}
+
+/* Parse a cast expression (C90 6.3.4, C99 6.5.4).
+
+   cast-expression:
+     unary-expression
+     ( type-name ) unary-expression
+*/
+
+static struct c_expr
+c_parser_cast_expression (c_parser *parser)
+{
+  /* If the expression begins with a parenthesized type name, it may
+     be either a cast or a compound literal; we need to see whether
+     the next character is '{' to tell the difference.  If not, it is
+     an unary expression.  */
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)
+      && c_token_starts_typename (c_lexer_peek_2nd_token (parser->lexer)))
+    {
+      struct c_type_name *type_name;
+      struct c_expr ret;
+      tree expr;
+      c_lexer_consume_token (parser->lexer);
+      type_name = c_parser_type_name (parser);
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+      if (type_name == NULL)
+	{
+	  ret.value = error_mark_node;
+	  ret.original_code = ERROR_MARK;
+	  return ret;
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+	return c_parser_postfix_expression_after_paren_type (parser,
+							     type_name);
+      expr = c_parser_cast_expression (parser).value;
+      ret.value = c_cast_expr (type_name, expr);
+      ret.original_code = ERROR_MARK;
+      return ret;
+    }
+  else
+    return c_parser_unary_expression (parser);
+}
+
+/* Parse an unary expression (C90 6.3.3, C99 6.5.3).
+
+   unary-expression:
+     postfix-expression
+     ++ unary-expression
+     -- unary-expression
+     unary-operator cast-expression
+     sizeof unary-expression
+     sizeof ( type-name )
+
+   unary-operator: one of
+     & * + - ~ !
+
+   GNU extensions:
+
+   unary-expression:
+     __alignof__ unary-expression
+     __alignof__ ( type-name )
+     && identifier
+
+   unary-operator: one of
+     __extension__ __real__ __imag__
+
+   In addition, the GNU syntax treats ++ and -- as unary operators, so
+   they may be applied to cast expressions with errors for non-lvalues
+   given later.  */
+
+static struct c_expr
+c_parser_unary_expression (c_parser *parser)
+{
+  int ext;
+  struct c_expr ret;
+  ret.original_code = ERROR_MARK;
+  switch (c_lexer_peek_token (parser->lexer)->type)
+    {
+    case CPP_PLUS_PLUS:
+      c_lexer_consume_token (parser->lexer);
+      ret.value = build_unary_op (PREINCREMENT_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_MINUS_MINUS:
+      c_lexer_consume_token (parser->lexer);
+      ret.value = build_unary_op (PREDECREMENT_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_AND:
+      c_lexer_consume_token (parser->lexer);
+      ret.value = build_unary_op (ADDR_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_MULT:
+      c_lexer_consume_token (parser->lexer);
+      ret.value = build_indirect_ref (c_parser_cast_expression (parser).value,
+				      "unary *");
+      return ret;
+    case CPP_PLUS:
+      c_lexer_consume_token (parser->lexer);
+      if (!c_dialect_objc () && warn_traditional && !in_system_header)
+	warning ("traditional C rejects the unary plus operator");
+      ret.value = build_unary_op (CONVERT_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_MINUS:
+      c_lexer_consume_token (parser->lexer);
+      ret.value = build_unary_op (NEGATE_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_COMPL:
+      c_lexer_consume_token (parser->lexer);
+      ret.value = build_unary_op (BIT_NOT_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_NOT:
+      c_lexer_consume_token (parser->lexer);
+      ret.value = build_unary_op (TRUTH_NOT_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_AND_AND:
+      /* Refer to the address of a label as a pointer.  */
+      c_lexer_consume_token (parser->lexer);
+      if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+	{
+	  ret.value = finish_label_address_expr
+	    (c_lexer_peek_token (parser->lexer)->value);
+	  c_lexer_consume_token (parser->lexer);
+	  return ret;
+	}
+      else
+	{
+	  c_parser_error (parser, "expected identifier");
+	  ret.value = error_mark_node;
+	  return ret;
+	}
+    case CPP_KEYWORD:
+      switch (c_lexer_peek_token (parser->lexer)->keyword)
+	{
+	case RID_SIZEOF:
+	  return c_parser_sizeof_expression (parser);
+	case RID_ALIGNOF:
+	  return c_parser_alignof_expression (parser);
+	case RID_EXTENSION:
+	  c_lexer_consume_token (parser->lexer);
+	  SAVE_EXT_FLAGS (ext);
+	  ret = c_parser_cast_expression (parser);
+	  RESTORE_EXT_FLAGS (ext);
+	  return ret;
+	case RID_REALPART:
+	  c_lexer_consume_token (parser->lexer);
+	  ret.value = build_unary_op (REALPART_EXPR,
+				      c_parser_cast_expression (parser).value,
+				      0);
+	  return ret;
+	case RID_IMAGPART:
+	  c_lexer_consume_token (parser->lexer);
+	  ret.value = build_unary_op (IMAGPART_EXPR,
+				      c_parser_cast_expression (parser).value,
+				      0);
+	  return ret;
+	default:
+	  return c_parser_postfix_expression (parser);
+	}
+    default:
+      return c_parser_postfix_expression (parser);
+    }
+}
+
+/* Parse a sizeof expression.  */
+
+static struct c_expr
+c_parser_sizeof_expression (c_parser *parser)
+{
+  struct c_expr expr;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_SIZEOF));
+  c_lexer_consume_token (parser->lexer);
+  skip_evaluation++;
+  in_sizeof++;
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)
+      && c_token_starts_typename (c_lexer_peek_2nd_token (parser->lexer)))
+    {
+      /* Either sizeof ( type-name ) or sizeof unary-expression
+	 starting with a compound literal.  */
+      struct c_type_name *type_name;
+      c_lexer_consume_token (parser->lexer);
+      type_name = c_parser_type_name (parser);
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+      if (type_name == NULL)
+	{
+	  struct c_expr ret;
+	  skip_evaluation--;
+	  in_sizeof--;
+	  ret.value = error_mark_node;
+	  ret.original_code = ERROR_MARK;
+	  return ret;
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+	{
+	  expr = c_parser_postfix_expression_after_paren_type (parser,
+							       type_name);
+	  goto sizeof_expr;
+	}
+      /* sizeof ( type-name ).  */
+      skip_evaluation--;
+      in_sizeof--;
+      return c_expr_sizeof_type (type_name);
+    }
+  else
+    {
+      expr = c_parser_unary_expression (parser);
+    sizeof_expr:
+      skip_evaluation--;
+      in_sizeof--;
+      if (TREE_CODE (expr.value) == COMPONENT_REF
+	  && DECL_C_BIT_FIELD (TREE_OPERAND (expr.value, 1)))
+	error ("%<sizeof%> applied to a bit-field");
+      return c_expr_sizeof_expr (expr);
+    }
+}
+
+/* Parse an alignof expression.  */
+
+static struct c_expr
+c_parser_alignof_expression (c_parser *parser)
+{
+  struct c_expr expr;
+  gcc_assert (c_lexer_next_token_is_keyword (parser->lexer, RID_ALIGNOF));
+  c_lexer_consume_token (parser->lexer);
+  skip_evaluation++;
+  in_alignof++;
+  if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)
+      && c_token_starts_typename (c_lexer_peek_2nd_token (parser->lexer)))
+    {
+      /* Either __alignof__ ( type-name ) or __alignof__
+	 unary-expression starting with a compound literal.  */
+      struct c_type_name *type_name;
+      struct c_expr ret;
+      c_lexer_consume_token (parser->lexer);
+      type_name = c_parser_type_name (parser);
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+      if (type_name == NULL)
+	{
+	  struct c_expr ret;
+	  skip_evaluation--;
+	  in_alignof--;
+	  ret.value = error_mark_node;
+	  ret.original_code = ERROR_MARK;
+	  return ret;
+	}
+      if (c_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE))
+	{
+	  expr = c_parser_postfix_expression_after_paren_type (parser,
+							       type_name);
+	  goto alignof_expr;
+	}
+      /* alignof ( type-name ).  */
+      skip_evaluation--;
+      in_alignof--;
+      ret.value = c_alignof (groktypename (type_name));
+      ret.original_code = ERROR_MARK;
+      return ret;
+    }
+  else
+    {
+      struct c_expr ret;
+      expr = c_parser_unary_expression (parser);
+    alignof_expr:
+      skip_evaluation--;
+      in_alignof--;
+      ret.value = c_alignof_expr (expr.value);
+      ret.original_code = ERROR_MARK;
+      return ret;
+    }
+}
+
+/* Parse a postfix expression (C90 6.3.1-6.3.2, C99 6.5.1-6.5.2).
+
+   postfix-expression:
+     primary-expression
+     postfix-expression [ expression ]
+     postfix-expression ( argument-expression-list[opt] )
+     postfix-expression . identifier
+     postfix-expression -> identifier
+     postfix-expression ++
+     postfix-expression --
+     ( type-name ) { initializer-list }
+     ( type-name ) { initializer-list , }
+
+   argument-expression-list:
+     argument-expression
+     argument-expression-list , argument-expression
+
+   primary-expression:
+     identifier
+     constant
+     string-literal
+     ( expression )
+
+   GNU extensions:
+
+   primary-expression:
+     __func__
+       (treated as a keyword in GNU C)
+     __FUNCTION__
+     __PRETTY_FUNCTION__
+     ( compound-statement )
+     __builtin_va_arg ( assignment-expression , type-name )
+     __builtin_offsetof ( type-name , offsetof-member-designator )
+     __builtin_choose_expr ( assignment-expression ,
+			     assignment-expression ,
+			     assignment-expression )
+     __builtin_types_compatible_p ( type-name , type-name )
+
+   offsetof-member-designator:
+     identifier
+     offsetof-member-designator . identifier
+     offsetof-member-designator [ expression ]
+
+   TODO: Objective-C.
+*/
+
+static struct c_expr
+c_parser_postfix_expression (c_parser *parser)
+{
+  struct c_expr expr, e1, e2, e3;
+  struct c_type_name *t1, *t2;
+  switch (c_lexer_peek_token (parser->lexer)->type)
+    {
+    case CPP_NUMBER:
+    case CPP_CHAR:
+    case CPP_WCHAR:
+      expr.value = c_lexer_peek_token (parser->lexer)->value;
+      expr.original_code = ERROR_MARK;
+      c_lexer_consume_token (parser->lexer);
+      break;
+    case CPP_STRING:
+    case CPP_WSTRING:
+      expr.value = c_lexer_peek_token (parser->lexer)->value;
+      expr.original_code = STRING_CST;
+      c_lexer_consume_token (parser->lexer);
+      break;
+    case CPP_NAME:
+      if (c_lexer_peek_token (parser->lexer)->id_kind != C_ID_ID)
+	{
+	  c_parser_error (parser, "expected expression");
+	  expr.value = error_mark_node;
+	  expr.original_code = ERROR_MARK;
+	  break;
+	}
+      expr.value = build_external_ref
+	(c_lexer_peek_token (parser->lexer)->value,
+	 c_lexer_peek_2nd_token (parser->lexer)->type == CPP_OPEN_PAREN);
+      expr.original_code = ERROR_MARK;
+      c_lexer_consume_token (parser->lexer);
+      break;
+    case CPP_OPEN_PAREN:
+      /* A parenthesized expression, statement expression or compound
+	 literal.  */
+      if (c_lexer_peek_2nd_token (parser->lexer)->type == CPP_OPEN_BRACE)
+	{
+	  /* A statement expression.  */
+	  tree stmt;
+	  c_lexer_consume_token (parser->lexer);
+	  c_lexer_consume_token (parser->lexer);
+	  if (cur_stmt_list == NULL)
+	    {
+	      error ("braced-group within expression allowed "
+		     "only inside a function");
+	      parser->error = true;
+	      c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL);
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  stmt = c_begin_stmt_expr ();
+	  c_parser_compound_statement_nostart (parser);
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  if (pedantic)
+	    pedwarn ("ISO C forbids braced-groups within expressions");
+	  expr.value = c_finish_stmt_expr (stmt);
+	  expr.original_code = ERROR_MARK;
+	}
+      else if (c_token_starts_typename (c_lexer_peek_2nd_token (parser->lexer)))
+	{
+	  /* A compound literal.  ??? Can we actually get here rather
+	     than going directly to
+	     c_parser_postfix_expression_after_paren_type from
+	     elsewhere?  */
+	  struct c_type_name *type_name;
+	  c_lexer_consume_token (parser->lexer);
+	  type_name = c_parser_type_name (parser);
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  if (type_name == NULL)
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	    }
+	  else
+	    expr = c_parser_postfix_expression_after_paren_type (parser,
+								 type_name);
+	}
+      else
+	{
+	  /* A parenthesized expression.  */
+	  c_lexer_consume_token (parser->lexer);
+	  expr = c_parser_expression (parser);
+	  if (TREE_CODE (expr.value) == MODIFY_EXPR)
+	    TREE_NO_WARNING (expr.value) = 1;
+	  expr.original_code = ERROR_MARK;
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	}
+      break;
+    case CPP_KEYWORD:
+      switch (c_lexer_peek_token (parser->lexer)->keyword)
+	{
+	case RID_FUNCTION_NAME:
+	case RID_PRETTY_FUNCTION_NAME:
+	case RID_C99_FUNCTION_NAME:
+	  expr.value = fname_decl (c_lexer_peek_token (parser->lexer)->keyword,
+				   c_lexer_peek_token (parser->lexer)->value);
+	  expr.original_code = ERROR_MARK;
+	  c_lexer_consume_token (parser->lexer);
+	  break;
+	case RID_VA_ARG:
+	  c_lexer_consume_token (parser->lexer);
+	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  e1 = c_parser_expr_no_commas (parser);
+	  if (!c_parser_require (parser, CPP_COMMA, "expected ','"))
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  t1 = c_parser_type_name (parser);
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  if (t1 == NULL)
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	    }
+	  else
+	    {
+	      expr.value = build_va_arg (e1.value, groktypename (t1));
+	      expr.original_code = ERROR_MARK;
+	    }
+	  break;
+	case RID_OFFSETOF:
+	  c_lexer_consume_token (parser->lexer);
+	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  t1 = c_parser_type_name (parser);
+	  if (t1 == NULL)
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  if (!c_parser_require (parser, CPP_COMMA, "expected ','"))
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  {
+	    tree type = groktypename (t1);
+	    tree offsetof_ref;
+	    if (type == error_mark_node)
+	      offsetof_ref = error_mark_node;
+	    else
+	      offsetof_ref = build1 (INDIRECT_REF, type, NULL);
+	    /* Parse the second argument to __builtin_offsetof.  We
+	       must have one identifier, and beyond that we want to
+	       accept sub structure and sub array references.  */
+	    if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+	      {
+		offsetof_ref = build_component_ref
+		  (offsetof_ref, c_lexer_peek_token (parser->lexer)->value);
+		c_lexer_consume_token (parser->lexer);
+		while (c_lexer_next_token_is (parser->lexer, CPP_DOT)
+		       || c_lexer_next_token_is (parser->lexer,
+						 CPP_OPEN_SQUARE))
+		  {
+		    if (c_lexer_next_token_is (parser->lexer, CPP_DOT))
+		      {
+			c_lexer_consume_token (parser->lexer);
+			if (c_lexer_next_token_is_not (parser->lexer,
+						       CPP_NAME))
+			  {
+			    c_parser_error (parser, "expected identifier");
+			    break;
+			  }
+			offsetof_ref = build_component_ref
+			  (offsetof_ref,
+			   c_lexer_peek_token (parser->lexer)->value);
+			c_lexer_consume_token (parser->lexer);
+		      }
+		    else
+		      {
+			tree idx;
+			c_lexer_consume_token (parser->lexer);
+			idx = c_parser_expression (parser).value;
+			c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE,
+						   "expected ']'");
+			offsetof_ref = build_array_ref (offsetof_ref, idx);
+		      }
+		  }
+	      }
+	    else
+	      c_parser_error (parser, "expected identifier");
+	    c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+				       "expected ')'");
+	    expr.value = fold_offsetof (offsetof_ref);
+	    expr.original_code = ERROR_MARK;
+	  }
+	  break;
+	case RID_CHOOSE_EXPR:
+	  c_lexer_consume_token (parser->lexer);
+	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  e1 = c_parser_expr_no_commas (parser);
+	  if (!c_parser_require (parser, CPP_COMMA, "expected ','"))
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  e2 = c_parser_expr_no_commas (parser);
+	  if (!c_parser_require (parser, CPP_COMMA, "expected ','"))
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  e3 = c_parser_expr_no_commas (parser);
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  {
+	    tree c;
+
+	    c = fold (e1.value);
+	    STRIP_NOPS (c);
+	    if (TREE_CODE (c) != INTEGER_CST)
+	      error ("first argument to __builtin_choose_expr not"
+		     " a constant");
+	    expr = integer_zerop (c) ? e3 : e2;
+	  }
+	  break;
+	case RID_TYPES_COMPATIBLE_P:
+	  c_lexer_consume_token (parser->lexer);
+	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  t1 = c_parser_type_name (parser);
+	  if (t1 == NULL)
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  if (!c_parser_require (parser, CPP_COMMA, "expected ','"))
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  t2 = c_parser_type_name (parser);
+	  if (t2 == NULL)
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  {
+	    tree e1, e2;
+
+	    e1 = TYPE_MAIN_VARIANT (groktypename (t1));
+	    e2 = TYPE_MAIN_VARIANT (groktypename (t2));
+
+	    expr.value = comptypes (e1, e2)
+	      ? build_int_cst (NULL_TREE, 1)
+	      : build_int_cst (NULL_TREE, 0);
+	    expr.original_code = ERROR_MARK;
+	  }
+	  break;
+	default:
+	  c_parser_error (parser, "expected expression");
+	  expr.value = error_mark_node;
+	  expr.original_code = ERROR_MARK;
+	  break;
+	}
+      break;
+    default:
+      c_parser_error (parser, "expected expression");
+      expr.value = error_mark_node;
+      expr.original_code = ERROR_MARK;
+      break;
+    }
+  return c_parser_postfix_expression_after_primary (parser, expr);
+}
+
+/* Parse a postfix expression after a parenthesized type name: the
+   brace-enclosed initializer of a compound literal, possibly followed
+   by some postfix operators.  This is separate because it is not
+   possible to tell until after the type name whether a cast
+   expression has a cast or a compound literal, or whether the operand
+   of sizeof is a parenthesized type name or starts with a compound
+   literal.  */
+
+static struct c_expr
+c_parser_postfix_expression_after_paren_type (c_parser *parser,
+					      struct c_type_name *type_name)
+{
+  tree type;
+  struct c_expr init;
+  struct c_expr expr;
+  start_init (NULL_TREE, NULL, 0);
+  type = groktypename (type_name);
+  if (C_TYPE_VARIABLE_SIZE (type))
+    {
+      error ("compound literal has variable size");
+      type = error_mark_node;
+    }
+  init = c_parser_braced_init (parser, type, false);
+  finish_init ();
+  maybe_warn_string_init (type, init);
+
+  if (pedantic && !flag_isoc99)
+    pedwarn ("ISO C90 forbids compound literals");
+  expr.value = build_compound_literal (type, init.value);
+  expr.original_code = ERROR_MARK;
+  return c_parser_postfix_expression_after_primary (parser, expr);
+}
+
+/* Parse a postfix expression after the initial primary or compound
+   literal; that is, parse a series of postfix operators.  */
+
+static struct c_expr
+c_parser_postfix_expression_after_primary (c_parser *parser,
+					   struct c_expr expr)
+{
+  tree ident, idx, exprlist;
+  while (true)
+    {
+      switch (c_lexer_peek_token (parser->lexer)->type)
+	{
+	case CPP_OPEN_SQUARE:
+	  /* Array reference.  */
+	  c_lexer_consume_token (parser->lexer);
+	  idx = c_parser_expression (parser).value;
+	  c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, "expected ']'");
+	  expr.value = build_array_ref (expr.value, idx);
+	  expr.original_code = ERROR_MARK;
+	  break;
+	case CPP_OPEN_PAREN:
+	  /* Function call.  */
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
+	    exprlist = NULL_TREE;
+	  else
+	    exprlist = c_parser_expr_list (parser);
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  expr.value = build_function_call (expr.value, exprlist);
+	  expr.original_code = ERROR_MARK;
+	  break;
+	case CPP_DOT:
+	  /* Structure element reference.  */
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+	    ident = c_lexer_peek_token (parser->lexer)->value;
+	  else
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      return expr;
+	    }
+	  c_lexer_consume_token (parser->lexer);
+	  expr.value = build_component_ref (expr.value, ident);
+	  expr.original_code = ERROR_MARK;
+	  break;
+	case CPP_DEREF:
+	  /* Structure element reference.  */
+	  c_lexer_consume_token (parser->lexer);
+	  if (c_lexer_next_token_is (parser->lexer, CPP_NAME))
+	    ident = c_lexer_peek_token (parser->lexer)->value;
+	  else
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      return expr;
+	    }
+	  c_lexer_consume_token (parser->lexer);
+	  expr.value = build_component_ref (build_indirect_ref (expr.value,
+								"->"), ident);
+	  expr.original_code = ERROR_MARK;
+	  break;
+	case CPP_PLUS_PLUS:
+	  /* Postincrement.  */
+	  c_lexer_consume_token (parser->lexer);
+	  expr.value = build_unary_op (POSTINCREMENT_EXPR, expr.value, 0);
+	  expr.original_code = ERROR_MARK;
+	  break;
+	case CPP_MINUS_MINUS:
+	  /* Postdecrement.  */
+	  c_lexer_consume_token (parser->lexer);
+	  expr.value = build_unary_op (POSTDECREMENT_EXPR, expr.value, 0);
+	  expr.original_code = ERROR_MARK;
+	  break;
+	default:
+	  return expr;
+	}
+    }
+}
+
+/* Parse an expression (C90 6.3.17, C99 6.5.17).
+
+   expression:
+     assignment-expression
+     expression , assignment-expression
+*/
+
+static struct c_expr
+c_parser_expression (c_parser *parser)
+{
+  struct c_expr expr;
+  expr = c_parser_expr_no_commas (parser);
+  while (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+    {
+      struct c_expr next;
+      c_lexer_consume_token (parser->lexer);
+      next = c_parser_expr_no_commas (parser);
+      expr.value = build_compound_expr (expr.value, next.value);
+      expr.original_code = COMPOUND_EXPR;
+    }
+  return expr;
+}
+
+/* Parse a non-empty list of expressions.
+
+   nonempty-expr-list:
+     assignment-expression
+     nonempty-expr-list , assignment-expression
+*/
+
+static tree
+c_parser_expr_list (c_parser *parser)
+{
+  struct c_expr expr;
+  tree ret;
+  expr = c_parser_expr_no_commas (parser);
+  ret = build_tree_list (NULL_TREE, expr.value);
+  while (c_lexer_next_token_is (parser->lexer, CPP_COMMA))
+    {
+      c_lexer_consume_token (parser->lexer);
+      expr = c_parser_expr_no_commas (parser);
+      ret = chainon (ret, build_tree_list (NULL_TREE, expr.value));
+    }
+  return ret;
+}
+
+\f
+/* The actual parser and external interface.  */
+
+static GTY (()) c_parser *the_parser;
+
+/* Parse a single source file.  */
+
+void
+c_parse_file (void)
+{
+  the_parser = c_parser_new ();
+  c_parser_translation_unit (the_parser);
+  the_parser = NULL;
+}
+
+#include "gt-c-parser.h"
diff -rupN GCC.orig/gcc/c-tree.h GCC/gcc/c-tree.h
--- GCC.orig/gcc/c-tree.h	2004-10-14 00:31:11.000000000 +0000
+++ GCC/gcc/c-tree.h	2004-10-16 21:50:02.000000000 +0000
@@ -204,6 +204,10 @@ struct c_declspecs {
   enum c_typespec_keyword typespec_word;
   /* The storage class specifier, or csc_none if none.  */
   enum c_storage_class storage_class;
+  /* Whether any declaration specifiers have been seen at all.  */
+  BOOL_BITFIELD declspecs_seen_p : 1;
+  /* Whether a type specifier has been seen.  */
+  BOOL_BITFIELD type_seen_p : 1;
   /* Whether something other than a storage class specifier or
      attribute has been seen.  This is used to warn for the
      obsolescent usage of storage class specifiers other than at the

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-23  1:25 New C parser [patch] Joseph S. Myers
                   ` (2 preceding siblings ...)
  2004-10-24 22:49 ` Joseph S. Myers
@ 2004-10-25 22:33 ` Ziemowit Laski
  2004-10-25 22:51   ` Joseph S. Myers
  3 siblings, 1 reply; 42+ messages in thread
From: Ziemowit Laski @ 2004-10-25 22:33 UTC (permalink / raw)
  To: Joseph S. Myers; +Cc: gcc-patches

Sorry for not responding sooner, but I do have one question:

On 22 Oct 2004, at 16.56, Joseph S. Myers wrote:

> This patch serves to provide a recursive-descent parser (facilitating
> OpenMP, etc.) within reasonably forseeable time, unlike other
> alternatives such as "merge the C and C++ front ends".

I'm just curious as to why you think that writing a C parser from 
scratch is
easier than tweaking Mark's parser to handle C.

I'm asking because merging the front-ends would allow ObjC/ObjC++ to be
simplified _tremendously_: we will be able to utilize C++ machinery
to implement ObjC subclassing and access control, not to mention a
more sensible interaction of ObjC objects with C++ EH and RTTI.

Thanks,

--Zem

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-25 22:33 ` Ziemowit Laski
@ 2004-10-25 22:51   ` Joseph S. Myers
  2004-10-25 23:45     ` Ziemowit Laski
  2004-10-26  0:03     ` Stan Shebs
  0 siblings, 2 replies; 42+ messages in thread
From: Joseph S. Myers @ 2004-10-25 22:51 UTC (permalink / raw)
  To: Ziemowit Laski; +Cc: gcc-patches

On Mon, 25 Oct 2004, Ziemowit Laski wrote:

> I'm just curious as to why you think that writing a C parser from scratch is
> easier than tweaking Mark's parser to handle C.

You have an interesting definition of "tweaking".  Geoffrey Keating gave 
an estimate of 6-9 man-years to merge the front ends making them handle 
the present languages as fast as at present.  This compares to 1 man-week 
to write a functional C parser for the exact present language accepted 
which also speeds up the compiler despite not having been profiled or 
tuned for performance at all.  (If I made c_parser_error ignore the passed 
diagnostic and just say "syntax error", I wouldn't be surprised if there 
are no, or very few, testsuite regressions remaining.)  A parser for C 
being much simpler than for C++ also makes the assurance from reading the 
code that it does actually parse the correct language rather better than 
for a parser trying to parse two very different languages.  (Though for 
additional assurance that the language hasn't changed with the C parser - 
that it parses the exact same GNU C as before, good or bad - I expect to 
produce testcases totalling many times longer than the parser itself.)

I look forward to seeing your merged front end that has no testsuite or 
performance regressions (save testcases that have been analysed and found 
to have different but not inferior diagnostics) on either language.  If 
you are to show that this is easier than writing a C parser from scratch, 
your merged front end should be ready within the next 168 hours.

> I'm asking because merging the front-ends would allow ObjC/ObjC++ to be
> simplified _tremendously_: we will be able to utilize C++ machinery
> to implement ObjC subclassing and access control, not to mention a
> more sensible interaction of ObjC objects with C++ EH and RTTI.

Merging front ends provides only marginal maintenance benefits - a great 
many changes to a merged front end would need to consider their possible 
effects on many more different languages and language versions than at 
present, making the burden associated with making each change that much 
greater though maybe slightly reducing the number of changes to be made.  
C and C++ have been diverging for over 20 years; many similarities are 
only superficial and hide underlying differences and are dangerous to rely 
on if you want a correct compiler.

-- 
Joseph S. Myers               http://www.srcf.ucam.org/~jsm28/gcc/
    jsm@polyomino.org.uk (personal mail)
    joseph@codesourcery.com (CodeSourcery mail)
    jsm28@gcc.gnu.org (Bugzilla assignments and CCs)

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-25 22:51   ` Joseph S. Myers
@ 2004-10-25 23:45     ` Ziemowit Laski
  2004-10-25 23:53       ` Scott Robert Ladd
  2004-10-26  0:03     ` Stan Shebs
  1 sibling, 1 reply; 42+ messages in thread
From: Ziemowit Laski @ 2004-10-25 23:45 UTC (permalink / raw)
  To: Joseph S. Myers; +Cc: gcc-patches


On 25 Oct 2004, at 15.44, Joseph S. Myers wrote:

> On Mon, 25 Oct 2004, Ziemowit Laski wrote:
>
>> I'm just curious as to why you think that writing a C parser from 
>> scratch is
>> easier than tweaking Mark's parser to handle C.
>
> You have an interesting definition of "tweaking".

I might indeed, but I haven't offered one yet. :-)

>   Geoffrey Keating gave
> an estimate of 6-9 man-years to merge the front ends making them handle
> the present languages as fast as at present.  This compares to 1 
> man-week
> to write a functional C parser for the exact present language accepted
> which also speeds up the compiler despite not having been profiled or
> tuned for performance at all.

While I don't buy these time estimates (not both simultaneously, 
anyway), I do
agree that you would take a performance hit, at least in the short-term,
especially for plain C code.

> Merging front ends provides only marginal maintenance benefits

That I buy even less than your time estimates.  :-(  But anyway, mine 
was just
a question, not an objection.

--Zem

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-25 23:45     ` Ziemowit Laski
@ 2004-10-25 23:53       ` Scott Robert Ladd
  2004-10-26  0:03         ` Unified front end for C and C++ (was Re: New C parser [patch]) Matt Austern
                           ` (2 more replies)
  0 siblings, 3 replies; 42+ messages in thread
From: Scott Robert Ladd @ 2004-10-25 23:53 UTC (permalink / raw)
  To: Ziemowit Laski; +Cc: Joseph S. Myers, gcc-patches

Ziemowit Laski wrote:
> While I don't buy these time estimates (not both simultaneously, 
> anyway), I do
> agree that you would take a performance hit, at least in the short-term,
> especially for plain C code.

I don't think this is a hit GCC's users can afford at this point. 
Another compile time regression may bring the hordes with pitchforks and 
torches... ;)

Also, C and C++ are heading down different and incompatible paths. It 
will be increasingly difficult to manage the subtle-but-important 
differences in a single front end if the two languages continue to diverge.

-- 
Scott Robert Ladd
site: http://www.coyotegulch.com
blog: http://chaoticcoyote.blogspot.com

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-25 22:51   ` Joseph S. Myers
  2004-10-25 23:45     ` Ziemowit Laski
@ 2004-10-26  0:03     ` Stan Shebs
  2004-10-26  1:46       ` Gabriel Dos Reis
  1 sibling, 1 reply; 42+ messages in thread
From: Stan Shebs @ 2004-10-26  0:03 UTC (permalink / raw)
  To: Joseph S. Myers; +Cc: Ziemowit Laski, gcc-patches

Joseph S. Myers wrote:

>On Mon, 25 Oct 2004, Ziemowit Laski wrote:
>
>
>>I'm just curious as to why you think that writing a C parser from scratch is
>>easier than tweaking Mark's parser to handle C.
>>
>
>You have an interesting definition of "tweaking".  Geoffrey Keating gave 
>an estimate of 6-9 man-years to merge the front ends making them handle 
>the present languages as fast as at present.  This compares to 1 man-week 
>to write a functional C parser for the exact present language accepted 
>which also speeds up the compiler despite not having been profiled or 
>tuned for performance at all.
>
To be fair though, Geoff's estimate accounted for merging a lot
of the semantic processing that continues to be semi-clones of
each other, and continues to cause performance problems that have
to be solved twice.

Even so, changing to a recursive descent parser is a step in the
right direction, because it will be easier to compare with the
C++ parser. And of course it's awesome that you did it in a week;
if you can do it that quickly, maybe it would only take you a
couple months to merge frontends! :-)

>>I'm asking because merging the front-ends would allow ObjC/ObjC++ to be
>>simplified _tremendously_: we will be able to utilize C++ machinery
>>to implement ObjC subclassing and access control, not to mention a
>>more sensible interaction of ObjC objects with C++ EH and RTTI.
>>
>
>Merging front ends provides only marginal maintenance benefits - a great 
>many changes to a merged front end would need to consider their possible 
>effects on many more different languages and language versions than at 
>present, making the burden associated with making each change that much 
>greater though maybe slightly reducing the number of changes to be made.  
>C and C++ have been diverging for over 20 years; many similarities are 
>only superficial and hide underlying differences and are dangerous to rely 
>on if you want a correct compiler.
>
>
As I understand it, the C/C++ divergence is not something that
anyone really wants - not the language committees, not implementors,
and most especially not users, who just don't buy any of our
explanations - their atttitude is "You control the languages and the
compilers, who's stopping you from solving the problem!?". One of the
things I think we could be doing for the users is to exert our influence
on language committee people to cooperate better than they have been,
and to lead by example, in the way that GCC is implemented.

The attitude that C and C++ are diverging, and so we might as well
go along, seems kind of defeatist, and will just encourage more of
the very things that we don't want to happen (just imagine further
divergence requiring two different preprocessor libraries - bleah!).

Stan

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Unified front end for C and C++ (was Re: New C parser [patch])
  2004-10-25 23:53       ` Scott Robert Ladd
@ 2004-10-26  0:03         ` Matt Austern
  2004-10-26  1:26           ` Scott Robert Ladd
  2004-10-26  0:28         ` New C parser [patch] Ziemowit Laski
  2004-10-26  0:37         ` New C parser [patch] Joseph S. Myers
  2 siblings, 1 reply; 42+ messages in thread
From: Matt Austern @ 2004-10-26  0:03 UTC (permalink / raw)
  To: Scott Robert Ladd; +Cc: Ziemowit Laski, gcc-patches, Joseph S. Myers

On Oct 25, 2004, at 4:42 PM, Scott Robert Ladd wrote:

> Ziemowit Laski wrote:
>> While I don't buy these time estimates (not both simultaneously, 
>> anyway), I do
>> agree that you would take a performance hit, at least in the 
>> short-term,
>> especially for plain C code.
>
> I don't think this is a hit GCC's users can afford at this point. 
> Another compile time regression may bring the hordes with pitchforks 
> and torches... ;)
>
> Also, C and C++ are heading down different and incompatible paths. It 
> will be increasingly difficult to manage the subtle-but-important 
> differences in a single front end if the two languages continue to 
> diverge.

On the one hand, that sounds plausible.  On the other hand: gcc is the 
only compiler I know of that has separate source bases for the C and 
C++ front ends.  Certainly EDG, MetroWerks, and Microsoft use the same 
source base for both, and at least one of those three uses not only the 
same source base but the same executable.

It may very well be true that getting from where we are to a common 
front end for C and C++ would be so hard that it's not worth doing.  
But we do have an existence proof that such a compiler is possible and 
that it's can be maintainable and fast.

(Changing the subject line because this no longer has much to do with 
Joseph's patch.)

			--Matt

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-25 23:53       ` Scott Robert Ladd
  2004-10-26  0:03         ` Unified front end for C and C++ (was Re: New C parser [patch]) Matt Austern
@ 2004-10-26  0:28         ` Ziemowit Laski
  2004-10-26  1:20           ` Scott Robert Ladd
  2004-10-26  0:37         ` New C parser [patch] Joseph S. Myers
  2 siblings, 1 reply; 42+ messages in thread
From: Ziemowit Laski @ 2004-10-26  0:28 UTC (permalink / raw)
  To: Scott Robert Ladd; +Cc: gcc-patches, Joseph S. Myers


On 25 Oct 2004, at 16.42, Scott Robert Ladd wrote:

> Ziemowit Laski wrote:
>> While I don't buy these time estimates (not both simultaneously, 
>> anyway), I do
>> agree that you would take a performance hit, at least in the 
>> short-term,
>> especially for plain C code.
>
> I don't think this is a hit GCC's users can afford at this point. 
> Another compile time regression may bring the hordes with pitchforks 
> and torches... ;)

That's probably true, esp. if you're asking users to migrate from the 
likes of CodeWarrior or Visual C++. :-(
>
> Also, C and C++ are heading down different and incompatible paths. It 
> will be increasingly difficult to manage the subtle-but-important 
> differences in a single front end if the two languages continue to 
> diverge.

So I take it there is no hope of the divergence being halted (let alone 
reversed)?  Or am I the only one who sees this divergence as an 
aberration? :-)

--Zem

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-24 22:49 ` Joseph S. Myers
@ 2004-10-26  0:32   ` Zack Weinberg
  2004-10-26  1:03     ` Andrew Pinski
                       ` (3 more replies)
  2004-10-27 20:25   ` Joseph S. Myers
  1 sibling, 4 replies; 42+ messages in thread
From: Zack Weinberg @ 2004-10-26  0:32 UTC (permalink / raw)
  To: Joseph S. Myers; +Cc: gcc-patches

"Joseph S. Myers" <jsm@polyomino.org.uk> writes:

> This second slightly revised patch version fixes the lexer lookahead
> strategy not to look ahead one token until required, so improving the
> correspondence of diagnostic locations with the new parser to those
> with the old parser.

I'm generally quite happy with this patch.  Could you enumerate all
the places where 2-token lookahead is needed, and give an opinion as
to how many of those could be eliminated through future cleanups?

Some kibitzes follow...

> +int yydebug;

Where is this used?

> +void
> +c_parse_init (void)
> +{
> +  init_reswords ();
> +}

Seems a little silly to have two functions one of which does nothing
but call the other.

> +/* A lexer with up to two tokens of look-ahead; more are not needed
> +   for C.  */
> +typedef struct c_lexer GTY (())
> +{
> +  /* The look-ahead tokens.  */
> +  c_token tokens[2];
> +  /* How many look-ahead tokens are available (0, 1 or 2).  */
> +  int tokens_avail;
> +} c_lexer;

Unlike C++, the lexer abstraction seems superfluous.  By folding this
into the parser structure, you would avoid an extra pointer
dereference on every lexer operation.

If you want to keep the distinction between parser and lexer, suggest
embedding this struct within the parser struct.  Come to think, I
could do that to the C++ parser too.  (makes note)

This is especially a good idea since ...

> +/* A parser structure recording information about the state and
> +   context of parsing.  */
> +typedef struct c_parser GTY(())
> +{
> +  /* The lexer.  */
> +  c_lexer *lexer;
> +  /* True if a syntax error is being recovered from; false otherwise.
> +     c_parser_error sets this flag.  It should clear this flag when
> +     enough tokens have been consumed to recover from the error.  */
> +  BOOL_BITFIELD error : 1;
> +} c_parser;

the parser structure has so little in it, and you could save a word of
memory by using a short int for the tokens_avail field.  This only
helps if you don't keep the separate lexer structure, because of tail
padding.

> +/* Issue a diagnostic of the form
> +      FILE:LINE: MESSAGE before TOKEN
> +   where TOKEN is the next token in the input stream of PARSER.
> +   MESSAGE (specified by the caller) is usually of the form "expected
> +   OTHER-TOKEN".
> +
> +   Do not issue a diagnostic if still recovering from an error.
> +
> +   ??? This is taken from the C++ parser, but building up messages in
> +   this way is not i18n-friendly and some other approach should be
> +   used.  */

As a tangential note, the C++ parser does it this way because the C
parser does it this way, and the C parser did it this way because
Bison's error reporting logic more or less forced it to.  Once your
new parser goes in, it will be easy to clean all of this up.

> +/* If the next token is the indicated keyword, consume it.  Otherwise,
> +   issue the error MSGID.  Returns true if found, false otherwise.  */
> +
> +static bool
> +c_parser_require_keyword (c_parser *parser,
> +			  enum rid keyword,
> +			  const char *msgid)

Please put this next to c_parser_require.

> +static void
> +c_parser_translation_unit (c_parser *parser)
> +{
> +  if (c_lexer_next_token_is (parser->lexer, CPP_EOF))
> +    {
> +      if (pedantic)
> +	pedwarn ("ISO C forbids an empty source file");
> +    }
> +  else
> +    {
> +      while (c_lexer_next_token_is_not (parser->lexer, CPP_EOF))
> +	{
> +	  void *obstack_position = obstack_alloc (&parser_obstack, 0);
> +	  ggc_collect ();
> +	  c_parser_external_declaration (parser);
> +	  obstack_free (&parser_obstack, obstack_position);
> +	}
> +    }
> +}

If control reaches the else block, we know the first token is not
CPP_EOF, so the while can be turned into a do-while; I do not think
the compiler can do that itself.

The obstack_alloc call can and should be hoisted out of the loop,
since your goal is to flush the entire obstack between external
declarations.


> +      int ext;
> +      SAVE_EXT_FLAGS (ext);

It is not obvious from the name that SAVE_EXT_FLAGS disables
diagnostics for extensions.  Also, it would be nice if the macro
returned a value (make it into an inline function maybe?) so that
this could be written

  int old_ext_flags = disable_extension_diagnostics ();
  ...
  restore_extension_diagnostics (old_ext_flags);

Also, (due to the recursive call to c_parser_external_declaration) you
accept arbitrary numbers of __extension__ keywords; is this really
intended?  (I see the grammar as commented does allow it.)

> +static void
> +c_parser_asm_definition (c_parser *parser)
> +{
> +  tree asm_str = c_parser_simple_asm_expr (parser);
> +  if (asm_str)
> +    assemble_asm (asm_str);
> +  c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected ';'");
> +}

This isn't your fault, but in the modern compiler, calling
assemble_anything directly from the parser is wrong.  The only way
asm() at top level can continue to make sense is if we pass it along
to cgraph, and somehow get cgraph to preserve the global order of
functions and asm()s.  [Mind you, I would not have any problem with
dropping this feature.]

> +	      c_parser_error (parser, "expected ',' or ';'");
> +	      break;
> +	    }
> +	}
> +      else
> +	{
> +	  c_parser_error (parser,
> +			  "expected ':', ',', ';' or '__attribute__'");
> +	  break;

These want %<%>ifying.  There are several other cases - I'm not going
to call them all out explicitly.

> +  /* ??? The old parser accepted wide string literals here, but do we
> +     want to?  */

The C++ parser does not accept wide string literals in any form
of asm() string, so I support not accepting them in C either.

I strongly recommend you grab the C++ parser's logic for
context-dependent string constant translation, by the way, so that the
horrible kludge used for C can go away and the C++ parser can stop
duplicating the get-a-sequence-of-strings-concatenate-and-translate-them 
routine.

> +   attrib:
> +     empty
> +     any-word
> +     any-word ( identifier )
> +     any-word ( identifier , nonempty-expr-list )
> +     any-word ( expr-list )
> +
> +   where the "identifier" must not be declared as a type, and
> +   "any-word" may be any identifier (including one declared as a
> +   type), a reserved word storage class specifier, type specifier or
> +   type qualifier.  ??? This still leaves out most reserved keywords
> +   (following the old parser), shouldn't we include them, and why not
> +   allow identifiers declared as types to start the arguments?  */

What's really going on is that there is a finite set of attribute
keywords which, in the "any-word" context, have that meaning and no
other.  This set really ought to be tagged explicitly by the lexer.
The grammar inside the parentheses has more constraints than you have
here, but it's also highly dependent on the specific attribute; maybe
we should have individual attribute-parser functions, as we do for
pragmas.

> +   ??? Allowing old-style [n] designators has as a side-effect (copied
> +   from the old parser) allowing ".member" without "=" as a
> +   designator, but perhaps as this has never been documented (and only
> +   worked from 2.95.x onwards) it could be freely removed.

I think I prefer allowing ".member" without "=" as a designator, until
and unless we decide to drop support for all of this grammar that's
not C99-ish.  In fact, I think I would prefer that the extension be
regularized down to (a) allowing the omission of the "=" from any
C99-style designator, and (b) allowing "identifier:" as meaning the
same thing as ".identifier =".  Unless that would introduce additional
grammar ambiguities, but I don't think it would.

It's not obvious to me from code or comment - we do allow 
"[ value ... value ] = initializer", yes?  (That might be
standardizable independently from the rest of the extensions in this
area.)

> +  /* Two cases cannot and do not have line numbers associated: If stmt
> +     is degenerate, such as "2;", then stmt is an INTEGER_CST, which
> +     cannot hold line numbers.  But that's OK because the statement
> +     will either be changed to a MODIFY_EXPR during gimplification of
> +     the statement expr, or discarded.  If stmt was compound, but
> +     without new variables, we will have skipped the creation of a
> +     BIND and will have a bare STATEMENT_LIST.  But that's OK because
> +     (recursively) all of the component statements should already have
> +     line numbers assigned.  */

Can we arrange to scrap all no-op statements very early - "2;" other
than as the last statement of a statement expr, and so on?

> +   ??? In accordance with the old parser, the declaration may be a
> +   nested function, which is then rejected in check_for_loop_decls,
> +   but does it make any sense for this to be included in the grammar?

First reaction: Barf.

Second reaction: It would be nice to pitch out invalid declarations
early.  check_for_loop_decls is potentially an expensive operation.

> +/* The actual parser and external interface.  */
> +
> +static GTY (()) c_parser *the_parser;

Is this global used for anything other than GGC, and does it actually
need to be allocated under the garbage collector?

zw

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-25 23:53       ` Scott Robert Ladd
  2004-10-26  0:03         ` Unified front end for C and C++ (was Re: New C parser [patch]) Matt Austern
  2004-10-26  0:28         ` New C parser [patch] Ziemowit Laski
@ 2004-10-26  0:37         ` Joseph S. Myers
  2 siblings, 0 replies; 42+ messages in thread
From: Joseph S. Myers @ 2004-10-26  0:37 UTC (permalink / raw)
  To: Scott Robert Ladd; +Cc: Ziemowit Laski, gcc-patches

On Mon, 25 Oct 2004, Scott Robert Ladd wrote:

> Also, C and C++ are heading down different and incompatible paths. It will be
> increasingly difficult to manage the subtle-but-important differences in a
> single front end if the two languages continue to diverge.

I would add that the calls a few years ago for merging the languages came 
generally from the C++ side; it has mainly been people with a largely C++ 
background calling for a merge of the languages, and the same may apply to 
merging front ends; in both cases the costs fall disproportionately on C.

(It has been said that there are three languages, C, C++ and C/C++, the 
last being that referred to by those who don't understand the difference.)

Starting 25 years ago, heading for a single language would have been a 
better direction.  However, we don't get to go back to 25 years ago and 
change the decisions that were made.  We have two languages evolving on 
diverging tracks; sometimes ideas and features come from one to the other, 
but because of the different histories may not be quite compatible.  

Where there are similarities in appearance and a subset of code works in 
both languages, the underlying concepts in the standards that define what 
it means in each language may still differ greatly - and there is a clear 
benefit to each compiler following the relevant underlying concepts 
directly, rather than just one set of concepts and trying to shoehorn the 
other on as a set of exceptions to those concepts.

There are many other standards layered on the existing C and C++ 
standards, which do not necessarily get updated for new standard versions, 
and users with code written and qualified for particular past standard 
versions, effectively meaning that the multiple C versions need supporting 
indefinitely (and note C90 is more widely used than C99) and when there is 
a new C++ version (one with new features as opposed to a defect-fix 
release such as C++03) there will also be a need for support of multiple 
C++ versions.

Of course code should be shared where appropriate and where it doesn't 
adversely affect maintenance.  For example, the preprocessor concepts are 
sufficiently nearly identical in the different standards that the 
preprocessor is usefully shared.  I am confident that in future the 
cp_lexer_* layer will be usable for C as well; the c_lexer_* layer in the 
new C parser is much simplified from what is there for C++ and is done 
that way to avoid combining other changes (e.g. lexing up front) not 
strictly needing to be combined with a new parser, but it is not something 
that need be different permanently.  C only needs 2-token look-ahead 
unlike the unlimited look-ahead needed for parsing C++, which makes things 
simpler, but with lexing up front I don't expect the simplicity will 
actually provide any performance advantages.

-- 
Joseph S. Myers               http://www.srcf.ucam.org/~jsm28/gcc/
    jsm@polyomino.org.uk (personal mail)
    joseph@codesourcery.com (CodeSourcery mail)
    jsm28@gcc.gnu.org (Bugzilla assignments and CCs)

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-26  1:03     ` Andrew Pinski
@ 2004-10-26  1:03       ` Zack Weinberg
  2004-10-26  1:11         ` Joseph S. Myers
  2004-10-26  1:30       ` Gabriel Dos Reis
  1 sibling, 1 reply; 42+ messages in thread
From: Zack Weinberg @ 2004-10-26  1:03 UTC (permalink / raw)
  To: Andrew Pinski; +Cc: gcc-patches, Joseph S. Myers

Andrew Pinski <pinskia@physics.uc.edu> writes:

> On Oct 25, 2004, at 8:03 PM, Zack Weinberg wrote:
>
>>> +	  c_parser_error (parser,
>>> +			  "expected ':', ',', ';' or '__attribute__'");
>>> +	  break;
>>
>> These want %<%>ifying.  There are several other cases - I'm not going
>> to call them all out explicitly.
>
> But I will say that we have C++ diagnostic regressions because of
> "%<%>ifying".  The reason why we have this problem is because
> c_parser_error takes a string but only does %s on it rather than passing
> it on to the diagnostic mechanism.

c_parse(r)_error have serious i18n issues already: I don't think this
one more makes much difference.  The way forward is to remove
c_parse_error entirely...

zw

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-26  0:32   ` Zack Weinberg
@ 2004-10-26  1:03     ` Andrew Pinski
  2004-10-26  1:03       ` Zack Weinberg
  2004-10-26  1:30       ` Gabriel Dos Reis
  2004-10-26  1:06     ` Joseph S. Myers
                       ` (2 subsequent siblings)
  3 siblings, 2 replies; 42+ messages in thread
From: Andrew Pinski @ 2004-10-26  1:03 UTC (permalink / raw)
  To: Zack Weinberg; +Cc: gcc-patches, Joseph S. Myers


On Oct 25, 2004, at 8:03 PM, Zack Weinberg wrote:

>> +	  c_parser_error (parser,
>> +			  "expected ':', ',', ';' or '__attribute__'");
>> +	  break;
>
> These want %<%>ifying.  There are several other cases - I'm not going
> to call them all out explicitly.

But I will say that we have C++ diagnostic regressions because of
"%<%>ifying".  The reason why we have this problem is because
c_parser_error takes a string but only does %s on it rather than passing
it on to the diagnostic mechanism.

Thanks,
Andrew Pinski

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-26  0:32   ` Zack Weinberg
  2004-10-26  1:03     ` Andrew Pinski
@ 2004-10-26  1:06     ` Joseph S. Myers
  2004-10-26  2:47       ` Joseph S. Myers
  2004-10-26 12:21       ` Kyuupi
  2004-10-26 11:42     ` Joseph S. Myers
  2004-10-27 19:04     ` Richard Henderson
  3 siblings, 2 replies; 42+ messages in thread
From: Joseph S. Myers @ 2004-10-26  1:06 UTC (permalink / raw)
  To: Zack Weinberg; +Cc: gcc-patches

On Mon, 25 Oct 2004, Zack Weinberg wrote:

> I'm generally quite happy with this patch.  Could you enumerate all
> the places where 2-token lookahead is needed, and give an opinion as
> to how many of those could be eliminated through future cleanups?

Distinguishing array declarators [*] and [*foo].

Distinguishing whether the first argument of attributes is an identifier 
or an expression starting with one.  (This needs cleaning up in future 
anyway as attributes taking an expression should allow e.g. an enum 
value.)

Distinguishing whether an identifier starting an initializer is an 
old-style field designator or the start of an expression.

Distinguishing whether an identifier where a statement is expected is a 
label or starts an expression.

Distinguishing whether __extension__ where a declaration or expression may 
occur (both in compound statements and at the start of for loops) starts a 
declaration or expression (in the latter case being an unary operator, not 
necessarily applying to the whole expression): this involves consuming all 
but one of the __extension__ tokens before the 2nd token lookahead 
suffices.

Distinguishing whether '(' in an expression starts a parenthesised 
subexpression, a statement expression or is followed by a type name.  
(Also after sizeof and alignof.)

Distinguishing whether an identifier in an expression is followed by '(' 
and so could be an implicit function declaration.

> > +int yydebug;
> 
> Where is this used?

Not used, set in c-opts.c.

> > +void
> > +c_parse_init (void)
> > +{
> > +  init_reswords ();
> > +}
> 
> Seems a little silly to have two functions one of which does nothing
> but call the other.

Pre-existing condition, but could indeed be cleaned up.

> Unlike C++, the lexer abstraction seems superfluous.  By folding this
> into the parser structure, you would avoid an extra pointer
> dereference on every lexer operation.
> 
> If you want to keep the distinction between parser and lexer, suggest
> embedding this struct within the parser struct.  Come to think, I
> could do that to the C++ parser too.  (makes note)

I do rather hope the lexer abstraction, lexing up front etc. could be 
shared between the C and C++ front ends.  It's rather a cut-down 
simplified version of the C++ lexer with the intention that when in future 
there is more complexity (e.g. to lex up front) then dropping in the C++ 
version may make sense.

> If control reaches the else block, we know the first token is not
> CPP_EOF, so the while can be turned into a do-while; I do not think
> the compiler can do that itself.

Somehow I'd be surprised if saving a single check per translation unit 
will ever be significant, but noted.

> Also, (due to the recursive call to c_parser_external_declaration) you
> accept arbitrary numbers of __extension__ keywords; is this really
> intended?  (I see the grammar as commented does allow it.)

It is intended as the existing parser permits it and it didn't seem 
something especially dodgy.  I could plausibly conceive of multiple 
__extension__ keywords being used in the presence of macros.

> This isn't your fault, but in the modern compiler, calling
> assemble_anything directly from the parser is wrong.  The only way
> asm() at top level can continue to make sense is if we pass it along
> to cgraph, and somehow get cgraph to preserve the global order of
> functions and asm()s.  [Mind you, I would not have any problem with
> dropping this feature.]

We do indeed need to pass asms to cgraph or otherwise fix code such as 
crtstuff.c that requires -fno-unit-at-a-time before we can get rid of 
non-unit-at-a-time mode.  This parser strictly follows the existing 
actions so any changes can be argued out separately - either before or 
after this parser went in.

> These want %<%>ifying.  There are several other cases - I'm not going
> to call them all out explicitly.

c_parser_error calls c_parse_error which outputs the argument literally, 
not as a format string, so the present code is correct until c_parse_error 
changes.

> > +  /* ??? The old parser accepted wide string literals here, but do we
> > +     want to?  */
> 
> The C++ parser does not accept wide string literals in any form
> of asm() string, so I support not accepting them in C either.

That's one of the changes I'm considering applying now to the current 
parser (to avoid the new one bringing in any syntax changes - and in this 
case because testcases that wide strings "work" in asms could be 
problematic) to clean up the syntax accepted.

> I strongly recommend you grab the C++ parser's logic for
> context-dependent string constant translation, by the way, so that the
> horrible kludge used for C can go away and the C++ parser can stop
> duplicating the get-a-sequence-of-strings-concatenate-and-translate-them 
> routine.

This is clearly a good idea - it's just the sort of thing I'd want to 
include in a separate follow-up patch.

> What's really going on is that there is a finite set of attribute
> keywords which, in the "any-word" context, have that meaning and no
> other.  This set really ought to be tagged explicitly by the lexer.
> The grammar inside the parentheses has more constraints than you have
> here, but it's also highly dependent on the specific attribute; maybe
> we should have individual attribute-parser functions, as we do for
> pragmas.

The grammar lists strictly what the parser accepts, not what makes sense 
to attribute handlers later.  My inclination would be that any keyword or 
identifier should be syntactically accepted as an attribute name; if not 
valid the warning should be given and the attribute arguments parsed for 
syntax but otherwise ignored; and if valid then it should determine 
whether the arguments start with an identifier or an expression so they 
can be parsed correctly.

> I think I prefer allowing ".member" without "=" as a designator, until
> and unless we decide to drop support for all of this grammar that's
> not C99-ish.  In fact, I think I would prefer that the extension be
> regularized down to (a) allowing the omission of the "=" from any
> C99-style designator, and (b) allowing "identifier:" as meaning the
> same thing as ".identifier =".  Unless that would introduce additional
> grammar ambiguities, but I don't think it would.

Given that ".member" without "=" was introduced accidentally, never 
documented and had a bug report about it (bug 16667), I just fixed that 
bug report by disallowing that syntax.  This leaves the old syntax as 
exactly the documented cases.

> It's not obvious to me from code or comment - we do allow 
> "[ value ... value ] = initializer", yes?  (That might be
> standardizable independently from the rest of the extensions in this
> area.)

That extension to "designator" is listed under GNU extensions.  In my 
current source the syntax now is

   initializer:
     assignment-expression
     { initializer-list }
     { initializer-list , }

   initializer-list:
     designation[opt] initializer
     initializer-list , designation[opt] initializer

   designation:
     designator-list =

   designator-list:
     designator
     designator-list designator

   designator:
     array-designator
     . identifier

   array-designator:
     [ constant-expression ]

   GNU extensions:

   initializer:
     { }

   designation:
     array-designator
     identifier :

   array-designator:
     [ constant-expression ... constant-expression ]

> > +  /* Two cases cannot and do not have line numbers associated: If stmt
> > +     is degenerate, such as "2;", then stmt is an INTEGER_CST, which
> > +     cannot hold line numbers.  But that's OK because the statement
> > +     will either be changed to a MODIFY_EXPR during gimplification of
> > +     the statement expr, or discarded.  If stmt was compound, but
> > +     without new variables, we will have skipped the creation of a
> > +     BIND and will have a bare STATEMENT_LIST.  But that's OK because
> > +     (recursively) all of the component statements should already have
> > +     line numbers assigned.  */
> 
> Can we arrange to scrap all no-op statements very early - "2;" other
> than as the last statement of a statement expr, and so on?

This code and comment is taken from the old parser.  I don't doubt there 
are cleanups possible; I'd just like to minimise the risk of this patch 
and keep non-required changes to separate patches, before or after.

> > +/* The actual parser and external interface.  */
> > +
> > +static GTY (()) c_parser *the_parser;
> 
> Is this global used for anything other than GGC, and does it actually
> need to be allocated under the garbage collector?

The global is only used for GGC, there is no real need for the 
parser/lexer to be garbage collected though I don't know whether tokens 
under there could be the only reference to some trees, but some such 
things were imitating the C++ parser until it's clearer quite how much of 
the lexer abstraction at least should be shared with C++.

-- 
Joseph S. Myers               http://www.srcf.ucam.org/~jsm28/gcc/
    jsm@polyomino.org.uk (personal mail)
    joseph@codesourcery.com (CodeSourcery mail)
    jsm28@gcc.gnu.org (Bugzilla assignments and CCs)

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-26  1:03       ` Zack Weinberg
@ 2004-10-26  1:11         ` Joseph S. Myers
  2004-10-26  8:23           ` Zack Weinberg
  0 siblings, 1 reply; 42+ messages in thread
From: Joseph S. Myers @ 2004-10-26  1:11 UTC (permalink / raw)
  To: Zack Weinberg; +Cc: Andrew Pinski, gcc-patches

On Mon, 25 Oct 2004, Zack Weinberg wrote:

> > But I will say that we have C++ diagnostic regressions because of
> > "%<%>ifying".  The reason why we have this problem is because
> > c_parser_error takes a string but only does %s on it rather than passing
> > it on to the diagnostic mechanism.
> 
> c_parse(r)_error have serious i18n issues already: I don't think this
> one more makes much difference.  The way forward is to remove
> c_parse_error entirely...

The regressions in English messages in the C++ front end are separate from 
the i18n problems.  For now, my parser uses literal '' to get properly 
formatted English messages.  When this area is cleaned up, or if 
c_parse_error starts accepting %<%>, then it can start using %<%>.  For 
now, the i18n is no worse than with the existing parser, and a key design 
feature of this patch is that it tries to be as minimal as possible for a 
parser replacement: it will follow cleanups made to the existing code 
before it goes in, and it will facilitate further cleanups once it is in, 
but will avoid leading on cleanups or changes that don't need entangling 
with a new parser.  I will however put ??? comments anywhere (as here) 
where it is suggested that the status quo is a mess and a good subject for 
a future cleanup.

-- 
Joseph S. Myers               http://www.srcf.ucam.org/~jsm28/gcc/
    jsm@polyomino.org.uk (personal mail)
    joseph@codesourcery.com (CodeSourcery mail)
    jsm28@gcc.gnu.org (Bugzilla assignments and CCs)

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-26  0:28         ` New C parser [patch] Ziemowit Laski
@ 2004-10-26  1:20           ` Scott Robert Ladd
  2004-10-26  6:08             ` Unified C and C++ front end (was Re: New C parser [patch]) Matt Austern
  0 siblings, 1 reply; 42+ messages in thread
From: Scott Robert Ladd @ 2004-10-26  1:20 UTC (permalink / raw)
  To: Ziemowit Laski; +Cc: gcc-patches, Joseph S. Myers

Ziemowit Laski wrote:
> So I take it there is no hope of the divergence being halted (let alone 
> reversed)?  Or am I the only one who sees this divergence as an 
> aberration? :-)

I haven't looked in on the debate for many months, but it boils down to 
the C++ people feeling that C99 was unnecessarily incompatible, and the 
C folk stating that they had no obligation to remain compatible with 
C++. Many of the differences are subtle, and some difficult to resolve 
in a common fashion.

I highly recommend David Tribble's excellent synopsis of the differences at:

     http://david.tribble.com/text/cdiffs.htm

-- 
Scott Robert Ladd
site: http://www.coyotegulch.com
blog: http://chaoticcoyote.blogspot.com

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Unified front end for C and C++ (was Re: New C parser [patch])
  2004-10-26  0:03         ` Unified front end for C and C++ (was Re: New C parser [patch]) Matt Austern
@ 2004-10-26  1:26           ` Scott Robert Ladd
  2004-10-26  1:43             ` Gabriel Dos Reis
  0 siblings, 1 reply; 42+ messages in thread
From: Scott Robert Ladd @ 2004-10-26  1:26 UTC (permalink / raw)
  To: Matt Austern; +Cc: Ziemowit Laski, gcc-patches, Joseph S. Myers

Matt Austern wrote:
> On the one hand, that sounds plausible.  On the other hand: gcc is the 
> only compiler I know of that has separate source bases for the C and C++ 
> front ends.  Certainly EDG, MetroWerks, and Microsoft use the same 
> source base for both, and at least one of those three uses not only the 
> same source base but the same executable.

None of those companies, so far as I know, implement C++, nor do they 
try to. I believe one of the goals of GCC is full C99 support, which is 
difficult to reconcile with C++.

> It may very well be true that getting from where we are to a common 
> front end for C and C++ would be so hard that it's not worth doing.  But 
> we do have an existence proof that such a compiler is possible and that 
> it's can be maintainable and fast.

And it may not be desirable if the two languages continue to diverge, 
which is likely.

-- 
Scott Robert Ladd
site: http://www.coyotegulch.com
blog: http://chaoticcoyote.blogspot.com

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-26  1:03     ` Andrew Pinski
  2004-10-26  1:03       ` Zack Weinberg
@ 2004-10-26  1:30       ` Gabriel Dos Reis
  1 sibling, 0 replies; 42+ messages in thread
From: Gabriel Dos Reis @ 2004-10-26  1:30 UTC (permalink / raw)
  To: Andrew Pinski; +Cc: Zack Weinberg, gcc-patches, Joseph S. Myers

Andrew Pinski <pinskia@physics.uc.edu> writes:

| On Oct 25, 2004, at 8:03 PM, Zack Weinberg wrote:
| 
| >> +	  c_parser_error (parser,
| >> +			  "expected ':', ',', ';' or '__attribute__'");
| >> +	  break;
| >
| > These want %<%>ifying.  There are several other cases - I'm not going
| > to call them all out explicitly.
| 
| But I will say that we have C++ diagnostic regressions because of
| "%<%>ifying".  The reason why we have this problem is because
| c_parser_error takes a string but only does %s on it rather than passing
| it on to the diagnostic mechanism.

The calling should be 

   c_parser_error (message)

instead of

   c_parser_error ("%s", message);

-- Gaby

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Unified front end for C and C++ (was Re: New C parser [patch])
  2004-10-26  1:26           ` Scott Robert Ladd
@ 2004-10-26  1:43             ` Gabriel Dos Reis
  2004-10-26  2:01               ` Scott Robert Ladd
  0 siblings, 1 reply; 42+ messages in thread
From: Gabriel Dos Reis @ 2004-10-26  1:43 UTC (permalink / raw)
  To: Scott Robert Ladd
  Cc: Matt Austern, Ziemowit Laski, gcc-patches, Joseph S. Myers

Scott Robert Ladd <coyote@coyotegulch.com> writes:

| Matt Austern wrote:
| > On the one hand, that sounds plausible.  On the other hand: gcc is
| > the only compiler I know of that has separate source bases for the C
| > and C++ front ends.  Certainly EDG, MetroWerks, and Microsoft use
| > the same source base for both, and at least one of those three uses
| > not only the same source base but the same executable.
| 
| None of those companies, so far as I know, implement C++, nor do they
| try to.

Pardon?

-- Gaby

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-26  0:03     ` Stan Shebs
@ 2004-10-26  1:46       ` Gabriel Dos Reis
  0 siblings, 0 replies; 42+ messages in thread
From: Gabriel Dos Reis @ 2004-10-26  1:46 UTC (permalink / raw)
  To: Stan Shebs; +Cc: Joseph S. Myers, Ziemowit Laski, gcc-patches

Stan Shebs <shebs@apple.com> writes:

| Joseph S. Myers wrote:
| 
| >On Mon, 25 Oct 2004, Ziemowit Laski wrote:
| >
| >
| >>I'm just curious as to why you think that writing a C parser from scratch is
| >>easier than tweaking Mark's parser to handle C.
| >>
| >
| > You have an interesting definition of "tweaking".  Geoffrey Keating
| > gave an estimate of 6-9 man-years to merge the front ends making
| > them handle the present languages as fast as at present.  This
| > compares to 1 man-week to write a functional C parser for the exact
| > present language accepted which also speeds up the compiler despite
| > not having been profiled or tuned for performance at all.
| >
| To be fair though, Geoff's estimate accounted for merging a lot
| of the semantic processing that continues to be semi-clones of
| each other, and continues to cause performance problems that have
| to be solved twice.

What are the basis of Geoff's estimates?

-- Gaby

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Unified front end for C and C++ (was Re: New C parser [patch])
  2004-10-26  1:43             ` Gabriel Dos Reis
@ 2004-10-26  2:01               ` Scott Robert Ladd
  2004-10-26 15:38                 ` Gabriel Dos Reis
  0 siblings, 1 reply; 42+ messages in thread
From: Scott Robert Ladd @ 2004-10-26  2:01 UTC (permalink / raw)
  To: Gabriel Dos Reis
  Cc: Matt Austern, Ziemowit Laski, gcc-patches, Joseph S. Myers

Gabriel Dos Reis wrote:
> | None of those companies, so far as I know, implement C++, nor do they
> | try to.
> 
> Pardon?

Yes, I meant C99, not C++. Microsoft has stated publicly that they have 
no interest in C99, for example.

Thanks for catching the tired typing.

-- 
Scott Robert Ladd
site: http://www.coyotegulch.com
blog: http://chaoticcoyote.blogspot.com

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-26  1:06     ` Joseph S. Myers
@ 2004-10-26  2:47       ` Joseph S. Myers
  2004-10-26  3:48         ` Mark Mitchell
  2004-10-26 12:21       ` Kyuupi
  1 sibling, 1 reply; 42+ messages in thread
From: Joseph S. Myers @ 2004-10-26  2:47 UTC (permalink / raw)
  To: Zack Weinberg; +Cc: gcc-patches

I wrote:

> Distinguishing whether an identifier where a statement is expected is a 
> label or starts an expression.

This description should also include distinguishing between a typedef name 
starting a declaration and being a label, where both declarations and 
statements are permitted: the checks look successively for a label; 
declaration specifiers; __extension__ (in which case then at the token 
after the sequence of __extension__); or else an unlabeled statement.

-- 
Joseph S. Myers               http://www.srcf.ucam.org/~jsm28/gcc/
    jsm@polyomino.org.uk (personal mail)
    joseph@codesourcery.com (CodeSourcery mail)
    jsm28@gcc.gnu.org (Bugzilla assignments and CCs)

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-26  2:47       ` Joseph S. Myers
@ 2004-10-26  3:48         ` Mark Mitchell
  0 siblings, 0 replies; 42+ messages in thread
From: Mark Mitchell @ 2004-10-26  3:48 UTC (permalink / raw)
  To: Joseph S. Myers; +Cc: Zack Weinberg, gcc-patches

There was some discussion in this thread about the practicality of 
merging the C and C++ front ends.

I think that merging the front ends is a good idea, despite the 
arguments about divergence between C99 and C++.  I think the commonality 
far outweights the differences, and that a C++ compiler ought to support 
the full C99 subset to the extent practical.  I do not believe it is 
nearly as hard as Geoff does, but I do not think it is trivial.  Joseph 
has proved that (for him) it is relatively easy to do a new C parser; we 
have an existence proof.

Further, I think that Joseph's patch is a good idea (for GCC 4.1) in 
that I do not think it will make it harder to merge the front ends, and 
I do think it will make it easier to work on the C front end until (if 
ever) the front ends actually are merged.

-- 
Mark Mitchell
CodeSourcery, LLC
(916) 791-8304
mark@codesourcery.com

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Unified C and C++ front end (was Re: New C parser [patch])
  2004-10-26  1:20           ` Scott Robert Ladd
@ 2004-10-26  6:08             ` Matt Austern
  2004-10-26 11:14               ` Joseph S. Myers
  0 siblings, 1 reply; 42+ messages in thread
From: Matt Austern @ 2004-10-26  6:08 UTC (permalink / raw)
  To: Scott Robert Ladd; +Cc: Ziemowit Laski, gcc-patches, Joseph S. Myers

On Oct 25, 2004, at 6:03 PM, Scott Robert Ladd wrote:

> Ziemowit Laski wrote:
>> So I take it there is no hope of the divergence being halted (let 
>> alone reversed)?  Or am I the only one who sees this divergence as an 
>> aberration? :-)
>
> I haven't looked in on the debate for many months, but it boils down 
> to the C++ people feeling that C99 was unnecessarily incompatible, and 
> the C folk stating that they had no obligation to remain compatible 
> with C++. Many of the differences are subtle, and some difficult to 
> resolve in a common fashion.
>
> I highly recommend David Tribble's excellent synopsis of the 
> differences at:
>
>     http://david.tribble.com/text/cdiffs.htm

Another useful summary, "written in support of the view that 
C/C++incompatibilities can and should be eliminated," is the following 
series of articles:
http://www.cuj.com/documents/s=8011/cuj0207stroustr/
http://www.cuj.com/documents/s=8010/cuj0208stroustr/
http://www.cuj.com/documents/s=8009/cuj0209stroustr/

			--Matt

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-26  1:11         ` Joseph S. Myers
@ 2004-10-26  8:23           ` Zack Weinberg
  0 siblings, 0 replies; 42+ messages in thread
From: Zack Weinberg @ 2004-10-26  8:23 UTC (permalink / raw)
  To: Joseph S. Myers; +Cc: Andrew Pinski, gcc-patches

"Joseph S. Myers" <jsm@polyomino.org.uk> writes:

> The regressions in English messages in the C++ front end are separate from 
> the i18n problems.  For now, my parser uses literal '' to get properly 
> formatted English messages.  When this area is cleaned up, or if 
> c_parse_error starts accepting %<%>, then it can start using %<%>.  For 
> now, the i18n is no worse than with the existing parser, and a key design 
> feature of this patch is that it tries to be as minimal as possible for a 
> parser replacement: it will follow cleanups made to the existing code 
> before it goes in, and it will facilitate further cleanups once it is in, 
> but will avoid leading on cleanups or changes that don't need entangling 
> with a new parser.  I will however put ??? comments anywhere (as here) 
> where it is suggested that the status quo is a mess and a good subject for 
> a future cleanup.

That's fine.  Just to be clear, I wrote down everything that came to
mind, without trying to distinguish between things that needed fixing
right now and things that should get fixed eventually.  I'm in
agreement with the plan of not entangling other changes with the
parser replacement.

zw

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Unified C and C++ front end (was Re: New C parser [patch])
  2004-10-26  6:08             ` Unified C and C++ front end (was Re: New C parser [patch]) Matt Austern
@ 2004-10-26 11:14               ` Joseph S. Myers
  2004-10-26 16:04                 ` Gabriel Dos Reis
  0 siblings, 1 reply; 42+ messages in thread
From: Joseph S. Myers @ 2004-10-26 11:14 UTC (permalink / raw)
  To: Matt Austern; +Cc: Scott Robert Ladd, Ziemowit Laski, gcc-patches

On Mon, 25 Oct 2004, Matt Austern wrote:

> Another useful summary, "written in support of the view that
> C/C++incompatibilities can and should be eliminated," is the following series
> of articles:
> http://www.cuj.com/documents/s=8011/cuj0207stroustr/
> http://www.cuj.com/documents/s=8010/cuj0208stroustr/
> http://www.cuj.com/documents/s=8009/cuj0209stroustr/

This looks rather like a follow-on to Stroustrup's previous attempts on 
the c++std-compat list in June 2001.  The general reception from the C 
side was rather unfavorable.  People don't think that incompatibilities 
are good in themselves (though maybe some people do) but that doesn't mean 
the subset language route is *now* (rather than 25 years ago) a good route 
to go for either language.  (And one view expressed was that there are too 
many kludges for the sake of compatibility in both languages and it would 
be better to let the languages go their own separate ways without needing 
such kludges.)

I do think there is a use for a common core machine model covering such 
things as sequence point rules and exactly what type and size of object is 
relevant for each memory access and when such an access is valid - hard 
questions which need addressing for both languages and where the existing 
definitions are sufficiently unclear that it cannot be said there is a 
clear incompatibility which existing code may rely on.  And some existing 
incompatibilities are probably susceptible to being fixed in future 
standards without too much incompatibility with real code - but we do have 
to live with the fact that for existing standard versions e.g. the rules 
for lexing UCNs differ.

-- 
Joseph S. Myers               http://www.srcf.ucam.org/~jsm28/gcc/
    jsm@polyomino.org.uk (personal mail)
    joseph@codesourcery.com (CodeSourcery mail)
    jsm28@gcc.gnu.org (Bugzilla assignments and CCs)

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-26  0:32   ` Zack Weinberg
  2004-10-26  1:03     ` Andrew Pinski
  2004-10-26  1:06     ` Joseph S. Myers
@ 2004-10-26 11:42     ` Joseph S. Myers
  2004-10-27 19:04     ` Richard Henderson
  3 siblings, 0 replies; 42+ messages in thread
From: Joseph S. Myers @ 2004-10-26 11:42 UTC (permalink / raw)
  To: Zack Weinberg; +Cc: gcc-patches

On Mon, 25 Oct 2004, Zack Weinberg wrote:

> Unlike C++, the lexer abstraction seems superfluous.  By folding this
> into the parser structure, you would avoid an extra pointer
> dereference on every lexer operation.

On consideration I'll fold it in and search-and-replace c_lexer_* 
(parser->lexer) with c_parser_* (parser).  If it then becomes useful to 
use the C++ lexer abstraction, these c_parser_* can become one-line inline 
functions redirecting to the c_lexer_* functions (and we can get a clearer 
notion of any performance differences involved by using the more efficient 
arrangement for C now).

-- 
Joseph S. Myers               http://www.srcf.ucam.org/~jsm28/gcc/
    jsm@polyomino.org.uk (personal mail)
    joseph@codesourcery.com (CodeSourcery mail)
    jsm28@gcc.gnu.org (Bugzilla assignments and CCs)

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-26  1:06     ` Joseph S. Myers
  2004-10-26  2:47       ` Joseph S. Myers
@ 2004-10-26 12:21       ` Kyuupi
  2004-10-26 12:32         ` Joseph S. Myers
  1 sibling, 1 reply; 42+ messages in thread
From: Kyuupi @ 2004-10-26 12:21 UTC (permalink / raw)
  To: Joseph S. Myers; +Cc: Zack Weinberg, gcc-patches

On Tue, 26 Oct 2004 00:37:21 +0000 (UTC), Joseph S. Myers
<jsm@polyomino.org.uk> wrote:

> On Mon, 25 Oct 2004, Zack Weinberg wrote:
> 
> > I'm generally quite happy with this patch.  Could you enumerate all
> > the places where 2-token lookahead is needed, and give an opinion as
> > to how many of those could be eliminated through future cleanups?
> 
> Distinguishing array declarators [*] and [*foo].
> 
> Distinguishing whether an identifier where a statement is expected is a
> label or starts an expression.

Those are the only two in standard C that I don't believe can be
reasonably avoided in a recursive descent parser.
 
> Distinguishing whether '(' in an expression starts a parenthesised
> subexpression, a statement expression or is followed by a type name.
> (Also after sizeof and alignof.)

This wasn't necessary in the parser I have written.
 
> Distinguishing whether an identifier in an expression is followed by '('
> and so could be an implicit function declaration.

I don't believe this is either.

Neil.

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-26 12:21       ` Kyuupi
@ 2004-10-26 12:32         ` Joseph S. Myers
  0 siblings, 0 replies; 42+ messages in thread
From: Joseph S. Myers @ 2004-10-26 12:32 UTC (permalink / raw)
  To: Kyuupi; +Cc: Zack Weinberg, gcc-patches

On Tue, 26 Oct 2004, Kyuupi wrote:

> > Distinguishing whether '(' in an expression starts a parenthesised
> > subexpression, a statement expression or is followed by a type name.
> > (Also after sizeof and alignof.)
> 
> This wasn't necessary in the parser I have written.

It's not strictly necessary if you consume the parenthesis and then have 
functions c_parser_{cast,unary}_expression_after_open_paren or similar.  
But I don't think there's anything intrinsically bad about using 2nd token 
lookahead (given it is needed elsewhere) and my parser doesn't recurse 
everywhere it could simply so as to be more "pure" recursive descent: it 
recurses where pragmatically convenient to make the code clearer or where 
a particular syntax production can occur in more than one place in the 
grammar.  Where more than 2nd token lookahead would be involved in "pure" 
recursive descent, the grammar is simply rearranged locally to avoid the 
problem (as with long sequences of __extension__, all but one discarded 
before deciding whether it is an unary operator or starts a declaration, 
or with sizeof compound-literal, only separable from sizeof ( type ) after 
the ( type ) has been parsed).

> > Distinguishing whether an identifier in an expression is followed by '('
> > and so could be an implicit function declaration.
> 
> I don't believe this is either.

This is rather an incident of the implementation: it could save the 
identifier in a temporary, consume the token and then look at the next 
token to determine the second argument to build_external_ref, but at 
present it uses slightly briefer source without the temporary.  This may 
change in a followup patch to fix bug 8927 by passing the location of the 
identifier to build_external_ref as well as its value.

-- 
Joseph S. Myers               http://www.srcf.ucam.org/~jsm28/gcc/
    jsm@polyomino.org.uk (personal mail)
    joseph@codesourcery.com (CodeSourcery mail)
    jsm28@gcc.gnu.org (Bugzilla assignments and CCs)

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Unified front end for C and C++ (was Re: New C parser [patch])
  2004-10-26  2:01               ` Scott Robert Ladd
@ 2004-10-26 15:38                 ` Gabriel Dos Reis
  0 siblings, 0 replies; 42+ messages in thread
From: Gabriel Dos Reis @ 2004-10-26 15:38 UTC (permalink / raw)
  To: Scott Robert Ladd
  Cc: Matt Austern, Ziemowit Laski, gcc-patches, Joseph S. Myers

Scott Robert Ladd <coyote@coyotegulch.com> writes:

| Gabriel Dos Reis wrote:
| > | None of those companies, so far as I know, implement C++, nor do they
| > | try to.
| > Pardon?
| 
| Yes, I meant C99, not C++.

Then, you're mistaken about EDG.

-- Gaby

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Unified C and C++ front end (was Re: New C parser [patch])
  2004-10-26 11:14               ` Joseph S. Myers
@ 2004-10-26 16:04                 ` Gabriel Dos Reis
  2004-10-26 16:51                   ` Joseph S. Myers
  0 siblings, 1 reply; 42+ messages in thread
From: Gabriel Dos Reis @ 2004-10-26 16:04 UTC (permalink / raw)
  To: Joseph S. Myers
  Cc: Matt Austern, Scott Robert Ladd, Ziemowit Laski, gcc-patches

"Joseph S. Myers" <jsm@polyomino.org.uk> writes:

| On Mon, 25 Oct 2004, Matt Austern wrote:
| 
| > Another useful summary, "written in support of the view that
| > C/C++incompatibilities can and should be eliminated," is the following series
| > of articles:
| > http://www.cuj.com/documents/s=8011/cuj0207stroustr/
| > http://www.cuj.com/documents/s=8010/cuj0208stroustr/
| > http://www.cuj.com/documents/s=8009/cuj0209stroustr/
| 
| This looks rather like a follow-on to Stroustrup's previous attempts on 
| the c++std-compat list in June 2001.  The general reception from the C 
| side was rather unfavorable.  People don't think that incompatibilities 
| are good in themselves (though maybe some people do) but that doesn't mean 
| the subset language route is *now* (rather than 25 years ago) a good route 
| to go for either language.  (And one view expressed was that there are too 
| many kludges for the sake of compatibility in both languages and it would 
| be better to let the languages go their own separate ways without needing 
| such kludges.)

Yet, both committees have agreed to work together on producing TRs
that tackle issues of interest to both languages.
My own perception, for working on C++ and lurking on C, is that
usually the "two languages should go incompatible" is the motto of a
vocal minority. At any case, I do not see that a productive way the
GCC project should take, for the design of the implementations.

-- Gaby

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: Unified C and C++ front end (was Re: New C parser [patch])
  2004-10-26 16:04                 ` Gabriel Dos Reis
@ 2004-10-26 16:51                   ` Joseph S. Myers
  0 siblings, 0 replies; 42+ messages in thread
From: Joseph S. Myers @ 2004-10-26 16:51 UTC (permalink / raw)
  To: Gabriel Dos Reis
  Cc: Matt Austern, Scott Robert Ladd, Ziemowit Laski, gcc-patches

On Tue, 26 Oct 2004, Gabriel Dos Reis wrote:

> Yet, both committees have agreed to work together on producing TRs
> that tackle issues of interest to both languages.

Just as there are issues of interest to both languages where work can 
usefully be shared, there are areas where front end code can usefully be 
shared - and just as importantly where internal representations can be 
shared.  This doesn't mean that "the whole thing" is the correct part to 
share, or that code or internal representations should be shared where the 
internal concepts in the languages have diverged too much for a single 
code base to be evidently correct in both cases even if the source code 
syntax is the same or similar in both languages.  (Nor does it mean the 
present c-common.c is a good selection of areas where sharing makes sense; 
most of the other shared source files, with better-defined aims, are 
rather better examples of sharing.)

-- 
Joseph S. Myers               http://www.srcf.ucam.org/~jsm28/gcc/
    jsm@polyomino.org.uk (personal mail)
    joseph@codesourcery.com (CodeSourcery mail)
    jsm28@gcc.gnu.org (Bugzilla assignments and CCs)

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-26  0:32   ` Zack Weinberg
                       ` (2 preceding siblings ...)
  2004-10-26 11:42     ` Joseph S. Myers
@ 2004-10-27 19:04     ` Richard Henderson
  2004-10-27 19:11       ` Richard Guenther
  2004-10-27 19:31       ` Zack Weinberg
  3 siblings, 2 replies; 42+ messages in thread
From: Richard Henderson @ 2004-10-27 19:04 UTC (permalink / raw)
  To: Zack Weinberg; +Cc: Joseph S. Myers, gcc-patches

On Mon, Oct 25, 2004 at 05:03:31PM -0700, Zack Weinberg wrote:
> The only way
> asm() at top level can continue to make sense is if we pass it along
> to cgraph, and somehow get cgraph to preserve the global order of
> functions and asm()s.  [Mind you, I would not have any problem with
> dropping this feature.]

False.

----
static void doit(int, int);

__asm__("\
	.ent doit	\n\
doit:			\n\
	...		\n\
	ret		\n\
	.end doit	\n\
");

void foo(int a)
{
  doit(a, a+1);
}
----

For non-trivial definitions of doit, this can be extremely useful.
Glibc, for instance, uses this in ld.so.


r~

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-27 19:04     ` Richard Henderson
@ 2004-10-27 19:11       ` Richard Guenther
  2004-10-27 19:41         ` Zack Weinberg
  2004-10-27 19:31       ` Zack Weinberg
  1 sibling, 1 reply; 42+ messages in thread
From: Richard Guenther @ 2004-10-27 19:11 UTC (permalink / raw)
  To: Richard Henderson, Zack Weinberg, Joseph S. Myers, gcc-patches

On Wed, 27 Oct 2004 11:49:57 -0700, Richard Henderson <rth@redhat.com> wrote:
> On Mon, Oct 25, 2004 at 05:03:31PM -0700, Zack Weinberg wrote:
> > The only way
> > asm() at top level can continue to make sense is if we pass it along
> > to cgraph, and somehow get cgraph to preserve the global order of
> > functions and asm()s.  [Mind you, I would not have any problem with
> > dropping this feature.]
> 
> False.
> 
> ----
> static void doit(int, int);
> 
> __asm__("\
>         .ent doit       \n\
> doit:                   \n\
>         ...             \n\
>         ret             \n\
>         .end doit       \n\
> ");
> 
> void foo(int a)
> {
>   doit(a, a+1);
> }
> ----
> 
> For non-trivial definitions of doit, this can be extremely useful.
> Glibc, for instance, uses this in ld.so.

Why doesn't it use an assembly file then?  You seem to gain nothing
by using gcc inline assembly this way.

Richard.

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-27 19:04     ` Richard Henderson
  2004-10-27 19:11       ` Richard Guenther
@ 2004-10-27 19:31       ` Zack Weinberg
  2004-10-27 21:31         ` Richard Henderson
  1 sibling, 1 reply; 42+ messages in thread
From: Zack Weinberg @ 2004-10-27 19:31 UTC (permalink / raw)
  To: Richard Henderson; +Cc: Joseph S. Myers, gcc-patches

Richard Henderson <rth@redhat.com> writes:

> On Mon, Oct 25, 2004 at 05:03:31PM -0700, Zack Weinberg wrote:
>> The only way
>> asm() at top level can continue to make sense is if we pass it along
>> to cgraph, and somehow get cgraph to preserve the global order of
>> functions and asm()s.  [Mind you, I would not have any problem with
>> dropping this feature.]
>
> False.
>
> ----
> static void doit(int, int);
>
> __asm__("\
> 	.ent doit	\n\
> doit:			\n\
> 	...		\n\
> 	ret		\n\
> 	.end doit	\n\
> ");

... Okay, so _some_ uses of asm() at top level aren't ordering-
sensitive.  Doesn't change the fact that some are, and that it's not
an obvious proposition to come up with another way to do them.  (If I
could think of a better way to write crtstuff.c I would have done it
long ago.)

On a more abstract note, whether or not we decide we want to keep
supporting order-sensitive top level asm() SOMEhow, it's still a
layering violation to be calling assembly output routines directly
from the parser.

zw

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-27 19:11       ` Richard Guenther
@ 2004-10-27 19:41         ` Zack Weinberg
  0 siblings, 0 replies; 42+ messages in thread
From: Zack Weinberg @ 2004-10-27 19:41 UTC (permalink / raw)
  To: Richard Guenther; +Cc: Richard Henderson, Joseph S. Myers, gcc-patches

Richard Guenther <richard.guenther@gmail.com> writes:

> On Wed, 27 Oct 2004 11:49:57 -0700, Richard Henderson <rth@redhat.com> wrote:
...
>> ----
>> static void doit(int, int);
...

>> For non-trivial definitions of doit, this can be extremely useful.
>> Glibc, for instance, uses this in ld.so.
>
> Why doesn't it use an assembly file then?  You seem to gain nothing
> by using gcc inline assembly this way.

Note the static declaration of doit, and the reference to the dynamic
loader.  I imagine it's crucial to generate a local call sequence for
the use from C (for instance, to prevent GCC from calling through the
PLT when it hasn't been set up yet).

zw

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-24 22:49 ` Joseph S. Myers
  2004-10-26  0:32   ` Zack Weinberg
@ 2004-10-27 20:25   ` Joseph S. Myers
  1 sibling, 0 replies; 42+ messages in thread
From: Joseph S. Myers @ 2004-10-27 20:25 UTC (permalink / raw)
  To: gcc-patches

This patch version has various cleanups people have suggested, in 
particular the c_lexer is no longer separate from the c_parser structure 
and __extension__ uses functions rather than macros.  It doesn't yet 
reject wide strings in asm because attempting to do so ran into bug 18164. 
I haven't yet started implementing ObjC support.

Bootstrapped i686-pc-linux-gnu (with regressions for tests involving 
syntax error diagnostics).  I haven't run fresh benchmarks for whether 
these changes improve performance.

-- 
Joseph S. Myers               http://www.srcf.ucam.org/~jsm28/gcc/
    jsm@polyomino.org.uk (personal mail)
    joseph@codesourcery.com (CodeSourcery mail)
    jsm28@gcc.gnu.org (Bugzilla assignments and CCs)

diff -rupN GCC.orig/gcc/Makefile.in GCC/gcc/Makefile.in
--- GCC.orig/gcc/Makefile.in	2004-10-25 22:08:21.000000000 +0000
+++ GCC/gcc/Makefile.in	2004-10-26 21:06:27.000000000 +0000
@@ -191,7 +191,6 @@ gcc.o-warn = -Wno-error
 build/insn-conditions.o-warn = -Wno-error
 # Bison-1.75 output often yields (harmless) -Wtraditional warnings
 build/gengtype-yacc.o-warn = -Wno-error
-c-parse.o-warn = -Wno-error
 # flex output may yield harmless "no previous prototype" warnings
 build/gengtype-lex.o-warn = -Wno-error
 # SYSCALLS.c misses prototypes
@@ -882,7 +881,7 @@ C_AND_OBJC_OBJS = attribs.o c-errors.o c
   c-gimplify.o tree-mudflap.o c-pretty-print.o
 
 # Language-specific object files for C.
-C_OBJS = c-parse.o c-lang.o stub-objc.o $(C_AND_OBJC_OBJS)
+C_OBJS = c-parser.o c-lang.o stub-objc.o $(C_AND_OBJC_OBJS)
 
 # Language-independent object files.
 OBJS-common = \
@@ -1348,24 +1347,15 @@ s-crt0:	$(CRT0_S) $(MCRT0_S) $(GCC_PASSE
 
 c-errors.o: c-errors.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
     $(C_TREE_H) $(FLAGS_H) $(DIAGNOSTIC_H) $(TM_P_H)
-c-parse.o : c-parse.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
+c-parser.o : c-parser.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
     $(GGC_H) intl.h $(C_TREE_H) input.h $(FLAGS_H) toplev.h output.h \
-    $(CPPLIB_H) varray.h gt-c-parse.h langhooks.h $(C_COMMON_H) $(C_PRAGMA_H)
+    $(CPPLIB_H) varray.h gt-c-parser.h langhooks.h $(C_COMMON_H) $(C_PRAGMA_H)
 
 srcextra: gcc.srcextra lang.srcextra
 
-gcc.srcextra: c-parse.y c-parse.c gengtype-lex.c gengtype-yacc.c gengtype-yacc.h
+gcc.srcextra: gengtype-lex.c gengtype-yacc.c gengtype-yacc.h
 	-cp -p $^ $(srcdir)
 
-c-parse.c: c-parse.y
-	-$(BISON) $(BISONFLAGS) -o $@ $<
-
-c-parse.y: c-parse.in
-	echo '/*WARNING: This file is automatically generated!*/' >tmp-c-parse.y
-	sed -e "/^@@ifobjc.*/,/^@@end_ifobjc.*/d" \
-	    -e "/^@@ifc.*/d" -e "/^@@end_ifc.*/d" $< >>tmp-c-parse.y
-	$(SHELL) $(srcdir)/../move-if-change tmp-c-parse.y $@
-
 c-incpath.o: c-incpath.c c-incpath.h $(CONFIG_H) $(SYSTEM_H) $(CPPLIB_H) \
 		intl.h prefix.h coretypes.h $(TM_H) cppdefault.h $(TARGET_H) \
 		$(MACHMODE_H)
@@ -2416,7 +2406,7 @@ GTFILES = $(srcdir)/input.h $(srcdir)/co
   $(srcdir)/sdbout.c $(srcdir)/stor-layout.c \
   $(srcdir)/stringpool.c $(srcdir)/tree.c $(srcdir)/varasm.c \
   $(srcdir)/tree-mudflap.c $(srcdir)/tree-flow.h \
-  $(srcdir)/c-objc-common.c $(srcdir)/c-common.c $(srcdir)/c-parse.in \
+  $(srcdir)/c-objc-common.c $(srcdir)/c-common.c $(srcdir)/c-parser.c \
   $(srcdir)/tree-ssanames.c $(srcdir)/tree-eh.c \
   $(srcdir)/tree-phinodes.c $(srcdir)/tree-cfg.c \
   $(srcdir)/tree-dfa.c $(srcdir)/tree-ssa-propagate.c \
@@ -2438,7 +2428,7 @@ gt-emit-rtl.h gt-explow.h gt-stor-layout
 gt-lists.h gt-alias.h gt-cselib.h gt-fold-const.h gt-gcse.h \
 gt-expr.h gt-sdbout.h gt-optabs.h gt-bitmap.h gt-dojump.h \
 gt-dwarf2out.h gt-ra-build.h gt-reg-stack.h gt-dwarf2asm.h \
-gt-dbxout.h gt-c-common.h gt-c-decl.h gt-c-parse.h \
+gt-dbxout.h gt-c-common.h gt-c-decl.h gt-c-parser.h \
 gt-c-pragma.h gtype-c.h gt-input.h gt-cfglayout.h \
 gt-tree-mudflap.h \
 gt-tree-ssa-ccp.h gt-tree-eh.h \
@@ -3108,7 +3098,7 @@ distclean: clean lang.distclean
 	-rm -f Makefile *.oaux
 	-rm -f gthr-default.h
 	-rm -f */stage1 */stage2 */stage3 */stage4 */include */stageprofile */stagefeedback
-	-rm -f c-parse.y c-parse.c c-parse.output TAGS */TAGS
+	-rm -f TAGS */TAGS
 	-rm -f *.asm
 	-rm -f site.exp site.bak testsuite/site.exp testsuite/site.bak
 	-rm -f testsuite/*.log testsuite/*.sum
@@ -3129,7 +3119,6 @@ maintainer-clean:
 	@echo 'This command is intended for maintainers to use; it'
 	@echo 'deletes files that may need special tools to rebuild.'
 	$(MAKE) lang.maintainer-clean distclean
-	-rm -f $(srcdir)/c-parse.y $(srcdir)/c-parse.c
 	-rm -f cpp.??s cpp.*aux
 	-rm -f gcc.??s gcc.*aux
 	-rm -f $(docdir)/*.info $(docdir)/*.1 $(docdir)/*.7 $(docdir)/*.dvi
@@ -3603,7 +3592,7 @@ TAGS: lang.tags
 	    incs="$$incs --include $$dir/TAGS.sub";	\
 	  fi;						\
 	done;						\
-	etags -o TAGS.sub *.y *.h *.c -l yacc c-parse.in; \
+	etags -o TAGS.sub *.y *.h *.c; \
 	etags --include TAGS.sub $$incs)
 
 # ------------------------------------------------------
diff -rupN GCC.orig/gcc/c-config-lang.in GCC/gcc/c-config-lang.in
--- GCC.orig/gcc/c-config-lang.in	2002-12-27 19:38:52.000000000 +0000
+++ GCC/gcc/c-config-lang.in	2004-10-21 23:47:38.000000000 +0000
@@ -23,4 +23,4 @@
 # files used by C that have garbage collection GTY macros in them
 # which therefore need to be scanned by gengtype.c.
 
-gtfiles="\$(srcdir)/c-lang.c \$(srcdir)/c-parse.in \$(srcdir)/c-tree.h \$(srcdir)/c-decl.c \$(srcdir)/c-common.c \$(srcdir)/c-common.h \$(srcdir)/c-pragma.c \$(srcdir)/c-objc-common.c"
+gtfiles="\$(srcdir)/c-lang.c \$(srcdir)/c-tree.h \$(srcdir)/c-decl.c \$(srcdir)/c-common.c \$(srcdir)/c-common.h \$(srcdir)/c-pragma.c \$(srcdir)/c-objc-common.c \$(srcdir)/c-parser.c"
diff -rupN GCC.orig/gcc/c-decl.c GCC/gcc/c-decl.c
--- GCC.orig/gcc/c-decl.c	2004-10-25 22:08:21.000000000 +0000
+++ GCC/gcc/c-decl.c	2004-10-26 21:06:27.000000000 +0000
@@ -6674,6 +6674,8 @@ build_null_declspecs (void)
   ret->attrs = 0;
   ret->typespec_word = cts_none;
   ret->storage_class = csc_none;
+  ret->declspecs_seen_p = false;
+  ret->type_seen_p = false;
   ret->non_sc_seen_p = false;
   ret->typedef_p = false;
   ret->tag_defined_p = false;
@@ -6703,6 +6705,7 @@ declspecs_add_qual (struct c_declspecs *
   enum rid i;
   bool dupe = false;
   specs->non_sc_seen_p = true;
+  specs->declspecs_seen_p = true;
   gcc_assert (TREE_CODE (qual) == IDENTIFIER_NODE
 	      && C_IS_RESERVED_WORD (qual));
   i = C_RID_CODE (qual);
@@ -6736,6 +6739,8 @@ declspecs_add_type (struct c_declspecs *
 {
   tree type = spec.spec;
   specs->non_sc_seen_p = true;
+  specs->declspecs_seen_p = true;
+  specs->type_seen_p = true;
   if (TREE_DEPRECATED (type))
     specs->deprecated_p = true;
 
@@ -7030,6 +7035,7 @@ declspecs_add_scspec (struct c_declspecs
   enum rid i;
   enum c_storage_class n = csc_none;
   bool dupe = false;
+  specs->declspecs_seen_p = true;
   gcc_assert (TREE_CODE (scspec) == IDENTIFIER_NODE
 	      && C_IS_RESERVED_WORD (scspec));
   i = C_RID_CODE (scspec);
@@ -7112,6 +7118,7 @@ struct c_declspecs *
 declspecs_add_attrs (struct c_declspecs *specs, tree attrs)
 {
   specs->attrs = chainon (attrs, specs->attrs);
+  specs->declspecs_seen_p = true;
   return specs;
 }
 
diff -rupN GCC.orig/gcc/c-parser.c GCC/gcc/c-parser.c
--- GCC.orig/gcc/c-parser.c	1970-01-01 00:00:00.000000000 +0000
+++ GCC/gcc/c-parser.c	2004-10-27 15:21:16.000000000 +0000
@@ -0,0 +1,4811 @@
+/* Parser for C and Objective-C.
+   Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997,
+   1998, 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+   Parser actions based on the old Bison parser; structure somewhat
+   influenced by and fragments based on the C++ parser.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
+
+/* TODO:
+
+   Support Objective-C.
+
+   Make sure all relevant comments, and all relevant code from all
+   actions, brought over from old parser.  Verify exact correspondence
+   of syntax accepted.
+
+   Add testcases covering every input symbol in every state in old and
+   new parsers.
+
+   Include full syntax for GNU C, including erroneous cases accepted
+   with error messages, in syntax productions in comments.
+
+   Make more diagnostics in the front end generally take an explicit
+   location rather than implicitly using input_location.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "tree.h"
+#include "langhooks.h"
+#include "input.h"
+#include "cpplib.h"
+#include "timevar.h"
+#include "c-pragma.h"
+#include "c-tree.h"
+#include "flags.h"
+#include "output.h"
+#include "toplev.h"
+#include "ggc.h"
+#include "c-common.h"
+
+\f
+/* Miscellaneous data and functions needed for the parser.  */
+
+int yydebug;
+
+/* Objective-C specific parser/lexer information.  */
+
+static int objc_pq_context = 0;
+
+/* The following flag is needed to contextualize Objective-C lexical
+   analysis.  In some cases (e.g., 'int NSObject;'), it is undesirable
+   to bind an identifier to an Objective-C class, even if a class with
+   that name exists.  */
+static int objc_need_raw_identifier = 0;
+#define OBJC_NEED_RAW_IDENTIFIER(VAL)		\
+  do {						\
+    if (c_dialect_objc ())			\
+      objc_need_raw_identifier = VAL;		\
+  } while (0)
+
+/* The reserved keyword table.  */
+struct resword
+{
+  const char *word;
+  ENUM_BITFIELD(rid) rid : 16;
+  unsigned int disable   : 16;
+};
+
+/* Disable mask.  Keywords are disabled if (reswords[i].disable &
+   mask) is _true_.  */
+#define D_C89	0x01	/* not in C89 */
+#define D_EXT	0x02	/* GCC extension */
+#define D_EXT89	0x04	/* GCC extension incorporated in C99 */
+#define D_OBJC	0x08	/* Objective C only */
+
+static const struct resword reswords[] =
+{
+  { "_Bool",		RID_BOOL,	0 },
+  { "_Complex",		RID_COMPLEX,	0 },
+  { "__FUNCTION__",	RID_FUNCTION_NAME, 0 },
+  { "__PRETTY_FUNCTION__", RID_PRETTY_FUNCTION_NAME, 0 },
+  { "__alignof",	RID_ALIGNOF,	0 },
+  { "__alignof__",	RID_ALIGNOF,	0 },
+  { "__asm",		RID_ASM,	0 },
+  { "__asm__",		RID_ASM,	0 },
+  { "__attribute",	RID_ATTRIBUTE,	0 },
+  { "__attribute__",	RID_ATTRIBUTE,	0 },
+  { "__builtin_choose_expr", RID_CHOOSE_EXPR, 0 },
+  { "__builtin_offsetof", RID_OFFSETOF, 0 },
+  { "__builtin_types_compatible_p", RID_TYPES_COMPATIBLE_P, 0 },
+  { "__builtin_va_arg",	RID_VA_ARG,	0 },
+  { "__complex",	RID_COMPLEX,	0 },
+  { "__complex__",	RID_COMPLEX,	0 },
+  { "__const",		RID_CONST,	0 },
+  { "__const__",	RID_CONST,	0 },
+  { "__extension__",	RID_EXTENSION,	0 },
+  { "__func__",		RID_C99_FUNCTION_NAME, 0 },
+  { "__imag",		RID_IMAGPART,	0 },
+  { "__imag__",		RID_IMAGPART,	0 },
+  { "__inline",		RID_INLINE,	0 },
+  { "__inline__",	RID_INLINE,	0 },
+  { "__label__",	RID_LABEL,	0 },
+  { "__real",		RID_REALPART,	0 },
+  { "__real__",		RID_REALPART,	0 },
+  { "__restrict",	RID_RESTRICT,	0 },
+  { "__restrict__",	RID_RESTRICT,	0 },
+  { "__signed",		RID_SIGNED,	0 },
+  { "__signed__",	RID_SIGNED,	0 },
+  { "__thread",		RID_THREAD,	0 },
+  { "__typeof",		RID_TYPEOF,	0 },
+  { "__typeof__",	RID_TYPEOF,	0 },
+  { "__volatile",	RID_VOLATILE,	0 },
+  { "__volatile__",	RID_VOLATILE,	0 },
+  { "asm",		RID_ASM,	D_EXT },
+  { "auto",		RID_AUTO,	0 },
+  { "break",		RID_BREAK,	0 },
+  { "case",		RID_CASE,	0 },
+  { "char",		RID_CHAR,	0 },
+  { "const",		RID_CONST,	0 },
+  { "continue",		RID_CONTINUE,	0 },
+  { "default",		RID_DEFAULT,	0 },
+  { "do",		RID_DO,		0 },
+  { "double",		RID_DOUBLE,	0 },
+  { "else",		RID_ELSE,	0 },
+  { "enum",		RID_ENUM,	0 },
+  { "extern",		RID_EXTERN,	0 },
+  { "float",		RID_FLOAT,	0 },
+  { "for",		RID_FOR,	0 },
+  { "goto",		RID_GOTO,	0 },
+  { "if",		RID_IF,		0 },
+  { "inline",		RID_INLINE,	D_EXT89 },
+  { "int",		RID_INT,	0 },
+  { "long",		RID_LONG,	0 },
+  { "register",		RID_REGISTER,	0 },
+  { "restrict",		RID_RESTRICT,	D_C89 },
+  { "return",		RID_RETURN,	0 },
+  { "short",		RID_SHORT,	0 },
+  { "signed",		RID_SIGNED,	0 },
+  { "sizeof",		RID_SIZEOF,	0 },
+  { "static",		RID_STATIC,	0 },
+  { "struct",		RID_STRUCT,	0 },
+  { "switch",		RID_SWITCH,	0 },
+  { "typedef",		RID_TYPEDEF,	0 },
+  { "typeof",		RID_TYPEOF,	D_EXT },
+  { "union",		RID_UNION,	0 },
+  { "unsigned",		RID_UNSIGNED,	0 },
+  { "void",		RID_VOID,	0 },
+  { "volatile",		RID_VOLATILE,	0 },
+  { "while",		RID_WHILE,	0 },
+  /* These Objective-C keywords are recognized only immediately after
+     an '@'.  */
+  { "class",		RID_AT_CLASS,		D_OBJC },
+  { "compatibility_alias", RID_AT_ALIAS,	D_OBJC },
+  { "defs",		RID_AT_DEFS,		D_OBJC },
+  { "encode",		RID_AT_ENCODE,		D_OBJC },
+  { "end",		RID_AT_END,		D_OBJC },
+  { "implementation",	RID_AT_IMPLEMENTATION,	D_OBJC },
+  { "interface",	RID_AT_INTERFACE,	D_OBJC },
+  { "private",		RID_AT_PRIVATE,		D_OBJC },
+  { "protected",	RID_AT_PROTECTED,	D_OBJC },
+  { "protocol",		RID_AT_PROTOCOL,	D_OBJC },
+  { "public",		RID_AT_PUBLIC,		D_OBJC },
+  { "selector",		RID_AT_SELECTOR,	D_OBJC },
+  { "throw",		RID_AT_THROW,		D_OBJC },
+  { "try",		RID_AT_TRY,		D_OBJC },
+  { "catch",		RID_AT_CATCH,		D_OBJC },
+  { "finally",		RID_AT_FINALLY,		D_OBJC },
+  { "synchronized",	RID_AT_SYNCHRONIZED,	D_OBJC },
+  /* These are recognized only in protocol-qualifier context
+     (see above) */
+  { "bycopy",		RID_BYCOPY,		D_OBJC },
+  { "byref",		RID_BYREF,		D_OBJC },
+  { "in",		RID_IN,			D_OBJC },
+  { "inout",		RID_INOUT,		D_OBJC },
+  { "oneway",		RID_ONEWAY,		D_OBJC },
+  { "out",		RID_OUT,		D_OBJC },
+};
+#define N_reswords (sizeof reswords / sizeof (struct resword))
+
+/* Initialization routine for this file.  */
+
+void
+c_parse_init (void)
+{
+  /* The only initialization required is of the reserved word
+     identifiers.  */
+  unsigned int i;
+  tree id;
+  int mask = (flag_isoc99 ? 0 : D_C89)
+	      | (flag_no_asm ? (flag_isoc99 ? D_EXT : D_EXT|D_EXT89) : 0);
+
+  if (!c_dialect_objc ())
+     mask |= D_OBJC;
+
+  ridpointers = GGC_CNEWVEC (tree, (int) RID_MAX);
+  for (i = 0; i < N_reswords; i++)
+    {
+      /* If a keyword is disabled, do not enter it into the table
+	 and so create a canonical spelling that isn't a keyword.  */
+      if (reswords[i].disable & mask)
+	continue;
+
+      id = get_identifier (reswords[i].word);
+      C_RID_CODE (id) = reswords[i].rid;
+      C_IS_RESERVED_WORD (id) = 1;
+      ridpointers [(int) reswords[i].rid] = id;
+    }
+}
+\f
+/* The C lexer intermediates between the lexer in cpplib and c-lex.c
+   and the C parser.  Unlike the C++ lexer, the parser structure
+   stores the lexer information instead of using a separate structure.
+   Identifiers are separated into ordinary identifiers, type names,
+   keywords and some other Objective-C types of identifiers, and some
+   look-ahead is maintained.
+
+   ??? It might be a good idea to lex the whole file up front (as for
+   C++).  It would then be possible to share more of the C and C++
+   lexer code, if desired.  */
+
+/* The following local token type is used.  */
+
+/* A keyword.  */
+#define CPP_KEYWORD ((enum cpp_ttype) (N_TTYPES + 1))
+
+/* The number of token types, including C-specific ones.  */
+#define N_C_TTYPES ((int) (CPP_KEYWORD + 1))
+
+/* More information about the type of a CPP_NAME token.  */
+typedef enum c_id_kind {
+  /* An ordinary identifier.  */
+  C_ID_ID,
+  /* An identifier declared as a typedef name.  */
+  C_ID_TYPENAME,
+  /* An identifier declared as an Objective-C class name.  */
+  C_ID_CLASSNAME,
+  /* Not an identifier.  */
+  C_ID_NONE
+} c_id_kind;
+
+/* A single C token after string literal concatenation and conversion
+   of preprocessing tokens to tokens.  */
+typedef struct c_token GTY (())
+{
+  /* The kind of token.  */
+  ENUM_BITFIELD (cpp_ttype) type : 8;
+  /* If this token is a CPP_NAME, this value indicates whether also
+     declared as some kind of type.  Otherwise, it is C_ID_NONE.  */
+  ENUM_BITFIELD (c_id_kind) id_kind : 8;
+  /* If this token is a keyword, this value indicates which keyword.
+     Otherwise, this value is RID_MAX.  */
+  ENUM_BITFIELD (rid) keyword : 8;
+  /* True if this token is from a system header.  */
+  BOOL_BITFIELD in_system_header : 1;
+  /* The value associated with this token, if any.  */
+  tree value;
+  /* The location at which this token was found.  */
+  location_t location;
+} c_token;
+
+/* A parser structure recording information about the state and
+   context of parsing.  Includes lexer information with up to two
+   tokens of look-ahead; more are not needed for C.  */
+typedef struct c_parser GTY(())
+{
+  /* The look-ahead tokens.  */
+  c_token tokens[2];
+  /* How many look-ahead tokens are available (0, 1 or 2).  */
+  short tokens_avail;
+  /* True if a syntax error is being recovered from; false otherwise.
+     c_parser_error sets this flag.  It should clear this flag when
+     enough tokens have been consumed to recover from the error.  */
+  BOOL_BITFIELD error : 1;
+} c_parser;
+
+/* Read in and lex a single token, storing it in *TOKEN.  */
+
+static void
+c_lex_one_token (c_token *token)
+{
+  timevar_push (TV_LEX);
+  token->type = c_lex (&token->value);
+  token->location = input_location;
+  token->in_system_header = in_system_header;
+  switch (token->type)
+    {
+    case CPP_NAME:
+      token->id_kind = C_ID_NONE;
+      token->keyword = RID_MAX;
+      {
+	tree decl;
+
+	int objc_force_identifier = objc_need_raw_identifier;
+	OBJC_NEED_RAW_IDENTIFIER (0);
+
+	if (C_IS_RESERVED_WORD (token->value))
+	  {
+	    enum rid rid_code = C_RID_CODE (token->value);
+
+	    if (c_dialect_objc ())
+	      {
+		if (!OBJC_IS_AT_KEYWORD (rid_code)
+		    && (!OBJC_IS_PQ_KEYWORD (rid_code) || objc_pq_context))
+		  {
+		    /* Return the canonical spelling for this keyword.  */
+		    token->value = ridpointers[(int) rid_code];
+		    token->type = CPP_KEYWORD;
+		    token->keyword = rid_code;
+		    break;
+		  }
+	      }
+	    else
+	      {
+		/* Return the canonical spelling for this keyword.  */
+		token->value = ridpointers[(int) rid_code];
+		token->type = CPP_KEYWORD;
+		token->keyword = rid_code;
+		break;
+	      }
+	  }
+
+	decl = lookup_name (token->value);
+	if (decl)
+	  {
+	    if (TREE_CODE (decl) == TYPE_DECL)
+	      {
+		token->id_kind = C_ID_TYPENAME;
+		break;
+	      }
+	  }
+	else if (c_dialect_objc ())
+	  {
+	    tree objc_interface_decl = objc_is_class_name (token->value);
+	    /* Objective-C class names are in the same namespace as
+	       variables and typedefs, and hence are shadowed by local
+	       declarations.  */
+	    if (objc_interface_decl
+		&& (global_bindings_p ()
+		    || (!objc_force_identifier && !decl)))
+	      {
+		token->value = objc_interface_decl;
+		token->id_kind = C_ID_CLASSNAME;
+		break;
+	      }
+	  }
+      }
+      token->id_kind = C_ID_ID;
+      break;
+    case CPP_AT_NAME:
+      /* This only happens in Objective-C; it must be a keyword.  */
+      token->type = CPP_KEYWORD;
+      token->id_kind = C_ID_NONE;
+      token->keyword = C_RID_CODE (token->value);
+      break;
+    case CPP_COLON:
+    case CPP_COMMA:
+    case CPP_CLOSE_PAREN:
+    case CPP_SEMICOLON:
+      /* These tokens may affect the interpretation of any identifiers
+	 following, if doing Objective-C.  */
+      OBJC_NEED_RAW_IDENTIFIER (0);
+      token->id_kind = C_ID_NONE;
+      token->keyword = RID_MAX;
+      break;
+    default:
+      token->id_kind = C_ID_NONE;
+      token->keyword = RID_MAX;
+      break;
+    }
+  timevar_pop (TV_LEX);
+}
+
+/* Return a pointer to the next token from PARSER, reading it in if
+   necessary.  */
+
+static inline c_token *
+c_parser_peek_token (c_parser *parser)
+{
+  if (parser->tokens_avail == 0)
+    {
+      c_lex_one_token (&parser->tokens[0]);
+      parser->tokens_avail = 1;
+    }
+  return &parser->tokens[0];
+}
+
+/* Return true if the next token from PARSER has the indicated
+   TYPE.  */
+
+static inline bool
+c_parser_next_token_is (c_parser *parser, enum cpp_ttype type)
+{
+  return c_parser_peek_token (parser)->type == type;
+}
+
+/* Return true if the next token from PARSER does not have the
+   indicated TYPE.  */
+
+static inline bool
+c_parser_next_token_is_not (c_parser *parser, enum cpp_ttype type)
+{
+  return !c_parser_next_token_is (parser, type);
+}
+
+/* Return true if the next token from PARSER is the indicated
+   KEYWORD.  */
+
+static inline bool
+c_parser_next_token_is_keyword (c_parser *parser, enum rid keyword)
+{
+  c_token *token;
+
+  /* Peek at the next token.  */
+  token = c_parser_peek_token (parser);
+  /* Check to see if it is the indicated keyword.  */
+  return token->keyword == keyword;
+}
+
+/* Return true if TOKEN can start a type name,
+   false otherwise.  */
+static bool
+c_token_starts_typename (c_token *token)
+{
+  switch (token->type)
+    {
+    case CPP_NAME:
+      return (token->id_kind == C_ID_TYPENAME);
+    case CPP_KEYWORD:
+      switch (token->keyword)
+	{
+	case RID_UNSIGNED:
+	case RID_LONG:
+	case RID_SHORT:
+	case RID_SIGNED:
+	case RID_COMPLEX:
+	case RID_INT:
+	case RID_CHAR:
+	case RID_FLOAT:
+	case RID_DOUBLE:
+	case RID_VOID:
+	case RID_BOOL:
+	case RID_ENUM:
+	case RID_STRUCT:
+	case RID_UNION:
+	case RID_TYPEOF:
+	case RID_CONST:
+	case RID_VOLATILE:
+	case RID_RESTRICT:
+	case RID_ATTRIBUTE:
+	  return true;
+	default:
+	  return false;
+	}
+    default:
+      return false;
+    }
+}
+
+/* Return true if the next token from PARSER can start a type name,
+   false otherwise.  */
+static inline bool
+c_parser_next_token_starts_typename (c_parser *parser)
+{
+  c_token *token = c_parser_peek_token (parser);
+  return c_token_starts_typename (token);
+}
+
+/* Return true if TOKEN can start declaration specifiers, false
+   otherwise.  */
+static bool
+c_token_starts_declspecs (c_token *token)
+{
+  switch (token->type)
+    {
+    case CPP_NAME:
+      return (token->id_kind == C_ID_TYPENAME);
+    case CPP_KEYWORD:
+      switch (token->keyword)
+	{
+	case RID_STATIC:
+	case RID_EXTERN:
+	case RID_REGISTER:
+	case RID_TYPEDEF:
+	case RID_INLINE:
+	case RID_AUTO:
+	case RID_THREAD:
+	case RID_UNSIGNED:
+	case RID_LONG:
+	case RID_SHORT:
+	case RID_SIGNED:
+	case RID_COMPLEX:
+	case RID_INT:
+	case RID_CHAR:
+	case RID_FLOAT:
+	case RID_DOUBLE:
+	case RID_VOID:
+	case RID_BOOL:
+	case RID_ENUM:
+	case RID_STRUCT:
+	case RID_UNION:
+	case RID_TYPEOF:
+	case RID_CONST:
+	case RID_VOLATILE:
+	case RID_RESTRICT:
+	case RID_ATTRIBUTE:
+	  return true;
+	default:
+	  return false;
+	}
+    default:
+      return false;
+    }
+}
+
+/* Return true if the next token from PARSER can start declaration
+   specifiers, false otherwise.  */
+static inline bool
+c_parser_next_token_starts_declspecs (c_parser *parser)
+{
+  c_token *token = c_parser_peek_token (parser);
+  return c_token_starts_declspecs (token);
+}
+
+/* Return a pointer to the next-but-one token from PARSER, reading it
+   in if necessary.  The next token is already read in.  */
+
+static c_token *
+c_parser_peek_2nd_token (c_parser *parser)
+{
+  if (parser->tokens_avail >= 2)
+    return &parser->tokens[1];
+  gcc_assert (parser->tokens_avail == 1);
+  gcc_assert (parser->tokens[0].type != CPP_EOF);
+  c_lex_one_token (&parser->tokens[1]);
+  parser->tokens_avail = 2;
+  return &parser->tokens[1];
+}
+
+/* Consume the next token from PARSER.  */
+
+static void
+c_parser_consume_token (c_parser *parser)
+{
+  if (parser->tokens_avail == 2)
+    parser->tokens[0] = parser->tokens[1];
+  else
+    {
+      gcc_assert (parser->tokens_avail == 1);
+      gcc_assert (parser->tokens[0].type != CPP_EOF);
+    }
+  parser->tokens_avail--;
+}
+
+/* Update the globals input_location and in_system_header from
+   TOKEN.  */
+static inline void
+c_parser_set_source_position_from_token (c_token *token)
+{
+  if (token->type != CPP_EOF)
+    {
+      input_location = token->location;
+      in_system_header = token->in_system_header;
+    }
+}
+
+/* Allocate a new parser.  */
+
+static c_parser *
+c_parser_new (void)
+{
+  /* Use local storage to lex the first token because loading a PCH
+     file may cause garbage collection.  */
+  c_parser tparser;
+  c_parser *ret;
+  memset (&tparser, 0, sizeof tparser);
+  c_lex_one_token (&tparser.tokens[0]);
+  tparser.tokens_avail = 1;
+  ret = GGC_NEW (c_parser);
+  memcpy (ret, &tparser, sizeof tparser);
+  return ret;
+}
+
+/* Issue a diagnostic of the form
+      FILE:LINE: MESSAGE before TOKEN
+   where TOKEN is the next token in the input stream of PARSER.
+   MESSAGE (specified by the caller) is usually of the form "expected
+   OTHER-TOKEN".
+
+   Do not issue a diagnostic if still recovering from an error.
+
+   ??? This is taken from the C++ parser, but building up messages in
+   this way is not i18n-friendly and some other approach should be
+   used.  */
+
+static void
+c_parser_error (c_parser *parser, const char *msgid)
+{
+  c_token *token = c_parser_peek_token (parser);
+  if (parser->error)
+    return;
+  parser->error = true;
+  if (!msgid)
+    return;
+  /* This diagnostic makes more sense if it is tagged to the line of
+     the token we just peeked at.  */
+  c_parser_set_source_position_from_token (token);
+  c_parse_error (msgid,
+		 /* Because c_parse_error does not understand
+		    CPP_KEYWORD, keywords are treated like
+		    identifiers.  */
+		 (token->type == CPP_KEYWORD ? CPP_NAME : token->type),
+		 token->value);
+}
+
+/* If the next token is of the indicated TYPE, consume it.  Otherwise,
+   issue the error MSGID.  If MSGID is NULL then a message has already
+   been produced and no message will be produced this time.  Returns
+   true if found, false otherwise.  */
+
+static bool
+c_parser_require (c_parser *parser,
+		  enum cpp_ttype type,
+		  const char *msgid)
+{
+  if (c_parser_next_token_is (parser, type))
+    {
+      c_parser_consume_token (parser);
+      return true;
+    }
+  else
+    {
+      c_parser_error (parser, msgid);
+      return false;
+    }
+}
+
+/* If the next token is the indicated keyword, consume it.  Otherwise,
+   issue the error MSGID.  Returns true if found, false otherwise.  */
+
+static bool
+c_parser_require_keyword (c_parser *parser,
+			  enum rid keyword,
+			  const char *msgid)
+{
+  if (c_parser_next_token_is_keyword (parser, keyword))
+    {
+      c_parser_consume_token (parser);
+      return true;
+    }
+  else
+    {
+      c_parser_error (parser, msgid);
+      return false;
+    }
+}
+
+/* Like c_parser_require, except that tokens will be skipped until the
+   desired token is found.  An error message is still produced if the
+   next token is not as expected.  If MSGID is NULL then a message has
+   already been produced and no message will be produced this
+   time.  */
+
+static void
+c_parser_skip_until_found (c_parser *parser,
+			   enum cpp_ttype type,
+			   const char *msgid)
+{
+  unsigned nesting_depth = 0;
+
+  if (c_parser_require (parser, type, msgid))
+    return;
+
+  /* Skip tokens until the desired token is found.  */
+  while (true)
+    {
+      /* Peek at the next token.  */
+      c_token *token = c_parser_peek_token (parser);
+      /* If we've reached the token we want, consume it and stop.  */
+      if (token->type == type && !nesting_depth)
+	{
+	  c_parser_consume_token (parser);
+	  return;
+	}
+      /* If we've run out of tokens, stop.  */
+      if (token->type == CPP_EOF)
+	return;
+      if (token->type == CPP_OPEN_BRACE
+	  || token->type == CPP_OPEN_PAREN
+	  || token->type == CPP_OPEN_SQUARE)
+	++nesting_depth;
+      else if (token->type == CPP_CLOSE_BRACE
+	       || token->type == CPP_CLOSE_PAREN
+	       || token->type == CPP_CLOSE_SQUARE)
+	{
+	  if (nesting_depth-- == 0)
+	    {
+	      parser->error = false;
+	      return;
+	    }
+	}
+      /* Consume this token.  */
+      c_parser_consume_token (parser);
+      parser->error = false;
+    }
+}
+
+/* Skip tokens until we have consumed an entire block, or until we
+   have consumed a non-nested ';'.  */
+
+static void
+c_parser_skip_to_end_of_block_or_statement (c_parser *parser)
+{
+  unsigned nesting_depth = 0;
+
+  while (true)
+    {
+      c_token *token;
+
+      /* Peek at the next token.  */
+      token = c_parser_peek_token (parser);
+      /* If we've run out of tokens, stop.  */
+      if (token->type == CPP_EOF)
+	break;
+      /* If the next token is a ';', we have reached the end of the
+	 statement.  */
+      if (token->type == CPP_SEMICOLON && !nesting_depth)
+	{
+	  /* Consume the ';'.  */
+	  c_parser_consume_token (parser);
+	  break;
+	}
+      /* If the next token is a non-nested '}', then we have reached
+	 the end of the current block.  */
+      if (token->type == CPP_CLOSE_BRACE
+	  && (nesting_depth == 0 || --nesting_depth == 0))
+	{
+	  c_parser_consume_token (parser);
+	  break;
+	}
+      /* If it the next token is a '{', then we are entering a new
+	 block.  Consume the entire block.  */
+      if (token->type == CPP_OPEN_BRACE)
+	++nesting_depth;
+      c_parser_consume_token (parser);
+    }
+  parser->error = false;
+}
+
+
+/* Save the warning flags which are controlled by __extension__.  */
+
+static inline int
+disable_extension_diagnostics (void)
+{
+  int ret = (pedantic
+	     | (warn_pointer_arith << 1)
+	     | (warn_traditional << 2)
+	     | (flag_iso << 3));
+  pedantic = 0;
+  warn_pointer_arith = 0;
+  warn_traditional = 0;
+  flag_iso = 0;
+  return ret;
+}
+
+/* Restore the warning flags which are controlled by __extension__.
+   FLAGS is the return value from disable_extension_diagnostics.  */
+
+static inline void
+restore_extension_diagnostics (int flags)
+{
+  pedantic = flags & 1;
+  warn_pointer_arith = (flags >> 1) & 1;
+  warn_traditional = (flags >> 2) & 1;
+  flag_iso = (flags >> 3) & 1;
+}
+
+/* Possibly kinds of declarator to parse.  */
+typedef enum c_dtr_syn {
+  /* A normal declarator with an identifier.  */
+  C_DTR_NORMAL,
+  /* An abstract declarator (maybe empty).  */
+  C_DTR_ABSTRACT,
+  /* A parameter declarator: may be either, but after a type name does
+     not redeclare a typedef name as an identifier if it can
+     alternatively be interpreted as a typedef name; see DR#009,
+     applied in C90 TC1, omitted from C99 and reapplied in C99 TC2
+     following DR#249.  For example, given a typedef T, "int T" and
+     "int *T" are valid parameter declarations redeclaring T, while
+     "int (T)" and "int * (T)" and "int (T[])" and "int (T (int))" are
+     abstract declarators rather than involving redundant parentheses;
+     the same applies with attributes inside the parentheses before
+     "T".  */
+  C_DTR_PARM
+} c_dtr_syn;
+
+static void c_parser_external_declaration (c_parser *);
+static void c_parser_asm_definition (c_parser *);
+static void c_parser_declaration_or_fndef (c_parser *, bool, bool, bool, bool);
+static void c_parser_declspecs (c_parser *, struct c_declspecs *, bool, bool,
+				bool);
+static struct c_typespec c_parser_enum_specifier (c_parser *);
+static struct c_typespec c_parser_struct_or_union_specifier (c_parser *);
+static tree c_parser_struct_declaration (c_parser *);
+static struct c_typespec c_parser_typeof_specifier (c_parser *);
+static struct c_declarator *c_parser_declarator (c_parser *, bool, c_dtr_syn,
+						 bool *);
+static struct c_declarator *c_parser_direct_declarator (c_parser *, bool,
+							c_dtr_syn, bool *);
+static struct c_declarator *c_parser_direct_declarator_inner (c_parser *,
+							      bool,
+							      struct c_declarator *);
+static struct c_arg_info *c_parser_parms_declarator (c_parser *, bool, tree);
+static struct c_arg_info *c_parser_parms_list_declarator (c_parser *, tree);
+static tree c_parser_simple_asm_expr (c_parser *);
+static tree c_parser_attributes (c_parser *);
+static struct c_type_name *c_parser_type_name (c_parser *);
+static struct c_expr c_parser_initializer (c_parser *);
+static struct c_expr c_parser_braced_init (c_parser *, tree, bool);
+static void c_parser_initelt (c_parser *);
+static void c_parser_initval (c_parser *);
+static tree c_parser_compound_statement (c_parser *);
+static void c_parser_compound_statement_nostart (c_parser *);
+static void c_parser_label (c_parser *);
+static void c_parser_statement (c_parser *);
+static void c_parser_statement_after_labels (c_parser *);
+static void c_parser_if_statement (c_parser *);
+static void c_parser_switch_statement (c_parser *);
+static void c_parser_while_statement (c_parser *);
+static void c_parser_do_statement (c_parser *);
+static void c_parser_for_statement (c_parser *);
+static tree c_parser_asm_statement (c_parser *);
+static tree c_parser_asm_operands (c_parser *);
+static tree c_parser_asm_clobbers (c_parser *);
+static struct c_expr c_parser_expr_no_commas (c_parser *);
+static struct c_expr c_parser_conditional_expression (c_parser *);
+static struct c_expr c_parser_binary_expression (c_parser *);
+static struct c_expr c_parser_cast_expression (c_parser *);
+static struct c_expr c_parser_unary_expression (c_parser *);
+static struct c_expr c_parser_sizeof_expression (c_parser *);
+static struct c_expr c_parser_alignof_expression (c_parser *);
+static struct c_expr c_parser_postfix_expression (c_parser *);
+static struct c_expr c_parser_postfix_expression_after_paren_type (c_parser *,
+								   struct c_type_name *);
+static struct c_expr c_parser_postfix_expression_after_primary (c_parser *,
+								struct c_expr);
+static struct c_expr c_parser_expression (c_parser *);
+static tree c_parser_expr_list (c_parser *);
+
+/* Parse a translation unit (C90 6.7, C99 6.9).
+
+   translation-unit:
+     external-declarations
+
+   external-declarations:
+     external-declaration
+     external-declarations external-declaration
+
+   GNU extensions:
+
+   translation-unit:
+     empty
+*/
+
+static void
+c_parser_translation_unit (c_parser *parser)
+{
+  if (c_parser_next_token_is (parser, CPP_EOF))
+    {
+      if (pedantic)
+	pedwarn ("ISO C forbids an empty source file");
+    }
+  else
+    {
+      void *obstack_position = obstack_alloc (&parser_obstack, 0);
+      do
+	{
+	  ggc_collect ();
+	  c_parser_external_declaration (parser);
+	  obstack_free (&parser_obstack, obstack_position);
+	}
+      while (c_parser_next_token_is_not (parser, CPP_EOF));
+    }
+}
+
+/* Parse an external declaration (C90 6.7, C99 6.9).
+
+   external-declaration:
+     function-definition
+     declaration
+
+   GNU extensions:
+
+   external-declaration:
+     asm-definition
+     ;
+     __extension__ external-declaration
+
+   TODO: Objective-C.
+*/
+
+static void
+c_parser_external_declaration (c_parser *parser)
+{
+  if (c_parser_next_token_is_keyword (parser, RID_EXTENSION))
+    {
+      int ext;
+      ext = disable_extension_diagnostics ();
+      c_parser_consume_token (parser);
+      c_parser_external_declaration (parser);
+      restore_extension_diagnostics (ext);
+    }
+  else if (c_parser_next_token_is_keyword (parser, RID_ASM))
+    {
+      c_parser_asm_definition (parser);
+    }
+  else if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+    {
+      if (pedantic)
+	pedwarn ("ISO C does not allow extra %<;%> outside of a function");
+      c_parser_consume_token (parser);
+    }
+  else
+    {
+      /* A declaration or a function definition.  We can only tell
+	 which after parsing the declaration specifiers, if any, and
+	 the first declarator.  */
+      c_parser_declaration_or_fndef (parser, true, true, false, true);
+    }
+}
+
+/* Parse a declaration or function definition (C90 6.5, 6.7.1, C99
+   6.7, 6.9.1).  If FNDEF_OK is true, a function definition is
+   accepted; otherwise (old-style parameter declarations) only other
+   declarations are accepted.  If NESTED is true, we are inside a
+   function or parsing old-style parameter declarations; any functions
+   encountered are nested functions and declaration specifiers are
+   required; otherwise we are at top level and functions are normal
+   functions and declaration specifiers may be optional.  If EMPTY_OK
+   is true, empty declarations are OK (subject to all other
+   constraints); otherwise (old-style parameter declarations) they are
+   diagnosed.  If START_ATTR_OK is true, the declaration specifiers
+   may start with attributes; otherwise they may not.
+
+   declaration:
+     declaration-specifiers init-declarator-list[opt] ;
+
+   function-definition:
+     declaration-specifiers[opt] declarator declaration-list[opt]
+       compound-statement
+
+   declaration-list:
+     declaration
+     declaration-list declaration
+
+   init-declarator-list:
+     init-declarator
+     init-declarator-list , init-declarator
+
+   init-declarator:
+     declarator simple-asm-expr[opt] attributes[opt]
+     declarator simple-asm-expr[opt] attributes[opt] = initializer
+
+   GNU extensions:
+
+   nested-function-definition:
+     declaration-specifiers declarator declaration-list[opt]
+       compound-statement
+
+   The simple-asm-expr and attributes are GNU extensions.
+
+   This function does not handle __extension__; that is handled in its
+   callers.  ??? Following the old parser, __extension__ may start
+   external declarations, declarations in functions and declarations
+   at the start of "for" loops, but not old-style parameter
+   declarations.
+
+   C99 requires declaration specifiers in a function definition; the
+   absence is diagnosed through the diagnosis of implicit int.  In GNU
+   C we also allow but diagnose declarations without declaration
+   specifiers, but only at top level (elsewhere they conflict with
+   other syntax).  */
+
+static void
+c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok, bool empty_ok,
+			       bool nested, bool start_attr_ok)
+{
+  struct c_declspecs *specs;
+  tree prefix_attrs;
+  tree all_prefix_attrs;
+  bool diagnosed_no_specs = false;
+  specs = build_null_declspecs ();
+  c_parser_declspecs (parser, specs, true, true, start_attr_ok);
+  if (parser->error)
+    {
+      c_parser_skip_to_end_of_block_or_statement (parser);
+      return;
+    }
+  if (nested && !specs->declspecs_seen_p)
+    {
+      c_parser_error (parser, "expected declaration specifiers");
+      c_parser_skip_to_end_of_block_or_statement (parser);
+      return;
+    }
+  finish_declspecs (specs);
+  if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+    {
+      if (empty_ok)
+	shadow_tag (specs);
+      else
+	{
+	  shadow_tag_warned (specs, 1);
+	  pedwarn ("empty declaration");
+	}
+      c_parser_consume_token (parser);
+      return;
+    }
+  pending_xref_error ();
+  prefix_attrs = specs->attrs;
+  all_prefix_attrs = prefix_attrs;
+  specs->attrs = NULL_TREE;
+  while (true)
+    {
+      struct c_declarator *declarator;
+      bool dummy = false;
+      tree fnbody;
+      /* Declaring either one or more declarators (in which case we
+	 should diagnose if there were no declaration specifiers) or a
+	 function definition (in which case the diagnostic for
+	 implicit int suffices).  */
+      declarator = c_parser_declarator (parser, specs->type_seen_p,
+					C_DTR_NORMAL, &dummy);
+      if (declarator == NULL)
+	{
+	  c_parser_skip_to_end_of_block_or_statement (parser);
+	  return;
+	}
+      if (c_parser_next_token_is (parser, CPP_EQ)
+	  || c_parser_next_token_is (parser, CPP_COMMA)
+	  || c_parser_next_token_is (parser, CPP_SEMICOLON)
+	  || c_parser_next_token_is_keyword (parser, RID_ASM)
+	  || c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
+	{
+	  tree asm_name = NULL_TREE;
+	  tree postfix_attrs = NULL_TREE;
+	  if (!diagnosed_no_specs && !specs->declspecs_seen_p)
+	    {
+	      diagnosed_no_specs = true;
+	      pedwarn ("data definition has no type or storage class");
+	    }
+	  /* Having seen a data definition, there cannot now be a
+	     function definition.  */
+	  fndef_ok = false;
+	  if (c_parser_next_token_is_keyword (parser, RID_ASM))
+	    asm_name = c_parser_simple_asm_expr (parser);
+	  if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
+	    postfix_attrs = c_parser_attributes (parser);
+	  if (c_parser_next_token_is (parser, CPP_EQ))
+	    {
+	      tree d;
+	      struct c_expr init;
+	      c_parser_consume_token (parser);
+	      /* The declaration of the variable is in effect while
+		 its initializer is parsed.  */
+	      d = start_decl (declarator, specs, true,
+			      chainon (postfix_attrs, all_prefix_attrs));
+	      start_init (d, asm_name, global_bindings_p ());
+	      init = c_parser_initializer (parser);
+	      finish_init ();
+	      maybe_warn_string_init (TREE_TYPE (d), init);
+	      finish_decl (d, init.value, asm_name);
+	    }
+	  else
+	    {
+	      tree d = start_decl (declarator, specs, false,
+				   chainon (postfix_attrs,
+					    all_prefix_attrs));
+	      finish_decl (d, NULL_TREE, asm_name);
+	    }
+	  if (c_parser_next_token_is (parser, CPP_COMMA))
+	    {
+	      c_parser_consume_token (parser);
+	      if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
+		all_prefix_attrs = chainon (c_parser_attributes (parser),
+					    prefix_attrs);
+	      else
+		all_prefix_attrs = prefix_attrs;
+	      continue;
+	    }
+	  else if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+	    {
+	      c_parser_consume_token (parser);
+	      return;
+	    }
+	  else
+	    {
+	      c_parser_error (parser, "expected ',' or ';'");
+	      c_parser_skip_to_end_of_block_or_statement (parser);
+	      return;
+	    }
+	}
+      else if (!fndef_ok)
+	{
+	  c_parser_error (parser,
+			  "expected '=', ',', ';', 'asm' or '__attribute__'");
+	  c_parser_skip_to_end_of_block_or_statement (parser);
+	  return;
+	}
+      /* Function definition (nested or otherwise).  */
+      if (nested)
+	{
+	  if (pedantic)
+	    pedwarn ("ISO C forbids nested functions");
+	  push_function_context ();
+	}
+      if (!start_function (specs, declarator, all_prefix_attrs))
+	{
+	  /* This can appear in many cases looking nothing like a
+	     function definition, so we don't give a more specific
+	     error suggesting there was one.  */
+	  c_parser_error (parser,
+			  "expected '=', ',', ';', 'asm' or '__attribute__'");
+	  if (nested)
+	    pop_function_context ();
+	  break;
+	}
+      /* Parse old-style parameter declarations.  ??? Attributes are
+	 not allowed to start declaration specifiers here because of a
+	 syntax conflict between a function declaration with attribute
+	 suffix and a function definition with an attribute prefix on
+	 first old-style parameter declaration.  Following the old
+	 parser, they are not accepted on subsequent old-style
+	 parameter declarations either.  However, there is no
+	 ambiguity after the first declaration, nor indeed on the
+	 first as long as we don't allow postfix attributes after a
+	 declarator with a nonempty identifier list in a definition;
+	 and postfix attributes have never been accepted here in
+	 function definitions either.  */
+      while (c_parser_next_token_is_not (parser, CPP_EOF)
+	     && c_parser_next_token_is_not (parser, CPP_OPEN_BRACE))
+	c_parser_declaration_or_fndef (parser, false, false, true, false);
+      DECL_SOURCE_LOCATION (current_function_decl)
+	= c_parser_peek_token (parser)->location;
+      store_parm_decls ();
+      fnbody = c_parser_compound_statement (parser);
+      if (nested)
+	{
+	  tree decl = current_function_decl;
+	  add_stmt (fnbody);
+	  finish_function ();
+	  pop_function_context ();
+	  add_stmt (build_stmt (DECL_EXPR, decl));
+	}
+      else
+	{
+	  add_stmt (fnbody);
+	  finish_function ();
+	}
+      break;
+    }
+}
+
+/* Parse an asm-definition (asm() outside a function body).  This is a
+   GNU extension.
+
+   asm-definition:
+     simple-asm-expr ;
+*/
+
+static void
+c_parser_asm_definition (c_parser *parser)
+{
+  tree asm_str = c_parser_simple_asm_expr (parser);
+  /* ??? This only works sensibly in the presence of
+     -fno-unit-at-a-time; file-scope asms really need to be passed to
+     cgraph which needs to preserve the order of functions and
+     file-scope asms.  */
+  if (asm_str)
+    assemble_asm (asm_str);
+  c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected ';'");
+}
+
+/* Parse some declaration specifiers (possibly none) (C90 6.5, C99
+   6.7), adding them to SPECS (which may already include some).
+   Storage class specifiers are accepted iff SCSPEC_OK; type
+   specifiers are accepted iff TYPESPEC_OK; attributes are accepted at
+   the start iff START_ATTR_OK.
+
+   declaration-specifiers:
+     storage-class-specifier declaration-specifiers[opt]
+     type-specifier declaration-specifiers[opt]
+     type-qualifier declaration-specifiers[opt]
+     function-specifier declaration-specifiers[opt]
+
+   Function specifiers (inline) are from C99, and are currently
+   handled as storage class specifiers, as is __thread.
+
+   C90 6.5.1, C99 6.7.1:
+   storage-class-specifier:
+     typedef
+     extern
+     static
+     auto
+     register
+
+   C99 6.7.4:
+   function-specifier:
+     inline
+
+   C90 6.5.2, C99 6.7.2:
+   type-specifier:
+     void
+     char
+     short
+     int
+     long
+     float
+     double
+     signed
+     unsigned
+     _Bool
+     _Complex
+     [_Imaginary removed in C99 TC2]
+     struct-or-union-specifier
+     enum-specifier
+     typedef-name
+
+   (_Bool and _Complex are new in C99.)
+
+   C90 6.5.3, C99 6.7.3:
+
+   type-qualifier:
+     const
+     restrict
+     volatile
+
+   (restrict is new in C99.)
+
+   GNU extensions:
+
+   declaration-specifiers:
+     attributes declaration-specifiers[opt]
+
+   storage-class-specifier:
+     __thread
+
+   type-specifier:
+     typeof-specifier
+
+   TODO: Objective-C.
+*/
+
+static void
+c_parser_declspecs (c_parser *parser, struct c_declspecs *specs,
+		    bool scspec_ok, bool typespec_ok, bool start_attr_ok)
+{
+  bool attrs_ok = start_attr_ok;
+  bool seen_type = specs->type_seen_p;
+  while (c_parser_next_token_is (parser, CPP_NAME)
+	 || c_parser_next_token_is (parser, CPP_KEYWORD))
+    {
+      struct c_typespec t;
+      tree attrs;
+      if (c_parser_next_token_is (parser, CPP_NAME))
+	{
+	  /* This finishes the specifiers unless a type name is OK, it
+	     is declared as a type name and a type name hasn't yet
+	     been seen.  */
+	  if (!typespec_ok || seen_type
+	      || c_parser_peek_token (parser)->id_kind != C_ID_TYPENAME)
+	    break;
+	  seen_type = true;
+	  attrs_ok = true;
+	  t.kind = ctsk_typedef;
+	  /* For a typedef name, record the meaning, not the name.
+	     In case of 'foo foo, bar;'.  */
+	  t.spec = lookup_name (c_parser_peek_token (parser)->value);
+	  declspecs_add_type (specs, t);
+	  c_parser_consume_token (parser);
+	  continue;
+	}
+      switch (c_parser_peek_token (parser)->keyword)
+	{
+	case RID_STATIC:
+	case RID_EXTERN:
+	case RID_REGISTER:
+	case RID_TYPEDEF:
+	case RID_INLINE:
+	case RID_AUTO:
+	case RID_THREAD:
+	  if (!scspec_ok)
+	    goto out;
+	  attrs_ok = true;
+	  /* TODO: Distinguish between function specifiers (inline)
+	     and storage class specifiers, either here or in
+	     declspecs_add_scspec.  */
+	  declspecs_add_scspec (specs, c_parser_peek_token (parser)->value);
+	  c_parser_consume_token (parser);
+	  break;
+	case RID_UNSIGNED:
+	case RID_LONG:
+	case RID_SHORT:
+	case RID_SIGNED:
+	case RID_COMPLEX:
+	case RID_INT:
+	case RID_CHAR:
+	case RID_FLOAT:
+	case RID_DOUBLE:
+	case RID_VOID:
+	case RID_BOOL:
+	  if (!typespec_ok)
+	    goto out;
+	  attrs_ok = true;
+	  seen_type = true;
+	  OBJC_NEED_RAW_IDENTIFIER (1);
+	  t.kind = ctsk_resword;
+	  t.spec = c_parser_peek_token (parser)->value;
+	  declspecs_add_type (specs, t);
+	  c_parser_consume_token (parser);
+	  break;
+	case RID_ENUM:
+	  if (!typespec_ok)
+	    goto out;
+	  attrs_ok = true;
+	  seen_type = true;
+	  t = c_parser_enum_specifier (parser);
+	  declspecs_add_type (specs, t);
+	  break;
+	case RID_STRUCT:
+	case RID_UNION:
+	  if (!typespec_ok)
+	    goto out;
+	  attrs_ok = true;
+	  seen_type = true;
+	  t = c_parser_struct_or_union_specifier (parser);
+	  declspecs_add_type (specs, t);
+	  break;
+	case RID_TYPEOF:
+	  /* ??? The old parser rejected typeof after other type
+	     specifiers, but is a syntax error the best way of
+	     handling this?  */
+	  if (!typespec_ok || seen_type)
+	    goto out;
+	  attrs_ok = true;
+	  seen_type = true;
+	  t = c_parser_typeof_specifier (parser);
+	  declspecs_add_type (specs, t);
+	  break;
+	case RID_CONST:
+	case RID_VOLATILE:
+	case RID_RESTRICT:
+	  attrs_ok = true;
+	  declspecs_add_qual (specs, c_parser_peek_token (parser)->value);
+	  c_parser_consume_token (parser);
+	  break;
+	case RID_ATTRIBUTE:
+	  if (!attrs_ok)
+	    goto out;
+	  attrs = c_parser_attributes (parser);
+	  declspecs_add_attrs (specs, attrs);
+	  break;
+	default:
+	  goto out;
+	}
+    }
+ out: ;
+}
+
+/* Parse an enum specifier (C90 6.5.2.2, C99 6.7.2.2).
+
+   enum-specifier:
+     enum attributes[opt] identifier[opt] { enumerator-list } attributes[opt]
+     enum attributes[opt] identifier[opt] { enumerator-list , } attributes[opt]
+     enum attributes[opt] identifier
+
+   The form with trailing comma is new in C99.  The forms with
+   attributes are GNU extensions.  In GNU C, we accept any expression
+   without commas in the syntax (assignment expressions, not just
+   conditional expressions); assignment expressions will be diagnosed
+   as non-constant.
+
+   enumerator-list:
+     enumerator
+     enumerator-list , enumerator
+
+   enumerator:
+     enumeration-constant
+     enumeration-constant = constant-expression
+*/
+
+static struct c_typespec
+c_parser_enum_specifier (c_parser *parser)
+{
+  struct c_typespec ret;
+  tree attrs;
+  tree ident = NULL_TREE;
+  gcc_assert (c_parser_next_token_is_keyword (parser, RID_ENUM));
+  c_parser_consume_token (parser);
+  attrs = c_parser_attributes (parser);
+  if (c_parser_next_token_is (parser, CPP_NAME))
+    {
+      ident = c_parser_peek_token (parser)->value;
+      c_parser_consume_token (parser);
+    }
+  if (c_parser_next_token_is (parser, CPP_OPEN_BRACE))
+    {
+      /* Parse an enum definition.  */
+      tree type = start_enum (ident);
+      tree postfix_attrs;
+      /* We chain the enumerators in reverse order, then put them in
+	 forward order at the end.  */
+      tree values = NULL_TREE;
+      c_parser_consume_token (parser);
+      while (true)
+	{
+	  tree enum_id;
+	  tree enum_value;
+	  tree enum_decl;
+	  bool seen_comma;
+	  if (c_parser_next_token_is_not (parser, CPP_NAME))
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL);
+	      values = error_mark_node;
+	      break;
+	    }
+	  enum_id = c_parser_peek_token (parser)->value;
+	  c_parser_consume_token (parser);
+	  if (c_parser_next_token_is (parser, CPP_EQ))
+	    {
+	      c_parser_consume_token (parser);
+	      enum_value = c_parser_expr_no_commas (parser).value;
+	    }
+	  else
+	    enum_value = NULL_TREE;
+	  enum_decl = build_enumerator (enum_id, enum_value);
+	  TREE_CHAIN (enum_decl) = values;
+	  values = enum_decl;
+	  seen_comma = false;
+	  if (c_parser_next_token_is (parser, CPP_COMMA))
+	    {
+	      seen_comma = true;
+	      c_parser_consume_token (parser);
+	    }
+	  if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE))
+	    {
+	      if (seen_comma && pedantic && !flag_isoc99)
+		pedwarn ("comma at end of enumerator list");
+	      c_parser_consume_token (parser);
+	      break;
+	    }
+	  if (!seen_comma)
+	    {
+	      c_parser_error (parser, "expected ',' or '}'");
+	      c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL);
+	      values = error_mark_node;
+	      break;
+	    }
+	}
+      postfix_attrs = c_parser_attributes (parser);
+      ret.spec = finish_enum (type, nreverse (values),
+			      chainon (attrs, postfix_attrs));
+      ret.kind = ctsk_tagdef;
+      return ret;
+    }
+  else if (!ident)
+    {
+      c_parser_error (parser, "expected '{'");
+      ret.spec = error_mark_node;
+      ret.kind = ctsk_tagref;
+      return ret;
+    }
+  ret = parser_xref_tag (ENUMERAL_TYPE, ident);
+  /* In ISO C, enumerated types can be referred to only if already
+     defined.  */
+  if (pedantic && !COMPLETE_TYPE_P (ret.spec))
+    pedwarn ("ISO C forbids forward references to %<enum%> types");
+  return ret;
+}
+
+/* Parse a struct or union specifier (C90 6.5.2.1, C99 6.7.2.1).
+
+   struct-or-union-specifier:
+     struct-or-union attributes[opt] identifier[opt]
+       { struct-declaration-list } attributes[opt]
+     struct-or-union attributes[opt] identifier
+
+   struct-declaration-list:
+     struct-declaration
+     struct-declaration-list struct-declaration
+
+   TODO: Objective-C.
+
+   GNU extensions: the semicolon at the end may be omitted; extra
+   semicolons may be included between, before or after
+   struct-declarations.  */
+
+static struct c_typespec
+c_parser_struct_or_union_specifier (c_parser *parser)
+{
+  struct c_typespec ret;
+  tree attrs;
+  tree ident = NULL_TREE;
+  enum tree_code code;
+  switch (c_parser_peek_token (parser)->keyword)
+    {
+    case RID_STRUCT:
+      code = RECORD_TYPE;
+      break;
+    case RID_UNION:
+      code = UNION_TYPE;
+      break;
+    default:
+      gcc_unreachable ();
+    }
+  c_parser_consume_token (parser);
+  attrs = c_parser_attributes (parser);
+  if (c_parser_next_token_is (parser, CPP_NAME))
+    {
+      ident = c_parser_peek_token (parser)->value;
+      c_parser_consume_token (parser);
+    }
+  if (c_parser_next_token_is (parser, CPP_OPEN_BRACE))
+    {
+      /* Parse a struct or union definition.  Start the scope of the
+	 tag before parsing components.  */
+      tree type = start_struct (code, ident);
+      tree postfix_attrs;
+      /* We chain the components in reverse order, then put them in
+	 forward order at the end.  Each struct-declaration may
+	 declare multiple components (comma-separated), so we must use
+	 chainon to join them, although when parsing each
+	 struct-declaration we can use TREE_CHAIN directly.
+
+	 The theory behind all this is that there will be more
+	 semicolon separated fields than comma separated fields, and
+	 so we'll be minimizing the number of node traversals required
+	 by chainon.  */
+      tree contents = NULL_TREE;
+      c_parser_consume_token (parser);
+      /* Parse the struct-declarations and semicolons.  Problems with
+	 semicolons are diagnosed here; empty structures are diagnosed
+	 elsewhere.  */
+      while (true)
+	{
+	  tree decls;
+	  /* Parse any stray semicolon.  */
+	  if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+	    {
+	      if (pedantic)
+		pedwarn ("extra semicolon in struct or union specified");
+	      c_parser_consume_token (parser);
+	      continue;
+	    }
+	  /* Stop if at the end of the struct or union contents.  */
+	  if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE))
+	    {
+	      c_parser_consume_token (parser);
+	      break;
+	    }
+	  /* Parse some comma-separated declarations, but not the
+	     trailing semicolon if any.  */
+	  decls = c_parser_struct_declaration (parser);
+	  contents = chainon (decls, contents);
+	  /* If no semicolon follows, either we have a parse error or
+	     are at the end of the struct or union and should
+	     pedwarn.  */
+	  if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+	    c_parser_consume_token (parser);
+	  else
+	    {
+	      if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE))
+		pedwarn ("no semicolon at end of struct or union");
+	      else
+		{
+		  c_parser_error (parser, "expected ';'");
+		  c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL);
+		  break;
+		}
+	    }
+	}
+      postfix_attrs = c_parser_attributes (parser);
+      ret.spec = finish_struct (type, nreverse (contents),
+				chainon (attrs, postfix_attrs));
+      ret.kind = ctsk_tagdef;
+      return ret;
+    }
+  else if (!ident)
+    {
+      c_parser_error (parser, "expected '{'");
+      ret.spec = error_mark_node;
+      ret.kind = ctsk_tagref;
+    }
+  ret = parser_xref_tag (code, ident);
+  return ret;
+}
+
+/* Parse a struct-declaration (C90 6.5.2.1, C99 6.7.2.1), *without*
+   the trailing semicolon.
+
+   struct-declaration:
+     specifier-qualifier-list struct-declarator-list
+
+   specifier-qualifier-list:
+     type-specifier specifier-qualifier-list[opt]
+     type-qualifier specifier-qualifier-list[opt]
+     attributes specifier-qualifier-list[opt]
+
+   struct-declarator-list:
+     struct-declarator
+     struct-declarator-list , attributes[opt] struct-declarator
+
+   struct-declarator:
+     declarator attributes[opt]
+     declarator[opt] : constant-expression attributes[opt]
+
+   GNU extensions: semicolons are handled elsewhere; attributes may be
+   used where shown; a struct-declarator-list may be empty;
+   __extension__ may be used at the start of a struct-declaration.  In
+   GNU C, we accept any expression without commas in the syntax
+   (assignment expressions, not just conditional expressions);
+   assignment expressions will be diagnosed as non-constant.  */
+
+static tree
+c_parser_struct_declaration (c_parser *parser)
+{
+  struct c_declspecs *specs;
+  tree prefix_attrs;
+  tree all_prefix_attrs;
+  tree decls;
+  if (c_parser_next_token_is_keyword (parser, RID_EXTENSION))
+    {
+      int ext;
+      tree decl;
+      ext = disable_extension_diagnostics ();
+      c_parser_consume_token (parser);
+      decl = c_parser_struct_declaration (parser);
+      restore_extension_diagnostics (ext);
+      return decl;
+    }
+  specs = build_null_declspecs ();
+  c_parser_declspecs (parser, specs, false, true, true);
+  if (parser->error)
+    return error_mark_node;
+  if (!specs->declspecs_seen_p)
+    {
+      c_parser_error (parser, "expected specifier-qualifier-list");
+      return NULL_TREE;
+    }
+  finish_declspecs (specs);
+  if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+    {
+      tree ret;
+      if (!specs->type_seen_p)
+	{
+	  if (pedantic)
+	    pedwarn ("ISO C forbids member declarations with no members");
+	  shadow_tag_warned (specs, pedantic);
+	  ret = NULL_TREE;
+	}
+      else
+	{
+	  /* Support for unnamed structs or unions as members of
+	     structs or unions (which is [a] useful and [b] supports
+	     MS P-SDK).  */
+	  ret = grokfield (build_id_declarator (NULL_TREE), specs, NULL_TREE);
+	}
+      return ret;
+    }
+  pending_xref_error ();
+  prefix_attrs = specs->attrs;
+  all_prefix_attrs = prefix_attrs;
+  specs->attrs = NULL_TREE;
+  decls = NULL_TREE;
+  while (true)
+    {
+      /* Declaring one or more declarators or un-named bit-fields.  */
+      struct c_declarator *declarator;
+      bool dummy = false;
+      if (c_parser_next_token_is (parser, CPP_COLON))
+	declarator = build_id_declarator (NULL_TREE);
+      else
+	declarator = c_parser_declarator (parser, specs->type_seen_p,
+					  C_DTR_NORMAL, &dummy);
+      if (declarator == NULL)
+	{
+	  c_parser_skip_to_end_of_block_or_statement (parser);
+	  break;
+	}
+      if (c_parser_next_token_is (parser, CPP_COLON)
+	  || c_parser_next_token_is (parser, CPP_COMMA)
+	  || c_parser_next_token_is (parser, CPP_SEMICOLON)
+	  || c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
+	{
+	  tree postfix_attrs = NULL_TREE;
+	  tree width = NULL_TREE;
+	  tree d;
+	  if (c_parser_next_token_is (parser, CPP_COLON))
+	    {
+	      c_parser_consume_token (parser);
+	      width = c_parser_expr_no_commas (parser).value;
+	    }
+	  if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
+	    postfix_attrs = c_parser_attributes (parser);
+	  d = grokfield (declarator, specs, width);
+	  decl_attributes (&d, chainon (postfix_attrs,
+					all_prefix_attrs), 0);
+	  TREE_CHAIN (d) = decls;
+	  decls = d;
+	  if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
+	    all_prefix_attrs = chainon (c_parser_attributes (parser),
+					prefix_attrs);
+	  else
+	    all_prefix_attrs = prefix_attrs;
+	  if (c_parser_next_token_is (parser, CPP_COMMA))
+	    c_parser_consume_token (parser);
+	  else if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+	    {
+	      /* Semicolon consumed in caller.  */
+	      break;
+	    }
+	  else
+	    {
+	      c_parser_error (parser, "expected ',' or ';'");
+	      break;
+	    }
+	}
+      else
+	{
+	  c_parser_error (parser,
+			  "expected ':', ',', ';' or '__attribute__'");
+	  break;
+	}
+    }
+  return decls;
+}
+
+/* Parse a typeof specifier (a GNU extension).
+
+   typeof-specifier:
+     typeof ( expression )
+     typeof ( type-name )
+*/
+
+static struct c_typespec
+c_parser_typeof_specifier (c_parser *parser)
+{
+  struct c_typespec ret;
+  ret.kind = ctsk_typeof;
+  ret.spec = error_mark_node;
+  gcc_assert (c_parser_next_token_is_keyword (parser, RID_TYPEOF));
+  c_parser_consume_token (parser);
+  skip_evaluation++;
+  in_typeof++;
+  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    {
+      skip_evaluation--;
+      in_typeof--;
+      return ret;
+    }
+  if (c_parser_next_token_starts_typename (parser))
+    {
+      struct c_type_name *type = c_parser_type_name (parser);
+      skip_evaluation--;
+      in_typeof--;
+      if (type != NULL)
+	{
+	  ret.spec = groktypename (type);
+	  pop_maybe_used (variably_modified_type_p (ret.spec, NULL_TREE));
+	}
+    }
+  else
+    {
+      struct c_expr expr = c_parser_expression (parser);
+      skip_evaluation--;
+      in_typeof--;
+      if (TREE_CODE (expr.value) == COMPONENT_REF
+	  && DECL_C_BIT_FIELD (TREE_OPERAND (expr.value, 1)))
+	error ("%<typeof%> applied to a bit-field");
+      ret.spec = TREE_TYPE (expr.value);
+      pop_maybe_used (variably_modified_type_p (ret.spec, NULL_TREE));
+    }
+  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+  return ret;
+}
+
+/* Parse a declarator, possibly an abstract declarator (C90 6.5.4,
+   6.5.5, C99 6.7.5, 6.7.6).  If TYPE_SEEN_P then a typedef name may
+   be redeclared; otherwise it may not.  KIND indicates which kind of
+   declarator is wanted.  Returns a valid declarator except in the
+   case of a syntax error in which case NULL is returned.  *SEEN_ID is
+   set to true if an identifier being declared is seen; this is used
+   to diagnose bad forms of abstract array declarators and to
+   determine whether an identifier list is syntactically permitted.
+
+   declarator:
+     pointer[opt] direct-declarator
+
+   direct-declarator:
+     identifier
+     ( attributes[opt] declarator )
+     direct-declarator array-declarator
+     direct-declarator ( parameter-type-list )
+     direct-declarator ( identifier-list[opt] )
+
+   pointer:
+     * type-qualifier-list[opt]
+     * type-qualifier-list[opt] pointer
+
+   type-qualifier-list:
+     type-qualifier
+     attributes
+     type-qualifier-list type-qualifier
+     type-qualifier-list attributes
+
+   parameter-type-list:
+     parameter-list
+     parameter-list , ...
+
+   parameter-list:
+     parameter-declaration
+     parameter-list , parameter-declaration
+
+   parameter-declaration:
+     declaration-specifiers declarator attributes[opt]
+     declaration-specifiers abstract-declarator[opt] attributes[opt]
+
+   identifier-list:
+     identifier
+     identifier-list , identifier
+
+   abstract-declarator:
+     pointer
+     pointer[opt] direct-abstract-declarator
+
+   direct-abstract-declarator:
+     ( attributes[opt] abstract-declarator )
+     direct-abstract-declarator[opt] array-declarator
+     direct-abstract-declarator[opt] ( parameter-type-list[opt] )
+
+   GNU extensions:
+
+   direct-declarator:
+     direct-declarator ( parameter-forward-declarations
+			 parameter-type-list[opt] )
+
+   direct-abstract-declarator:
+     direct-abstract-declarator[opt] ( parameter-forward-declarations 
+				       parameter-type-list[opt] )
+
+   parameter-forward-declarations:
+     parameter-list ;
+     parameter-forward-declarations parameter-list ;
+
+   The uses of attributes shown above are GNU extensions.
+
+   Some forms of array declarator are not included in C99 in the
+   syntax for abstract declarators; these are disallowed elsewhere.
+   This may be a defect (DR#289).
+
+   This function also accepts an omitted abstract declarator as being
+   an abstract declarator, although not part of the formal syntax.  */
+
+static struct c_declarator *
+c_parser_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
+		     bool *seen_id)
+{
+  /* Parse any initial pointer part.  */
+  if (c_parser_next_token_is (parser, CPP_MULT))
+    {
+      struct c_declspecs *quals_attrs = build_null_declspecs ();
+      struct c_declarator *inner;
+      c_parser_consume_token (parser);
+      c_parser_declspecs (parser, quals_attrs, false, false, true);
+      inner = c_parser_declarator (parser, type_seen_p, kind, seen_id);
+      if (inner == NULL)
+	return NULL;
+      else
+	return make_pointer_declarator (quals_attrs, inner);
+    }
+  /* Now we have a direct declarator, direct abstract declarator or
+     nothing (which counts as a direct abstract declarator here).  */
+  return c_parser_direct_declarator (parser, type_seen_p, kind, seen_id);
+}
+
+/* Parse a direct declarator or direct abstract declarator; arguments
+   as c_parser_declarator.  */
+
+static struct c_declarator *
+c_parser_direct_declarator (c_parser *parser, bool type_seen_p, c_dtr_syn kind,
+			    bool *seen_id)
+{
+  /* The direct declarator must start with an identifier (possibly
+     omitted) or a parenthesized declarator (possibly abstract).  In
+     an ordinary declarator, initial parentheses must start a
+     parenthesized declarator.  In an abstract declarator or parameter
+     declarator, they could start a parenthesized declarator or a
+     parameter list.  To tell which, the open parenthesis and any
+     following attributes must be read.  If a declaration specifier
+     follows, then it is a parameter list; if the specifier is a
+     typedef name, there might be an ambiguity about redeclaring it,
+     which is resolved in the direction of treating it as a typedef
+     name.  If a close parenthesis follows, it is also an empty
+     parameter list, as the syntax does not permit empty abstract
+     declarators.  Otherwise, it is a parenthesised declarator (in
+     which case the analysis may be repeated inside it, recursively).
+
+     ??? There is an ambiguity in a parameter declaration "int
+     (__attribute__((foo)) x)", where x is not a typedef name: it
+     could be an abstract declarator for a function, or declare x with
+     parentheses.  The proper resolution of this ambiguity needs
+     documenting.  At present we follow an accident of the old
+     parser's implementation, whereby the first parameter must have
+     some declaration specifiers other than just attributes.  Thus as
+     a parameter declaration it is treated as a parenthesised
+     parameter named x, and as an abstract declarator it is
+     rejected.
+
+     ??? Also following the old parser, attributes inside an empty
+     parameter list are ignored, making it a list not yielding a
+     prototype, rather than giving an error or making it have one
+     parameter with implicit type int.  */
+
+  if (kind != C_DTR_ABSTRACT
+      && c_parser_next_token_is (parser, CPP_NAME)
+      && (type_seen_p || c_parser_peek_token (parser)->id_kind == C_ID_ID))
+    {
+      struct c_declarator *inner
+	= build_id_declarator (c_parser_peek_token (parser)->value);
+      *seen_id = true;
+      c_parser_consume_token (parser);
+      return c_parser_direct_declarator_inner (parser, *seen_id, inner);
+    }
+
+  if (kind != C_DTR_NORMAL
+      && c_parser_next_token_is (parser, CPP_OPEN_SQUARE))
+    {
+      struct c_declarator *inner = build_id_declarator (NULL_TREE);
+      return c_parser_direct_declarator_inner (parser, *seen_id, inner);
+    }
+
+  /* Either we are at the end of an abstract declarator, or we have
+     parentheses.  */
+
+  if (c_parser_next_token_is (parser, CPP_OPEN_PAREN))
+    {
+      tree attrs;
+      struct c_declarator *inner;
+      c_parser_consume_token (parser);
+      attrs = c_parser_attributes (parser);
+      if (kind != C_DTR_NORMAL
+	  && (c_parser_next_token_starts_declspecs (parser)
+	      || c_parser_next_token_is (parser, CPP_CLOSE_PAREN)))
+	{
+	  struct c_arg_info *args
+	    = c_parser_parms_declarator (parser, kind == C_DTR_NORMAL,
+					 attrs);
+	  if (args == NULL)
+	    return NULL;
+	  else
+	    {
+	      inner
+		= build_function_declarator (args,
+					     build_id_declarator (NULL_TREE));
+	      return c_parser_direct_declarator_inner (parser, *seen_id,
+						       inner);
+	    }
+	}
+      /* A parenthesized declarator.  */
+      inner = c_parser_declarator (parser, type_seen_p, kind, seen_id);
+      if (inner != NULL && attrs != NULL)
+	inner = build_attrs_declarator (attrs, inner);
+      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	{
+	  c_parser_consume_token (parser);
+	  if (inner == NULL)
+	    return NULL;
+	  else
+	    return c_parser_direct_declarator_inner (parser, *seen_id, inner);
+	}
+      else
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  return NULL;
+	}
+    }
+  else
+    {
+      if (kind == C_DTR_NORMAL)
+	{
+	  c_parser_error (parser, "expected identifier or '('");
+	  return NULL;
+	}
+      else
+	return build_id_declarator (NULL_TREE);
+    }
+}
+
+/* Parse part of a direct declarator or direct abstract declarator,
+   given that some (in INNER) has already been parsed; ID_PRESENT is
+   true if an identifier is present, false for an abstract
+   declarator.  */
+
+static struct c_declarator *
+c_parser_direct_declarator_inner (c_parser *parser, bool id_present,
+				  struct c_declarator *inner)
+{
+  /* Parse a sequence of array declarators and parameter lists.  */
+  if (c_parser_next_token_is (parser, CPP_OPEN_SQUARE))
+    {
+      struct c_declarator *declarator;
+      struct c_declspecs *quals_attrs = build_null_declspecs ();
+      bool static_seen;
+      bool star_seen;
+      tree dimen;
+      c_parser_consume_token (parser);
+      c_parser_declspecs (parser, quals_attrs, false, false, true);
+      static_seen = c_parser_next_token_is_keyword (parser, RID_STATIC);
+      if (static_seen)
+	c_parser_consume_token (parser);
+      if (static_seen && !quals_attrs->declspecs_seen_p)
+	c_parser_declspecs (parser, quals_attrs, false, false, true);
+      if (!quals_attrs->declspecs_seen_p)
+	quals_attrs = NULL;
+      /* If "static" is present, there must be an array dimension.
+	 Otherwise, there may be a dimension, "*", or no
+	 dimension.  */
+      if (static_seen)
+	{
+	  star_seen = false;
+	  dimen = c_parser_expr_no_commas (parser).value;
+	}
+      else
+	{
+	  if (c_parser_next_token_is (parser, CPP_CLOSE_SQUARE))
+	    {
+	      dimen = NULL_TREE;
+	      star_seen = false;
+	    }
+	  else if (c_parser_next_token_is (parser, CPP_MULT))
+	    {
+	      if (c_parser_peek_2nd_token (parser)->type == CPP_CLOSE_SQUARE)
+		{
+		  dimen = NULL_TREE;
+		  star_seen = true;
+		  c_parser_consume_token (parser);
+		}
+	      else
+		{
+		  star_seen = false;
+		  dimen = c_parser_expr_no_commas (parser).value;
+		}
+	    }
+	  else
+	    {
+	      star_seen = false;
+	      dimen = c_parser_expr_no_commas (parser).value;
+	    }
+	}
+      if (c_parser_next_token_is (parser, CPP_CLOSE_SQUARE))
+	c_parser_consume_token (parser);
+      else
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, "expected ']'");
+	  return NULL;
+	}
+      declarator = build_array_declarator (dimen, quals_attrs, static_seen,
+					   star_seen);
+      inner = set_array_declarator_inner (declarator, inner, !id_present);
+      return c_parser_direct_declarator_inner (parser, id_present, inner);
+    }
+  else if (c_parser_next_token_is (parser, CPP_OPEN_PAREN))
+    {
+      tree attrs;
+      struct c_arg_info *args;
+      c_parser_consume_token (parser);
+      attrs = c_parser_attributes (parser);
+      args = c_parser_parms_declarator (parser, id_present, attrs);
+      if (args == NULL)
+	return NULL;
+      else
+	{
+	  inner = build_function_declarator (args, inner);
+	  return c_parser_direct_declarator_inner (parser, id_present, inner);
+	}
+    }
+  return inner;
+}
+
+/* Parse a parameter list or identifier list, including the closing
+   parenthesis but not the opening one.  ATTRS are the attributes at
+   the start of the list.  ID_LIST_OK is true if an identifier list is
+   acceptable; such a list must not have attributes at the start.  */
+
+static struct c_arg_info *
+c_parser_parms_declarator (c_parser *parser, bool id_list_ok, tree attrs)
+{
+  push_scope ();
+  declare_parm_level ();
+  /* If the list starts with an identifier, it is an identifier list.
+     Otherwise, it is either a prototype list or an empty list.  */
+  if (id_list_ok
+      && !attrs
+      && c_parser_next_token_is (parser, CPP_NAME)
+      && c_parser_peek_token (parser)->id_kind == C_ID_ID)
+    {
+      tree list = NULL_TREE;
+      while (c_parser_next_token_is (parser, CPP_NAME)
+	     && c_parser_peek_token (parser)->id_kind == C_ID_ID)
+	{
+	  list = chainon (list, build_tree_list (NULL_TREE,
+						 c_parser_peek_token (parser)->value));
+	  c_parser_consume_token (parser);
+	  if (c_parser_next_token_is_not (parser, CPP_COMMA))
+	    break;
+	  c_parser_consume_token (parser);
+	  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      break;
+	    }
+	}
+      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	{
+	  struct c_arg_info *ret = XOBNEW (&parser_obstack, struct c_arg_info);
+	  ret->parms = 0;
+	  ret->tags = 0;
+	  ret->types = list;
+	  ret->others = 0;
+	  c_parser_consume_token (parser);
+	  pop_scope ();
+	  return ret;
+	}
+      else
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  pop_scope ();
+	  return NULL;
+	}
+    }
+  else
+    {
+      struct c_arg_info *ret = c_parser_parms_list_declarator (parser, attrs);
+      pop_scope ();
+      return ret;
+    }
+}
+
+/* Parse a parameter list (possibly empty), including the closing
+   parenthesis but not the opening one.  ATTRS are the attributes at
+   the start of the list.  */
+
+static struct c_arg_info *
+c_parser_parms_list_declarator (c_parser *parser, tree attrs)
+{
+  /* ??? Following the old parser, forward parameter declarations may
+     use abstract declarators, and if no real parameter declarations
+     follow the forward declarations then this is not diagnosed.  Also
+     note as above that attributes are ignored as the only contents of
+     the parentheses, or as the only contents after forward
+     declarations.  */
+  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+    {
+      struct c_arg_info *ret = XOBNEW (&parser_obstack, struct c_arg_info);
+      ret->parms = 0;
+      ret->tags = 0;
+      ret->types = 0;
+      ret->others = 0;
+      c_parser_consume_token (parser);
+      return ret;
+    }
+  if (c_parser_next_token_is (parser, CPP_ELLIPSIS))
+    {
+      struct c_arg_info *ret = XOBNEW (&parser_obstack, struct c_arg_info);
+      ret->parms = 0;
+      ret->tags = 0;
+      ret->others = 0;
+      /* Suppress -Wold-style-definition for this case.  */
+      ret->types = error_mark_node;
+      error ("ISO C requires a named argument before %<...%>");
+      c_parser_consume_token (parser);
+      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	{
+	  c_parser_consume_token (parser);
+	  return ret;
+	}
+      else
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  return NULL;
+	}
+    }
+  /* Nonempty list of parameters, either terminated with semicolon
+     (forward declarations; recurse) or with close parenthesis (normal
+     function) or with ", ... )" (variadic function).  */
+  while (true)
+    {
+      /* Parse a parameter.  */
+      struct c_declspecs *specs;
+      struct c_declarator *declarator;
+      tree prefix_attrs;
+      tree postfix_attrs = NULL_TREE;
+      bool dummy = false;
+      if (!c_parser_next_token_starts_declspecs (parser))
+	{
+	  c_parser_error (parser, "expected declaration specifiers or '...'");
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  return NULL;
+	}
+      specs = build_null_declspecs ();
+      if (attrs)
+	{
+	  declspecs_add_attrs (specs, attrs);
+	  attrs = NULL_TREE;
+	}
+      c_parser_declspecs (parser, specs, true, true, true);
+      finish_declspecs (specs);
+      pending_xref_error ();
+      prefix_attrs = specs->attrs;
+      specs->attrs = NULL_TREE;
+      declarator = c_parser_declarator (parser, specs->type_seen_p,
+					C_DTR_PARM, &dummy);
+      if (declarator == NULL)
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  return NULL;
+	}
+      if (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
+	postfix_attrs = c_parser_attributes (parser);
+      push_parm_decl (build_c_parm (specs,
+				    chainon (postfix_attrs,
+					     prefix_attrs), declarator));
+      if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+	{
+	  tree new_attrs;
+	  c_parser_consume_token (parser);
+	  new_attrs = c_parser_attributes (parser);
+	  return c_parser_parms_list_declarator (parser, new_attrs);
+	}
+      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	{
+	  c_parser_consume_token (parser);
+	  return get_parm_info (false);
+	}
+      if (!c_parser_require (parser, CPP_COMMA, "expected ';', ',' or ')'"))
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  return NULL;
+	}
+      if (c_parser_next_token_is (parser, CPP_ELLIPSIS))
+	{
+	  c_parser_consume_token (parser);
+	  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	    {
+	      c_parser_consume_token (parser);
+	      return get_parm_info (true);
+	    }
+	  else
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+					 "expected ')'");
+	      return NULL;
+	    }
+	}
+    }
+}
+
+/* Parse a string literal in an asm expression.  It should not be
+   translated, and wide string literals are an error although
+   permitted by the syntax.  This is a GNU extension.
+
+   asm-string-literal:
+     string-literal
+
+   ??? At present, following the old parser, the caller needs to have
+   set c_lex_string_translate to 0.  It would be better to follow the
+   C++ parser rather than using the c_lex_string_translate kludge.  */
+
+static tree
+c_parser_asm_string_literal (c_parser *parser)
+{
+  tree str;
+  if (c_parser_next_token_is (parser, CPP_STRING))
+    {
+      str = c_parser_peek_token (parser)->value;
+      c_parser_consume_token (parser);
+    }
+  else if (c_parser_next_token_is (parser, CPP_WSTRING))
+    {
+      /* ??? At present, following the old parser, wide strings are
+	 handled like narrow strings here.  */
+      str = c_parser_peek_token (parser)->value;
+      c_parser_consume_token (parser);
+    }
+  else
+    {
+      c_parser_error (parser, "expected string literal");
+      str = NULL_TREE;
+    }
+  return str;
+}
+
+/* Parse a simple asm expression.  This is used in restricted
+   contexts, where a full expression with inputs and outputs does not
+   make sense.  This is a GNU extension.
+
+   simple-asm-expr:
+     asm ( asm-string-literal )
+*/
+
+static tree
+c_parser_simple_asm_expr (c_parser *parser)
+{
+  tree str;
+  gcc_assert (c_parser_next_token_is_keyword (parser, RID_ASM));
+  /* ??? Follow the C++ parser rather than using the
+     c_lex_string_translate kludge.  */
+  c_lex_string_translate = 0;
+  c_parser_consume_token (parser);
+  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    {
+      c_lex_string_translate = 1;
+      return NULL_TREE;
+    }
+  str = c_parser_asm_string_literal (parser);
+  c_lex_string_translate = 1;
+  if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected ')'"))
+    {
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  return str;
+}
+
+/* Parse (possibly empty) attributes.  This is a GNU extension.
+
+   attributes:
+     empty
+     attributes attribute
+
+   attribute:
+     __attribute__ ( ( attribute-list ) )
+
+   attribute-list:
+     attrib
+     attribute_list , attrib
+
+   attrib:
+     empty
+     any-word
+     any-word ( identifier )
+     any-word ( identifier , nonempty-expr-list )
+     any-word ( expr-list )
+
+   where the "identifier" must not be declared as a type, and
+   "any-word" may be any identifier (including one declared as a
+   type), a reserved word storage class specifier, type specifier or
+   type qualifier.  ??? This still leaves out most reserved keywords
+   (following the old parser), shouldn't we include them, and why not
+   allow identifiers declared as types to start the arguments?  */
+
+static tree
+c_parser_attributes (c_parser *parser)
+{
+  tree attrs = NULL_TREE;
+  while (c_parser_next_token_is_keyword (parser, RID_ATTRIBUTE))
+    {
+      /* ??? Follow the C++ parser rather than using the
+	 c_lex_string_translate kludge.  */
+      c_lex_string_translate = 0;
+      c_parser_consume_token (parser);
+      if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	{
+	  c_lex_string_translate = 1;
+	  return attrs;
+	}
+      if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	{
+	  c_lex_string_translate = 1;
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  return attrs;
+	}
+      /* Parse the attribute list.  */
+      while (c_parser_next_token_is (parser, CPP_COMMA)
+	     || c_parser_next_token_is (parser, CPP_NAME)
+	     || c_parser_next_token_is (parser, CPP_KEYWORD))
+	{
+	  tree attr, attr_name, attr_args;
+	  if (c_parser_next_token_is (parser, CPP_COMMA))
+	    {
+	      c_parser_consume_token (parser);
+	      continue;
+	    }
+	  if (c_parser_next_token_is (parser, CPP_KEYWORD))
+	    {
+	      /* ??? See comment above about what keywords are
+		 accepted here.  */
+	      bool ok;
+	      switch (c_parser_peek_token (parser)->keyword)
+		{
+		case RID_STATIC:
+		case RID_UNSIGNED:
+		case RID_LONG:
+		case RID_CONST:
+		case RID_EXTERN:
+		case RID_REGISTER:
+		case RID_TYPEDEF:
+		case RID_SHORT:
+		case RID_INLINE:
+		case RID_VOLATILE:
+		case RID_SIGNED:
+		case RID_AUTO:
+		case RID_RESTRICT:
+		case RID_COMPLEX:
+		case RID_THREAD:
+		case RID_INT:
+		case RID_CHAR:
+		case RID_FLOAT:
+		case RID_DOUBLE:
+		case RID_VOID:
+		case RID_BOOL:
+		  ok = true;
+		  break;
+		default:
+		  ok = false;
+		  break;
+		}
+	      if (!ok)
+		break;
+	    }
+	  attr_name = c_parser_peek_token (parser)->value;
+	  c_parser_consume_token (parser);
+	  if (c_parser_next_token_is_not (parser, CPP_OPEN_PAREN))
+	    {
+	      attr = build_tree_list (attr_name, NULL_TREE);
+	      attrs = chainon (attrs, attr);
+	      continue;
+	    }
+	  c_parser_consume_token (parser);
+	  /* Parse the attribute contents.  If they start with an
+	     identifier which is followed by a comma or close
+	     parenthesis, then the arguments start with that
+	     identifier; otherwise they are an expression list.  */
+	  if (c_parser_next_token_is (parser, CPP_NAME)
+	      && c_parser_peek_token (parser)->id_kind == C_ID_ID
+	      && ((c_parser_peek_2nd_token (parser)->type == CPP_COMMA)
+		  || (c_parser_peek_2nd_token (parser)->type
+		      == CPP_CLOSE_PAREN)))
+	    {
+	      tree arg1 = c_parser_peek_token (parser)->value;
+	      c_parser_consume_token (parser);
+	      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+		attr_args = build_tree_list (NULL_TREE, arg1);
+	      else
+		{
+		  c_parser_consume_token (parser);
+		  attr_args = tree_cons (NULL_TREE, arg1,
+					 c_parser_expr_list (parser));
+		}
+	    }
+	  else
+	    {
+	      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+		attr_args = NULL_TREE;
+	      else
+		attr_args = c_parser_expr_list (parser);
+	    }
+	  attr = build_tree_list (attr_name, attr_args);
+	  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	    c_parser_consume_token (parser);
+	  else
+	    {
+	      c_lex_string_translate = 1;
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+					 "expected ')'");
+	      return attrs;
+	    }
+	  attrs = chainon (attrs, attr);
+	}
+      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	c_parser_consume_token (parser);
+      else
+	{
+	  c_lex_string_translate = 1;
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  return attrs;
+	}
+      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	c_parser_consume_token (parser);
+      else
+	{
+	  c_lex_string_translate = 1;
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  return attrs;
+	}
+      c_lex_string_translate = 1;
+    }
+  return attrs;
+}
+
+/* Parse a type name (C90 6.5.5, C99 6.7.6).
+
+   type-name:
+     specifier-qualifier-list abstract-declarator[opt]
+*/
+
+static struct c_type_name *
+c_parser_type_name (c_parser *parser)
+{
+  struct c_declspecs *specs = build_null_declspecs ();
+  struct c_declarator *declarator;
+  struct c_type_name *ret;
+  bool dummy = false;
+  c_parser_declspecs (parser, specs, false, true, true);
+  if (!specs->declspecs_seen_p)
+    {
+      c_parser_error (parser, "expected specifier-qualifier-list");
+      return NULL;
+    }
+  pending_xref_error ();
+  finish_declspecs (specs);
+  declarator = c_parser_declarator (parser, specs->type_seen_p,
+				    C_DTR_ABSTRACT, &dummy);
+  if (declarator == NULL)
+    return NULL;
+  ret = XOBNEW (&parser_obstack, struct c_type_name);
+  ret->specs = specs;
+  ret->declarator = declarator;
+  return ret;
+}
+
+/* Parse an initializer (C90 6.5.7, C99 6.7.8).
+
+   initializer:
+     assignment-expression
+     { initializer-list }
+     { initializer-list , }
+
+   initializer-list:
+     designation[opt] initializer
+     initializer-list , designation[opt] initializer
+
+   designation:
+     designator-list =
+
+   designator-list:
+     designator
+     designator-list designator
+
+   designator:
+     array-designator
+     . identifier
+
+   array-designator:
+     [ constant-expression ]
+
+   GNU extensions:
+
+   initializer:
+     { }
+
+   designation:
+     array-designator
+     identifier :
+
+   array-designator:
+     [ constant-expression ... constant-expression ]
+
+   Any expression without commas is accepted in the syntax for the
+   constant-expressions, with non-constant expressions rejected later.
+
+   This function is only used for top-level initializers; for nested
+   ones, see c_parser_initval.  */
+
+static struct c_expr
+c_parser_initializer (c_parser *parser)
+{
+  if (c_parser_next_token_is (parser, CPP_OPEN_BRACE))
+    return c_parser_braced_init (parser, NULL_TREE, false);
+  else
+    return c_parser_expr_no_commas (parser);
+}
+
+/* Parse a braced initializer list.  TYPE is the type specified for a
+   compound literal, and NULL_TREE for other initializers and for
+   nested braced lists.  NESTED_P is true for nested braced lists,
+   false for the list of a compound literal or the list that is the
+   top-level initializer in a declaration.  */
+
+static struct c_expr
+c_parser_braced_init (c_parser *parser, tree type, bool nested_p)
+{
+  gcc_assert (c_parser_next_token_is (parser, CPP_OPEN_BRACE));
+  c_parser_consume_token (parser);
+  if (nested_p)
+    push_init_level (0);
+  else
+    really_start_incremental_init (type);
+  if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE))
+    {
+      if (pedantic)
+	pedwarn ("ISO C forbids empty initializer braces");
+    }
+  else
+    {
+      /* Parse a non-empty initializer list, possibly with a trailing
+	 comma.  */
+      while (true)
+	{
+	  c_parser_initelt (parser);
+	  if (parser->error)
+	    break;
+	  if (c_parser_next_token_is (parser, CPP_COMMA))
+	    c_parser_consume_token (parser);
+	  else
+	    break;
+	  if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE))
+	    break;
+	}
+    }
+  if (c_parser_next_token_is_not (parser, CPP_CLOSE_BRACE))
+    {
+      struct c_expr ret;
+      ret.value = error_mark_node;
+      ret.original_code = ERROR_MARK;
+      c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, "expected '}'");
+      return ret;
+    }
+  c_parser_consume_token (parser);
+  return pop_init_level (0);
+}
+
+/* Parse a nested initializer, including designators.  */
+
+static void
+c_parser_initelt (c_parser *parser)
+{
+  /* Parse any designator or designator list.  A single array
+     designator may have the subsequent "=" omitted in GNU C, but a
+     longer list or a structure member designator may not.  */
+  if (c_parser_next_token_is (parser, CPP_NAME)
+      && c_parser_peek_2nd_token (parser)->type == CPP_COLON)
+    {
+      /* Old-style structure member designator.  */
+      set_init_label (c_parser_peek_token (parser)->value);
+      if (pedantic)
+	pedwarn ("obsolete use of designated initializer with %<:%>");
+      c_parser_consume_token (parser);
+      c_parser_consume_token (parser);
+    }
+  else
+    {
+      /* des_seen is 0 if there have been no designators, 1 if there
+	 has been a single array designator and 2 otherwise.  */
+      int des_seen = 0;
+      while (c_parser_next_token_is (parser, CPP_OPEN_SQUARE)
+	     || c_parser_next_token_is (parser, CPP_DOT))
+	{
+	  if (des_seen < 2)
+	    des_seen++;
+	  if (c_parser_next_token_is (parser, CPP_DOT))
+	    {
+	      des_seen = 2;
+	      c_parser_consume_token (parser);
+	      if (c_parser_next_token_is (parser, CPP_NAME))
+		{
+		  set_init_label (c_parser_peek_token (parser)->value);
+		  c_parser_consume_token (parser);
+		}
+	      else
+		{
+		  c_parser_error (parser, "expected identifier");
+		  return;
+		}
+	    }
+	  else
+	    {
+	      tree first, second;
+	      c_parser_consume_token (parser);
+	      first = c_parser_expr_no_commas (parser).value;
+	      if (c_parser_next_token_is (parser, CPP_ELLIPSIS))
+		{
+		  c_parser_consume_token (parser);
+		  second = c_parser_expr_no_commas (parser).value;
+		}
+	      else
+		second = NULL_TREE;
+	      if (c_parser_next_token_is (parser, CPP_CLOSE_SQUARE))
+		{
+		  c_parser_consume_token (parser);
+		  set_init_index (first, second);
+		  if (pedantic && second)
+		    pedwarn ("ISO C forbids specifying range of "
+			     "elements to initialize");
+		}
+	      else
+		c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE,
+					   "expected ']'");
+	    }
+	}
+      if (des_seen >= 1)
+	{
+	  if (c_parser_next_token_is (parser, CPP_EQ))
+	    {
+	      if (pedantic && !flag_isoc99)
+		pedwarn ("ISO C90 forbids specifying subobject to initialize");
+	      c_parser_consume_token (parser);
+	    }
+	  else
+	    {
+	      if (des_seen == 1)
+		{
+		  if (pedantic)
+		    pedwarn ("obsolete use of designated initializer "
+			     "without %<=%>");
+		}
+	      else
+		{
+		  c_parser_error (parser, "expected '='");
+		  return;
+		}
+	    }
+	}
+    }
+  c_parser_initval (parser);
+}
+
+/* Parse a nested initializer; as c_parser_initializer but parses
+   initializers within braced lists, after any designators have been
+   applied.  */
+
+static void
+c_parser_initval (c_parser *parser)
+{
+  struct c_expr init;
+  if (c_parser_next_token_is (parser, CPP_OPEN_BRACE))
+    init = c_parser_braced_init (parser, NULL_TREE, true);
+  else
+    init = c_parser_expr_no_commas (parser);
+  process_init_element (init);
+}
+
+/* Parse a compound statement (possibly a function body) (C90 6.6.2,
+   C99 6.8.2).
+
+   compound-statement:
+     { block-item-list[opt] }
+     { label-decls block-item-list }
+
+   block-item-list:
+     block-item
+     block-item-list block-item
+
+   block-item:
+     nested-declaration
+     statement
+
+   nested-declaration:
+     declaration
+
+   GNU extensions:
+
+   compound-statement:
+     { label-decls block-item-list }
+
+   nested-declaration:
+     __extension__ nested-declaration
+     nested-function-definition
+
+   label-decls:
+     label-decl
+     label-decls label-decl
+
+   label-decl:
+     __label__ identifier-list ;
+
+   Allowing the mixing of declarations and code is new in C99.  The
+   GNU syntax also permits (not shown above) labels at the end of
+   compound statements, which yield an error.  We don't allow labels
+   on declarations; this might seem like a natural extension, but
+   there would be a conflict between attributes on the label and
+   prefix attributes on the declaration.  ??? The syntax follows the
+   old parser in requiring something after label declarations.
+   Although they are erroneous if the labels declared aren't defined,
+   is it useful for the syntax to be this way?  */
+
+static tree
+c_parser_compound_statement (c_parser *parser)
+{
+  tree stmt;
+  if (!c_parser_require (parser, CPP_OPEN_BRACE, "expected '{'"))
+    return NULL_TREE;
+  stmt = c_begin_compound_stmt (true);
+  c_parser_compound_statement_nostart (parser);
+  return c_end_compound_stmt (stmt, true);
+}
+
+/* Parse a compound statement except for the opening brace.  This is
+   used for parsing both compound statements and statement expressions
+   (which follow different paths to handling the opening).  */
+
+static void
+c_parser_compound_statement_nostart (c_parser *parser)
+{
+  bool last_stmt = false;
+  bool last_label = false;
+  if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE))
+    {
+      c_parser_consume_token (parser);
+      return;
+    }
+  if (c_parser_next_token_is_keyword (parser, RID_LABEL))
+    {
+      /* Read zero or more forward-declarations for labels that nested
+	 functions can jump to.  */
+      while (c_parser_next_token_is_keyword (parser, RID_LABEL))
+	{
+	  c_parser_consume_token (parser);
+	  /* Any identifiers, including those declared as type names,
+	     are OK here.  */
+	  if (c_parser_next_token_is_not (parser, CPP_NAME))
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      c_parser_skip_until_found (parser, CPP_SEMICOLON, NULL);
+	      continue;
+	    }
+	  while (true)
+	    {
+	      tree label;
+	      if (c_parser_next_token_is_not (parser, CPP_NAME))
+		{
+		  c_parser_error (parser, "expected identifier");
+		  break;
+		}
+	      label
+		= declare_label (c_parser_peek_token (parser)->value);
+	      C_DECLARED_LABEL_FLAG (label) = 1;
+	      add_stmt (build_stmt (DECL_EXPR, label));
+	      c_parser_consume_token (parser);
+	      if (c_parser_next_token_is (parser, CPP_COMMA))
+		c_parser_consume_token (parser);
+	      else
+		break;
+	    }
+	  c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected ';'");
+	}
+      if (pedantic)
+	pedwarn ("ISO C forbids label declarations");
+    }
+  /* We must now have at least one statement, label or declaration.  */
+  if (c_parser_next_token_is (parser, CPP_CLOSE_BRACE))
+    {
+      c_parser_error (parser, "expected declaration or statement");
+      c_parser_consume_token (parser);
+      return;
+    }
+  while (c_parser_next_token_is_not (parser, CPP_CLOSE_BRACE))
+    {
+      location_t loc = c_parser_peek_token (parser)->location;
+      if (c_parser_next_token_is_keyword (parser, RID_CASE)
+	  || c_parser_next_token_is_keyword (parser, RID_DEFAULT)
+	  || (c_parser_next_token_is (parser, CPP_NAME)
+	      && c_parser_peek_2nd_token (parser)->type == CPP_COLON))
+	{
+	  last_label = true;
+	  last_stmt = false;
+	  c_parser_label (parser);
+	}
+      else if (!last_label
+	       && c_parser_next_token_starts_declspecs (parser))
+	{
+	  last_label = false;
+	  c_parser_declaration_or_fndef (parser, true, true, true, true);
+	  if (last_stmt
+	      && ((pedantic && !flag_isoc99)
+		  || warn_declaration_after_statement))
+	    pedwarn_c90 ("%HISO C90 forbids mixed declarations and code",
+			 &loc);
+	  last_stmt = false;
+	}
+      else if (!last_label
+	       && c_parser_next_token_is_keyword (parser, RID_EXTENSION))
+	{
+	  /* __extension__ can start a declaration, but is also an
+	     unary operator that can start an expression.  Consume all
+	     but the last of a possible series of __extension__ to
+	     determine which.  */
+	  while (c_parser_peek_2nd_token (parser)->type == CPP_KEYWORD
+		 && (c_parser_peek_2nd_token (parser)->keyword
+		     == RID_EXTENSION))
+	    c_parser_consume_token (parser);
+	  if (c_token_starts_declspecs (c_parser_peek_2nd_token (parser)))
+	    {
+	      int ext;
+	      ext = disable_extension_diagnostics ();
+	      c_parser_consume_token (parser);
+	      last_label = false;
+	      c_parser_declaration_or_fndef (parser, true, true, true, true);
+	      /* Following the old parser, __extension__ does not
+		 disable this diagnostic.  */
+	      restore_extension_diagnostics (ext);
+	      if (last_stmt
+		  && ((pedantic && !flag_isoc99)
+		      || warn_declaration_after_statement))
+		pedwarn_c90 ("%HISO C90 forbids mixed declarations and code",
+			     &loc);
+	      last_stmt = false;
+	    }
+	  else
+	    goto statement;
+	}
+      else
+	{
+	statement:
+	  last_label = false;
+	  last_stmt = true;
+	  c_parser_statement_after_labels (parser);
+	}
+    }
+  if (last_label)
+    error ("label at end of compound statement");
+  c_parser_consume_token (parser);
+}
+
+/* Parse a label (C90 6.6.1, C99 6.8.1).
+
+   label:
+     identifier : attributes[opt]
+     case constant-expression :
+     default :
+
+   GNU extensions:
+
+   label:
+     case constant-expression ... constant-expression :
+
+   The use of attributes on labels is a GNU extension.  The syntax in
+   GNU C accepts any expressions without commas, non-constant
+   expressions being rejected later.  */
+
+static void
+c_parser_label (c_parser *parser)
+{
+  location_t loc1 = c_parser_peek_token (parser)->location;
+  tree label = NULL_TREE;
+  if (c_parser_next_token_is_keyword (parser, RID_CASE))
+    {
+      tree exp1, exp2;
+      c_parser_consume_token (parser);
+      exp1 = c_parser_expr_no_commas (parser).value;
+      if (c_parser_next_token_is (parser, CPP_COLON))
+	{
+	  c_parser_consume_token (parser);
+	  label = do_case (exp1, NULL_TREE);
+	}
+      else if (c_parser_next_token_is (parser, CPP_ELLIPSIS))
+	{
+	  c_parser_consume_token (parser);
+	  exp2 = c_parser_expr_no_commas (parser).value;
+	  if (c_parser_require (parser, CPP_COLON, "expected ':'"))
+	    label = do_case (exp1, exp2);
+	}
+      else
+	c_parser_error (parser, "expected ':' or '...'");
+    }
+  else if (c_parser_next_token_is_keyword (parser, RID_DEFAULT))
+    {
+      c_parser_consume_token (parser);
+      if (c_parser_require (parser, CPP_COLON, "expected ':'"))
+	label = do_case (NULL_TREE, NULL_TREE);
+    }
+  else
+    {
+      tree name = c_parser_peek_token (parser)->value;
+      tree tlab;
+      location_t loc2;
+      tree attrs;
+      gcc_assert (c_parser_next_token_is (parser, CPP_NAME));
+      c_parser_consume_token (parser);
+      gcc_assert (c_parser_next_token_is (parser, CPP_COLON));
+      loc2 = c_parser_peek_token (parser)->location;
+      c_parser_consume_token (parser);
+      attrs = c_parser_attributes (parser);
+      tlab = define_label (loc2, name);
+      if (tlab)
+	{
+	  decl_attributes (&tlab, attrs, 0);
+	  label = add_stmt (build_stmt (LABEL_EXPR, tlab));
+	}
+    }
+  if (label)
+    SET_EXPR_LOCATION (label, loc1);
+}
+
+/* Parse a statement (C90 6.6, C99 6.8).
+
+   statement:
+     labeled-statement
+     compound-statement
+     expression-statement
+     selection-statement
+     iteration-statement
+     jump-statement
+
+   labeled-statement:
+     label statement
+
+   expression-statement:
+     expression[opt] ;
+
+   selection-statement:
+     if-statement
+     switch-statement
+
+   iteration-statement:
+     while-statement
+     do-statement
+     for-statement
+
+   jump-statement:
+     goto identifier ;
+     continue ;
+     break ;
+     return expression[opt] ;
+
+   GNU extensions:
+
+   statement:
+     asm-statement
+
+   jump-statement:
+     goto * expression ;
+
+   TODO: Objective-C.
+*/
+
+static void
+c_parser_statement (c_parser *parser)
+{
+  while (c_parser_next_token_is_keyword (parser, RID_CASE)
+	 || c_parser_next_token_is_keyword (parser, RID_DEFAULT)
+	 || (c_parser_next_token_is (parser, CPP_NAME)
+	     && c_parser_peek_2nd_token (parser)->type == CPP_COLON))
+    c_parser_label (parser);
+  c_parser_statement_after_labels (parser);
+}
+
+/* Parse a statement, other than a labeled statement.  */
+
+static void
+c_parser_statement_after_labels (c_parser *parser)
+{
+  location_t loc = c_parser_peek_token (parser)->location;
+  tree stmt = NULL_TREE;
+  switch (c_parser_peek_token (parser)->type)
+    {
+    case CPP_OPEN_BRACE:
+      add_stmt (c_parser_compound_statement (parser));
+      break;
+    case CPP_KEYWORD:
+      switch (c_parser_peek_token (parser)->keyword)
+	{
+	case RID_IF:
+	  c_parser_if_statement (parser);
+	  break;
+	case RID_SWITCH:
+	  c_parser_switch_statement (parser);
+	  break;
+	case RID_WHILE:
+	  c_parser_while_statement (parser);
+	  break;
+	case RID_DO:
+	  c_parser_do_statement (parser);
+	  break;
+	case RID_FOR:
+	  c_parser_for_statement (parser);
+	  break;
+	case RID_GOTO:
+	  c_parser_consume_token (parser);
+	  if (c_parser_next_token_is (parser, CPP_NAME))
+	    {
+	      stmt = c_finish_goto_label (c_parser_peek_token (parser)->value);
+	      c_parser_consume_token (parser);
+	    }
+	  else if (c_parser_next_token_is (parser, CPP_MULT))
+	    {
+	      c_parser_consume_token (parser);
+	      stmt = c_finish_goto_ptr (c_parser_expression (parser).value);
+	    }
+	  else
+	    c_parser_error (parser, "expected identifier or '*'");
+	  goto expect_semicolon;
+	case RID_CONTINUE:
+	  c_parser_consume_token (parser);
+	  stmt = c_finish_bc_stmt (&c_cont_label, false);
+	  goto expect_semicolon;
+	case RID_BREAK:
+	  c_parser_consume_token (parser);
+	  stmt = c_finish_bc_stmt (&c_break_label, true);
+	  goto expect_semicolon;
+	case RID_RETURN:
+	  c_parser_consume_token (parser);
+	  if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+	    {
+	      stmt = c_finish_return (NULL_TREE);
+	      c_parser_consume_token (parser);
+	    }
+	  else
+	    {
+	      stmt = c_finish_return (c_parser_expression (parser).value);
+	      goto expect_semicolon;
+	    }
+	  break;
+	case RID_ASM:
+	  stmt = c_parser_asm_statement (parser);
+	  break;
+	default:
+	  goto expr_stmt;
+	}
+      break;
+    case CPP_SEMICOLON:
+      c_parser_consume_token (parser);
+      break;
+    default:
+    expr_stmt:
+      stmt = c_finish_expr_stmt (c_parser_expression (parser).value);
+    expect_semicolon:
+      c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected ';'");
+      break;
+    }
+  /* Two cases cannot and do not have line numbers associated: If stmt
+     is degenerate, such as "2;", then stmt is an INTEGER_CST, which
+     cannot hold line numbers.  But that's OK because the statement
+     will either be changed to a MODIFY_EXPR during gimplification of
+     the statement expr, or discarded.  If stmt was compound, but
+     without new variables, we will have skipped the creation of a
+     BIND and will have a bare STATEMENT_LIST.  But that's OK because
+     (recursively) all of the component statements should already have
+     line numbers assigned.  ??? Can we discard no-op statements
+     earlier?  */
+  if (stmt && EXPR_P (stmt))
+    SET_EXPR_LOCATION (stmt, loc);
+}
+
+/* Parse a parenthesized condition from an if, do or while statement.
+
+   condition:
+     ( expression )
+*/
+static tree
+c_parser_paren_condition (c_parser *parser)
+{
+  location_t loc;
+  tree cond;
+  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    return error_mark_node;
+  loc = c_parser_peek_token (parser)->location;
+  cond = lang_hooks.truthvalue_conversion (c_parser_expression (parser).value);
+  if (EXPR_P (cond))
+    SET_EXPR_LOCATION (cond, loc);
+  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+  return cond;
+}
+
+/* Parse a statement which is a block in C99.  */
+
+static tree
+c_parser_c99_block_statement (c_parser *parser)
+{
+  tree block = c_begin_compound_stmt (flag_isoc99);
+  c_parser_statement (parser);
+  return c_end_compound_stmt (block, flag_isoc99);
+}
+
+/* Parse the body of an if statement or the else half thereof.  This
+   is just parsing a statement but (a) it is a block in C99, (b) we
+   track whether the body is an if statement for the sake of
+   -Wparentheses warnings, (c) we handle an empty body specially for
+   the sake of -Wextra warnings.  */
+
+static tree
+c_parser_if_body (c_parser *parser, bool *if_p)
+{
+  tree block = c_begin_compound_stmt (flag_isoc99);
+  while (c_parser_next_token_is_keyword (parser, RID_CASE)
+	 || c_parser_next_token_is_keyword (parser, RID_DEFAULT)
+	 || (c_parser_next_token_is (parser, CPP_NAME)
+	     && c_parser_peek_2nd_token (parser)->type == CPP_COLON))
+    c_parser_label (parser);
+  *if_p = c_parser_next_token_is_keyword (parser, RID_IF);
+  if (extra_warnings && c_parser_next_token_is (parser, CPP_SEMICOLON))
+    add_stmt (build (NOP_EXPR, NULL_TREE, NULL_TREE));
+  c_parser_statement_after_labels (parser);
+  return c_end_compound_stmt (block, flag_isoc99);
+}
+
+/* Parse an if statement (C90 6.6.4, C99 6.8.4).
+
+   if-statement:
+     if ( expression ) statement
+     if ( expression ) statement else statement
+*/
+
+static void
+c_parser_if_statement (c_parser *parser)
+{
+  tree block;
+  location_t loc;
+  tree cond;
+  bool first_if = false, second_if = false;
+  tree first_body, second_body;
+  gcc_assert (c_parser_next_token_is_keyword (parser, RID_IF));
+  c_parser_consume_token (parser);
+  block = c_begin_compound_stmt (flag_isoc99);
+  loc = c_parser_peek_token (parser)->location;
+  cond = c_parser_paren_condition (parser);
+  first_body = c_parser_if_body (parser, &first_if);
+  if (c_parser_next_token_is_keyword (parser, RID_ELSE))
+    {
+      c_parser_consume_token (parser);
+      second_body = c_parser_if_body (parser, &second_if);
+    }
+  else
+    second_body = NULL_TREE;
+  c_finish_if_stmt (loc, cond, first_body, second_body, first_if);
+  add_stmt (c_end_compound_stmt (block, flag_isoc99));
+}
+
+/* Parse a switch statement (C90 6.6.4, C99 6.8.4).
+
+   switch-statement:
+     switch (expression) statement
+*/
+
+static void
+c_parser_switch_statement (c_parser *parser)
+{
+  tree block, expr, body, save_break;
+  gcc_assert (c_parser_next_token_is_keyword (parser, RID_SWITCH));
+  c_parser_consume_token (parser);
+  block = c_begin_compound_stmt (flag_isoc99);
+  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    {
+      expr = c_parser_expression (parser).value;
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+    }
+  else
+    expr = error_mark_node;
+  c_start_case (expr);
+  save_break = c_break_label;
+  c_break_label = NULL_TREE;
+  body = c_parser_c99_block_statement (parser);
+  c_finish_case (body);
+  if (c_break_label)
+    add_stmt (build (LABEL_EXPR, void_type_node, c_break_label));
+  c_break_label = save_break;
+  add_stmt (c_end_compound_stmt (block, flag_isoc99));
+}
+
+/* Parse a while statement (C90 6.6.5, C99 6.8.5).
+
+   while-statement:
+      while (expression) statement
+*/
+
+static void
+c_parser_while_statement (c_parser *parser)
+{
+  tree block, cond, body, save_break, save_cont;
+  location_t loc;
+  gcc_assert (c_parser_next_token_is_keyword (parser, RID_WHILE));
+  c_parser_consume_token (parser);
+  block = c_begin_compound_stmt (flag_isoc99);
+  loc = c_parser_peek_token (parser)->location;
+  cond = c_parser_paren_condition (parser);
+  save_break = c_break_label;
+  c_break_label = NULL_TREE;
+  save_cont = c_cont_label;
+  c_cont_label = NULL_TREE;
+  body = c_parser_c99_block_statement (parser);
+  c_finish_loop (loc, cond, NULL, body, c_break_label, c_cont_label, true);
+  add_stmt (c_end_compound_stmt (block, flag_isoc99));
+  c_break_label = save_break;
+  c_cont_label = save_cont;
+}
+
+/* Parse a do statement (C90 6.6.5, C99 6.8.5).
+
+   do-statement:
+     do statement while ( expression ) ;
+*/
+
+static void
+c_parser_do_statement (c_parser *parser)
+{
+  tree block, cond, body, save_break, save_cont, new_break, new_cont;
+  location_t loc;
+  gcc_assert (c_parser_next_token_is_keyword (parser, RID_DO));
+  c_parser_consume_token (parser);
+  block = c_begin_compound_stmt (flag_isoc99);
+  loc = c_parser_peek_token (parser)->location;
+  save_break = c_break_label;
+  c_break_label = NULL_TREE;
+  save_cont = c_cont_label;
+  c_cont_label = NULL_TREE;
+  body = c_parser_c99_block_statement (parser);
+  c_parser_require_keyword (parser, RID_WHILE, "expected 'while'");
+  new_break = c_break_label;
+  c_break_label = save_break;
+  new_cont = c_cont_label;
+  c_cont_label = save_cont;
+  cond = c_parser_paren_condition (parser);
+  if (!c_parser_require (parser, CPP_SEMICOLON, "expected ';'"))
+    c_parser_skip_to_end_of_block_or_statement (parser);
+  c_finish_loop (loc, cond, NULL, body, new_break, new_cont, false);
+  add_stmt (c_end_compound_stmt (block, flag_isoc99));
+}
+
+/* Parse a for statement (C90 6.6.5, C99 6.8.5).
+
+   for-statement:
+     for ( expression[opt] ; expression[opt] ; expression[opt] ) statement
+     for ( nested-declaration expression[opt] ; expression[opt] ) statement
+
+   The form with a declaration is new in C99.
+
+   ??? In accordance with the old parser, the declaration may be a
+   nested function, which is then rejected in check_for_loop_decls,
+   but does it make any sense for this to be included in the grammar?
+   Note in particular that the nested function does not include a
+   trailing ';', whereas the "declaration" production includes one.
+   Also, can we reject bad declarations earlier and cheaper than
+   check_for_loop_decls?  */
+
+static void
+c_parser_for_statement (c_parser *parser)
+{
+  tree block, cond, incr, save_break, save_cont, body;
+  location_t loc;
+  gcc_assert (c_parser_next_token_is_keyword (parser, RID_FOR));
+  c_parser_consume_token (parser);
+  block = c_begin_compound_stmt (flag_isoc99);
+  if (c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    {
+      /* Parse the initialization declaration or expression.  */
+      if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+	{
+	  c_parser_consume_token (parser);
+	  c_finish_expr_stmt (NULL_TREE);
+	}
+      else if (c_parser_next_token_starts_declspecs (parser))
+	{
+	  c_parser_declaration_or_fndef (parser, true, true, true, true);
+	  check_for_loop_decls ();
+	}
+      else if (c_parser_next_token_is_keyword (parser, RID_EXTENSION))
+	{
+	  /* __extension__ can start a declaration, but is also an
+	     unary operator that can start an expression.  Consume all
+	     but the last of a possible series of __extension__ to
+	     determine which.  */
+	  while (c_parser_peek_2nd_token (parser)->type == CPP_KEYWORD
+		 && (c_parser_peek_2nd_token (parser)->keyword
+		     == RID_EXTENSION))
+	    c_parser_consume_token (parser);
+	  if (c_token_starts_declspecs (c_parser_peek_2nd_token (parser)))
+	    {
+	      int ext;
+	      ext = disable_extension_diagnostics ();
+	      c_parser_consume_token (parser);
+	      c_parser_declaration_or_fndef (parser, true, true, true, true);
+	      restore_extension_diagnostics (ext);
+	      check_for_loop_decls ();
+	    }
+	  else
+	    goto init_expr;
+	}
+      else
+	{
+	init_expr:
+	  c_finish_expr_stmt (c_parser_expression (parser).value);
+	  c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected ';'");
+	}
+      /* Parse the loop condition.  */
+      loc = c_parser_peek_token (parser)->location;
+      if (c_parser_next_token_is (parser, CPP_SEMICOLON))
+	{
+	  c_parser_consume_token (parser);
+	  cond = NULL_TREE;
+	}
+      else
+	{
+	  tree ocond = c_parser_expression (parser).value;
+	  cond = lang_hooks.truthvalue_conversion (ocond);
+	  if (EXPR_P (cond))
+	    SET_EXPR_LOCATION (cond, loc);
+	  c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected ';'");
+	}
+      /* Parse the increment expression.  */
+      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	incr = c_process_expr_stmt (NULL_TREE);
+      else
+	incr = c_process_expr_stmt (c_parser_expression (parser).value);
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+    }
+  else
+    {
+      cond = error_mark_node;
+      incr = error_mark_node;
+    }
+  save_break = c_break_label;
+  c_break_label = NULL_TREE;
+  save_cont = c_cont_label;
+  c_cont_label = NULL_TREE;
+  body = c_parser_c99_block_statement (parser);
+  c_finish_loop (loc, cond, incr, body, c_break_label, c_cont_label, true);
+  add_stmt (c_end_compound_stmt (block, flag_isoc99));
+  c_break_label = save_break;
+  c_cont_label = save_cont;
+}
+
+/* Parse an asm statement, a GNU extension.  This is a full-blown asm
+   statement with inputs, outputs, clobbers, and volatile tag
+   allowed.
+
+   asm-statement:
+     asm type-qualifier[opt] ( asm-argument ) ;
+
+   asm-argument:
+     asm-string-literal
+     asm-string-literal : asm-operands[opt]
+     asm-string-literal : asm-operands[opt] : asm-operands[opt]
+     asm-string-literal : asm-operands[opt] : asm-operands[opt] : asm-clobbers
+
+   Qualifiers other than volatile are accepted in the syntax but
+   warned for.  */
+
+static tree
+c_parser_asm_statement (c_parser *parser)
+{
+  tree quals, str, outputs, inputs, clobbers, ret;
+  bool simple;
+  gcc_assert (c_parser_next_token_is_keyword (parser, RID_ASM));
+  c_parser_consume_token (parser);
+  if (c_parser_next_token_is_keyword (parser, RID_VOLATILE))
+    {
+      quals = c_parser_peek_token (parser)->value;
+      c_parser_consume_token (parser);
+    }
+  else if (c_parser_next_token_is_keyword (parser, RID_CONST)
+	   || c_parser_next_token_is_keyword (parser, RID_RESTRICT))
+    {
+      warning ("%E qualifier ignored on asm",
+	       c_parser_peek_token (parser)->value);
+      quals = NULL_TREE;
+      c_parser_consume_token (parser);
+    }
+  else
+    quals = NULL_TREE;
+  /* ??? Follow the C++ parser rather than using the
+     c_lex_string_translate kludge.  */
+  c_lex_string_translate = 0;
+  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+    {
+      c_lex_string_translate = 1;
+      return NULL_TREE;
+    }
+  str = c_parser_asm_string_literal (parser);
+  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+    {
+      simple = true;
+      outputs = NULL_TREE;
+      inputs = NULL_TREE;
+      clobbers = NULL_TREE;
+      goto done_asm;
+    }
+  if (!c_parser_require (parser, CPP_COLON, "expected ':' or ')'"))
+    {
+      c_lex_string_translate = 1;
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  simple = false;
+  /* Parse outputs.  */
+  if (c_parser_next_token_is (parser, CPP_COLON)
+      || c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+    outputs = NULL_TREE;
+  else
+    outputs = c_parser_asm_operands (parser);
+  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+    {
+      inputs = NULL_TREE;
+      clobbers = NULL_TREE;
+      goto done_asm;
+    }
+  if (!c_parser_require (parser, CPP_COLON, "expected ':' or ')'"))
+    {
+      c_lex_string_translate = 1;
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  /* Parse inputs.  */
+  if (c_parser_next_token_is (parser, CPP_COLON)
+      || c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+    inputs = NULL_TREE;
+  else
+    inputs = c_parser_asm_operands (parser);
+  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+    {
+      clobbers = NULL_TREE;
+      goto done_asm;
+    }
+  if (!c_parser_require (parser, CPP_COLON, "expected ':' or ')'"))
+    {
+      c_lex_string_translate = 1;
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  /* Parse clobbers.  */
+  clobbers = c_parser_asm_clobbers (parser);
+ done_asm:
+  c_lex_string_translate = 1;
+  if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected ')'"))
+    {
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+      return NULL_TREE;
+    }
+  if (!c_parser_require (parser, CPP_SEMICOLON, "expected ';'"))
+    c_parser_skip_to_end_of_block_or_statement (parser);
+  ret = build_asm_stmt (quals, build_asm_expr (str, outputs, inputs,
+					       clobbers, simple));
+  return ret;
+}
+
+/* Parse asm operands, a GNU extension.
+
+   asm-operands:
+     asm-operand
+     asm-operands , asm-operand
+
+   asm-operand:
+     asm-string-literal ( expression )
+     [ identifier ] asm-string-literal ( expression )
+*/
+
+static tree
+c_parser_asm_operands (c_parser *parser)
+{
+  tree list = NULL_TREE;
+  while (true)
+    {
+      tree name, str, expr;
+      if (c_parser_next_token_is (parser, CPP_OPEN_SQUARE))
+	{
+	  c_parser_consume_token (parser);
+	  if (c_parser_next_token_is (parser, CPP_NAME))
+	    {
+	      tree id = c_parser_peek_token (parser)->value;
+	      c_parser_consume_token (parser);
+	      name = build_string (IDENTIFIER_LENGTH (id),
+				   IDENTIFIER_POINTER (id));
+	    }
+	  else
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, NULL);
+	      return NULL_TREE;
+	    }
+	  c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, "expected ']'");
+	}
+      else
+	name = NULL_TREE;
+      str = c_parser_asm_string_literal (parser);
+      if (str == NULL_TREE)
+	return NULL_TREE;
+      c_lex_string_translate = 1;
+      if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	{
+	  c_lex_string_translate = 0;
+	  return NULL_TREE;
+	}
+      expr = c_parser_expression (parser).value;
+      c_lex_string_translate = 0;
+      if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected ')'"))
+	{
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	  return NULL_TREE;
+	}
+      list = chainon (list, build_tree_list (build_tree_list (name, str),
+					     expr));
+      if (c_parser_next_token_is (parser, CPP_COMMA))
+	c_parser_consume_token (parser);
+      else
+	break;
+    }
+  return list;
+}
+
+/* Parse asm clobbers, a GNU extension.
+
+   asm-clobbers:
+     asm-string-literal
+     asm-clobbers , asm-string-literal
+*/
+
+static tree
+c_parser_asm_clobbers (c_parser *parser)
+{
+  tree list = NULL_TREE;
+  while (true)
+    {
+      tree str = c_parser_asm_string_literal (parser);
+      if (str)
+	list = tree_cons (NULL_TREE, str, list);
+      else
+	return NULL_TREE;
+      if (c_parser_next_token_is (parser, CPP_COMMA))
+	c_parser_consume_token (parser);
+      else
+	break;
+    }
+  return list;
+}
+
+/* Parse an expression other than a compound expression; that is, an
+   assignment expression (C90 6.3.16, C99 6.5.16).
+
+   assignment-expression:
+     conditional-expression
+     unary-expression assignment-operator assignment-expression
+
+   assignment-operator: one of
+     = *= /= %= += -= <<= >>= &= ^= |=
+
+   In GNU C we accept any conditional expression on the LHS and
+   diagnose the invalid lvalue rather than producing a syntax
+   error.  */
+
+static struct c_expr
+c_parser_expr_no_commas (c_parser *parser)
+{
+  struct c_expr lhs, rhs, ret;
+  enum tree_code code;
+  lhs = c_parser_conditional_expression (parser);
+  switch (c_parser_peek_token (parser)->type)
+    {
+    case CPP_EQ:
+      code = NOP_EXPR;
+      break;
+    case CPP_MULT_EQ:
+      code = MULT_EXPR;
+      break;
+    case CPP_DIV_EQ:
+      code = TRUNC_DIV_EXPR;
+      break;
+    case CPP_MOD_EQ:
+      code = TRUNC_MOD_EXPR;
+      break;
+    case CPP_PLUS_EQ:
+      code = PLUS_EXPR;
+      break;
+    case CPP_MINUS_EQ:
+      code = MINUS_EXPR;
+      break;
+    case CPP_LSHIFT_EQ:
+      code = LSHIFT_EXPR;
+      break;
+    case CPP_RSHIFT_EQ:
+      code = RSHIFT_EXPR;
+      break;
+    case CPP_AND_EQ:
+      code = BIT_AND_EXPR;
+      break;
+    case CPP_XOR_EQ:
+      code = BIT_XOR_EXPR;
+      break;
+    case CPP_OR_EQ:
+      code = BIT_IOR_EXPR;
+      break;
+    default:
+      return lhs;
+    }
+  c_parser_consume_token (parser);
+  rhs = c_parser_expr_no_commas (parser);
+  ret.value = build_modify_expr (lhs.value, code, rhs.value);
+  if (code == NOP_EXPR)
+    ret.original_code = MODIFY_EXPR;
+  else
+    {
+      TREE_NO_WARNING (ret.value) = 1;
+      ret.original_code = ERROR_MARK;
+    }
+  return ret;
+}
+
+/* Parse a conditional expression (C90 6.3.15, C99 6.5.15).
+
+   conditional-expression:
+     logical-OR-expression
+     logical-OR-expression ? expression : conditional-expression
+
+   GNU extensions:
+
+   conditional-expression:
+     logical-OR-expression ? : conditional-expression
+*/
+
+static struct c_expr
+c_parser_conditional_expression (c_parser *parser)
+{
+  struct c_expr cond, exp1, exp2, ret;
+  cond = c_parser_binary_expression (parser);
+  if (c_parser_next_token_is_not (parser, CPP_QUERY))
+    return cond;
+  c_parser_consume_token (parser);
+  if (c_parser_next_token_is (parser, CPP_COLON))
+    {
+      if (pedantic)
+	pedwarn ("ISO C forbids omitting the middle term of a ?: expression");
+      /* Make sure first operand is calculated only once.  */
+      exp1.value = save_expr (default_conversion (cond.value));
+      cond.value = lang_hooks.truthvalue_conversion (exp1.value);
+      skip_evaluation += cond.value == truthvalue_true_node;
+    }
+  else
+    {
+      cond.value
+	= lang_hooks.truthvalue_conversion (default_conversion (cond.value));
+      skip_evaluation += cond.value == truthvalue_false_node;
+      exp1 = c_parser_expression (parser);
+      skip_evaluation += ((cond.value == truthvalue_true_node)
+			  - (cond.value == truthvalue_false_node));
+    }
+  if (!c_parser_require (parser, CPP_COLON, "expected ':'"))
+    {
+      skip_evaluation -= cond.value == truthvalue_true_node;
+      ret.value = error_mark_node;
+      ret.original_code = ERROR_MARK;
+      return ret;
+    }
+  exp2 = c_parser_conditional_expression (parser);
+  skip_evaluation -= cond.value == truthvalue_true_node;
+  ret.value = build_conditional_expr (cond.value, exp1.value, exp2.value);
+  ret.original_code = ERROR_MARK;
+  return ret;
+}
+
+/* Parse a binary expression; that is, a logical-OR-expression (C90
+   6.3.5-6.3.14, C99 6.5.5-6.5.14).
+
+   multiplicative-expression:
+     cast-expression
+     multiplicative-expression * cast-expression
+     multiplicative-expression / cast-expression
+     multiplicative-expression % cast-expression
+
+   additive-expression:
+     multiplicative-expression
+     additive-expression + multiplicative-expression
+     additive-expression - multiplicative-expression
+
+   shift-expression:
+     additive-expression
+     shift-expression << additive-expression
+     shift-expression >> additive-expression
+
+   relational-expression:
+     shift-expression
+     relational-expression < shift-expression
+     relational-expression > shift-expression
+     relational-expression <= shift-expression
+     relational-expression >= shift-expression
+
+   equality-expression:
+     relational-expression
+     equality-expression == relational-expression
+     equality-expression != relational-expression
+
+   AND-expression:
+     equality-expression
+     AND-expression & equality-expression
+
+   exclusive-OR-expression:
+     AND-expression
+     exclusive-OR-expression ^ AND-expression
+
+   inclusive-OR-expression:
+     exclusive-OR-expression
+     inclusive-OR-expression | exclusive-OR-expression
+
+   logical-AND-expression:
+     inclusive-OR-expression
+     logical-AND-expression && inclusive-OR-expression
+
+   logical-OR-expression:
+     logical-AND-expression
+     logical-OR-expression || logical-AND-expression
+*/
+
+static struct c_expr
+c_parser_binary_expression (c_parser *parser)
+{
+  /* A binary expression is parsed using operator-precedence parsing,
+     with the operands being cast expressions.  All the binary
+     operators are left-associative.  Thus a binary expression is of
+     form:
+
+     E0 op1 E1 op2 E2 ...
+
+     which we represent on a stack.  On the stack, the precedence
+     levels are strictly increasing.  When a new operator is
+     encountered of higher precedence than that at the top of the
+     stack, it is pushed; its LHS is the top expression, and its RHS
+     is everything parsed until it is popped.  When a new operator is
+     encountered with precedence less than or equal to that at the top
+     of the stack, triples E[i-1] op[i] E[i] are popped and replaced
+     by the result of the operation until the operator at the top of
+     the stack has lower precedence than the new operator or there is
+     only one element on the stack; then the top expression is the LHS
+     of the new operator.  In the case of logical AND and OR
+     expressions, we also need to adjust skip_evaluation as
+     appropriate when the operators are pushed and popped.  */
+
+  /* The precedence levels, where 0 is a dummy lowest level used for
+     the bottom of the stack.  */
+  enum prec {
+    PREC_NONE,
+    PREC_LOGOR,
+    PREC_LOGAND,
+    PREC_BITOR,
+    PREC_BITXOR,
+    PREC_BITAND,
+    PREC_EQ,
+    PREC_REL,
+    PREC_SHIFT,
+    PREC_ADD,
+    PREC_MULT,
+    NUM_PRECS
+  };
+  struct {
+    /* The expression at this stack level.  */
+    struct c_expr expr;
+    /* The precedence of the operator on its left, PREC_NONE at the
+       bottom of the stack.  */
+    enum prec prec;
+    /* The operation on its left.  */
+    enum tree_code op;
+  } stack[NUM_PRECS];
+  int sp;
+#define POP								      \
+  do {									      \
+    switch (stack[sp].op)						      \
+      {									      \
+      case TRUTH_ANDIF_EXPR:						      \
+	skip_evaluation -= stack[sp - 1].expr.value == truthvalue_false_node; \
+	break;								      \
+      case TRUTH_ORIF_EXPR:						      \
+	skip_evaluation -= stack[sp - 1].expr.value == truthvalue_true_node;  \
+	break;								      \
+      default:								      \
+	break;								      \
+      }									      \
+    stack[sp - 1].expr = parser_build_binary_op (stack[sp].op,		      \
+						 stack[sp - 1].expr,	      \
+						 stack[sp].expr);	      \
+    sp--;								      \
+  } while (0)
+  stack[0].expr = c_parser_cast_expression (parser);
+  stack[0].prec = PREC_NONE;
+  sp = 0;
+  while (true)
+    {
+      enum prec oprec;
+      enum tree_code ocode;
+      if (parser->error)
+	goto out;
+      switch (c_parser_peek_token (parser)->type)
+	{
+	case CPP_MULT:
+	  oprec = PREC_MULT;
+	  ocode = MULT_EXPR;
+	  break;
+	case CPP_DIV:
+	  oprec = PREC_MULT;
+	  ocode = TRUNC_DIV_EXPR;
+	  break;
+	case CPP_MOD:
+	  oprec = PREC_MULT;
+	  ocode = TRUNC_MOD_EXPR;
+	  break;
+	case CPP_PLUS:
+	  oprec = PREC_ADD;
+	  ocode = PLUS_EXPR;
+	  break;
+	case CPP_MINUS:
+	  oprec = PREC_ADD;
+	  ocode = MINUS_EXPR;
+	  break;
+	case CPP_LSHIFT:
+	  oprec = PREC_SHIFT;
+	  ocode = LSHIFT_EXPR;
+	  break;
+	case CPP_RSHIFT:
+	  oprec = PREC_SHIFT;
+	  ocode = RSHIFT_EXPR;
+	  break;
+	case CPP_LESS:
+	  oprec = PREC_REL;
+	  ocode = LT_EXPR;
+	  break;
+	case CPP_GREATER:
+	  oprec = PREC_REL;
+	  ocode = GT_EXPR;
+	  break;
+	case CPP_LESS_EQ:
+	  oprec = PREC_REL;
+	  ocode = LE_EXPR;
+	  break;
+	case CPP_GREATER_EQ:
+	  oprec = PREC_REL;
+	  ocode = GE_EXPR;
+	  break;
+	case CPP_EQ_EQ:
+	  oprec = PREC_EQ;
+	  ocode = EQ_EXPR;
+	  break;
+	case CPP_NOT_EQ:
+	  oprec = PREC_EQ;
+	  ocode = NE_EXPR;
+	  break;
+	case CPP_AND:
+	  oprec = PREC_BITAND;
+	  ocode = BIT_AND_EXPR;
+	  break;
+	case CPP_XOR:
+	  oprec = PREC_BITXOR;
+	  ocode = BIT_XOR_EXPR;
+	  break;
+	case CPP_OR:
+	  oprec = PREC_BITOR;
+	  ocode = BIT_IOR_EXPR;
+	  break;
+	case CPP_AND_AND:
+	  oprec = PREC_LOGAND;
+	  ocode = TRUTH_ANDIF_EXPR;
+	  break;
+	case CPP_OR_OR:
+	  oprec = PREC_LOGOR;
+	  ocode = TRUTH_ORIF_EXPR;
+	  break;
+	default:
+	  /* Not a binary operator, so end of the binary
+	     expression.  */
+	  goto out;
+	}
+      c_parser_consume_token (parser);
+      while (oprec <= stack[sp].prec)
+	POP;
+      switch (ocode)
+	{
+	case TRUTH_ANDIF_EXPR:
+	  stack[sp].expr.value = lang_hooks.truthvalue_conversion
+	    (default_conversion (stack[sp].expr.value));
+	  skip_evaluation += stack[sp].expr.value == truthvalue_false_node;
+	  break;
+	case TRUTH_ORIF_EXPR:
+	  stack[sp].expr.value = lang_hooks.truthvalue_conversion
+	    (default_conversion (stack[sp].expr.value));
+	  skip_evaluation += stack[sp].expr.value == truthvalue_true_node;
+	  break;
+	default:
+	  break;
+	}
+      sp++;
+      stack[sp].expr = c_parser_cast_expression (parser);
+      stack[sp].prec = oprec;
+      stack[sp].op = ocode;
+    }
+ out:
+  while (sp > 0)
+    POP;
+  return stack[0].expr;
+#undef POP
+}
+
+/* Parse a cast expression (C90 6.3.4, C99 6.5.4).
+
+   cast-expression:
+     unary-expression
+     ( type-name ) unary-expression
+*/
+
+static struct c_expr
+c_parser_cast_expression (c_parser *parser)
+{
+  /* If the expression begins with a parenthesized type name, it may
+     be either a cast or a compound literal; we need to see whether
+     the next character is '{' to tell the difference.  If not, it is
+     an unary expression.  */
+  if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)
+      && c_token_starts_typename (c_parser_peek_2nd_token (parser)))
+    {
+      struct c_type_name *type_name;
+      struct c_expr ret;
+      tree expr;
+      c_parser_consume_token (parser);
+      type_name = c_parser_type_name (parser);
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+      if (type_name == NULL)
+	{
+	  ret.value = error_mark_node;
+	  ret.original_code = ERROR_MARK;
+	  return ret;
+	}
+      if (c_parser_next_token_is (parser, CPP_OPEN_BRACE))
+	return c_parser_postfix_expression_after_paren_type (parser,
+							     type_name);
+      expr = c_parser_cast_expression (parser).value;
+      ret.value = c_cast_expr (type_name, expr);
+      ret.original_code = ERROR_MARK;
+      return ret;
+    }
+  else
+    return c_parser_unary_expression (parser);
+}
+
+/* Parse an unary expression (C90 6.3.3, C99 6.5.3).
+
+   unary-expression:
+     postfix-expression
+     ++ unary-expression
+     -- unary-expression
+     unary-operator cast-expression
+     sizeof unary-expression
+     sizeof ( type-name )
+
+   unary-operator: one of
+     & * + - ~ !
+
+   GNU extensions:
+
+   unary-expression:
+     __alignof__ unary-expression
+     __alignof__ ( type-name )
+     && identifier
+
+   unary-operator: one of
+     __extension__ __real__ __imag__
+
+   In addition, the GNU syntax treats ++ and -- as unary operators, so
+   they may be applied to cast expressions with errors for non-lvalues
+   given later.  */
+
+static struct c_expr
+c_parser_unary_expression (c_parser *parser)
+{
+  int ext;
+  struct c_expr ret;
+  ret.original_code = ERROR_MARK;
+  switch (c_parser_peek_token (parser)->type)
+    {
+    case CPP_PLUS_PLUS:
+      c_parser_consume_token (parser);
+      ret.value = build_unary_op (PREINCREMENT_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_MINUS_MINUS:
+      c_parser_consume_token (parser);
+      ret.value = build_unary_op (PREDECREMENT_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_AND:
+      c_parser_consume_token (parser);
+      ret.value = build_unary_op (ADDR_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_MULT:
+      c_parser_consume_token (parser);
+      ret.value = build_indirect_ref (c_parser_cast_expression (parser).value,
+				      "unary *");
+      return ret;
+    case CPP_PLUS:
+      c_parser_consume_token (parser);
+      if (!c_dialect_objc () && warn_traditional && !in_system_header)
+	warning ("traditional C rejects the unary plus operator");
+      ret.value = build_unary_op (CONVERT_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_MINUS:
+      c_parser_consume_token (parser);
+      ret.value = build_unary_op (NEGATE_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_COMPL:
+      c_parser_consume_token (parser);
+      ret.value = build_unary_op (BIT_NOT_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_NOT:
+      c_parser_consume_token (parser);
+      ret.value = build_unary_op (TRUTH_NOT_EXPR,
+				  c_parser_cast_expression (parser).value, 0);
+      overflow_warning (ret.value);
+      return ret;
+    case CPP_AND_AND:
+      /* Refer to the address of a label as a pointer.  */
+      c_parser_consume_token (parser);
+      if (c_parser_next_token_is (parser, CPP_NAME))
+	{
+	  ret.value = finish_label_address_expr
+	    (c_parser_peek_token (parser)->value);
+	  c_parser_consume_token (parser);
+	  return ret;
+	}
+      else
+	{
+	  c_parser_error (parser, "expected identifier");
+	  ret.value = error_mark_node;
+	  return ret;
+	}
+    case CPP_KEYWORD:
+      switch (c_parser_peek_token (parser)->keyword)
+	{
+	case RID_SIZEOF:
+	  return c_parser_sizeof_expression (parser);
+	case RID_ALIGNOF:
+	  return c_parser_alignof_expression (parser);
+	case RID_EXTENSION:
+	  c_parser_consume_token (parser);
+	  ext = disable_extension_diagnostics ();
+	  ret = c_parser_cast_expression (parser);
+	  restore_extension_diagnostics (ext);
+	  return ret;
+	case RID_REALPART:
+	  c_parser_consume_token (parser);
+	  ret.value = build_unary_op (REALPART_EXPR,
+				      c_parser_cast_expression (parser).value,
+				      0);
+	  return ret;
+	case RID_IMAGPART:
+	  c_parser_consume_token (parser);
+	  ret.value = build_unary_op (IMAGPART_EXPR,
+				      c_parser_cast_expression (parser).value,
+				      0);
+	  return ret;
+	default:
+	  return c_parser_postfix_expression (parser);
+	}
+    default:
+      return c_parser_postfix_expression (parser);
+    }
+}
+
+/* Parse a sizeof expression.  */
+
+static struct c_expr
+c_parser_sizeof_expression (c_parser *parser)
+{
+  struct c_expr expr;
+  gcc_assert (c_parser_next_token_is_keyword (parser, RID_SIZEOF));
+  c_parser_consume_token (parser);
+  skip_evaluation++;
+  in_sizeof++;
+  if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)
+      && c_token_starts_typename (c_parser_peek_2nd_token (parser)))
+    {
+      /* Either sizeof ( type-name ) or sizeof unary-expression
+	 starting with a compound literal.  */
+      struct c_type_name *type_name;
+      c_parser_consume_token (parser);
+      type_name = c_parser_type_name (parser);
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+      if (type_name == NULL)
+	{
+	  struct c_expr ret;
+	  skip_evaluation--;
+	  in_sizeof--;
+	  ret.value = error_mark_node;
+	  ret.original_code = ERROR_MARK;
+	  return ret;
+	}
+      if (c_parser_next_token_is (parser, CPP_OPEN_BRACE))
+	{
+	  expr = c_parser_postfix_expression_after_paren_type (parser,
+							       type_name);
+	  goto sizeof_expr;
+	}
+      /* sizeof ( type-name ).  */
+      skip_evaluation--;
+      in_sizeof--;
+      return c_expr_sizeof_type (type_name);
+    }
+  else
+    {
+      expr = c_parser_unary_expression (parser);
+    sizeof_expr:
+      skip_evaluation--;
+      in_sizeof--;
+      if (TREE_CODE (expr.value) == COMPONENT_REF
+	  && DECL_C_BIT_FIELD (TREE_OPERAND (expr.value, 1)))
+	error ("%<sizeof%> applied to a bit-field");
+      return c_expr_sizeof_expr (expr);
+    }
+}
+
+/* Parse an alignof expression.  */
+
+static struct c_expr
+c_parser_alignof_expression (c_parser *parser)
+{
+  struct c_expr expr;
+  gcc_assert (c_parser_next_token_is_keyword (parser, RID_ALIGNOF));
+  c_parser_consume_token (parser);
+  skip_evaluation++;
+  in_alignof++;
+  if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)
+      && c_token_starts_typename (c_parser_peek_2nd_token (parser)))
+    {
+      /* Either __alignof__ ( type-name ) or __alignof__
+	 unary-expression starting with a compound literal.  */
+      struct c_type_name *type_name;
+      struct c_expr ret;
+      c_parser_consume_token (parser);
+      type_name = c_parser_type_name (parser);
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+      if (type_name == NULL)
+	{
+	  struct c_expr ret;
+	  skip_evaluation--;
+	  in_alignof--;
+	  ret.value = error_mark_node;
+	  ret.original_code = ERROR_MARK;
+	  return ret;
+	}
+      if (c_parser_next_token_is (parser, CPP_OPEN_BRACE))
+	{
+	  expr = c_parser_postfix_expression_after_paren_type (parser,
+							       type_name);
+	  goto alignof_expr;
+	}
+      /* alignof ( type-name ).  */
+      skip_evaluation--;
+      in_alignof--;
+      ret.value = c_alignof (groktypename (type_name));
+      ret.original_code = ERROR_MARK;
+      return ret;
+    }
+  else
+    {
+      struct c_expr ret;
+      expr = c_parser_unary_expression (parser);
+    alignof_expr:
+      skip_evaluation--;
+      in_alignof--;
+      ret.value = c_alignof_expr (expr.value);
+      ret.original_code = ERROR_MARK;
+      return ret;
+    }
+}
+
+/* Parse a postfix expression (C90 6.3.1-6.3.2, C99 6.5.1-6.5.2).
+
+   postfix-expression:
+     primary-expression
+     postfix-expression [ expression ]
+     postfix-expression ( argument-expression-list[opt] )
+     postfix-expression . identifier
+     postfix-expression -> identifier
+     postfix-expression ++
+     postfix-expression --
+     ( type-name ) { initializer-list }
+     ( type-name ) { initializer-list , }
+
+   argument-expression-list:
+     argument-expression
+     argument-expression-list , argument-expression
+
+   primary-expression:
+     identifier
+     constant
+     string-literal
+     ( expression )
+
+   GNU extensions:
+
+   primary-expression:
+     __func__
+       (treated as a keyword in GNU C)
+     __FUNCTION__
+     __PRETTY_FUNCTION__
+     ( compound-statement )
+     __builtin_va_arg ( assignment-expression , type-name )
+     __builtin_offsetof ( type-name , offsetof-member-designator )
+     __builtin_choose_expr ( assignment-expression ,
+			     assignment-expression ,
+			     assignment-expression )
+     __builtin_types_compatible_p ( type-name , type-name )
+
+   offsetof-member-designator:
+     identifier
+     offsetof-member-designator . identifier
+     offsetof-member-designator [ expression ]
+
+   TODO: Objective-C.
+*/
+
+static struct c_expr
+c_parser_postfix_expression (c_parser *parser)
+{
+  struct c_expr expr, e1, e2, e3;
+  struct c_type_name *t1, *t2;
+  tree id;
+  switch (c_parser_peek_token (parser)->type)
+    {
+    case CPP_NUMBER:
+    case CPP_CHAR:
+    case CPP_WCHAR:
+      expr.value = c_parser_peek_token (parser)->value;
+      expr.original_code = ERROR_MARK;
+      c_parser_consume_token (parser);
+      break;
+    case CPP_STRING:
+    case CPP_WSTRING:
+      expr.value = c_parser_peek_token (parser)->value;
+      expr.original_code = STRING_CST;
+      c_parser_consume_token (parser);
+      break;
+    case CPP_NAME:
+      if (c_parser_peek_token (parser)->id_kind != C_ID_ID)
+	{
+	  c_parser_error (parser, "expected expression");
+	  expr.value = error_mark_node;
+	  expr.original_code = ERROR_MARK;
+	  break;
+	}
+      id = c_parser_peek_token (parser)->value;
+      c_parser_consume_token (parser);
+      expr.value = build_external_ref (id,
+				       (c_parser_peek_token (parser)->type
+					== CPP_OPEN_PAREN));
+      expr.original_code = ERROR_MARK;
+      break;
+    case CPP_OPEN_PAREN:
+      /* A parenthesized expression, statement expression or compound
+	 literal.  */
+      if (c_parser_peek_2nd_token (parser)->type == CPP_OPEN_BRACE)
+	{
+	  /* A statement expression.  */
+	  tree stmt;
+	  c_parser_consume_token (parser);
+	  c_parser_consume_token (parser);
+	  if (cur_stmt_list == NULL)
+	    {
+	      error ("braced-group within expression allowed "
+		     "only inside a function");
+	      parser->error = true;
+	      c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL);
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  stmt = c_begin_stmt_expr ();
+	  c_parser_compound_statement_nostart (parser);
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  if (pedantic)
+	    pedwarn ("ISO C forbids braced-groups within expressions");
+	  expr.value = c_finish_stmt_expr (stmt);
+	  expr.original_code = ERROR_MARK;
+	}
+      else if (c_token_starts_typename (c_parser_peek_2nd_token (parser)))
+	{
+	  /* A compound literal.  ??? Can we actually get here rather
+	     than going directly to
+	     c_parser_postfix_expression_after_paren_type from
+	     elsewhere?  */
+	  struct c_type_name *type_name;
+	  c_parser_consume_token (parser);
+	  type_name = c_parser_type_name (parser);
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  if (type_name == NULL)
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	    }
+	  else
+	    expr = c_parser_postfix_expression_after_paren_type (parser,
+								 type_name);
+	}
+      else
+	{
+	  /* A parenthesized expression.  */
+	  c_parser_consume_token (parser);
+	  expr = c_parser_expression (parser);
+	  if (TREE_CODE (expr.value) == MODIFY_EXPR)
+	    TREE_NO_WARNING (expr.value) = 1;
+	  expr.original_code = ERROR_MARK;
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	}
+      break;
+    case CPP_KEYWORD:
+      switch (c_parser_peek_token (parser)->keyword)
+	{
+	case RID_FUNCTION_NAME:
+	case RID_PRETTY_FUNCTION_NAME:
+	case RID_C99_FUNCTION_NAME:
+	  expr.value = fname_decl (c_parser_peek_token (parser)->keyword,
+				   c_parser_peek_token (parser)->value);
+	  expr.original_code = ERROR_MARK;
+	  c_parser_consume_token (parser);
+	  break;
+	case RID_VA_ARG:
+	  c_parser_consume_token (parser);
+	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  e1 = c_parser_expr_no_commas (parser);
+	  if (!c_parser_require (parser, CPP_COMMA, "expected ','"))
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  t1 = c_parser_type_name (parser);
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  if (t1 == NULL)
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	    }
+	  else
+	    {
+	      expr.value = build_va_arg (e1.value, groktypename (t1));
+	      expr.original_code = ERROR_MARK;
+	    }
+	  break;
+	case RID_OFFSETOF:
+	  c_parser_consume_token (parser);
+	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  t1 = c_parser_type_name (parser);
+	  if (t1 == NULL)
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  if (!c_parser_require (parser, CPP_COMMA, "expected ','"))
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  {
+	    tree type = groktypename (t1);
+	    tree offsetof_ref;
+	    if (type == error_mark_node)
+	      offsetof_ref = error_mark_node;
+	    else
+	      offsetof_ref = build1 (INDIRECT_REF, type, NULL);
+	    /* Parse the second argument to __builtin_offsetof.  We
+	       must have one identifier, and beyond that we want to
+	       accept sub structure and sub array references.  */
+	    if (c_parser_next_token_is (parser, CPP_NAME))
+	      {
+		offsetof_ref = build_component_ref
+		  (offsetof_ref, c_parser_peek_token (parser)->value);
+		c_parser_consume_token (parser);
+		while (c_parser_next_token_is (parser, CPP_DOT)
+		       || c_parser_next_token_is (parser,
+						  CPP_OPEN_SQUARE))
+		  {
+		    if (c_parser_next_token_is (parser, CPP_DOT))
+		      {
+			c_parser_consume_token (parser);
+			if (c_parser_next_token_is_not (parser,
+							CPP_NAME))
+			  {
+			    c_parser_error (parser, "expected identifier");
+			    break;
+			  }
+			offsetof_ref = build_component_ref
+			  (offsetof_ref,
+			   c_parser_peek_token (parser)->value);
+			c_parser_consume_token (parser);
+		      }
+		    else
+		      {
+			tree idx;
+			c_parser_consume_token (parser);
+			idx = c_parser_expression (parser).value;
+			c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE,
+						   "expected ']'");
+			offsetof_ref = build_array_ref (offsetof_ref, idx);
+		      }
+		  }
+	      }
+	    else
+	      c_parser_error (parser, "expected identifier");
+	    c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+				       "expected ')'");
+	    expr.value = fold_offsetof (offsetof_ref);
+	    expr.original_code = ERROR_MARK;
+	  }
+	  break;
+	case RID_CHOOSE_EXPR:
+	  c_parser_consume_token (parser);
+	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  e1 = c_parser_expr_no_commas (parser);
+	  if (!c_parser_require (parser, CPP_COMMA, "expected ','"))
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  e2 = c_parser_expr_no_commas (parser);
+	  if (!c_parser_require (parser, CPP_COMMA, "expected ','"))
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  e3 = c_parser_expr_no_commas (parser);
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  {
+	    tree c;
+
+	    c = fold (e1.value);
+	    STRIP_NOPS (c);
+	    if (TREE_CODE (c) != INTEGER_CST)
+	      error ("first argument to __builtin_choose_expr not"
+		     " a constant");
+	    expr = integer_zerop (c) ? e3 : e2;
+	  }
+	  break;
+	case RID_TYPES_COMPATIBLE_P:
+	  c_parser_consume_token (parser);
+	  if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected '('"))
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  t1 = c_parser_type_name (parser);
+	  if (t1 == NULL)
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  if (!c_parser_require (parser, CPP_COMMA, "expected ','"))
+	    {
+	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  t2 = c_parser_type_name (parser);
+	  if (t2 == NULL)
+	    {
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      break;
+	    }
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  {
+	    tree e1, e2;
+
+	    e1 = TYPE_MAIN_VARIANT (groktypename (t1));
+	    e2 = TYPE_MAIN_VARIANT (groktypename (t2));
+
+	    expr.value = comptypes (e1, e2)
+	      ? build_int_cst (NULL_TREE, 1)
+	      : build_int_cst (NULL_TREE, 0);
+	    expr.original_code = ERROR_MARK;
+	  }
+	  break;
+	default:
+	  c_parser_error (parser, "expected expression");
+	  expr.value = error_mark_node;
+	  expr.original_code = ERROR_MARK;
+	  break;
+	}
+      break;
+    default:
+      c_parser_error (parser, "expected expression");
+      expr.value = error_mark_node;
+      expr.original_code = ERROR_MARK;
+      break;
+    }
+  return c_parser_postfix_expression_after_primary (parser, expr);
+}
+
+/* Parse a postfix expression after a parenthesized type name: the
+   brace-enclosed initializer of a compound literal, possibly followed
+   by some postfix operators.  This is separate because it is not
+   possible to tell until after the type name whether a cast
+   expression has a cast or a compound literal, or whether the operand
+   of sizeof is a parenthesized type name or starts with a compound
+   literal.  */
+
+static struct c_expr
+c_parser_postfix_expression_after_paren_type (c_parser *parser,
+					      struct c_type_name *type_name)
+{
+  tree type;
+  struct c_expr init;
+  struct c_expr expr;
+  start_init (NULL_TREE, NULL, 0);
+  type = groktypename (type_name);
+  if (C_TYPE_VARIABLE_SIZE (type))
+    {
+      error ("compound literal has variable size");
+      type = error_mark_node;
+    }
+  init = c_parser_braced_init (parser, type, false);
+  finish_init ();
+  maybe_warn_string_init (type, init);
+
+  if (pedantic && !flag_isoc99)
+    pedwarn ("ISO C90 forbids compound literals");
+  expr.value = build_compound_literal (type, init.value);
+  expr.original_code = ERROR_MARK;
+  return c_parser_postfix_expression_after_primary (parser, expr);
+}
+
+/* Parse a postfix expression after the initial primary or compound
+   literal; that is, parse a series of postfix operators.  */
+
+static struct c_expr
+c_parser_postfix_expression_after_primary (c_parser *parser,
+					   struct c_expr expr)
+{
+  tree ident, idx, exprlist;
+  while (true)
+    {
+      switch (c_parser_peek_token (parser)->type)
+	{
+	case CPP_OPEN_SQUARE:
+	  /* Array reference.  */
+	  c_parser_consume_token (parser);
+	  idx = c_parser_expression (parser).value;
+	  c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, "expected ']'");
+	  expr.value = build_array_ref (expr.value, idx);
+	  expr.original_code = ERROR_MARK;
+	  break;
+	case CPP_OPEN_PAREN:
+	  /* Function call.  */
+	  c_parser_consume_token (parser);
+	  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	    exprlist = NULL_TREE;
+	  else
+	    exprlist = c_parser_expr_list (parser);
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected ')'");
+	  expr.value = build_function_call (expr.value, exprlist);
+	  expr.original_code = ERROR_MARK;
+	  break;
+	case CPP_DOT:
+	  /* Structure element reference.  */
+	  c_parser_consume_token (parser);
+	  if (c_parser_next_token_is (parser, CPP_NAME))
+	    ident = c_parser_peek_token (parser)->value;
+	  else
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      return expr;
+	    }
+	  c_parser_consume_token (parser);
+	  expr.value = build_component_ref (expr.value, ident);
+	  expr.original_code = ERROR_MARK;
+	  break;
+	case CPP_DEREF:
+	  /* Structure element reference.  */
+	  c_parser_consume_token (parser);
+	  if (c_parser_next_token_is (parser, CPP_NAME))
+	    ident = c_parser_peek_token (parser)->value;
+	  else
+	    {
+	      c_parser_error (parser, "expected identifier");
+	      expr.value = error_mark_node;
+	      expr.original_code = ERROR_MARK;
+	      return expr;
+	    }
+	  c_parser_consume_token (parser);
+	  expr.value = build_component_ref (build_indirect_ref (expr.value,
+								"->"), ident);
+	  expr.original_code = ERROR_MARK;
+	  break;
+	case CPP_PLUS_PLUS:
+	  /* Postincrement.  */
+	  c_parser_consume_token (parser);
+	  expr.value = build_unary_op (POSTINCREMENT_EXPR, expr.value, 0);
+	  expr.original_code = ERROR_MARK;
+	  break;
+	case CPP_MINUS_MINUS:
+	  /* Postdecrement.  */
+	  c_parser_consume_token (parser);
+	  expr.value = build_unary_op (POSTDECREMENT_EXPR, expr.value, 0);
+	  expr.original_code = ERROR_MARK;
+	  break;
+	default:
+	  return expr;
+	}
+    }
+}
+
+/* Parse an expression (C90 6.3.17, C99 6.5.17).
+
+   expression:
+     assignment-expression
+     expression , assignment-expression
+*/
+
+static struct c_expr
+c_parser_expression (c_parser *parser)
+{
+  struct c_expr expr;
+  expr = c_parser_expr_no_commas (parser);
+  while (c_parser_next_token_is (parser, CPP_COMMA))
+    {
+      struct c_expr next;
+      c_parser_consume_token (parser);
+      next = c_parser_expr_no_commas (parser);
+      expr.value = build_compound_expr (expr.value, next.value);
+      expr.original_code = COMPOUND_EXPR;
+    }
+  return expr;
+}
+
+/* Parse a non-empty list of expressions.
+
+   nonempty-expr-list:
+     assignment-expression
+     nonempty-expr-list , assignment-expression
+*/
+
+static tree
+c_parser_expr_list (c_parser *parser)
+{
+  struct c_expr expr;
+  tree ret;
+  expr = c_parser_expr_no_commas (parser);
+  ret = build_tree_list (NULL_TREE, expr.value);
+  while (c_parser_next_token_is (parser, CPP_COMMA))
+    {
+      c_parser_consume_token (parser);
+      expr = c_parser_expr_no_commas (parser);
+      ret = chainon (ret, build_tree_list (NULL_TREE, expr.value));
+    }
+  return ret;
+}
+
+\f
+/* The actual parser and external interface.  ??? Does this need to be
+   garbage-collected?  */
+
+static GTY (()) c_parser *the_parser;
+
+/* Parse a single source file.  */
+
+void
+c_parse_file (void)
+{
+  the_parser = c_parser_new ();
+  c_parser_translation_unit (the_parser);
+  the_parser = NULL;
+}
+
+#include "gt-c-parser.h"
diff -rupN GCC.orig/gcc/c-tree.h GCC/gcc/c-tree.h
--- GCC.orig/gcc/c-tree.h	2004-10-14 00:31:11.000000000 +0000
+++ GCC/gcc/c-tree.h	2004-10-16 21:50:02.000000000 +0000
@@ -204,6 +204,10 @@ struct c_declspecs {
   enum c_typespec_keyword typespec_word;
   /* The storage class specifier, or csc_none if none.  */
   enum c_storage_class storage_class;
+  /* Whether any declaration specifiers have been seen at all.  */
+  BOOL_BITFIELD declspecs_seen_p : 1;
+  /* Whether a type specifier has been seen.  */
+  BOOL_BITFIELD type_seen_p : 1;
   /* Whether something other than a storage class specifier or
      attribute has been seen.  This is used to warn for the
      obsolescent usage of storage class specifiers other than at the

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-27 19:31       ` Zack Weinberg
@ 2004-10-27 21:31         ` Richard Henderson
  2004-10-28  7:38           ` Alan Modra
  0 siblings, 1 reply; 42+ messages in thread
From: Richard Henderson @ 2004-10-27 21:31 UTC (permalink / raw)
  To: Zack Weinberg; +Cc: Joseph S. Myers, gcc-patches

On Wed, Oct 27, 2004 at 12:22:25PM -0700, Zack Weinberg wrote:
> ... it's still a
> layering violation to be calling assembly output routines directly
> from the parser.

Granted.  Just wanted to nip the "it's not useful" thought in the bud.

Personally, I think there should be no ordering guarantees between any
top-level objects.  If you need particular ordering from your assembly,
then you should write one top-level asm and not two.


r~

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: New C parser [patch]
  2004-10-27 21:31         ` Richard Henderson
@ 2004-10-28  7:38           ` Alan Modra
  0 siblings, 0 replies; 42+ messages in thread
From: Alan Modra @ 2004-10-28  7:38 UTC (permalink / raw)
  To: Zack Weinberg, gcc-patches

On Wed, Oct 27, 2004 at 02:22:16PM -0700, Richard Henderson wrote:
> Personally, I think there should be no ordering guarantees between any
> top-level objects.  If you need particular ordering from your assembly,
> then you should write one top-level asm and not two.

You can also use subsections to enforce ordering, so even when multiple
asms are needed there's no real need to have gcc emit them in any
particular order.

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre

^ permalink raw reply	[flat|nested] 42+ messages in thread

end of thread, other threads:[~2004-10-28  6:00 UTC | newest]

Thread overview: 42+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-10-23  1:25 New C parser [patch] Joseph S. Myers
2004-10-23  2:39 ` Steven Bosscher
2004-10-23  4:15   ` Joseph S. Myers
2004-10-23  5:44 ` Scott Robert Ladd
2004-10-24 22:49 ` Joseph S. Myers
2004-10-26  0:32   ` Zack Weinberg
2004-10-26  1:03     ` Andrew Pinski
2004-10-26  1:03       ` Zack Weinberg
2004-10-26  1:11         ` Joseph S. Myers
2004-10-26  8:23           ` Zack Weinberg
2004-10-26  1:30       ` Gabriel Dos Reis
2004-10-26  1:06     ` Joseph S. Myers
2004-10-26  2:47       ` Joseph S. Myers
2004-10-26  3:48         ` Mark Mitchell
2004-10-26 12:21       ` Kyuupi
2004-10-26 12:32         ` Joseph S. Myers
2004-10-26 11:42     ` Joseph S. Myers
2004-10-27 19:04     ` Richard Henderson
2004-10-27 19:11       ` Richard Guenther
2004-10-27 19:41         ` Zack Weinberg
2004-10-27 19:31       ` Zack Weinberg
2004-10-27 21:31         ` Richard Henderson
2004-10-28  7:38           ` Alan Modra
2004-10-27 20:25   ` Joseph S. Myers
2004-10-25 22:33 ` Ziemowit Laski
2004-10-25 22:51   ` Joseph S. Myers
2004-10-25 23:45     ` Ziemowit Laski
2004-10-25 23:53       ` Scott Robert Ladd
2004-10-26  0:03         ` Unified front end for C and C++ (was Re: New C parser [patch]) Matt Austern
2004-10-26  1:26           ` Scott Robert Ladd
2004-10-26  1:43             ` Gabriel Dos Reis
2004-10-26  2:01               ` Scott Robert Ladd
2004-10-26 15:38                 ` Gabriel Dos Reis
2004-10-26  0:28         ` New C parser [patch] Ziemowit Laski
2004-10-26  1:20           ` Scott Robert Ladd
2004-10-26  6:08             ` Unified C and C++ front end (was Re: New C parser [patch]) Matt Austern
2004-10-26 11:14               ` Joseph S. Myers
2004-10-26 16:04                 ` Gabriel Dos Reis
2004-10-26 16:51                   ` Joseph S. Myers
2004-10-26  0:37         ` New C parser [patch] Joseph S. Myers
2004-10-26  0:03     ` Stan Shebs
2004-10-26  1:46       ` Gabriel Dos Reis

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).