diff mbox series

[pushed] Objective-C/C++ (parsers) : Update @property attribute parsing.

Message ID D23026A5-BC7A-406A-B276-857B6FD4C176@sandoe.co.uk
State New
Headers show
Series [pushed] Objective-C/C++ (parsers) : Update @property attribute parsing. | expand

Commit Message

Iain Sandoe Nov. 6, 2020, 7:54 p.m. UTC
Hi

This is preparatory work for bringing at least one aspect of the
Objective-C implementation up to date.

Tested on a number of Darwin revisions, and x86_64-linux-gnu
pushed to master,
thanks
Iain

-------

At present, we are missing parsing and checking for around
half of the property attributes in use.  The existing ad hoc scheme
for the parser's communication with the Objective C validation
is not suitable for extending to cover all the missing cases.

Additionally:

1/ We were declaring errors in two cases that the reference
   implementation warns (or is silent).

   I've elected to warn for both those cases, (Wattributes) it
   could be that we should implement Wobjc-xxx-property warning
   masks (TODO).

2/ We were emitting spurious complaints about missing property
   attributes when these were not being parsed because we gave
   up on the first syntax error.

3/ The quality of the diagnostic locations was poor (that's
   true for much of Objective-C, we will have to improve it as
   we modernise areas).

We continue to attempt to keep the code, warning and error output
similar (preferably identical output) between the C and C++ front
ends.

The interface to the Objective-C-specific parts of the parsing is
simplified to a vector of parsed (but not fully-checked) property
attributes, this will simplify the addition of new attributes.

gcc/c-family/ChangeLog:

	* c-objc.h (enum objc_property_attribute_group): New
	(enum objc_property_attribute_kind): New.
	(OBJC_PROPATTR_GROUP_MASK): New.
	(struct property_attribute_info): Small class encapsulating
	parser output from property attributes.
	(objc_prop_attr_kind_for_rid): New
	(objc_add_property_declaration): Simplify interface.
	* stub-objc.c (enum rid): Dummy type.
	(objc_add_property_declaration): Simplify interface.
	(objc_prop_attr_kind_for_rid): New.

gcc/c/ChangeLog:

	* c-parser.c (c_parser_objc_at_property_declaration):
	Improve parsing fidelity. Associate better location info
	with @property attributes.  Clean up the interface to
	objc_add_property_declaration ().

gcc/cp/ChangeLog:

	* parser.c (cp_parser_objc_at_property_declaration):
	Improve parsing fidelity. Associate better location info
	with @property attributes.  Clean up the interface to
	objc_add_property_declaration ().

gcc/objc/ChangeLog:

	* objc-act.c (objc_prop_attr_kind_for_rid): New.
	(objc_add_property_declaration): Adjust to consume the
	parser output using a vector of parsed attributes.

gcc/testsuite/ChangeLog:

	* obj-c++.dg/property/at-property-1.mm: Adjust expected
	diagnostics.
	* obj-c++.dg/property/at-property-29.mm: Likewise.
	* obj-c++.dg/property/at-property-4.mm: Likewise.
	* obj-c++.dg/property/property-neg-2.mm: Likewise.
	* objc.dg/property/at-property-1.m: Likewise.
	* objc.dg/property/at-property-29.m: Likewise.
	* objc.dg/property/at-property-4.m: Likewise.
	* objc.dg/property/at-property-5.m: Likewise.
	* objc.dg/property/property-neg-2.m: Likewise.
---
 gcc/c-family/c-objc.h                         |  65 +++-
 gcc/c-family/stub-objc.c                      |  21 +-
 gcc/c/c-parser.c                              | 280 ++++++++++-------
 gcc/cp/parser.c                               | 266 +++++++++-------
 gcc/objc/objc-act.c                           | 295 +++++++++++-------
 .../obj-c++.dg/property/at-property-1.mm      |  12 +-
 .../obj-c++.dg/property/at-property-29.mm     |   8 +-
 .../obj-c++.dg/property/at-property-4.mm      |  10 +-
 .../obj-c++.dg/property/property-neg-2.mm     |   2 +-
 .../objc.dg/property/at-property-1.m          |  12 +-
 .../objc.dg/property/at-property-29.m         |   7 +-
 .../objc.dg/property/at-property-4.m          |  10 +-
 .../objc.dg/property/at-property-5.m          |   2 +-
 .../objc.dg/property/property-neg-2.m         |   2 +-
 14 files changed, 598 insertions(+), 394 deletions(-)

Comments

Martin Liška Nov. 16, 2020, 8:01 a.m. UTC | #1
On 11/6/20 8:54 PM, Iain Sandoe wrote:
> +/* Provide a dummy type for the RID enum used as an argument to
> +   objc_prop_attr_kind_for_rid () */
> +enum rid { DUMMY };
> +

Hello.

I've noticed the hunk caused the following -Wodr warning:

/home/mliska/Programming/gcc/objdir/./prev-gcc/xg++ -B/home/mliska/Programming/gcc/objdir/./prev-gcc/ -B/usr/local/x86_64-pc-linux-gnu/bin/ -nostdinc++ -B/home/mliska/Programming/gcc/objdir/prev-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs -B/home/mliska/Programming/gcc/objdir/prev-x86_64-pc-linux-gnu/libstdc++-v3/libsupc++/.libs  -I/home/mliska/Programming/gcc/objdir/prev-x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu  -I/home/mliska/Programming/gcc/objdir/prev-x86_64-pc-linux-gnu/libstdc++-v3/include  -I/home/mliska/Programming/gcc/libstdc++-v3/libsupc++ -L/home/mliska/Programming/gcc/objdir/prev-x86_64-pc-linux-gnu/libstdc++-v3/src/.libs -L/home/mliska/Programming/gcc/objdir/prev-x86_64-pc-linux-gnu/libstdc++-v3/libsupc++/.libs -no-pie   -g -O2 -fchecking=1 -flto=jobserver -frandom-seed=1 -DIN_GCC     -fno-exceptions -fno-rtti -fasynchronous-unwind-tables -W -Wall -Wno-narrowing -Wwrite-strings -Wcast-qual -Wno-error=format-diag -Wmissing-format-attribute -Woverloaded-virtual -pedantic -Wno-long-long -Wno-variadic-macros -Wno-overlength-strings -fno-common  -DHAVE_CONFIG_H -static-libstdc++ -static-libgcc  -o cc1plus \
       cp/cp-lang.o c-family/stub-objc.o cp/call.o cp/class.o cp/constexpr.o cp/constraint.o cp/coroutines.o cp/cp-gimplify.o cp/cp-objcp-common.o cp/cp-ubsan.o cp/cvt.o cp/cxx-pretty-print.o cp/decl.o cp/decl2.o cp/dump.o cp/error.o cp/except.o cp/expr.o cp/friend.o cp/init.o cp/lambda.o cp/lex.o cp/logic.o cp/mangle.o cp/method.o cp/name-lookup.o cp/optimize.o cp/parser.o cp/pt.o cp/ptree.o cp/rtti.o cp/search.o cp/semantics.o cp/tree.o cp/typeck.o cp/typeck2.o cp/vtable-class-hierarchy.o attribs.o incpath.o c-family/c-common.o c-family/c-cppbuiltin.o c-family/c-dump.o c-family/c-format.o c-family/c-gimplify.o c-family/c-indentation.o c-family/c-lex.o c-family/c-omp.o c-family/c-opts.o c-family/c-pch.o c-family/c-ppoutput.o c-family/c-pragma.o c-family/c-pretty-print.o c-family/c-semantics.o c-family/c-ada-spec.o c-family/c-ubsan.o c-family/known-headers.o c-family/c-attribs.o c-family/c-warn.o c-family/c-spellcheck.o i386-c.o glibc-c.o cc1plus-checksum.o libbackend.a main.o libcommon-target.a libcommon.a ../libcpp/libcpp.a ../libdecnumber/libdecnumber.a libcommon.a ../libcpp/libcpp.a   ../libbacktrace/.libs/libbacktrace.a ../libiberty/libiberty.a ../libdecnumber/libdecnumber.a   -lmpc -lmpfr -lgmp -rdynamic -ldl  -L./../zlib -lz
../../gcc/c-family/c-common.h:63: warning: type ‘rid’ violates the C++ One Definition Rule [-Wodr]
    63 | enum rid
       |
../../gcc/c-family/stub-objc.c:30: note: an enum with different value name is defined in another translation unit
    30 | enum rid { DUMMY };
       |
../../gcc/c-family/c-common.h:67: note: name ‘RID_STATIC’ differs from name ‘DUMMY’ defined in another translation unit
    67 |   RID_STATIC = 0,
       |
../../gcc/c-family/stub-objc.c:30: note: mismatching definition
    30 | enum rid { DUMMY };
       |
../../gcc/c-family/c-common.h:63: warning: type ‘rid’ violates the C++ One Definition Rule [-Wodr]
    63 | enum rid
       |
../../gcc/c-family/stub-objc.c:30: note: an enum with different value name is defined in another translation unit
    30 | enum rid { DUMMY };
       |
../../gcc/c-family/c-common.h:67: note: name ‘RID_STATIC’ differs from name ‘DUMMY’ defined in another translation unit
    67 |   RID_STATIC = 0,
       |
../../gcc/c-family/stub-objc.c:30: note: mismatching definition
    30 | enum rid { DUMMY };
       |

Can you please take a look?
Thanks,
Martin
Iain Sandoe Nov. 16, 2020, 9 a.m. UTC | #2
Hi Martin,

Martin Liška <mliska@suse.cz> wrote:

> On 11/6/20 8:54 PM, Iain Sandoe wrote:
>> +/* Provide a dummy type for the RID enum used as an argument to
>> +   objc_prop_attr_kind_for_rid () */
>> +enum rid { DUMMY };
>> +
>
> I've noticed the hunk caused the following -Wodr warning:

> ../libdecnumber/libdecnumber.a   -lmpc -lmpfr -lgmp -rdynamic -ldl   
> -L./../zlib -lz
> ../../gcc/c-family/c-common.h:63: warning: type ‘rid’ violates the C++  
> One Definition Rule [-Wodr]
>   63 | enum rid
>      |
> ../../gcc/c-family/stub-objc.c:30: note: an enum with different value  
> name is defined in another translation unit
>   30 | enum rid { DUMMY };

I’m not disagreeing, of course (and will sort it out)..
.. but I don’t see this warning in my builds on either Linux or Darwin -  
what are the build conditions you use to get it?

thanks
Iain
Martin Liška Nov. 16, 2020, 9:16 a.m. UTC | #3
On 11/16/20 10:00 AM, Iain Sandoe wrote:
> Hi Martin,
> 
> Martin Liška <mliska@suse.cz> wrote:
> 
>> On 11/6/20 8:54 PM, Iain Sandoe wrote:
>>> +/* Provide a dummy type for the RID enum used as an argument to
>>> +   objc_prop_attr_kind_for_rid () */
>>> +enum rid { DUMMY };
>>> +
>>
>> I've noticed the hunk caused the following -Wodr warning:
> 
>> ../libdecnumber/libdecnumber.a   -lmpc -lmpfr -lgmp -rdynamic -ldl  -L./../zlib -lz
>> ../../gcc/c-family/c-common.h:63: warning: type ‘rid’ violates the C++ One Definition Rule [-Wodr]
>>   63 | enum rid
>>      |
>> ../../gcc/c-family/stub-objc.c:30: note: an enum with different value name is defined in another translation unit
>>   30 | enum rid { DUMMY };
> 
> I’m not disagreeing, of course (and will sort it out)..
> .. but I don’t see this warning in my builds on either Linux or Darwin - what are the build conditions you use to get it?

You can see it with: ./configure --with-build-config=bootstrap-lto && make ..

Martin

> 
> thanks
> Iain
> 
>
Iain Sandoe Nov. 16, 2020, 9:42 p.m. UTC | #4
Martin Liška <mliska@suse.cz> wrote:

> On 11/16/20 10:00 AM, Iain Sandoe wrote:

>>> ../../gcc/c-family/stub-objc.c:30: note: an enum with different value  
>>> name is defined in another translation unit
>>>   30 | enum rid { DUMMY };
>> I’m not disagreeing, of course (and will sort it out)..
>> .. but I don’t see this warning in my builds on either Linux or Darwin -  
>> what are the build conditions you use to get it?
>
> You can see it with: ./configure --with-build-config=bootstrap-lto &&  
> make ..

thanks, should be fixed by:

https://gcc.gnu.org/pipermail/gcc-patches/2020-November/559278.html

thanks
Iain
diff mbox series

Patch

diff --git a/gcc/c-family/c-objc.h b/gcc/c-family/c-objc.h
index 4577e4f1c7f..a2ca112dcc6 100644
--- a/gcc/c-family/c-objc.h
+++ b/gcc/c-family/c-objc.h
@@ -28,6 +28,67 @@  enum GTY(()) objc_ivar_visibility_kind {
   OBJC_IVAR_VIS_PACKAGE   = 3
 };
 
+/* ObjC property attribute kinds.
+   These have two fields; a unique value (that identifies which attribute)
+   and a group key that indicates membership of an exclusion group.
+   Only one member may be present from an exclusion group in a given attribute
+   list.
+   getters and setters have additional rules, since they are excluded from
+   non-overlapping group sets.  */
+
+enum objc_property_attribute_group
+{
+  OBJC_PROPATTR_GROUP_UNKNOWN = 0,
+  OBJC_PROPATTR_GROUP_GETTER,
+  OBJC_PROPATTR_GROUP_SETTER,
+  OBJC_PROPATTR_GROUP_READWRITE,
+  OBJC_PROPATTR_GROUP_ASSIGN,
+  OBJC_PROPATTR_GROUP_ATOMIC,
+  OBJC_PROPATTR_GROUP_MAX
+};
+
+enum objc_property_attribute_kind
+{
+  OBJC_PROPERTY_ATTR_UNKNOWN =		0|OBJC_PROPATTR_GROUP_UNKNOWN,
+  OBJC_PROPERTY_ATTR_GETTER =		( 1 << 8)|OBJC_PROPATTR_GROUP_GETTER,
+  OBJC_PROPERTY_ATTR_SETTER =		( 2 << 8)|OBJC_PROPATTR_GROUP_SETTER,
+  OBJC_PROPERTY_ATTR_READONLY =		( 3 << 8)|OBJC_PROPATTR_GROUP_READWRITE,
+  OBJC_PROPERTY_ATTR_READWRITE =	( 4 << 8)|OBJC_PROPATTR_GROUP_READWRITE,
+  OBJC_PROPERTY_ATTR_ASSIGN =		( 5 << 8)|OBJC_PROPATTR_GROUP_ASSIGN,
+  OBJC_PROPERTY_ATTR_RETAIN =		( 6 << 8)|OBJC_PROPATTR_GROUP_ASSIGN,
+  OBJC_PROPERTY_ATTR_COPY =		( 7 << 8)|OBJC_PROPATTR_GROUP_ASSIGN,
+  OBJC_PROPERTY_ATTR_ATOMIC =		( 8 << 8)|OBJC_PROPATTR_GROUP_ATOMIC,
+  OBJC_PROPERTY_ATTR_NONATOMIC =	( 9 << 8)|OBJC_PROPATTR_GROUP_ATOMIC,
+  OBJC_PROPERTY_ATTR_MAX =		(255 << 8|OBJC_PROPATTR_GROUP_MAX)
+};
+
+#define OBJC_PROPATTR_GROUP_MASK 0x0f
+
+/* To contain parsed, but unverified, information about a single property
+   attribute.  */
+struct property_attribute_info
+{
+  property_attribute_info () = default;
+  property_attribute_info (tree name, location_t loc,
+			   enum objc_property_attribute_kind k)
+   : name (name), ident (NULL_TREE), prop_loc (loc), prop_kind (k),
+     parse_error (false) {}
+
+  enum objc_property_attribute_group group ()
+    {
+      return (enum objc_property_attribute_group)
+	((unsigned)prop_kind & OBJC_PROPATTR_GROUP_MASK);
+    }
+
+  tree name; /* Name of the attribute.  */
+  tree ident; /* For getter/setter cases, the method/selector name.  */
+  location_t prop_loc; /* Extended location covering the parsed attr.  */
+  enum objc_property_attribute_kind prop_kind : 16;
+  unsigned parse_error : 1; /* The C/C++ parser saw an error in this attr.  */
+};
+
+extern enum objc_property_attribute_kind objc_prop_attr_kind_for_rid (enum rid);
+
 /* Objective-C / Objective-C++ entry points.  */
 
 /* The following ObjC/ObjC++ functions are called by the C and/or C++
@@ -90,8 +151,8 @@  extern tree objc_generate_write_barrier (tree, enum tree_code, tree);
 extern void objc_set_method_opt (bool);
 extern void objc_finish_foreach_loop (location_t, tree, tree, tree, tree, tree);
 extern bool  objc_method_decl (enum tree_code);
-extern void objc_add_property_declaration (location_t, tree, bool, bool, bool, 
-					   bool, bool, bool, tree, tree);
+extern void objc_add_property_declaration (location_t, tree,
+					   vec<property_attribute_info *>&);
 extern tree objc_maybe_build_component_ref (tree, tree);
 extern tree objc_build_class_component_ref (tree, tree);
 extern tree objc_maybe_build_modify_expr (tree, tree);
diff --git a/gcc/c-family/stub-objc.c b/gcc/c-family/stub-objc.c
index d017acfd6fe..2f535578789 100644
--- a/gcc/c-family/stub-objc.c
+++ b/gcc/c-family/stub-objc.c
@@ -23,6 +23,12 @@  along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "tree.h"
+#include "vec.h"
+
+/* Provide a dummy type for the RID enum used as an argument to
+   objc_prop_attr_kind_for_rid () */
+enum rid { DUMMY };
+
 #include "c-objc.h"
 
 tree
@@ -314,14 +320,8 @@  objc_get_class_ivars (tree ARG_UNUSED (name))
 void
 objc_add_property_declaration (location_t ARG_UNUSED (location), 
 			       tree ARG_UNUSED (decl),
-			       bool ARG_UNUSED (parsed_property_readonly),
-			       bool ARG_UNUSED (parsed_property_readwrite),
-			       bool ARG_UNUSED (parsed_property_assign),
-			       bool ARG_UNUSED (parsed_property_retain),
-			       bool ARG_UNUSED (parsed_property_copy),
-			       bool ARG_UNUSED (parsed_property_nonatomic),
-			       tree ARG_UNUSED (parsed_property_getter_ident),
-			       tree ARG_UNUSED (parsed_property_setter_ident))
+			       vec<property_attribute_info *>&
+			       /*prop_attr_list*/)
 {
 }
 
@@ -465,3 +465,8 @@  void
 objc_maybe_warn_exceptions (location_t ARG_UNUSED (loc))
 {
 }
+
+enum objc_property_attribute_kind objc_prop_attr_kind_for_rid (enum rid)
+{
+  return OBJC_PROPERTY_ATTR_UNKNOWN;
+}
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index dedfb8472d0..da8278a8257 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -11958,158 +11958,196 @@  c_parser_objc_diagnose_bad_element_prefix (c_parser *parser,
 static void
 c_parser_objc_at_property_declaration (c_parser *parser)
 {
-  /* The following variables hold the attributes of the properties as
-     parsed.  They are 'false' or 'NULL_TREE' if the attribute was not
-     seen.  When we see an attribute, we set them to 'true' (if they
-     are boolean properties) or to the identifier (if they have an
-     argument, ie, for getter and setter).  Note that here we only
-     parse the list of attributes, check the syntax and accumulate the
-     attributes that we find.  objc_add_property_declaration() will
-     then process the information.  */
-  bool property_assign = false;
-  bool property_copy = false;
-  tree property_getter_ident = NULL_TREE;
-  bool property_nonatomic = false;
-  bool property_readonly = false;
-  bool property_readwrite = false;
-  bool property_retain = false;
-  tree property_setter_ident = NULL_TREE;
-
-  /* 'properties' is the list of properties that we read.  Usually a
-     single one, but maybe more (eg, in "@property int a, b, c;" there
-     are three).  */
-  tree properties;
-  location_t loc;
-
-  loc = c_parser_peek_token (parser)->location;
   gcc_assert (c_parser_next_token_is_keyword (parser, RID_AT_PROPERTY));
-
+  location_t loc = c_parser_peek_token (parser)->location;
   c_parser_consume_token (parser);  /* Eat '@property'.  */
 
-  /* Parse the optional attribute list...  */
+  /* Parse the optional attribute list.
+
+     A list of parsed, but not verified, attributes.  */
+  vec<property_attribute_info *> prop_attr_list = vNULL;
+
+  bool syntax_error = false;
   if (c_parser_next_token_is (parser, CPP_OPEN_PAREN))
     {
       matching_parens parens;
 
+      location_t attr_start = c_parser_peek_token (parser)->location;
       /* Eat the '(' */
       parens.consume_open (parser);
 
       /* Property attribute keywords are valid now.  */
       parser->objc_property_attr_context = true;
 
-      while (true)
+      /* Allow @property (), with a warning.  */
+      location_t attr_end = c_parser_peek_token (parser)->location;
+
+      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
 	{
-	  bool syntax_error = false;
-	  c_token *token = c_parser_peek_token (parser);
-	  enum rid keyword;
+	  location_t attr_comb = make_location (attr_end, attr_start, attr_end);
+	  warning_at (attr_comb, OPT_Wattributes,
+		      "empty property attribute list");
+	}
+      else
+	while (true)
+	  {
+	    c_token *token = c_parser_peek_token (parser);
+	    attr_start = token->location;
+	    attr_end = get_finish (token->location);
+	    location_t attr_comb = make_location (attr_start, attr_start,
+						  attr_end);
 
-	  if (token->type != CPP_KEYWORD)
-	    {
-	      if (token->type == CPP_CLOSE_PAREN)
-		c_parser_error (parser, "expected identifier");
-	      else
-		{
-		  c_parser_consume_token (parser);
-		  c_parser_error (parser, "unknown property attribute");
-		}
-	      break;
-	    }
-	  keyword = token->keyword;
-	  c_parser_consume_token (parser);
-	  switch (keyword)
-	    {
-	    case RID_ASSIGN:    property_assign = true;    break;
-	    case RID_COPY:      property_copy = true;      break;
-	    case RID_NONATOMIC: property_nonatomic = true; break;
-	    case RID_READONLY:  property_readonly = true;  break;
-	    case RID_READWRITE: property_readwrite = true; break;
-	    case RID_RETAIN:    property_retain = true;    break;
-
-	    case RID_GETTER:
-	    case RID_SETTER:
-	      if (c_parser_next_token_is_not (parser, CPP_EQ))
-		{
-		  if (keyword == RID_GETTER)
-		    c_parser_error (parser,
-				    "missing %<=%> (after %<getter%> attribute)");
-		  else
-		    c_parser_error (parser,
-				    "missing %<=%> (after %<setter%> attribute)");
-		  syntax_error = true;
-		  break;
-		}
-	      c_parser_consume_token (parser); /* eat the = */
-	      if (c_parser_next_token_is_not (parser, CPP_NAME))
-		{
-		  c_parser_error (parser, "expected identifier");
-		  syntax_error = true;
+	    if (token->type == CPP_CLOSE_PAREN || token->type == CPP_COMMA)
+	      {
+		warning_at (attr_comb, OPT_Wattributes,
+			    "missing property attribute");
+		if (token->type == CPP_CLOSE_PAREN)
 		  break;
-		}
-	      if (keyword == RID_SETTER)
-		{
-		  if (property_setter_ident != NULL_TREE)
-		    c_parser_error (parser, "the %<setter%> attribute may only be specified once");
-		  else
-		    property_setter_ident = c_parser_peek_token (parser)->value;
-		  c_parser_consume_token (parser);
-		  if (c_parser_next_token_is_not (parser, CPP_COLON))
-		    c_parser_error (parser, "setter name must terminate with %<:%>");
-		  else
-		    c_parser_consume_token (parser);
-		}
-	      else
-		{
-		  if (property_getter_ident != NULL_TREE)
-		    c_parser_error (parser, "the %<getter%> attribute may only be specified once");
-		  else
-		    property_getter_ident = c_parser_peek_token (parser)->value;
-		  c_parser_consume_token (parser);
-		}
-	      break;
-	    default:
-	      c_parser_error (parser, "unknown property attribute");
-	      syntax_error = true;
-	      break;
+		c_parser_consume_token (parser);
+		continue;
+	      }
+
+	    tree attr_name = NULL_TREE;
+	    enum rid keyword = RID_MAX; /* Not a valid property attribute.  */
+	    bool add_at = false;
+	    if (token->type == CPP_KEYWORD)
+	      {
+		keyword = token->keyword;
+		if (OBJC_IS_AT_KEYWORD (keyword))
+		  {
+		    /* For '@' keywords the token value has the keyword,
+		       prepend the '@' for diagnostics.  */
+		    attr_name = token->value;
+		    add_at = true;
+		  }
+		else
+		  attr_name = ridpointers[(int)keyword];
+	      }
+	    else if (token->type == CPP_NAME)
+	      attr_name = token->value;
+	    c_parser_consume_token (parser);
+
+	    enum objc_property_attribute_kind prop_kind
+	      = objc_prop_attr_kind_for_rid (keyword);
+	    property_attribute_info *prop
+	      = new property_attribute_info (attr_name, attr_comb, prop_kind);
+	    prop_attr_list.safe_push (prop);
+
+	    tree meth_name;
+	    switch (prop->prop_kind)
+	      {
+	      default: break;
+	      case OBJC_PROPERTY_ATTR_UNKNOWN:
+		if (attr_name)
+		  error_at (attr_comb, "unknown property attribute %<%s%s%>",
+			    add_at ? "@" : "", IDENTIFIER_POINTER (attr_name));
+		else
+		  error_at (attr_comb, "unknown property attribute");
+		prop->parse_error = syntax_error = true;
+		break;
+
+	      case OBJC_PROPERTY_ATTR_GETTER:
+	      case OBJC_PROPERTY_ATTR_SETTER:
+		if (c_parser_next_token_is_not (parser, CPP_EQ))
+		  {
+		    attr_comb = make_location (attr_end, attr_start, attr_end);
+		    error_at (attr_comb, "expected %<=%> after Objective-C %qE",
+			      attr_name);
+		    prop->parse_error = syntax_error = true;
+		    break;
+		  }
+		token = c_parser_peek_token (parser);
+		attr_end = token->location;
+		c_parser_consume_token (parser); /* eat the = */
+		if (c_parser_next_token_is_not (parser, CPP_NAME))
+		  {
+		    attr_comb = make_location (attr_end, attr_start, attr_end);
+		    error_at (attr_comb, "expected %qE selector name",
+			      attr_name);
+		    prop->parse_error = syntax_error = true;
+		    break;
+		  }
+		/* Get the end of the method name, and consume the name.  */
+		token = c_parser_peek_token (parser);
+		attr_end = get_finish (token->location);
+		meth_name = token->value;
+		c_parser_consume_token (parser);
+		if (prop->prop_kind == OBJC_PROPERTY_ATTR_SETTER)
+		  {
+		    if (c_parser_next_token_is_not (parser, CPP_COLON))
+		      {
+			attr_comb = make_location (attr_end, attr_start,
+						   attr_end);
+			error_at (attr_comb, "setter method names must"
+				  " terminate with %<:%>");
+			prop->parse_error = syntax_error = true;
+		      }
+		    else
+		      {
+			attr_end = get_finish (c_parser_peek_token
+					       (parser)->location);
+			c_parser_consume_token (parser);
+		      }
+		    attr_comb = make_location (attr_start, attr_start,
+					       attr_end);
+		  }
+		else
+		  attr_comb = make_location (attr_start, attr_start,
+					       attr_end);
+		prop->ident = meth_name;
+		/* Updated location including all that was successfully
+		   parsed.  */
+		prop->prop_loc = attr_comb;
+		break;
 	    }
 
-	  if (syntax_error)
-	    break;
-	  
+	  /* If we see a comma here, then keep going - even if we already
+	     saw a syntax error.  For simple mistakes e.g. (asign, getter=x)
+	     this makes a more useful output and avoid spurious warnings about
+	     missing attributes that are, in fact, specified after the one with
+	     the syntax error.  */
 	  if (c_parser_next_token_is (parser, CPP_COMMA))
 	    c_parser_consume_token (parser);
 	  else
 	    break;
 	}
       parser->objc_property_attr_context = false;
-      parens.skip_until_found_close (parser);
-    }
-  /* ... and the property declaration(s).  */
-  properties = c_parser_struct_declaration (parser);
 
-  if (properties == error_mark_node)
-    {
-      c_parser_skip_until_found (parser, CPP_SEMICOLON, NULL);
-      parser->error = false;
-      return;
+      if (syntax_error && c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN))
+	/* We don't really want to chew the whole of the file looking for a
+	   matching closing parenthesis, so we will try to read the decl and
+	   let the error handling for that close out the statement.  */
+	;
+      else
+	syntax_error = false, parens.skip_until_found_close (parser);
     }
 
-  if (properties == NULL_TREE)
-    c_parser_error (parser, "expected identifier");
+  /* 'properties' is the list of properties that we read.  Usually a
+     single one, but maybe more (eg, in "@property int a, b, c;" there
+     are three).  */
+  tree properties = c_parser_struct_declaration (parser);
+
+  if (properties == error_mark_node)
+    c_parser_skip_until_found (parser, CPP_SEMICOLON, NULL);
   else
     {
-      /* Comma-separated properties are chained together in
-	 reverse order; add them one by one.  */
-      properties = nreverse (properties);
-      
-      for (; properties; properties = TREE_CHAIN (properties))
-	objc_add_property_declaration (loc, copy_node (properties),
-				       property_readonly, property_readwrite,
-				       property_assign, property_retain,
-				       property_copy, property_nonatomic,
-				       property_getter_ident, property_setter_ident);
+      if (properties == NULL_TREE)
+	c_parser_error (parser, "expected identifier");
+      else
+	{
+	  /* Comma-separated properties are chained together in reverse order;
+	     add them one by one.  */
+	  properties = nreverse (properties);
+	  for (; properties; properties = TREE_CHAIN (properties))
+	    objc_add_property_declaration (loc, copy_node (properties),
+					    prop_attr_list);
+	}
+      c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
     }
 
-  c_parser_skip_until_found (parser, CPP_SEMICOLON, "expected %<;%>");
+  while (!prop_attr_list.is_empty())
+    delete prop_attr_list.pop ();
+  prop_attr_list.release ();
   parser->error = false;
 }
 
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 6e7b982f073..c4c672efa09 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -34001,30 +34001,11 @@  cp_parser_objc_struct_declaration (cp_parser *parser)
 static void
 cp_parser_objc_at_property_declaration (cp_parser *parser)
 {
-  /* The following variables hold the attributes of the properties as
-     parsed.  They are 'false' or 'NULL_TREE' if the attribute was not
-     seen.  When we see an attribute, we set them to 'true' (if they
-     are boolean properties) or to the identifier (if they have an
-     argument, ie, for getter and setter).  Note that here we only
-     parse the list of attributes, check the syntax and accumulate the
-     attributes that we find.  objc_add_property_declaration() will
-     then process the information.  */
-  bool property_assign = false;
-  bool property_copy = false;
-  tree property_getter_ident = NULL_TREE;
-  bool property_nonatomic = false;
-  bool property_readonly = false;
-  bool property_readwrite = false;
-  bool property_retain = false;
-  tree property_setter_ident = NULL_TREE;
+  /* Parse the optional attribute list.
 
-  /* 'properties' is the list of properties that we read.  Usually a
-     single one, but maybe more (eg, in "@property int a, b, c;" there
-     are three).  */
-  tree properties;
-  location_t loc;
-
-  loc = cp_lexer_peek_token (parser->lexer)->location;
+     A list of parsed, but not verified, attributes.  */
+  vec<property_attribute_info *> prop_attr_list = vNULL;
+  location_t loc = cp_lexer_peek_token (parser->lexer)->location;
 
   cp_lexer_consume_token (parser->lexer);  /* Eat '@property'.  */
 
@@ -34033,127 +34014,172 @@  cp_parser_objc_at_property_declaration (cp_parser *parser)
     {
       /* Eat the '('.  */
       matching_parens parens;
+      location_t attr_start = cp_lexer_peek_token (parser->lexer)->location;
       parens.consume_open (parser);
       bool syntax_error = false;
 
-      while (true)
+      /* Allow empty @property attribute lists, but with a warning.  */
+      location_t attr_end = cp_lexer_peek_token (parser->lexer)->location;
+      location_t attr_comb;
+      if (cp_lexer_next_token_is (parser->lexer, CPP_CLOSE_PAREN))
 	{
-	  cp_token *token = cp_lexer_peek_token (parser->lexer);
-      	  enum rid keyword;
+	  attr_comb = make_location (attr_end, attr_start, attr_end);
+	  warning_at (attr_comb, OPT_Wattributes,
+		      "empty property attribute list");
+	}
+      else
+	while (true)
+	  {
+	    cp_token *token = cp_lexer_peek_token (parser->lexer);
+	    attr_start = token->location;
+	    attr_end = get_finish (token->location);
+	    attr_comb = make_location (attr_start, attr_start, attr_end);
 
-	  if (token->type != CPP_NAME)
-	    {
-	      cp_parser_error (parser, "expected identifier");
-	      syntax_error = true;
-	      break;
-	    }
-	  keyword = C_RID_CODE (token->u.value);
-	  cp_lexer_consume_token (parser->lexer);
-	  switch (keyword)
-	    {
-	    case RID_ASSIGN:    property_assign = true;    break;
-	    case RID_COPY:      property_copy = true;      break;
-	    case RID_NONATOMIC: property_nonatomic = true; break;
-	    case RID_READONLY:  property_readonly = true;  break;
-	    case RID_READWRITE: property_readwrite = true; break;
-	    case RID_RETAIN:    property_retain = true;    break;
-
-	    case RID_GETTER:
-	    case RID_SETTER:
-	      if (cp_lexer_next_token_is_not (parser->lexer, CPP_EQ))
-		{
-		  if (keyword == RID_GETTER)
-		    cp_parser_error (parser,
-				     "missing %<=%> (after %<getter%> attribute)");
-		  else
-		    cp_parser_error (parser,
-				     "missing %<=%> (after %<setter%> attribute)");
-		  syntax_error = true;
-		  break;
-		}
-	      cp_lexer_consume_token (parser->lexer); /* eat the = */
-	      if (!cp_parser_objc_selector_p (cp_lexer_peek_token (parser->lexer)->type))
-		{
-		  cp_parser_error (parser, "expected identifier");
-		  syntax_error = true;
+	    if (token->type == CPP_CLOSE_PAREN || token->type == CPP_COMMA)
+	      {
+		warning_at (attr_comb, OPT_Wattributes,
+			    "missing property attribute");
+		if (token->type == CPP_CLOSE_PAREN)
 		  break;
-		}
-	      if (keyword == RID_SETTER)
-		{
-		  if (property_setter_ident != NULL_TREE)
-		    {
-		      cp_parser_error (parser, "the %<setter%> attribute may only be specified once");
-		      cp_lexer_consume_token (parser->lexer);
-		    }
-		  else
-		    property_setter_ident = cp_parser_objc_selector (parser);
-		  if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON))
-		    cp_parser_error (parser, "setter name must terminate with %<:%>");
-		  else
-		    cp_lexer_consume_token (parser->lexer);
-		}
-	      else
-		{
-		  if (property_getter_ident != NULL_TREE)
-		    {
-		      cp_parser_error (parser, "the %<getter%> attribute may only be specified once");
-		      cp_lexer_consume_token (parser->lexer);
-		    }
-		  else
-		    property_getter_ident = cp_parser_objc_selector (parser);
-		}
-	      break;
-	    default:
-	      cp_parser_error (parser, "unknown property attribute");
-	      syntax_error = true;
-	      break;
-	    }
+		cp_lexer_consume_token (parser->lexer);
+		continue;
+	      }
 
-	  if (syntax_error)
-	    break;
+	    tree attr_name = NULL_TREE;
+	    if (identifier_p (token->u.value))
+	      attr_name = token->u.value;
 
-	  if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	    enum rid keyword;
+	    if (token->type == CPP_NAME)
+	      keyword = C_RID_CODE (token->u.value);
+	    else
+	      keyword = RID_MAX; /* By definition, an unknown property.  */
 	    cp_lexer_consume_token (parser->lexer);
-	  else
-	    break;
-	}
+
+	    enum objc_property_attribute_kind prop_kind
+	      = objc_prop_attr_kind_for_rid (keyword);
+	    property_attribute_info *prop
+	      = new property_attribute_info (attr_name, attr_comb, prop_kind);
+	    prop_attr_list.safe_push (prop);
+
+	    tree meth_name;
+	    switch (prop->prop_kind)
+	      {
+	      default: break;
+	      case OBJC_PROPERTY_ATTR_UNKNOWN:
+		if (attr_name)
+		  error_at (attr_start, "unknown property attribute %qE",
+			    attr_name);
+		else
+		  error_at (attr_start, "unknown property attribute");
+		prop->parse_error = syntax_error = true;
+		break;
+
+	      case OBJC_PROPERTY_ATTR_GETTER:
+	      case OBJC_PROPERTY_ATTR_SETTER:
+		if (cp_lexer_next_token_is_not (parser->lexer, CPP_EQ))
+		  {
+		    attr_comb = make_location (attr_end, attr_start, attr_end);
+		    error_at (attr_comb, "expected %<=%> after Objective-C %qE",
+			      attr_name);
+		    prop->parse_error = syntax_error = true;
+		    break;
+		  }
+
+		token = cp_lexer_peek_token (parser->lexer);
+		attr_end = token->location;
+		cp_lexer_consume_token (parser->lexer); /* eat the = */
+
+		if (!cp_parser_objc_selector_p
+		     (cp_lexer_peek_token (parser->lexer)->type))
+		  {
+		    attr_comb = make_location (attr_end, attr_start, attr_end);
+		    error_at (attr_comb, "expected %qE selector name",
+			      attr_name);
+		    prop->parse_error = syntax_error = true;
+		    break;
+		  }
+
+		/* Get the end of the method name, and consume the name.  */
+		token = cp_lexer_peek_token (parser->lexer);
+		attr_end = get_finish (token->location);
+		/* Because method names may contain C++ keywords, we have a
+		   routine to fetch them (this also consumes the token).  */
+		meth_name = cp_parser_objc_selector (parser);
+
+		if (prop->prop_kind == OBJC_PROPERTY_ATTR_SETTER)
+		  {
+		    if (cp_lexer_next_token_is_not (parser->lexer, CPP_COLON))
+		      {
+			attr_comb = make_location (attr_end, attr_start,
+						   attr_end);
+			error_at (attr_comb, "setter method names must"
+				  " terminate with %<:%>");
+			prop->parse_error = syntax_error = true;
+		      }
+		    else
+		      {
+			attr_end = get_finish (cp_lexer_peek_token
+					       (parser->lexer)->location);
+			cp_lexer_consume_token (parser->lexer);
+		      }
+		    attr_comb = make_location (attr_start, attr_start,
+					       attr_end);
+		  }
+		else
+		  attr_comb = make_location (attr_start, attr_start,
+					     attr_end);
+		prop->ident = meth_name;
+		/* Updated location including all that was successfully
+		   parsed.  */
+		prop->prop_loc = attr_comb;
+		break;
+	      }
+
+	    /* If we see a comma here, then keep going - even if we already
+	       saw a syntax error.  For simple mistakes e.g. (asign, getter=x)
+	       this makes a more useful output and avoid spurious warnings
+	       about missing attributes that are, in fact, specified after the
+	       one with the syntax error.  */
+	    if (cp_lexer_next_token_is (parser->lexer, CPP_COMMA))
+	      cp_lexer_consume_token (parser->lexer);
+	    else
+	      break;
+	  }
 
       if (syntax_error || !parens.require_close (parser))
-	cp_parser_skip_to_closing_parenthesis (parser,
-						 /*recovering=*/true,
-						 /*or_comma=*/false,
-						 /*consume_paren=*/true);
+	cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
+					       /*or_comma=*/false,
+					       /*consume_paren=*/true);
     }
 
-  /* ... and the property declaration(s).  */
-  properties = cp_parser_objc_struct_declaration (parser);
+  /* 'properties' is the list of properties that we read.  Usually a
+     single one, but maybe more (eg, in "@property int a, b, c;" there
+     are three).
+     TODO: Update this parsing so that it accepts (erroneous) bitfields so
+     that we can issue a meaningful and consistent (between C/C++) error
+     message from objc_add_property_declaration ().  */
+  tree properties = cp_parser_objc_struct_declaration (parser);
 
   if (properties == error_mark_node)
-    {
-      cp_parser_skip_to_end_of_statement (parser);
-      /* If the next token is now a `;', consume it.  */
-      if (cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
-	cp_lexer_consume_token (parser->lexer);
-      return;
-    }
-
-  if (properties == NULL_TREE)
+    cp_parser_skip_to_end_of_statement (parser);
+  else if (properties == NULL_TREE)
     cp_parser_error (parser, "expected identifier");
   else
     {
-      /* Comma-separated properties are chained together in
-	 reverse order; add them one by one.  */
+      /* Comma-separated properties are chained together in reverse order;
+	 add them one by one.  */
       properties = nreverse (properties);
-
       for (; properties; properties = TREE_CHAIN (properties))
 	objc_add_property_declaration (loc, copy_node (properties),
-				       property_readonly, property_readwrite,
-				       property_assign, property_retain,
-				       property_copy, property_nonatomic,
-				       property_getter_ident, property_setter_ident);
+				       prop_attr_list);
     }
 
   cp_parser_consume_semicolon_at_end_of_statement (parser);
+
+  while (!prop_attr_list.is_empty())
+    delete prop_attr_list.pop ();
+  prop_attr_list.release ();
 }
 
 /* Parse an Objective-C++ @synthesize declaration.  The syntax is:
diff --git a/gcc/objc/objc-act.c b/gcc/objc/objc-act.c
index c0d07ae9182..26cdeddfc5a 100644
--- a/gcc/objc/objc-act.c
+++ b/gcc/objc/objc-act.c
@@ -804,119 +804,74 @@  lookup_property (tree interface_type, tree property)
   return inter;
 }
 
+/* This routine returns a PROPERTY_KIND for the front end RID code supplied.  */
+
+enum objc_property_attribute_kind
+objc_prop_attr_kind_for_rid (enum rid prop_rid)
+{
+  switch (prop_rid)
+    {
+      default:			return OBJC_PROPERTY_ATTR_UNKNOWN;
+      case RID_GETTER:		return OBJC_PROPERTY_ATTR_GETTER;
+      case RID_SETTER:		return OBJC_PROPERTY_ATTR_SETTER;
+
+      case RID_READONLY:	return OBJC_PROPERTY_ATTR_READONLY;
+      case RID_READWRITE:	return OBJC_PROPERTY_ATTR_READWRITE;
+
+      case RID_ASSIGN:		return OBJC_PROPERTY_ATTR_ASSIGN;
+      case RID_RETAIN:		return OBJC_PROPERTY_ATTR_RETAIN;
+      case RID_COPY:		return OBJC_PROPERTY_ATTR_COPY;
+
+      case RID_NONATOMIC:	return OBJC_PROPERTY_ATTR_NONATOMIC;
+
+    }
+}
+
 /* This routine is called by the parser when a
    @property... declaration is found.  'decl' is the declaration of
    the property (type/identifier), and the other arguments represent
    property attributes that may have been specified in the Objective-C
    declaration.  'parsed_property_readonly' is 'true' if the attribute
    'readonly' was specified, and 'false' if not; similarly for the
-   other bool parameters.  'parsed_property_getter_ident' is NULL_TREE
+   other bool parameters.  'property_getter_ident' is NULL_TREE
    if the attribute 'getter' was not specified, and is the identifier
    corresponding to the specified getter if it was; similarly for
-   'parsed_property_setter_ident'.  */
+   'property_setter_ident'.  */
 void
 objc_add_property_declaration (location_t location, tree decl,
-			       bool parsed_property_readonly, bool parsed_property_readwrite,
-			       bool parsed_property_assign, bool parsed_property_retain,
-			       bool parsed_property_copy, bool parsed_property_nonatomic,
-			       tree parsed_property_getter_ident, tree parsed_property_setter_ident)
+			       vec<property_attribute_info *>& prop_attr_list)
 {
-  tree property_decl;
-  tree x;
-  /* 'property_readonly' and 'property_assign_semantics' are the final
-     attributes of the property after all parsed attributes have been
-     considered (eg, if we parsed no 'readonly' and no 'readwrite', ie
-     parsed_property_readonly = false and parsed_property_readwrite =
-     false, then property_readonly will be false because the default
-     is readwrite).  */
-  bool property_readonly = false;
-  objc_property_assign_semantics property_assign_semantics = OBJC_PROPERTY_ASSIGN;
-  bool property_extension_in_class_extension = false;
-
   if (flag_objc1_only)
-    error_at (input_location, "%<@property%> is not available in Objective-C 1.0");
-
-  if (parsed_property_readonly && parsed_property_readwrite)
-    {
-      error_at (location, "%<readonly%> attribute conflicts with %<readwrite%> attribute");
-      /* In case of conflicting attributes (here and below), after
-	 producing an error, we pick one of the attributes and keep
-	 going.  */
-      property_readonly = false;
-    }
-  else
-    {
-      if (parsed_property_readonly)
-	property_readonly = true;
-
-      if (parsed_property_readwrite)
-	property_readonly = false;
-    }
-
-  if (parsed_property_readonly && parsed_property_setter_ident)
-    {
-      error_at (location, "%<readonly%> attribute conflicts with %<setter%> attribute");
-      property_readonly = false;
-    }
-
-  if (parsed_property_assign && parsed_property_retain)
-    {
-      error_at (location, "%<assign%> attribute conflicts with %<retain%> attribute");
-      property_assign_semantics = OBJC_PROPERTY_RETAIN;
-    }
-  else if (parsed_property_assign && parsed_property_copy)
-    {
-      error_at (location, "%<assign%> attribute conflicts with %<copy%> attribute");
-      property_assign_semantics = OBJC_PROPERTY_COPY;
-    }
-  else if (parsed_property_retain && parsed_property_copy)
-    {
-      error_at (location, "%<retain%> attribute conflicts with %<copy%> attribute");
-      property_assign_semantics = OBJC_PROPERTY_COPY;
-    }
-  else
-    {
-      if (parsed_property_assign)
-	property_assign_semantics = OBJC_PROPERTY_ASSIGN;
-
-      if (parsed_property_retain)
-	property_assign_semantics = OBJC_PROPERTY_RETAIN;
-
-      if (parsed_property_copy)
-	property_assign_semantics = OBJC_PROPERTY_COPY;
-    }
+    /* FIXME: we probably ought to bail out at this point.  */
+    error_at (location, "%<@property%> is not available in Objective-C 1.0");
 
+  /* We must be in an interface, category, or protocol.  */
   if (!objc_interface_context)
     {
-      error_at (location, "property declaration not in @interface or @protocol context");
+      error_at (location, "property declaration not in %<@interface%>,"
+			  " %<@protocol%> or %<category%> context");
       return;
     }
 
-  /* At this point we know that we are either in an interface, a
-     category, or a protocol.  */
+  /* Do some spot-checks for the most obvious invalid cases.  */
+
+  gcc_checking_assert (decl && TREE_CODE (decl) == FIELD_DECL);
 
-  /* We expect a FIELD_DECL from the parser.  Make sure we didn't get
-     something else, as that would confuse the checks below.  */
-  if (TREE_CODE (decl) != FIELD_DECL)
+  if (decl && !DECL_NAME (decl))
     {
-      error_at (location, "invalid property declaration");
+      error_at (location, "properties must be named");
       return;
     }
 
-  /* Do some spot-checks for the most obvious invalid types.  */
-
+  location_t decl_loc = DECL_SOURCE_LOCATION (decl);
+  decl_loc = make_location (decl_loc, location, decl_loc);
   if (TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE)
     {
-      error_at (location, "property cannot be an array");
+      error_at (decl_loc, "property cannot be an array");
       return;
     }
 
-  /* The C++/ObjC++ parser seems to reject the ':' for a bitfield when
-     parsing, while the C/ObjC parser accepts it and gives us a
-     FIELD_DECL with a DECL_INITIAL set.  So we use the DECL_INITIAL
-     to check for a bitfield when doing ObjC.  */
-#ifndef OBJCPLUS
-  if (DECL_INITIAL (decl))
+  if (DECL_C_BIT_FIELD (decl))
     {
       /* A @property is not an actual variable, but it is a way to
 	 describe a pair of accessor methods, so its type (which is
@@ -925,10 +880,110 @@  objc_add_property_declaration (location_t location, tree decl,
 	 and arguments of functions cannot be bitfields).  The
 	 underlying instance variable could be a bitfield, but that is
 	 a different matter.  */
-      error_at (location, "property cannot be a bit-field");
+      error_at (decl_loc, "property cannot be a bit-field");
       return;
     }
-#endif
+
+  /* The final results of parsing the (growing number) of property
+     attributes.  */
+  property_attribute_info *attrs[OBJC_PROPATTR_GROUP_MAX] = { nullptr };
+
+  tree property_getter_ident = NULL_TREE;
+  tree property_setter_ident = NULL_TREE;
+  for (unsigned pn = 0; pn < prop_attr_list.length (); ++pn)
+    {
+      if (prop_attr_list[pn]->parse_error)
+	continue; /* Ignore attributes known to be wrongly parsed.  */
+
+      switch (int g = (int) prop_attr_list[pn]->group())
+	{
+	case OBJC_PROPATTR_GROUP_UNKNOWN:
+	  continue;
+	case OBJC_PROPATTR_GROUP_SETTER:
+	case OBJC_PROPATTR_GROUP_GETTER:
+	  if (attrs[g])
+	    {
+	      warning_at (prop_attr_list[pn]->prop_loc, OPT_Wattributes,
+			  "multiple property %qE methods specified, the latest"
+			  " one will be used", attrs[g]->name);
+	      inform (attrs[g]->prop_loc, "previous specification");
+	    }
+	  attrs[g] = prop_attr_list[pn];
+	  if (g == OBJC_PROPATTR_GROUP_SETTER)
+	    property_setter_ident = attrs[g]->ident;
+	  else
+	    property_getter_ident = attrs[g]->ident;
+	  continue;
+	default:
+	  {
+	    if (!attrs[g])
+	      ;
+	    else if (attrs[g]->prop_kind != prop_attr_list[pn]->prop_kind)
+	      {
+		error_at (prop_attr_list[pn]->prop_loc,
+			  "%qE attribute conflicts with %qE attribute",
+			  prop_attr_list[pn]->name, attrs[g]->name);
+		inform (attrs[g]->prop_loc, "%qE specified here",
+			attrs[g]->name );
+	      }
+	    else
+	      {
+		warning_at (prop_attr_list[pn]->prop_loc, OPT_Wattributes,
+			    "duplicate %qE attribute", attrs[g]->name);
+		inform (attrs[g]->prop_loc, "first specified here");
+	      }
+	    attrs[g] = prop_attr_list[pn];
+	  }
+	  continue;
+	}
+    }
+
+  /* The defaults for atomicity (atomic) and write-ability (readwrite) apply
+     even if the user provides no specified attributes.  */
+  bool property_nonatomic = false;
+  bool property_readonly = false;
+
+  /* Set the values from any specified by the user; these are easy, only two
+     states.  */
+  if (attrs[OBJC_PROPATTR_GROUP_ATOMIC])
+    property_nonatomic = attrs[OBJC_PROPATTR_GROUP_ATOMIC]->prop_kind
+			 == OBJC_PROPERTY_ATTR_NONATOMIC;
+
+  if (attrs[OBJC_PROPATTR_GROUP_READWRITE])
+    property_readonly = attrs[OBJC_PROPATTR_GROUP_READWRITE]->prop_kind
+			 == OBJC_PROPERTY_ATTR_READONLY;
+
+  /* One can't set a readonly value; we issue an error, but force the property
+     to readwrite as well.  */
+  if (property_readonly && property_setter_ident)
+    {
+      error_at (attrs[OBJC_PROPATTR_GROUP_READWRITE]->prop_loc, "%<readonly%>"
+		" attribute conflicts with %<setter%> attribute");
+      gcc_checking_assert (attrs[OBJC_PROPATTR_GROUP_SETTER]);
+      inform (attrs[OBJC_PROPATTR_GROUP_SETTER]->prop_loc, "%<setter%>"
+	      " specified here");
+      property_readonly = false;
+    }
+
+  /* Assign semantics is a tri-state property, and also needs some further
+     checking against the object type.  */
+  objc_property_assign_semantics property_assign_semantics
+    = OBJC_PROPERTY_ASSIGN;
+
+  if (attrs[OBJC_PROPATTR_GROUP_ASSIGN])
+    {
+      if (attrs[OBJC_PROPATTR_GROUP_ASSIGN]->prop_kind
+	  == OBJC_PROPERTY_ATTR_ASSIGN)
+	property_assign_semantics = OBJC_PROPERTY_ASSIGN;
+      else if (attrs[OBJC_PROPATTR_GROUP_ASSIGN]->prop_kind
+	       == OBJC_PROPERTY_ATTR_RETAIN)
+	property_assign_semantics = OBJC_PROPERTY_RETAIN;
+      else if (attrs[OBJC_PROPATTR_GROUP_ASSIGN]->prop_kind
+	       == OBJC_PROPERTY_ATTR_COPY)
+	property_assign_semantics = OBJC_PROPERTY_COPY;
+      else
+	gcc_unreachable ();
+    }
 
   /* TODO: Check that the property type is an Objective-C object or a
      "POD".  */
@@ -950,69 +1005,77 @@  objc_add_property_declaration (location_t location, tree decl,
 	 for non-{Objective-C objects}, and to 'retain' for
 	 Objective-C objects.  But that would break compatibility with
 	 other compilers.  */
-      if (!parsed_property_assign && !parsed_property_retain && !parsed_property_copy)
+      if (!attrs[OBJC_PROPATTR_GROUP_ASSIGN])
 	{
 	  /* Use 'false' so we do not warn for Class objects.  */
 	  if (objc_type_valid_for_messaging (TREE_TYPE (decl), false))
 	    {
-	      warning_at (location,
-			  0,
-			  "object property %qD has no %<assign%>, %<retain%> or %<copy%> attribute; assuming %<assign%>",
-			  decl);
-	      inform (location,
-		      "%<assign%> can be unsafe for Objective-C objects; please state explicitly if you need it");
+	      warning_at (decl_loc, 0, "object property %qD has no %<assign%>,"
+			  " %<retain%> or %<copy%> attribute; assuming"
+			  " %<assign%>", decl);
+	      inform (decl_loc, "%<assign%> can be unsafe for Objective-C"
+		      " objects; please state explicitly if you need it");
 	    }
 	}
     }
 
-  if (property_assign_semantics == OBJC_PROPERTY_RETAIN
-      && !objc_type_valid_for_messaging (TREE_TYPE (decl), true))
-    error_at (location, "%<retain%> attribute is only valid for Objective-C objects");
+  /* Some attributes make no sense unless applied to an Objective-C object.  */
+  bool prop_objc_object_p
+    = objc_type_valid_for_messaging (TREE_TYPE (decl), true);
+  if (!prop_objc_object_p)
+    {
+      tree p_name = NULL_TREE;
+      if (property_assign_semantics == OBJC_PROPERTY_RETAIN
+	  || property_assign_semantics == OBJC_PROPERTY_COPY)
+	p_name = attrs[OBJC_PROPATTR_GROUP_ASSIGN]->name;
 
-  if (property_assign_semantics == OBJC_PROPERTY_COPY
-      && !objc_type_valid_for_messaging (TREE_TYPE (decl), true))
-    error_at (location, "%<copy%> attribute is only valid for Objective-C objects");
+      if (p_name)
+	error_at (decl_loc, "%qE attribute is only valid for Objective-C"
+		  " objects", p_name);
+    }
 
   /* Now determine the final property getter and setter names.  They
      will be stored in the PROPERTY_DECL, from which they'll always be
      extracted and used.  */
 
   /* Adjust, or fill in, setter and getter names.  We overwrite the
-     parsed_property_setter_ident and parsed_property_getter_ident
+     property_setter_ident and property_getter_ident
      with the final setter and getter identifiers that will be
      used.  */
-  if (parsed_property_setter_ident)
+  if (property_setter_ident)
     {
       /* The setter should be terminated by ':', but the parser only
 	 gives us an identifier without ':'.  So, we need to add ':'
 	 at the end.  */
-      const char *parsed_setter = IDENTIFIER_POINTER (parsed_property_setter_ident);
+      const char *parsed_setter = IDENTIFIER_POINTER (property_setter_ident);
       size_t length = strlen (parsed_setter);
       char *final_setter = (char *)alloca (length + 2);
 
       sprintf (final_setter, "%s:", parsed_setter);
-      parsed_property_setter_ident = get_identifier (final_setter);
+      property_setter_ident = get_identifier (final_setter);
     }
   else
     {
       if (!property_readonly)
-	parsed_property_setter_ident = get_identifier (objc_build_property_setter_name
+	property_setter_ident = get_identifier (objc_build_property_setter_name
 						       (DECL_NAME (decl)));
     }
 
-  if (!parsed_property_getter_ident)
-    parsed_property_getter_ident = DECL_NAME (decl);
+  if (!property_getter_ident)
+    property_getter_ident = DECL_NAME (decl);
 
   /* Check for duplicate property declarations.  We first check the
      immediate context for a property with the same name.  Any such
      declarations are an error, unless this is a class extension and
      we are extending a property from readonly to readwrite.  */
+  bool property_extension_in_class_extension = false;
+  tree x = NULL_TREE;
   for (x = CLASS_PROPERTY_DECL (objc_interface_context); x; x = TREE_CHAIN (x))
     {
       if (PROPERTY_NAME (x) == DECL_NAME (decl))
 	{
 	  if (objc_in_class_extension
-	      && property_readonly == 0
+	      && !property_readonly
 	      && PROPERTY_READONLY (x) == 1)
 	    {
 	      /* This is a class extension, and we are extending an
@@ -1087,7 +1150,7 @@  objc_add_property_declaration (location_t location, tree decl,
 	 types, or it is compatible.  */
       location_t original_location = DECL_SOURCE_LOCATION (x);
 
-      if (PROPERTY_NONATOMIC (x) != parsed_property_nonatomic)
+      if (PROPERTY_NONATOMIC (x) != property_nonatomic)
 	{
 	  warning_at (location, 0,
 		      "%<nonatomic%> attribute of property %qD conflicts with "
@@ -1098,7 +1161,7 @@  objc_add_property_declaration (location_t location, tree decl,
 	  return;
 	}
 
-      if (PROPERTY_GETTER_NAME (x) != parsed_property_getter_ident)
+      if (PROPERTY_GETTER_NAME (x) != property_getter_ident)
 	{
 	  warning_at (location, 0,
 		      "%<getter%> attribute of property %qD conflicts with "
@@ -1112,7 +1175,7 @@  objc_add_property_declaration (location_t location, tree decl,
       /* We can only compare the setter names if both the old and new property have a setter.  */
       if (!property_readonly  &&  !PROPERTY_READONLY(x))
 	{
-	  if (PROPERTY_SETTER_NAME (x) != parsed_property_setter_ident)
+	  if (PROPERTY_SETTER_NAME (x) != property_setter_ident)
 	    {
 	      warning_at (location, 0,
 			  "%<setter%> attribute of property %qD conflicts with "
@@ -1190,13 +1253,13 @@  objc_add_property_declaration (location_t location, tree decl,
       if (property_extension_in_class_extension)
 	{
 	  PROPERTY_READONLY (x) = 0;
-	  PROPERTY_SETTER_NAME (x) = parsed_property_setter_ident;
+	  PROPERTY_SETTER_NAME (x) = property_setter_ident;
 	  return;
 	}
     }
 
   /* Create a PROPERTY_DECL node.  */
-  property_decl = make_node (PROPERTY_DECL);
+  tree property_decl = make_node (PROPERTY_DECL);
 
   /* Copy the basic information from the original decl.  */
   TREE_TYPE (property_decl) = TREE_TYPE (decl);
@@ -1205,10 +1268,10 @@  objc_add_property_declaration (location_t location, tree decl,
 
   /* Add property-specific information.  */
   PROPERTY_NAME (property_decl) = DECL_NAME (decl);
-  PROPERTY_GETTER_NAME (property_decl) = parsed_property_getter_ident;
-  PROPERTY_SETTER_NAME (property_decl) = parsed_property_setter_ident;
+  PROPERTY_GETTER_NAME (property_decl) = property_getter_ident;
+  PROPERTY_SETTER_NAME (property_decl) = property_setter_ident;
   PROPERTY_READONLY (property_decl) = property_readonly;
-  PROPERTY_NONATOMIC (property_decl) = parsed_property_nonatomic;
+  PROPERTY_NONATOMIC (property_decl) = property_nonatomic;
   PROPERTY_ASSIGN_SEMANTICS (property_decl) = property_assign_semantics;
   PROPERTY_IVAR_NAME (property_decl) = NULL_TREE;
   PROPERTY_DYNAMIC (property_decl) = 0;
diff --git a/gcc/testsuite/obj-c++.dg/property/at-property-1.mm b/gcc/testsuite/obj-c++.dg/property/at-property-1.mm
index 6a90471d89c..332582384a3 100644
--- a/gcc/testsuite/obj-c++.dg/property/at-property-1.mm
+++ b/gcc/testsuite/obj-c++.dg/property/at-property-1.mm
@@ -6,14 +6,18 @@ 
 {
   Class isa;
 }
-@property;                      /* { dg-error "expected identifier" } */
+@property;                      /* { dg-error "expected" } */
 @property int;                  /* { dg-error "expected identifier" } */
+
 @property int a;
 @property int b, c;
-@property () int d;             /* { dg-error "expected identifier" } */
+@property () int d;             /* { dg-warning "empty property attribute list" } */
 @property (readonly) int e;
-@property (readonly,) int f;    /* { dg-error "expected identifier" } */
+@property (readonly,) int f;    /* { dg-warning "missing property attribute" } */
 @property (xxx) int g;          /* { dg-error "unknown property attribute" } */
 @property (readonly,xxx) int h; /* { dg-error "unknown property attribute" } */
-@property ( int i;              /* { dg-error "expected identifier" } */
+@property ( int i;              /* { dg-error "unknown property attribute" } */
+				/* { dg-error "expected" "" { target *-*-* } .-1 } */
+@property (assign,,nonatomic) int j; /* { dg-warning "missing property attribute" } */
+@property (assign nonatomic) int k; /* { dg-error {expected } } */
 @end
diff --git a/gcc/testsuite/obj-c++.dg/property/at-property-29.mm b/gcc/testsuite/obj-c++.dg/property/at-property-29.mm
index 0f31617f841..64dfe83cd59 100644
--- a/gcc/testsuite/obj-c++.dg/property/at-property-29.mm
+++ b/gcc/testsuite/obj-c++.dg/property/at-property-29.mm
@@ -8,7 +8,9 @@ 
   Class isa;
 }
 /* Test missing '=' in setter/getter attributes.  */
-@property (getter)  int property_a; /* { dg-error "missing .=. .after .getter. attribute." } */
-@property (setter) int property_b;  /* { dg-error "missing .=. .after .setter. attribute." } */
-@property (assign, getter) int property_c; /* { dg-error "missing .=. .after .getter. attribute." } */
+@property (getter)  int property_a; /* { dg-error {expected '=' after Objective-C 'getter'} } */
+@property (setter) int property_b;  /* { dg-error {expected '=' after Objective-C 'setter'} } */
+@property (assign, getter) int property_c; /* { dg-error {expected '=' after Objective-C 'getter'} } */
+@property (retain, getter=) id x; /* { dg-error {expected 'getter' selector name} } */
+@property (retain, setter=) id y; /* { dg-error {expected 'setter' selector name} } */
 @end
diff --git a/gcc/testsuite/obj-c++.dg/property/at-property-4.mm b/gcc/testsuite/obj-c++.dg/property/at-property-4.mm
index 941aab8e33c..4083947de71 100644
--- a/gcc/testsuite/obj-c++.dg/property/at-property-4.mm
+++ b/gcc/testsuite/obj-c++.dg/property/at-property-4.mm
@@ -27,14 +27,14 @@ 
 
 /* Now test various problems.  */
 
-@property (readonly, readwrite) int a;    /* { dg-error ".readonly. attribute conflicts with .readwrite. attribute" } */
+@property (readonly, readwrite) int a;    /* { dg-error ".readwrite. attribute conflicts with .readonly. attribute" } */
 @property (readonly, setter=mySetterB:) int b; /* { dg-error ".readonly. attribute conflicts with .setter. attribute" } */
 
-@property (assign, retain) id c;          /* { dg-error ".assign. attribute conflicts with .retain. attribute" } */
-@property (assign, copy) id d;            /* { dg-error ".assign. attribute conflicts with .copy. attribute" } */
+@property (assign, retain) id c;          /* { dg-error ".retain. attribute conflicts with .assign. attribute" } */
+@property (assign, copy) id d;            /* { dg-error ".copy. attribute conflicts with .assign. attribute" } */
 @property (copy, retain) id e;            /* { dg-error ".retain. attribute conflicts with .copy. attribute" } */
 
-@property (setter=mySetter:,setter=mySetter2:)  int f; /* { dg-error ".setter. attribute may only be specified once" } */
-@property (getter=myGetter, getter=myGetter2 )  int g; /* { dg-error ".getter. attribute may only be specified once" } */
+@property (setter=mySetter:,setter=mySetter2:)  int f; /* { dg-warning {multiple property 'setter' methods specified, the latest one will be used} } */
+@property (getter=myGetter, getter=myGetter2 )  int g; /* { dg-warning {multiple property 'getter' methods specified, the latest one will be used} } */
 
 @end
diff --git a/gcc/testsuite/obj-c++.dg/property/property-neg-2.mm b/gcc/testsuite/obj-c++.dg/property/property-neg-2.mm
index f730fe84644..794f2bd0e96 100644
--- a/gcc/testsuite/obj-c++.dg/property/property-neg-2.mm
+++ b/gcc/testsuite/obj-c++.dg/property/property-neg-2.mm
@@ -4,5 +4,5 @@ 
 @end
 
 @implementation Bar
-@property int FooBar; /* { dg-error "property declaration not in @interface or @protocol context" } */
+@property int FooBar; /* { dg-error {property declaration not in '@interface', '@protocol' or 'category' context} } */
 @end
diff --git a/gcc/testsuite/objc.dg/property/at-property-1.m b/gcc/testsuite/objc.dg/property/at-property-1.m
index fa12fa282c6..6dba8f4b84a 100644
--- a/gcc/testsuite/objc.dg/property/at-property-1.m
+++ b/gcc/testsuite/objc.dg/property/at-property-1.m
@@ -11,11 +11,15 @@ 
                                 /* { dg-warning "declaration does not declare anything" "" { target *-*-* } .-1 } */
 @property int a;
 @property int b, c;
-@property () int d;             /* { dg-error "expected identifier" } */
+@property () int d;             /* { dg-warning "empty property attribute list" } */
 @property (readonly) int e;
-@property (readonly,) int f;    /* { dg-error "expected identifier" } */
+@property (readonly,) int f;    /* { dg-warning "missing property attribute" } */
 @property (xxx) int g;          /* { dg-error "unknown property attribute" } */
 @property (readonly,xxx) int h; /* { dg-error "unknown property attribute" } */
 @property ( int i;              /* { dg-error "unknown property attribute" } */
-/* Because the last syntax error opens a '(' and never closes it, we get to the end of input.  */
-@end                            /* { dg-error "expected ..end. at end of input" } */
+				/* { dg-error "expected" "" { target *-*-* } .-1 } */
+@property (assign,,nonatomic) int j; /* { dg-warning "missing property attribute" } */
+@property (assign nonatomic) int k; /* { dg-error {expected } } */
+@property (assign) int l[4]; /* { dg-error {property cannot be an array} } */
+@property (assign) int : 5; /* { dg-error {properties must be named} } */
+@end
diff --git a/gcc/testsuite/objc.dg/property/at-property-29.m b/gcc/testsuite/objc.dg/property/at-property-29.m
index 0f31617f841..0b34e1cf671 100644
--- a/gcc/testsuite/objc.dg/property/at-property-29.m
+++ b/gcc/testsuite/objc.dg/property/at-property-29.m
@@ -8,7 +8,8 @@ 
   Class isa;
 }
 /* Test missing '=' in setter/getter attributes.  */
-@property (getter)  int property_a; /* { dg-error "missing .=. .after .getter. attribute." } */
-@property (setter) int property_b;  /* { dg-error "missing .=. .after .setter. attribute." } */
-@property (assign, getter) int property_c; /* { dg-error "missing .=. .after .getter. attribute." } */
+@property (getter)  int property_a; /* { dg-error {expected '=' after Objective-C 'getter'} } */
+@property (setter) int property_b;  /* { dg-error {expected '=' after Objective-C 'setter'} } */
+@property (assign, getter) int property_c; /* { dg-error {expected '=' after Objective-C 'getter'} } */
+@property (retain, getter=) id x; /* { dg-error {expected 'getter' selector name} } */
 @end
diff --git a/gcc/testsuite/objc.dg/property/at-property-4.m b/gcc/testsuite/objc.dg/property/at-property-4.m
index 941aab8e33c..4083947de71 100644
--- a/gcc/testsuite/objc.dg/property/at-property-4.m
+++ b/gcc/testsuite/objc.dg/property/at-property-4.m
@@ -27,14 +27,14 @@ 
 
 /* Now test various problems.  */
 
-@property (readonly, readwrite) int a;    /* { dg-error ".readonly. attribute conflicts with .readwrite. attribute" } */
+@property (readonly, readwrite) int a;    /* { dg-error ".readwrite. attribute conflicts with .readonly. attribute" } */
 @property (readonly, setter=mySetterB:) int b; /* { dg-error ".readonly. attribute conflicts with .setter. attribute" } */
 
-@property (assign, retain) id c;          /* { dg-error ".assign. attribute conflicts with .retain. attribute" } */
-@property (assign, copy) id d;            /* { dg-error ".assign. attribute conflicts with .copy. attribute" } */
+@property (assign, retain) id c;          /* { dg-error ".retain. attribute conflicts with .assign. attribute" } */
+@property (assign, copy) id d;            /* { dg-error ".copy. attribute conflicts with .assign. attribute" } */
 @property (copy, retain) id e;            /* { dg-error ".retain. attribute conflicts with .copy. attribute" } */
 
-@property (setter=mySetter:,setter=mySetter2:)  int f; /* { dg-error ".setter. attribute may only be specified once" } */
-@property (getter=myGetter, getter=myGetter2 )  int g; /* { dg-error ".getter. attribute may only be specified once" } */
+@property (setter=mySetter:,setter=mySetter2:)  int f; /* { dg-warning {multiple property 'setter' methods specified, the latest one will be used} } */
+@property (getter=myGetter, getter=myGetter2 )  int g; /* { dg-warning {multiple property 'getter' methods specified, the latest one will be used} } */
 
 @end
diff --git a/gcc/testsuite/objc.dg/property/at-property-5.m b/gcc/testsuite/objc.dg/property/at-property-5.m
index 1267df318ed..820f5b3101c 100644
--- a/gcc/testsuite/objc.dg/property/at-property-5.m
+++ b/gcc/testsuite/objc.dg/property/at-property-5.m
@@ -31,4 +31,4 @@ 
    			          /* { dg-message "originally specified here" "" { target *-*-* } property_e_first } */
 @end
 
-@property id test; /* { dg-error "property declaration not in .interface or .protocol context" } */
+@property id test; /* { dg-error {property declaration not in '@interface', '@protocol' or 'category' context} } */
diff --git a/gcc/testsuite/objc.dg/property/property-neg-2.m b/gcc/testsuite/objc.dg/property/property-neg-2.m
index f730fe84644..794f2bd0e96 100644
--- a/gcc/testsuite/objc.dg/property/property-neg-2.m
+++ b/gcc/testsuite/objc.dg/property/property-neg-2.m
@@ -4,5 +4,5 @@ 
 @end
 
 @implementation Bar
-@property int FooBar; /* { dg-error "property declaration not in @interface or @protocol context" } */
+@property int FooBar; /* { dg-error {property declaration not in '@interface', '@protocol' or 'category' context} } */
 @end