diff mbox series

add simple attribute introspection

Message ID 55577012-4f85-bc8a-5c13-a53d5e0987f6@gmail.com
State New
Headers show
Series add simple attribute introspection | expand

Commit Message

Martin Sebor Oct. 11, 2018, 2:55 a.m. UTC
While writing tests for fixes and enhancements for attribute
handling I keep finding myself coming up with the same boiler-
plate code to verify whether an attribute has or has not been
successfully applied.  It's often error-prone because it
depends on the subtle and unique effects each attribute has
on optimizers or code generation in general.

Implementing an API just for testing might perhaps be excessive
but I suspect that users would find use for such a feature too.
I can even envision wanting to selectively apply attributes to
one's own symbols depending on their presence or absence on
other symbols (as an extension to the patch I posted for pr81824
that introduces the copy attribute to copy attributes from one
symbol to another).

The attached patch introduces a built-in function called
__builtin_has_attribute that makes some of this possible.
See the documentation and tests for details.

The C++ implementation is only tested using the C tests and
I'm pretty sure it doesn't do the right thing for templates
but I think it can be extended to do that in a followup patch
or an update to this one.

The C tests don't exhaustively exercise all attributes so it's
quite possible there are gaps/bugs where the built-in doesn't
return the right value due to missing special handling.
Attribute format validation is nearly non-existent.  I view
these shortcomings as minor and they too can be plugged in
a followup patch.

Ultimately, if this is viewed as useful as I'm hoping it will
be, I'd like to move the special handling from has_attribute
and to a callback function pointed from attribute_spec.  That
way each attribute, front-end and back-end alike, could, in
addition the attribute handler, implement its own special
query routine without the details leaking into the rest of
the compiler.

Martin

Comments

Joseph Myers Oct. 11, 2018, 12:04 p.m. UTC | #1
On Thu, 11 Oct 2018, Martin Sebor wrote:

> The attached patch introduces a built-in function called
> __builtin_has_attribute that makes some of this possible.
> See the documentation and tests for details.

I see nothing in the documentation about handling of equivalent forms of 
an attribute - for example, specifying __aligned__ in the attribute but 
aligned in __builtin_has_attribute, or vice versa.  I'd expect that to be 
documented to work (both of those should return true), with associated 
tests.  (And likewise the semantics should allow for a format attribute 
using printf in one place and __printf__ in the other, for example, or the 
same constant argument represented with different expressions.)

What are the semantics of __builtin_has_attribute for attributes that 
can't be tested for?  (E.g. the mode attribute, which ends up resulting in 
some existing type with the required mode being used, so there's nothing 
to indicate the attribute was originally used to declare things.)
Martin Sebor Oct. 11, 2018, 2:54 p.m. UTC | #2
On 10/11/2018 06:04 AM, Joseph Myers wrote:
> On Thu, 11 Oct 2018, Martin Sebor wrote:
>
>> The attached patch introduces a built-in function called
>> __builtin_has_attribute that makes some of this possible.
>> See the documentation and tests for details.
>
> I see nothing in the documentation about handling of equivalent forms of
> an attribute - for example, specifying __aligned__ in the attribute but
> aligned in __builtin_has_attribute, or vice versa.  I'd expect that to be
> documented to work (both of those should return true), with associated
> tests.  (And likewise the semantics should allow for a format attribute
> using printf in one place and __printf__ in the other, for example, or the
> same constant argument represented with different expressions.)

Yes, it occurred to me belatedly that I should add a test for those
as well.  I can also mention it in the documentation, although I'd
have thought it would be implicit in how attributes work in general.
(Or are there some differences between the underscored forms and
the one without it)?

>
> What are the semantics of __builtin_has_attribute for attributes that
> can't be tested for?  (E.g. the mode attribute, which ends up resulting in
> some existing type with the required mode being used, so there's nothing
> to indicate the attribute was originally used to declare things.)

With a few exceptions (like aligned) the built-in returns false
for attributes that aren't attached to a node.  I haven't exercised
nearly all the attributes yet, and this one could very well be among
those that aren't and perhaps can't be handled.  I suspect some
target attributes might be in the same group.  If there's no way
to tell it should probably be documented as a limitation of
the function, maybe also under the attribute itself that can't
be detected.  Alternatively, the built-in return type could be
changed to a tri-state: "don't know," false, true.  Can you
think of a better solution?

Martin
Joseph Myers Oct. 11, 2018, 5:52 p.m. UTC | #3
On Thu, 11 Oct 2018, Martin Sebor wrote:

> (Or are there some differences between the underscored forms and
> the one without it)?

They should always behave the same.

> With a few exceptions (like aligned) the built-in returns false
> for attributes that aren't attached to a node.  I haven't exercised
> nearly all the attributes yet, and this one could very well be among
> those that aren't and perhaps can't be handled.  I suspect some
> target attributes might be in the same group.  If there's no way
> to tell it should probably be documented as a limitation of
> the function, maybe also under the attribute itself that can't
> be detected.  Alternatively, the built-in return type could be
> changed to a tri-state: "don't know," false, true.  Can you
> think of a better solution?

I don't have a better solution.
Eric Gallager Oct. 12, 2018, 4:55 a.m. UTC | #4
On 10/10/18, Martin Sebor <msebor@gmail.com> wrote:
> While writing tests for fixes and enhancements for attribute
> handling I keep finding myself coming up with the same boiler-
> plate code to verify whether an attribute has or has not been
> successfully applied.  It's often error-prone because it
> depends on the subtle and unique effects each attribute has
> on optimizers or code generation in general.
>
> Implementing an API just for testing might perhaps be excessive
> but I suspect that users would find use for such a feature too.
> I can even envision wanting to selectively apply attributes to
> one's own symbols depending on their presence or absence on
> other symbols (as an extension to the patch I posted for pr81824
> that introduces the copy attribute to copy attributes from one
> symbol to another).
>
> The attached patch introduces a built-in function called
> __builtin_has_attribute that makes some of this possible.
> See the documentation and tests for details.

__builtin_has_attribute sounds confusingly close to the
__has_attribute macro, and yet from this description it sounds like it
does something different. Maybe clear that up?

>
> The C++ implementation is only tested using the C tests and
> I'm pretty sure it doesn't do the right thing for templates
> but I think it can be extended to do that in a followup patch
> or an update to this one.
>
> The C tests don't exhaustively exercise all attributes so it's
> quite possible there are gaps/bugs where the built-in doesn't
> return the right value due to missing special handling.
> Attribute format validation is nearly non-existent.  I view
> these shortcomings as minor and they too can be plugged in
> a followup patch.
>
> Ultimately, if this is viewed as useful as I'm hoping it will
> be, I'd like to move the special handling from has_attribute
> and to a callback function pointed from attribute_spec.  That
> way each attribute, front-end and back-end alike, could, in
> addition the attribute handler, implement its own special
> query routine without the details leaking into the rest of
> the compiler.
>
> Martin
>
Martin Sebor Oct. 14, 2018, 12:19 a.m. UTC | #5
Attached is an updated/enhanced patch with many more tests
and the suggested documentation tweak.  It also restores
the handling of empty attributes that the first revision
inadvertently removed from the C parser.

The tests are much more comprehensive now but still not
exhaustive.  I have added warning for the mode attribute
that cannot be supported.  Enumerator attributes aren't
detected in C because they are folded to constants before
they reach the built-in, and label attributes aren't handled
yet either in C or in C++.  Supporting those will take a minor
enhancement.  I haven only added handful of test cases for
x86_64 target attributes,  The built-in is not exercised
for any other target yet.  I don't expect any surprises
there.  Either it will work or (where the attributes aren't
hanging off a node) it will return false.  Supporting those
will have to wait until the later (I think the best way is
to add a callback to struct attribute_spec to let each back
end query a node for the properties unique to such attributes
analogously to attribute vector_size).

I haven't done any work on supporting templates.  I would
like to but I don't expect to be able to get it done before
stage 1 is over (I have another feature I need to finish,
the one that prompted this work to begin with).  I think
the new built-in is quite useful even without template
support.

I've kept the name __builtin_has_attribute: it is close to
the __has_attribute macro, and I think that's fine because
the built-in's purpose is very close to that of the macro.

Martin

On 10/11/2018 08:54 AM, Martin Sebor wrote:
> On 10/11/2018 06:04 AM, Joseph Myers wrote:
>> On Thu, 11 Oct 2018, Martin Sebor wrote:
>>
>>> The attached patch introduces a built-in function called
>>> __builtin_has_attribute that makes some of this possible.
>>> See the documentation and tests for details.
>>
>> I see nothing in the documentation about handling of equivalent forms of
>> an attribute - for example, specifying __aligned__ in the attribute but
>> aligned in __builtin_has_attribute, or vice versa.  I'd expect that to be
>> documented to work (both of those should return true), with associated
>> tests.  (And likewise the semantics should allow for a format attribute
>> using printf in one place and __printf__ in the other, for example, or
>> the
>> same constant argument represented with different expressions.)
>
> Yes, it occurred to me belatedly that I should add a test for those
> as well.  I can also mention it in the documentation, although I'd
> have thought it would be implicit in how attributes work in general.
> (Or are there some differences between the underscored forms and
> the one without it)?
>
>>
>> What are the semantics of __builtin_has_attribute for attributes that
>> can't be tested for?  (E.g. the mode attribute, which ends up
>> resulting in
>> some existing type with the required mode being used, so there's nothing
>> to indicate the attribute was originally used to declare things.)
>
> With a few exceptions (like aligned) the built-in returns false
> for attributes that aren't attached to a node.  I haven't exercised
> nearly all the attributes yet, and this one could very well be among
> those that aren't and perhaps can't be handled.  I suspect some
> target attributes might be in the same group.  If there's no way
> to tell it should probably be documented as a limitation of
> the function, maybe also under the attribute itself that can't
> be detected.  Alternatively, the built-in return type could be
> changed to a tri-state: "don't know," false, true.  Can you
> think of a better solution?
>
> Martin
gcc/c/ChangeLog:

	* c-parser.c (c_parser_has_attribute_expression): New function.
	(c_parser_attribute): New function.
	(c_parser_attributes): Move code into c_parser_attribute.
	(c_parser_unary_expression): Handle RID_HAS_ATTRIBUTE_EXPRESSION.

gcc/c-family/ChangeLog:

	* c-attribs.c (type_for_vector_size): New function.
	(type_valid_for_vector_size): Same.
	(handle_vector_size_attribute): Move code to the functions above
	and call them.
	(validate_attribute, has_attribute): New functions.
	* c-common.h (has_attribute): Declare.
	(rid): Add RID_HAS_ATTRIBUTE_EXPRESSION.
	* c-common.c (c_common_resword): Same.

gcc/cp/ChangeLog:

	* cp-tree.h (cp_check_const_attributes): Declare.
	* decl2.c (cp_check_const_attributes): Declare extern.
	* parser.c (cp_parser_has_attribute_expression): New function.
	(cp_parser_unary_expression): Handle RID_HAS_ATTRIBUTE_EXPRESSION.
	(cp_parser_gnu_attribute_list): Add argument.

gcc/ChangeLog:

	* doc/extend.texi (Other Builtins): Add __builtin_has_attribute.

gcc/testsuite/ChangeLog:

	* c-c++-common/builtin-has-attribute-2.c: New test.
	* c-c++-common/builtin-has-attribute-3.c: New test.
	* c-c++-common/builtin-has-attribute-4.c: New test.
	* c-c++-common/builtin-has-attribute.c: New test.
	* gcc.dg/builtin-has-attribute.c: New test.
	* gcc/testsuite/gcc.target/i386/builtin-has-attribute.c: New test.

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 3a88766..c0a1bb5 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -3128,34 +3128,11 @@ handle_deprecated_attribute (tree *node, tree name,
   return NULL_TREE;
 }
 
-/* Handle a "vector_size" attribute; arguments as in
-   struct attribute_spec.handler.  */
-
+/* Return the "base" type from TYPE that is suitable to apply attribute
+   vector_size to by stripping arrays, function types, etc.  */
 static tree
-handle_vector_size_attribute (tree *node, tree name, tree args,
-			      int ARG_UNUSED (flags),
-			      bool *no_add_attrs)
+type_for_vector_size (tree type)
 {
-  unsigned HOST_WIDE_INT vecsize, nunits;
-  machine_mode orig_mode;
-  tree type = *node, new_type, size;
-
-  *no_add_attrs = true;
-
-  size = TREE_VALUE (args);
-  if (size && TREE_CODE (size) != IDENTIFIER_NODE
-      && TREE_CODE (size) != FUNCTION_DECL)
-    size = default_conversion (size);
-
-  if (!tree_fits_uhwi_p (size))
-    {
-      warning (OPT_Wattributes, "%qE attribute ignored", name);
-      return NULL_TREE;
-    }
-
-  /* Get the vector size (in bytes).  */
-  vecsize = tree_to_uhwi (size);
-
   /* We need to provide for vector pointers, vector arrays, and
      functions returning vectors.  For example:
 
@@ -3171,8 +3148,25 @@ handle_vector_size_attribute (tree *node, tree name, tree args,
 	 || TREE_CODE (type) == OFFSET_TYPE)
     type = TREE_TYPE (type);
 
+  return type;
+}
+
+/* Given TYPE, return the bas type to which the vector_size attribute
+   ATNAME with ARGS, when non-null, can be applied, if one exists.
+   On success and when both ARGS and PTRNUNITS are non-null, set
+   *PNUNINTS to the number of vector units.  When PTRNUNITS is not
+   null, issue a warning when the attribute argument is not constant
+   and an error if there is no such type.  Otherwise issue a warning
+   in the latter case and return null.  */
+
+static tree
+type_valid_for_vector_size (tree type, tree atname, tree args,
+			    unsigned HOST_WIDE_INT *ptrnunits)
+{
+  bool error_p = ptrnunits != NULL;
+
   /* Get the mode of the type being modified.  */
-  orig_mode = TYPE_MODE (type);
+  machine_mode orig_mode = TYPE_MODE (type);
 
   if ((!INTEGRAL_TYPE_P (type)
        && !SCALAR_FLOAT_TYPE_P (type)
@@ -3183,14 +3177,38 @@ handle_vector_size_attribute (tree *node, tree name, tree args,
       || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))
       || TREE_CODE (type) == BOOLEAN_TYPE)
     {
-      error ("invalid vector type for attribute %qE", name);
+      if (error_p)
+	error ("invalid vector type for attribute %qE", atname);
+      else
+	warning (OPT_Wattributes, "invalid vector type for attribute %qE",
+		 atname);
       return NULL_TREE;
     }
 
+  /* When no argument has been provided this just a request to validate
+     the type above.  Return TYPE to indicate success.  */
+  if (!args)
+    return type;
+
+  tree size = TREE_VALUE (args);
+  if (size && TREE_CODE (size) != IDENTIFIER_NODE
+      && TREE_CODE (size) != FUNCTION_DECL)
+    size = default_conversion (size);
+
+  if (!tree_fits_uhwi_p (size))
+    {
+      /* FIXME: make the error message more informative.  */
+      if (error_p)
+	warning (OPT_Wattributes, "%qE attribute ignored", atname);
+      return NULL_TREE;
+    }
+
+  unsigned HOST_WIDE_INT vecsize = tree_to_uhwi (size);
   if (vecsize % tree_to_uhwi (TYPE_SIZE_UNIT (type)))
     {
-      error ("vector size not an integral multiple of component size");
-      return NULL;
+      if (error_p)
+	error ("vector size not an integral multiple of component size");
+      return NULL_TREE;
     }
 
   if (vecsize == 0)
@@ -3200,14 +3218,44 @@ handle_vector_size_attribute (tree *node, tree name, tree args,
     }
 
   /* Calculate how many units fit in the vector.  */
-  nunits = vecsize / tree_to_uhwi (TYPE_SIZE_UNIT (type));
+  unsigned HOST_WIDE_INT nunits = vecsize / tree_to_uhwi (TYPE_SIZE_UNIT (type));
   if (nunits & (nunits - 1))
     {
-      error ("number of components of the vector not a power of two");
+      if (error_p)
+	error ("number of components of the vector not a power of two");
+      else
+	warning (OPT_Wattributes,
+		 "number of components of the vector not a power of two");
       return NULL_TREE;
     }
 
-  new_type = build_vector_type (type, nunits);
+  if (ptrnunits)
+    *ptrnunits = nunits;
+
+  return type;
+}
+
+/* Handle a "vector_size" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_vector_size_attribute (tree *node, tree name, tree args,
+			      int ARG_UNUSED (flags),
+			      bool *no_add_attrs)
+{
+  *no_add_attrs = true;
+
+  /* Determine the "base" type to apply the attribute to.  */
+  tree type = type_for_vector_size (*node);
+
+  /* Get the vector size (in bytes) and let the function compute
+     the number of vector units.  */
+  unsigned HOST_WIDE_INT nunits;
+  type = type_valid_for_vector_size (type, name, args, &nunits);
+  if (!type)
+    return NULL_TREE;
+
+  tree new_type = build_vector_type (type, nunits);
 
   /* Build back pointers if needed.  */
   *node = lang_hooks.types.reconstruct_complex_type (*node, new_type);
@@ -3678,3 +3726,324 @@ handle_patchable_function_entry_attribute (tree *, tree, tree, int, bool *)
   /* Nothing to be done here.  */
   return NULL_TREE;
 }
+
+/* Attempt to partially validate a single attribute ATTR as if
+   it were to be applied to an entity OPER.  */
+
+static bool
+validate_attribute (location_t atloc, tree oper, tree attr)
+{
+  /* Determine whether the name of the attribute is valid
+     and fail with an error if not.  */
+  tree atname = get_attribute_name (attr);
+  if (!lookup_attribute_spec (atname))
+    {
+      if (atloc != UNKNOWN_LOCATION)
+	error_at (atloc, "unknown attribute %qE", atname);
+      return false;
+    }
+
+  tree args = TREE_VALUE (attr);
+  if (!args)
+    return true;
+
+  /* FIXME: Do some validation.  */
+  const char *atstr = IDENTIFIER_POINTER (atname);
+  if (!strcmp (atstr, "format"))
+    return true;
+
+  /* Only when attribute arguments have been provided try to validate
+     the whole thing.  decl_attributes doesn't return an indication of
+     success or failure so proceed regardless.  */
+  const char tmpname[] = "__builtin_has_attribute_tmp.";
+  tree tmpid = get_identifier (tmpname);
+  tree tmpdecl;
+  if (!strcmp (atstr, "vector_size"))
+    {
+      tree type = TYPE_P (oper) ? oper : TREE_TYPE (oper);
+      /* Check for function type here since type_for_vector_size
+	 strips it while looking for a function's return type.  */
+      if (FUNC_OR_METHOD_TYPE_P (type))
+	{
+	  warning_at (atloc, OPT_Wattributes,
+		      "invalid operand type %qT for %qs", type, atstr);
+	  return false;
+	}
+
+      type = type_for_vector_size (type);
+      if (VECTOR_TYPE_P (type))
+	type = TREE_TYPE (type);
+      /* Avoid trying to apply attribute vector_size to OPER since
+	 it's overly restrictive.  Simply make sure it has the right
+	 type.  */
+      return type_valid_for_vector_size (type, atname, args, NULL);
+    }
+
+  if (TYPE_P (oper))
+    tmpdecl = build_decl (atloc, TYPE_DECL, tmpid, oper);
+  else
+    tmpdecl = build_decl (atloc, TREE_CODE (oper), tmpid, TREE_TYPE (oper));
+
+  /* Temporarily clear CURRENT_FUNCTION_DECL to make decl_attributes
+     believe the DECL declared above is at file scope.  (See bug 87526.)  */
+  tree save_curfunc = current_function_decl;
+  current_function_decl = NULL_TREE;
+  if (DECL_P (tmpdecl))
+    {
+      if (DECL_P (oper))
+	/* An alias cannot be a defintion so declare the symbol extern.  */
+	DECL_EXTERNAL (tmpdecl) = true;
+      /* Attribute visibility only applies to symbols visible from other
+	 translation units so make it "public."   */
+      TREE_PUBLIC (tmpdecl) = TREE_PUBLIC (oper);
+    }
+  decl_attributes (&tmpdecl, attr, 0);
+  current_function_decl = save_curfunc;
+
+  /* FIXME: Change decl_attributes to indicate success or failure (and
+     parameterize it to avoid failing with errors).  */
+  return true;
+}
+
+/* Return true if the DECL, EXPR, or TYPE t has been declared with
+   attribute ATTR.  For DECL, consider also its type.  For EXPR,
+   consider just its type.  */
+
+bool
+has_attribute (location_t atloc, tree t, tree attr, tree (*convert)(tree))
+{
+  if (!attr || !t || t == error_mark_node)
+    return false;
+
+  if (!validate_attribute (atloc, t, attr))
+    return false;
+
+  tree type = NULL_TREE;
+  tree expr = NULL_TREE;
+  if (TYPE_P (t))
+    type = t;
+  else
+    {
+      do
+	{
+	  /* Determine the array element/member declaration from
+	     an ARRAY/COMPONENT_REF.  */
+	  STRIP_NOPS (t);
+	  tree_code code = TREE_CODE (t);
+	  if (code == ARRAY_REF)
+	    t = TREE_OPERAND (t, 0);
+	  else if (code == COMPONENT_REF)
+	    t = TREE_OPERAND (t, 1);
+	  else
+	    break;
+	} while (true);
+      expr = t;
+    }
+
+  /* Set to true when an attribute is found in the referenced entity
+     that matches the specified attribute.  */
+  bool found_match = false;
+
+  tree atname = get_attribute_name (attr);
+  const char *namestr = IDENTIFIER_POINTER (atname);
+
+   /* Iterate once for a type and twice for a function or variable
+     declaration: once for the DECL and the second time for its
+     TYPE.  */
+  for (bool done = false; !found_match && !done; )
+    {
+      tree atlist;
+      if (type)
+	{
+	  if (type == error_mark_node)
+	    {
+	      /* This could be a label.  FIXME: add support for labels.  */
+	      warning_at (atloc, OPT_Wattributes,
+			  (TYPE_P (t)
+			   ? G_("%qs attribute not supported for %qT "
+				"in %<__builtin_has_attribute%>")
+			   : G_("%qs attribute not supported for %qE "
+				"in %<__builtin_has_attribute%>")),
+			  namestr, t);
+	      return false;
+	    }
+
+	  /* Clear EXPR to prevent considering it again below.  */
+	  atlist = TYPE_ATTRIBUTES (type);
+	  expr = NULL_TREE;
+	  done = true;
+	}
+      else if (DECL_P (expr))
+	{
+	  /* Set TYPE to the DECL's type to process it on the next
+	     iteration.  */
+	  atlist = DECL_ATTRIBUTES (expr);
+	  type = TREE_TYPE (expr);
+	}
+      else
+	{
+	  atlist = TYPE_ATTRIBUTES (TREE_TYPE (expr));
+	  done = true;
+	}
+
+     /* True when an attribute with the sought name (though not necessarily
+	 with the sought attributes) has been found on the attribute chain.  */
+      bool found_attr = false;
+
+      /* For attribute aligned ignore the attribute list and consider
+	 the tree node itself instead.  */
+      if (type && !strcmp ("aligned", namestr))
+	atlist = NULL_TREE;
+
+      /* When clear, the first mismatched attribute argument results
+	 in failure.  Otherwise, the first matched attribute argument
+	 results in success.  */
+      bool attr_nonnull = !strcmp ("nonnull", namestr);
+      bool ignore_mismatches = attr_nonnull;
+
+      /* Iterate over the instances of the sought attribute on the DECL or
+	 TYPE (there may be multiple instances with different arguments).  */
+      for (; (atlist = lookup_attribute (namestr, atlist));
+	   found_attr = true, atlist = TREE_CHAIN (atlist))
+	{
+	  /* If there are no arguments to match the result is true except
+	     for nonnull where the attribute with no arguments must match.  */
+	  if (!TREE_VALUE (attr))
+	    return attr_nonnull ? !TREE_VALUE (atlist) : true;
+
+	  /* Attribute nonnull with no arguments subsumes all values of
+	     the attribute.  FIXME: This is overly broad since it only
+	     applies to pointer arguments, but querying non-pointer
+	     arguments is diagnosed.  */
+	  if (!TREE_VALUE (atlist) && attr_nonnull)
+	    return true;
+
+	  /* Iterate over the DECL or TYPE attribute argument's values.  */
+	  for (tree val = TREE_VALUE (atlist); val; val = TREE_CHAIN (val))
+	    {
+	      /* Iterate over the arguments in the sought attribute comparing
+		 their values to those specified for the DECL or TYPE.  */
+	      for (tree arg = TREE_VALUE (attr); arg; arg = TREE_CHAIN (arg))
+		{
+		  tree v1 = TREE_VALUE (val);
+		  tree v2 = TREE_VALUE (arg);
+		  if (v1 == v2)
+		    return true;
+
+		  if (!v1 || !v2)
+		    break;
+
+		  if (TREE_CODE (v1) == IDENTIFIER_NODE
+		      || TREE_CODE (v2) == IDENTIFIER_NODE)
+		    /* Two identifiers are the same if their values are
+		       equal (that's handled above).  Otherwise ther are
+		       either not the same or oneis not an identifier.  */
+		    return false;
+
+		  /* Convert to make them equality-comparable.  */
+		  v1 = convert (v1);
+		  v2 = convert (v2);
+
+		  /* A positive value indicates equality, negative means
+		     "don't know."  */
+		  if (simple_cst_equal (v1, v2) == 1)
+		    return true;
+
+		  if (!ignore_mismatches)
+		    break;
+		}
+	    }
+	}
+
+      if (!found_attr)
+	{
+	  /* Some attributes are encoded directly in the tree node.  */
+	  if (!strcmp ("aligned", namestr))
+	    {
+	      if (tree arg = TREE_VALUE (attr))
+		{
+		  arg = convert (TREE_VALUE (arg));
+		  if (expr && DECL_P (expr)
+		      && DECL_USER_ALIGN (expr)
+		      && tree_fits_uhwi_p (arg))
+		    found_match = DECL_ALIGN_UNIT (expr) == tree_to_uhwi (arg);
+		  else if (type && TYPE_USER_ALIGN (type))
+		    found_match = TYPE_ALIGN_UNIT (type) == tree_to_uhwi (arg);
+		}
+	      else if (expr && DECL_P (expr))
+		found_match = DECL_USER_ALIGN (expr);
+	      else if (type)
+		found_match = TYPE_USER_ALIGN (type);
+	    }
+	  else if (!strcmp ("const", namestr))
+	    {
+	      if (expr && DECL_P (expr))
+		found_match = TREE_READONLY (expr);
+	    }
+	  else if (!strcmp ("const", namestr))
+	    {
+	      if (expr && DECL_P (expr))
+		found_match = DECL_PURE_P (expr);
+	    }
+	  else if (!strcmp ("deprecated", namestr))
+	    {
+	      found_match = TREE_DEPRECATED (expr ? expr : type);
+	      if (found_match)
+		return true;
+	    }
+	  else if (!strcmp ("vector_size", namestr))
+	    {
+	      if (!type)
+		continue;
+
+	      /* Determine the base type from arrays, pointers, and such.
+		 Fail if the base type is not a vector.  */
+	      type = type_for_vector_size (type);
+	      if (!VECTOR_TYPE_P (type))
+		return false;
+
+	      if (tree arg = TREE_VALUE (attr))
+		{
+		  /* Compare the vector size argument for equality.  */
+		  arg = convert (TREE_VALUE (arg));
+		  return tree_int_cst_equal (arg, TYPE_SIZE_UNIT (type)) == 1;
+		}
+	      else
+		return true;
+	    }
+	  else if (!strcmp ("warn_if_not_aligned", namestr))
+	    {
+	      if (tree arg = TREE_VALUE (attr))
+		{
+		  arg = convert (TREE_VALUE (arg));
+		  if (expr && DECL_P (expr))
+		    found_match = (DECL_WARN_IF_NOT_ALIGN (expr)
+				   == tree_to_uhwi (arg) * BITS_PER_UNIT);
+		  else if (type)
+		    found_match = (TYPE_WARN_IF_NOT_ALIGN (type)
+				   == tree_to_uhwi (arg) * BITS_PER_UNIT);
+		}
+	      else if (expr && DECL_P (expr))
+		found_match = DECL_WARN_IF_NOT_ALIGN (expr);
+	      else if (type)
+		found_match = TYPE_WARN_IF_NOT_ALIGN (type);
+	    }
+	  else if (!strcmp ("transparent_union", namestr))
+	    {
+	      if (type)
+		found_match = TYPE_TRANSPARENT_AGGR (type) != 0;
+	    }
+	  else if (!strcmp ("mode", namestr))
+	    {
+	      /* Finally issue a warning for attributes that cannot
+		 be supported in this context.  Attribute mode is not
+		 added to a symbol and cannot be determined from it.  */
+	      warning_at (atloc, OPT_Wattributes,
+			  "%qs attribute not supported in "
+			  "%<__builtin_has_attribute%>", namestr);
+	      break;
+	    }
+	}
+    }
+  return found_match;
+}
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 10a8bc2..7d139be 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -372,6 +372,7 @@ const struct c_common_resword c_common_reswords[] =
     RID_BUILTIN_CALL_WITH_STATIC_CHAIN, D_CONLY },
   { "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY },
   { "__builtin_complex", RID_BUILTIN_COMPLEX, D_CONLY },
+  { "__builtin_has_attribute", RID_BUILTIN_HAS_ATTRIBUTE, 0 },
   { "__builtin_launder", RID_BUILTIN_LAUNDER, D_CXXONLY },
   { "__builtin_shuffle", RID_BUILTIN_SHUFFLE, 0 },
   { "__builtin_tgmath", RID_BUILTIN_TGMATH, D_CONLY },
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 9e86876..6447ecb 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -103,6 +103,7 @@ enum rid
   RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL,      RID_CHOOSE_EXPR,
   RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,	     RID_BUILTIN_SHUFFLE,
   RID_BUILTIN_TGMATH,
+  RID_BUILTIN_HAS_ATTRIBUTE,
   RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,
 
   /* TS 18661-3 keywords, in the same sequence as the TI_* values.  */
@@ -1333,6 +1334,7 @@ extern void maybe_suggest_missing_token_insertion (rich_location *richloc,
 						   enum cpp_ttype token_type,
 						   location_t prev_token_loc);
 extern tree braced_list_to_string (tree, tree);
+extern bool has_attribute (location_t, tree, tree, tree (*)(tree));
 
 #if CHECKING_P
 namespace selftest {
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 1f173fc..090eef2 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -1439,6 +1439,8 @@ static vec<tree, va_gc> *c_parser_expr_list (c_parser *, bool, bool,
 					     vec<tree, va_gc> **, location_t *,
 					     tree *, vec<location_t> *,
 					     unsigned int * = NULL);
+static struct c_expr c_parser_has_attribute_expression (c_parser *);
+
 static void c_parser_oacc_declare (c_parser *);
 static void c_parser_oacc_enter_exit_data (c_parser *, bool);
 static void c_parser_oacc_update (c_parser *);
@@ -4290,7 +4292,126 @@ c_parser_attribute_any_word (c_parser *parser)
    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?  */
+   allow identifiers declared as types to start the arguments?
+   When EXPECT_COMMA is true, expect the attribute to be preceded
+   by a comma and fail if it isn't.
+   When EMPTY_OK is true, allow and consume any number of consecutive
+   commas with no attributes in between.  */
+
+static tree
+c_parser_attribute (c_parser *parser, tree attrs,
+		    bool expect_comma = false, bool empty_ok = true)
+{
+  bool comma_first = c_parser_next_token_is (parser, CPP_COMMA);
+  if (!comma_first
+      && !c_parser_next_token_is (parser, CPP_NAME)
+      && !c_parser_next_token_is (parser, CPP_KEYWORD))
+    return NULL_TREE;
+
+  while (c_parser_next_token_is (parser, CPP_COMMA))
+    {
+      c_parser_consume_token (parser);
+      if (!empty_ok)
+	return attrs;
+    }
+
+  tree attr_name = c_parser_attribute_any_word (parser);
+  if (attr_name == NULL_TREE)
+    return NULL_TREE;
+
+  attr_name = canonicalize_attr_name (attr_name);
+  c_parser_consume_token (parser);
+
+  tree attr;
+  if (c_parser_next_token_is_not (parser, CPP_OPEN_PAREN))
+    {
+      if (expect_comma && !comma_first)
+	{
+	  /* A comma is missing between the last attribute on the chain
+	     and this one.  */
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+				     "expected %<)%>");
+	  return error_mark_node;
+	}
+      attr = build_tree_list (attr_name, NULL_TREE);
+      /* Add this attribute to the list.  */
+      attrs = chainon (attrs, attr);
+      return attrs;
+    }
+  c_parser_consume_token (parser);
+
+  vec<tree, va_gc> *expr_list;
+  tree attr_args;
+  /* 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.
+     In objective-c the identifier may be a classname.  */
+  if (c_parser_next_token_is (parser, CPP_NAME)
+      && (c_parser_peek_token (parser)->id_kind == C_ID_ID
+	  || (c_dialect_objc ()
+	      && c_parser_peek_token (parser)->id_kind
+	      == C_ID_CLASSNAME))
+      && ((c_parser_peek_2nd_token (parser)->type == CPP_COMMA)
+	  || (c_parser_peek_2nd_token (parser)->type
+	      == CPP_CLOSE_PAREN))
+      && (attribute_takes_identifier_p (attr_name)
+	  || (c_dialect_objc ()
+	      && c_parser_peek_token (parser)->id_kind
+	      == C_ID_CLASSNAME)))
+    {
+      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
+	{
+	  tree tree_list;
+	  c_parser_consume_token (parser);
+	  expr_list = c_parser_expr_list (parser, false, true,
+					  NULL, NULL, NULL, NULL);
+	  tree_list = build_tree_list_vec (expr_list);
+	  attr_args = tree_cons (NULL_TREE, arg1, tree_list);
+	  release_tree_vector (expr_list);
+	}
+    }
+  else
+    {
+      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	attr_args = NULL_TREE;
+      else
+	{
+	  expr_list = c_parser_expr_list (parser, false, true,
+					  NULL, NULL, NULL, NULL);
+	  attr_args = build_tree_list_vec (expr_list);
+	  release_tree_vector (expr_list);
+	}
+    }
+
+  attr = build_tree_list (attr_name, attr_args);
+  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+    c_parser_consume_token (parser);
+  else
+    {
+      parser->lex_untranslated_string = false;
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+				 "expected %<)%>");
+      return error_mark_node;
+    }
+
+  if (expect_comma && !comma_first)
+    {
+      /* A comma is missing between the last attribute on the chain
+	 and this one.  */
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+				 "expected %<)%>");
+      return error_mark_node;
+    }
+
+  /* Add this attribute to the list.  */
+  attrs = chainon (attrs, attr);
+  return attrs;
+}
 
 static tree
 c_parser_attributes (c_parser *parser)
@@ -4315,97 +4436,19 @@ c_parser_attributes (c_parser *parser)
 	  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))
+      /* Parse the attribute list.  Require a comma between successive
+	 (possibly empty) attributes.  */
+      for (bool expect_comma = false; ; expect_comma = true)
 	{
-	  tree attr, attr_name, attr_args;
-	  vec<tree, va_gc> *expr_list;
-	  if (c_parser_next_token_is (parser, CPP_COMMA))
-	    {
-	      c_parser_consume_token (parser);
-	      continue;
-	    }
-
-	  attr_name = c_parser_attribute_any_word (parser);
-	  if (attr_name == NULL)
+	  /* Parse a single attribute.  */
+	  tree attr = c_parser_attribute (parser, attrs, expect_comma);
+	  if (attr == error_mark_node)
+	    return attrs;
+	  if (!attr)
 	    break;
-	  attr_name = canonicalize_attr_name (attr_name);
-	  c_parser_consume_token (parser);
-	  if (c_parser_next_token_is_not (parser, CPP_OPEN_PAREN))
-	    {
-	      attr = build_tree_list (attr_name, NULL_TREE);
-	      /* Add this attribute to the list.  */
-	      attrs = chainon (attrs, attr);
-	      /* If the next token isn't a comma, we're done.  */
-	      if (!c_parser_next_token_is (parser, CPP_COMMA))
-		break;
-	      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.  
-	     In objective-c the identifier may be a classname.  */
-	  if (c_parser_next_token_is (parser, CPP_NAME)
-	      && (c_parser_peek_token (parser)->id_kind == C_ID_ID
-		  || (c_dialect_objc ()
-		      && c_parser_peek_token (parser)->id_kind
-			 == C_ID_CLASSNAME))
-	      && ((c_parser_peek_2nd_token (parser)->type == CPP_COMMA)
-		  || (c_parser_peek_2nd_token (parser)->type
-		      == CPP_CLOSE_PAREN))
-	      && (attribute_takes_identifier_p (attr_name)
-		  || (c_dialect_objc ()
-		      && c_parser_peek_token (parser)->id_kind
-			 == C_ID_CLASSNAME)))
-	    {
-	      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
-		{
-		  tree tree_list;
-		  c_parser_consume_token (parser);
-		  expr_list = c_parser_expr_list (parser, false, true,
-						  NULL, NULL, NULL, NULL);
-		  tree_list = build_tree_list_vec (expr_list);
-		  attr_args = tree_cons (NULL_TREE, arg1, tree_list);
-		  release_tree_vector (expr_list);
-		}
-	    }
-	  else
-	    {
-	      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
-		attr_args = NULL_TREE;
-	      else
-		{
-		  expr_list = c_parser_expr_list (parser, false, true,
-						  NULL, NULL, NULL, NULL);
-		  attr_args = build_tree_list_vec (expr_list);
-		  release_tree_vector (expr_list);
-		}
-	    }
+	  attrs = attr;
+      }
 
-	  attr = build_tree_list (attr_name, attr_args);
-	  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
-	    c_parser_consume_token (parser);
-	  else
-	    {
-	      parser->lex_untranslated_string = false;
-	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
-					 "expected %<)%>");
-	      return attrs;
-	    }
-	  /* Add this attribute to the list.  */
-	  attrs = chainon (attrs, attr);
-	  /* If the next token isn't a comma, we're done.  */
-	  if (!c_parser_next_token_is (parser, CPP_COMMA))
-	    break;
-	}
       /* Look for the two `)' tokens.  */
       if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
 	c_parser_consume_token (parser);
@@ -7240,6 +7283,8 @@ c_parser_unary_expression (c_parser *parser)
 	  return c_parser_sizeof_expression (parser);
 	case RID_ALIGNOF:
 	  return c_parser_alignof_expression (parser);
+	case RID_BUILTIN_HAS_ATTRIBUTE:
+	  return c_parser_has_attribute_expression (parser);
 	case RID_EXTENSION:
 	  c_parser_consume_token (parser);
 	  ext = disable_extension_diagnostics ();
@@ -7439,6 +7484,123 @@ c_parser_alignof_expression (c_parser *parser)
     }
 }
 
+/* Parse the __builtin_has_attribute ([expr|type], attribute-spec)
+   expression.  */
+
+static struct c_expr
+c_parser_has_attribute_expression (c_parser *parser)
+{
+  gcc_assert (c_parser_next_token_is_keyword (parser,
+					      RID_BUILTIN_HAS_ATTRIBUTE));
+  c_parser_consume_token (parser);
+
+  c_inhibit_evaluation_warnings++;
+
+  matching_parens parens;
+  if (!parens.require_open (parser))
+    {
+      c_inhibit_evaluation_warnings--;
+      in_typeof--;
+
+      struct c_expr result;
+      result.set_error ();
+      result.original_code = ERROR_MARK;
+      result.original_type = NULL;
+      return result;
+    }
+
+  /* Treat the type argument the same way as in typeof for the purposes
+     of warnings.  FIXME: Generalize this so the warning refers to
+     __builtin_has_attribute rather than typeof.  */
+  in_typeof++;
+
+  /* The first operand: one of DECL, EXPR, or TYPE.  */
+  tree oper = NULL_TREE;
+  if (c_parser_next_tokens_start_typename (parser, cla_prefer_id))
+    {
+      struct c_type_name *tname = c_parser_type_name (parser);
+      in_typeof--;
+      if (tname)
+	{
+	  oper = groktypename (tname, NULL, NULL);
+	  pop_maybe_used (variably_modified_type_p (oper, NULL_TREE));
+	}
+    }
+  else
+    {
+      struct c_expr cexpr = c_parser_expr_no_commas (parser, NULL);
+      c_inhibit_evaluation_warnings--;
+      in_typeof--;
+      if (cexpr.value != error_mark_node)
+	{
+	  mark_exp_read (cexpr.value);
+	  oper = cexpr.value;
+	  tree etype = TREE_TYPE (oper);
+	  bool was_vm = variably_modified_type_p (etype, NULL_TREE);
+	  /* This is returned with the type so that when the type is
+	     evaluated, this can be evaluated.  */
+	  if (was_vm)
+	    oper = c_fully_fold (oper, false, NULL);
+	  pop_maybe_used (was_vm);
+	}
+    }
+
+  struct c_expr result;
+  result.original_code = ERROR_MARK;
+  result.original_type = NULL;
+
+  if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>"))
+    {
+      /* Consume the closing parenthesis if that's the next token
+	 in the likely case the built-in was invoked with fewer
+	 than two arguments.  */
+      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	c_parser_consume_token (parser);
+      c_inhibit_evaluation_warnings--;
+      result.set_error ();
+      return result;
+    }
+
+  parser->lex_untranslated_string = true;
+
+  location_t atloc = c_parser_peek_token (parser)->location;
+  /* Parse a single attribute.  Require no leading comma and do not
+     allow empty attributes.  */
+  tree attr = c_parser_attribute (parser, NULL_TREE, false, false);
+
+  parser->lex_untranslated_string = false;
+
+  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+    c_parser_consume_token (parser);
+  else
+    {
+      c_parser_error (parser, "expected identifier");
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+
+      result.set_error ();
+      return result;
+    }
+
+  if (!attr)
+    {
+      error_at (atloc, "expected identifier");
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+				 "expected %<)%>");
+      result.set_error ();
+      return result;
+    }
+
+  result.original_code = INTEGER_CST;
+  result.original_type = boolean_type_node;
+
+  if (has_attribute (atloc, oper, attr, default_conversion))
+    result.value = boolean_true_node;
+  else
+    result.value =  boolean_false_node;
+
+  return result;
+}
+
 /* Helper function to read arguments of builtins which are interfaces
    for the middle-end nodes like COMPLEX_EXPR, VEC_PERM_EXPR and
    others.  The name of the builtin is passed using BNAME parameter.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index efbdad8..8e8af94 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6428,6 +6428,7 @@ extern int parm_index                           (tree);
 extern tree vtv_start_verification_constructor_init_function (void);
 extern tree vtv_finish_verification_constructor_init_function (tree);
 extern bool cp_omp_mappable_type		(tree);
+extern void cp_check_const_attributes (tree);
 
 /* in error.c */
 extern const char *type_as_string		(tree, int);
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index a5ad0ee..baf303f 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -1378,7 +1378,7 @@ cp_reconstruct_complex_type (tree type, tree bottom)
 /* Replaces any constexpr expression that may be into the attributes
    arguments with their reduced value.  */
 
-static void
+void
 cp_check_const_attributes (tree attributes)
 {
   if (attributes == error_mark_node)
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 6696f17..aff098e 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2049,6 +2049,8 @@ static cp_expr cp_parser_unary_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false, bool = false);
 static enum tree_code cp_parser_unary_operator
   (cp_token *);
+static tree cp_parser_has_attribute_expression
+  (cp_parser *);
 static tree cp_parser_new_expression
   (cp_parser *);
 static vec<tree, va_gc> *cp_parser_new_placement
@@ -2380,7 +2382,7 @@ static tree cp_parser_attributes_opt
 static tree cp_parser_gnu_attributes_opt
   (cp_parser *);
 static tree cp_parser_gnu_attribute_list
-  (cp_parser *);
+  (cp_parser *, bool = false);
 static tree cp_parser_std_attribute
   (cp_parser *, tree);
 static tree cp_parser_std_attribute_spec
@@ -8065,6 +8067,9 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 	    return ret_expr;
 	  }
 
+	case RID_BUILTIN_HAS_ATTRIBUTE:
+	  return cp_parser_has_attribute_expression (parser);
+
 	case RID_NEW:
 	  return cp_parser_new_expression (parser);
 
@@ -8362,6 +8367,135 @@ cp_parser_unary_operator (cp_token* token)
     }
 }
 
+/* Parse a __builtin_has_attribute([expr|type], attribute-spec) expression.
+   Returns a representation of the expression.  */
+
+static tree
+cp_parser_has_attribute_expression (cp_parser *parser)
+{
+  location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
+
+  /* Consume the __builtin_has_attribute token.  */
+  cp_lexer_consume_token (parser->lexer);
+
+  matching_parens parens;
+  if (!parens.require_open (parser))
+    return error_mark_node;
+
+  /* Types cannot be defined in a `sizeof' expression.  Save away the
+     old message.  */
+  const char *saved_message = parser->type_definition_forbidden_message;
+  /* And create the new one.  */
+  const int kwd = RID_BUILTIN_HAS_ATTRIBUTE;
+  char *tmp = concat ("types may not be defined in %<",
+		      IDENTIFIER_POINTER (ridpointers[kwd]),
+		      "%> expressions", NULL);
+  parser->type_definition_forbidden_message = tmp;
+
+  /* The restrictions on constant-expressions do not apply inside
+     sizeof expressions.  */
+  bool saved_integral_constant_expression_p
+    = parser->integral_constant_expression_p;
+  bool saved_non_integral_constant_expression_p
+    = parser->non_integral_constant_expression_p;
+  parser->integral_constant_expression_p = false;
+
+  /* Do not actually evaluate the expression.  */
+  ++cp_unevaluated_operand;
+  ++c_inhibit_evaluation_warnings;
+
+  tree oper = NULL_TREE;
+
+  /* We can't be sure yet whether we're looking at a type-id or an
+     expression.  */
+  cp_parser_parse_tentatively (parser);
+
+  bool saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p;
+  parser->in_type_id_in_expr_p = true;
+  /* Look for the type-id.  */
+  oper = cp_parser_type_id (parser);
+  parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
+
+  if (cp_parser_parse_definitely (parser))
+    {
+      /* If all went well, set OPER to the type.  */
+      cp_decl_specifier_seq decl_specs;
+
+      /* Build a trivial decl-specifier-seq.  */
+      clear_decl_specs (&decl_specs);
+      decl_specs.type = oper;
+
+      /* Call grokdeclarator to figure out what type this is.  */
+      oper = grokdeclarator (NULL,
+			     &decl_specs,
+			     TYPENAME,
+			     /*initialized=*/0,
+			     /*attrlist=*/NULL);
+    }
+
+  /* If the type-id production did not work out, then we must be
+     looking at the unary-expression production.  */
+  if (!oper || oper == error_mark_node)
+    oper = cp_parser_unary_expression (parser);
+
+  /* Go back to evaluating expressions.  */
+  --cp_unevaluated_operand;
+  --c_inhibit_evaluation_warnings;
+
+  /* Free the message we created.  */
+  free (tmp);
+  /* And restore the old one.  */
+  parser->type_definition_forbidden_message = saved_message;
+  parser->integral_constant_expression_p
+    = saved_integral_constant_expression_p;
+  parser->non_integral_constant_expression_p
+    = saved_non_integral_constant_expression_p;
+
+  /* Consume the comma if it's there.  */
+  if (!cp_parser_require (parser, CPP_COMMA, RT_COMMA))
+    {
+      parens.require_close (parser);
+      return error_mark_node;
+    }
+
+  /* Parse the attribute specification.  */
+  bool ret = false;
+  location_t atloc = cp_lexer_peek_token (parser->lexer)->location;
+  if (tree attr = cp_parser_gnu_attribute_list (parser, /*exactly_one=*/true))
+    {
+      if (oper != error_mark_node)
+	{
+	  /* Fold constant expressions used in attributes first.  */
+	  cp_check_const_attributes (attr);
+
+	  /* Finally, see if OPER has been declared with ATTR.  */
+	  ret = has_attribute (atloc, oper, attr, default_conversion);
+	}
+    }
+  else
+    {
+      error_at (atloc, "expected identifier");
+      cp_parser_skip_to_closing_parenthesis (parser, true, false, false);
+    }
+
+  parens.require_close (parser);
+
+  /* Construct a location e.g. :
+     __builtin_has_attribute (oper, attr)
+     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     with start == caret at the start of the built-in token,
+     and with the endpoint at the final closing paren.  */
+  location_t finish_loc
+    = cp_lexer_previous_token (parser->lexer)->location;
+  location_t compound_loc
+    = make_location (start_loc, start_loc, finish_loc);
+
+  cp_expr ret_expr (ret ? boolean_true_node : boolean_false_node);
+  ret_expr.set_location (compound_loc);
+  ret_expr = ret_expr.maybe_add_location_wrapper ();
+  return ret_expr;
+}
+
 /* Parse a new-expression.
 
    new-expression:
@@ -25154,7 +25288,7 @@ cp_parser_gnu_attributes_opt (cp_parser* parser)
    the arguments, if any.  */
 
 static tree
-cp_parser_gnu_attribute_list (cp_parser* parser)
+cp_parser_gnu_attribute_list (cp_parser* parser, bool exactly_one /* = false */)
 {
   tree attribute_list = NULL_TREE;
   bool save_translate_strings_p = parser->translate_strings_p;
@@ -25221,9 +25355,9 @@ cp_parser_gnu_attribute_list (cp_parser* parser)
 
 	  token = cp_lexer_peek_token (parser->lexer);
 	}
-      /* Now, look for more attributes.  If the next token isn't a
-	 `,', we're done.  */
-      if (token->type != CPP_COMMA)
+      /* Unless EXACTLY_ONE is set look for more attributes.
+	 If the next token isn't a `,', we're done.  */
+      if (exactly_one || token->type != CPP_COMMA)
 	break;
 
       /* Consume the comma and keep going.  */
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8ffb0cd..dcf4747 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -2649,8 +2649,9 @@ explicit @code{externally_visible} attributes are still necessary.
 @cindex @code{flatten} function attribute
 Generally, inlining into a function is limited.  For a function marked with
 this attribute, every call inside this function is inlined, if possible.
-Whether the function itself is considered for inlining depends on its size and
-the current inlining parameters.
+Functions declared with attribute @code{noinline} and similar are not
+inlined.  Whether the function itself is considered for inlining depends
+on its size and the current inlining parameters.
 
 @item format (@var{archetype}, @var{string-index}, @var{first-to-check})
 @cindex @code{format} function attribute
@@ -11069,6 +11070,7 @@ is called and the @var{flag} argument passed to it.
 @findex __builtin_call_with_static_chain
 @findex __builtin_extend_pointer
 @findex __builtin_fpclassify
+@findex __builtin_has_attribute
 @findex __builtin_isfinite
 @findex __builtin_isnormal
 @findex __builtin_isgreater
@@ -11726,6 +11728,33 @@ check its compatibility with @var{size}.
 
 @end deftypefn
 
+@deftypefn {Built-in Function} bool __builtin_has_attribute (@var{type-or-expression}, @var{attribute})
+The @code{__builtin_has_attribute} function evaluates to an integer constant
+expression equal to @code{true} if the symbol or type referenced by
+the @var{type-or-expression} argument has been declared with
+the @var{attribute} referenced by the second argument.  Neither argument
+is valuated.  The @var{type-or-expression} argument is subject to the same
+restrictions as the argument to @code{typeof} (@pxref{Typeof}).  The
+@var{attribute} argument is an attribute name optionally followed by
+arguments.  Both forms of attribute names---with and without double
+leading and trailing underscores---are recognized.  See @xref{Attribute Syntax}
+for details.  When no attribute arguments are specified for an attribute
+that expects one or more arguments the function returns @code{true} if
+@var{type-or-expression} has been declared with the attribute regardless
+of the attribute argument values.  For example, the first call to
+the function below evaluates to @code{true} because @code{x} is
+declared with the @code{aligned} attribute but the second call evaluates
+to @code{false} because @code{x} is declared @code{aligned (8)} and
+not @code{aligned (4)}.
+
+@smallexample
+__attribute__ ((aligned (8))) int x;
+_Static_assert (__builtin_has_attribute (x, aligned), "aligned");
+_Static_assert (!__builtin_has_attribute (x, aligned (4)), "aligned (4)");
+@end smallexample
+
+@end deftypefn
+
 @deftypefn {Built-in Function} @var{type} __builtin_speculation_safe_value (@var{type} val, @var{type} failval)
 
 This built-in function can be used to help mitigate against unsafe
diff --git a/gcc/testsuite/c-c++-common/builtin-has-attribute-5.c b/gcc/testsuite/c-c++-common/builtin-has-attribute-5.c
index bf1ccbb..a2eaf91 100644
--- a/gcc/testsuite/c-c++-common/builtin-has-attribute-5.c
+++ b/gcc/testsuite/c-c++-common/builtin-has-attribute-5.c
@@ -1,4 +1,4 @@
-/* Verify __builtin_has_attribute return value for enumerators
+/* Verify __builtin_has_attribute return value for enumerators.
    and labels.
    { dg-do compile }
    { dg-options "-Wall -ftrack-macro-expansion=0" }
@@ -25,6 +25,9 @@ void test_deprecated (void)
   A (0, e2, deprecated);
 }
 
+# if 0
+
+/* Labels are not supported yet.  */
 
 void test_lebel (void)
 {
@@ -38,16 +41,17 @@ void test_lebel (void)
   A (0, l_none, hot);
   A (0, l_none, unused);
 
-  A (1, l_cold, cold);
+  A (0, l_cold, cold);
   A (0, l_cold, hot);
   A (0, l_cold, unused);
 
   A (0, l_hot, cold);
-  A (1, l_hot, hot);
+  A (0, l_hot, hot);
   A (0, l_hot, unused);
 
-  A (1, l_cold_unused, cold);
+  A (0, l_cold_unused, cold);
   A (0, l_cold_unused, hot);
-  A (1, l_cold_unused, unused);
-
+  A (0, l_cold_unused, unused);
 }
+
+#endif
diff --git a/gcc/testsuite/c-c++-common/builtin-has-attribute-2.c b/gcc/testsuite/c-c++-common/builtin-has-attribute-2.c
new file mode 100644
index 0000000..0f692ff
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/builtin-has-attribute-2.c
@@ -0,0 +1,206 @@
+/* Verify __builtin_has_attribute return value for types.
+   { dg-do compile }
+   { dg-options "-Wall -ftrack-macro-expansion=0" }
+   { dg-options "-Wall -Wno-narrowing -Wno-unused-local-typedefs -ftrack-macro-expansion=0" { target c++ } }  */
+
+#define ATTR(...) __attribute__ ((__VA_ARGS__))
+
+#define A(expect, sym, attr)						\
+  typedef int Assert [1 - 2 * !(__builtin_has_attribute (sym, attr) == expect)]
+
+struct ATTR (packed) Packed { char c; int i; };
+
+void fvoid (void);
+struct Packed fpacked (void);
+
+union OrdinaryUnion { void *p; int i; };
+union ATTR (transparent_union) TransparentUnion { void *p; int i; };
+
+/* Exercise __builtin_has_attribute with the first argument that
+   is a type.  */
+
+void test_type (int n)
+{
+  /* Verify both forms of the attribute spelling.  Unlike the attribute
+     keyword that can be spelled three ways (with either leading or
+     trailing underscores, or with both), attribute names can only be
+     spelled two ways.  */
+  A (0, int, aligned);
+  A (0, int, __aligned__);
+
+  A (0, int, aligned (1));
+  A (0, int, aligned (2));
+  A (0, int[1], aligned);
+  A (0, int[1], aligned (2));
+  A (0, int[n], aligned);
+  A (0, int[n], aligned (4));
+
+  /* Again, verify both forms of the attribute spelling.  */
+  A (1, ATTR (aligned) char, aligned);
+  A (1, ATTR (aligned (2)) short, aligned);
+  A (1, ATTR (aligned (4)) int, __aligned__);
+
+  A (0, int ATTR (aligned (4)), aligned (2));
+  A (0, int ATTR (aligned (2)), aligned (4));
+  /* GCC retains both attributes in the */
+  A (0, int ATTR (aligned (2), aligned (4)), aligned (2));
+  A (1, int ATTR (aligned (2), aligned (4)), aligned (4));
+  /* The following fails due to bug 87524.
+     A (1, int ATTR (aligned (4), aligned (2))), aligned (4)); */
+  A (0, int ATTR (aligned (4), aligned (2)), aligned (8));
+
+  A (1, int ATTR (aligned (8)), aligned (1 + 7));
+
+  enum { eight = 8 };
+  A (1, int ATTR (aligned (8)), aligned (eight));
+  A (1, int ATTR (aligned (eight)), aligned (1 + 7));
+
+  struct NotPacked { char c; int i; };
+  A (0, struct NotPacked, packed);
+  A (1, struct Packed, packed);
+
+  /* Exercise types returned from a function.  */
+  A (0, fvoid (), packed);
+  A (1, fpacked (), packed);
+
+  struct ATTR (aligned (2), packed) Aligned2Packed { char c; int i; };
+  A (1, struct Aligned2Packed, aligned);
+  A (1, struct Aligned2Packed, aligned (2));
+  A (0, struct Aligned2Packed, aligned (4));
+  A (1, struct Aligned2Packed, packed);
+
+  A (0, int, may_alias);
+  A (1, ATTR (may_alias) int, may_alias);
+
+  A (0, char, warn_if_not_aligned (1));
+  A (0, char, warn_if_not_aligned (2));
+
+  A (1, ATTR (warn_if_not_aligned (2)) char, warn_if_not_aligned);
+  A (0, ATTR (warn_if_not_aligned (2)) char, warn_if_not_aligned (1));
+  A (1, ATTR (warn_if_not_aligned (2)) char, warn_if_not_aligned (2));
+  A (0, ATTR (warn_if_not_aligned (2)) char, warn_if_not_aligned (4));
+
+  A (0, union OrdinaryUnion, transparent_union);
+
+  A (1, union TransparentUnion, transparent_union);
+  A (1, const union TransparentUnion, transparent_union);
+}
+
+/* Exercise __builtin_has_attribute with the first argument that
+   is a typedef.  */
+
+void test_typedef (int n)
+{
+  typedef char A1[1];
+  A (0, A1, aligned);
+  A (0, A1, aligned (1));
+  A (0, A1, aligned (2));
+
+  typedef char An[n];
+  A (0, An, aligned);
+  A (0, An, aligned (1));
+  A (0, An, aligned (2));
+
+  typedef ATTR (aligned (8)) short AI8;
+  A (1, AI8, aligned);
+  A (0, AI8, aligned (4));
+  A (1, AI8, aligned (8));
+  A (0, AI8, aligned (16));
+
+  A (1, const AI8, aligned);
+  A (1, const volatile AI8, aligned);
+
+  typedef ATTR (aligned (2), aligned (8), aligned (16)) int AI16;
+  A (1, AI16, aligned);
+  A (0, AI16, aligned (1));
+  A (0, AI16, aligned (2));
+  A (0, AI16, aligned (4));
+  A (0, AI16, aligned (8));
+  A (1, AI16, aligned (16));
+  A (0, AI16, aligned (32));
+
+  typedef const AI16 CAI16;
+  A (1, CAI16, aligned);
+  A (0, CAI16, aligned (1));
+  A (1, CAI16, aligned (16));
+
+  typedef int I;
+  A (0, I, may_alias);
+  A (0, AI8, may_alias);
+
+  typedef ATTR (may_alias) int MAI;
+  A (1, MAI, may_alias);
+
+  typedef ATTR (aligned (4), may_alias) char A4MAC;
+  A (0, A4MAC, aligned (0));
+  A (0, A4MAC, aligned (1));
+  A (0, A4MAC, aligned (2));
+  A (1, A4MAC, aligned (4));
+  A (0, A4MAC, aligned (8));
+  A (1, A4MAC, may_alias);
+
+  typedef ATTR (may_alias, aligned (8)) char A8MAC;
+  A (1, A8MAC, aligned);
+  A (0, A8MAC, aligned (0));
+  A (0, A8MAC, aligned (1));
+  A (0, A8MAC, aligned (2));
+  A (0, A8MAC, aligned (4));
+  A (1, A8MAC, aligned (8));
+  A (0, A8MAC, aligned (16));
+  A (1, A8MAC, may_alias);
+
+  typedef ATTR (may_alias) const AI8 CMAI8;
+  A (1, CMAI8, aligned);
+  A (1, CMAI8, may_alias);
+  A (0, CMAI8, aligned (4));
+  A (1, CMAI8, aligned (8));
+
+  typedef void Fnull (void*, void*, void*);
+  A (0, Fnull, nonnull);
+  A (0, Fnull, nonnull (1));
+  A (0, Fnull, nonnull (2));
+  A (0, Fnull, nonnull (3));
+
+  typedef ATTR (nonnull) Fnull Fnonnull;
+  A (1, Fnonnull, nonnull);
+  A (1, Fnonnull, nonnull (1));
+  A (1, Fnonnull, nonnull (2));
+  A (1, Fnonnull, nonnull (3));
+
+  typedef ATTR (nonnull (2)) void Fnonnull_2 (void*, void*, void*);
+  A (0, Fnonnull_2, nonnull);
+  A (0, Fnonnull_2, nonnull (1));
+  A (1, Fnonnull_2, nonnull (2));
+  A (0, Fnonnull_2, nonnull (3));
+
+  typedef ATTR (nonnull (1), nonnull (2), nonnull (3))
+    void Fnonnull_1_2_3 (void*, void*, void*);
+
+  /* The following fails because  the built-in doesn't recognize that
+     a single nonnull with no arguments is the same as one nonnull for
+     each function parameter.  Disable the testing for now.
+     A (1, Fnonnull_1_2_3, nonnull);
+  */
+  A (1, Fnonnull_1_2_3, nonnull (1));
+  A (1, Fnonnull_1_2_3, nonnull (2));
+  A (1, Fnonnull_1_2_3, nonnull (3));
+
+  typedef void Freturns (void);
+  A (0, Fnull, noreturn);
+  A (0, Freturns, noreturn);
+
+  typedef ATTR (warn_if_not_aligned (8)) char CWA8;
+  A (0, CWA8, warn_if_not_aligned (2));
+  A (0, CWA8, warn_if_not_aligned (4));
+  A (1, CWA8, warn_if_not_aligned (8));
+  A (0, CWA8, warn_if_not_aligned (16));
+
+  typedef union OrdinaryUnion OrdUnion;
+  A (0, OrdUnion, transparent_union);
+
+  /* The attribute is ignored on typedefs but GCC fails to diagnose
+     it (see bug ).  */
+  typedef union ATTR (transparent_union)
+    OrdinaryUnion TransUnion;   /* { dg-warning "\\\[-Wattributes" "pr87578" { xfail { ! { c++ } } } } */
+  A (0, TransUnion, transparent_union);
+}
diff --git a/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c b/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c
new file mode 100644
index 0000000..237dc72
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c
@@ -0,0 +1,314 @@
+/* Verify __builtin_has_attribute return value for functions.
+   { dg-do compile }
+   { dg-options "-Wall -ftrack-macro-expansion=0" }
+   { dg-options "-Wall -Wno-narrowing -Wno-unused-local-typedefs -ftrack-macro-expansion=0" { target c++ } }  */
+
+#define ATTR(...) __attribute__ ((__VA_ARGS__))
+
+void fnone (void);
+
+ATTR (aligned) void faligned (void);
+ATTR (aligned (1)) void faligned_1 (void);
+ATTR (aligned (2)) void faligned_2 (void);
+ATTR (aligned (4)) void faligned_4 (void);
+ATTR (aligned (8)) void faligned_8 (void);
+
+ATTR (alloc_size (1)) void* falloc_size_1 (int, int);
+ATTR (alloc_size (2)) void* falloc_size_2 (int, int);
+ATTR (alloc_size (2, 4)) void* falloc_size_2_4 (int, int, int, int);
+
+ATTR (alloc_align (1)) void* falloc_align_1 (int, int);
+ATTR (alloc_align (2)) void* falloc_align_2 (int, int);
+ATTR (alloc_align (1), alloc_size (2)) void* falloc_align_1_size_2 (int, int);
+ATTR (alloc_align (2), alloc_size (1)) void* falloc_align_2_size_1 (int, int);
+
+#if __cplusplus
+extern "C"
+#endif
+ATTR (noreturn) void fnoreturn (void) { __builtin_abort (); }
+
+ATTR (alias ("fnoreturn")) void falias (void);
+
+#define A(expect, sym, attr)						\
+  typedef int Assert [1 - 2 * !(__builtin_has_attribute (sym, attr) == expect)]
+
+void test_aligned (void)
+{
+  A (0, fnone, aligned);
+  A (0, fnone, aligned (0));
+  A (0, fnone, aligned (1));
+  A (0, fnone, aligned (2));
+  A (0, fnone, aligned (4));
+  A (0, fnone, aligned (8));
+  A (0, fnone, aligned (16));
+
+  A (1, faligned, aligned);
+  A (0, faligned, aligned (0));
+  A (0, faligned, aligned (1));
+  A (0, faligned, aligned (2));
+
+  A (1, faligned_1, aligned);
+  A (0, faligned_1, aligned (0));
+  A (1, faligned_1, aligned (1));
+  A (0, faligned_1, aligned (2));
+  A (0, faligned_1, aligned (4));
+
+  A (1, faligned_2, aligned);
+  A (0, faligned_2, aligned (0));
+  A (0, faligned_2, aligned (1));
+  A (1, faligned_2, aligned (2));
+  A (0, faligned_2, aligned (4));
+}
+
+
+void test_alloc_align (void)
+{
+  A (0, fnone, alloc_align);
+  A (0, falloc_size_1, alloc_align);
+  A (1, falloc_align_1, alloc_align);
+  A (1, falloc_align_2, alloc_align);
+
+  A (0, fnone, alloc_align (1));        /* { dg-warning "\\\[-Wattributes" } */
+  A (0, falloc_size_1, alloc_align (1));
+  A (1, falloc_align_1, alloc_align (1));
+  A (0, falloc_align_2, alloc_align (1));
+  A (1, falloc_align_2, alloc_align (2));
+}
+
+
+void test_alloc_size_malloc (void)
+{
+  A (0, fnone, alloc_size);
+  A (0, fnone, alloc_size (1));         /* { dg-warning "\\\[-Wattributes" } */
+  A (0, fnone, alloc_size (2));         /* { dg-warning "\\\[-Wattributes" } */
+  A (0, falloc_align_1, alloc_size (1));
+  A (0, falloc_align_2, alloc_size (1));
+  A (1, falloc_size_1, alloc_size (1));
+  A (0, falloc_size_1, alloc_size (2));
+  A (0, falloc_size_2, alloc_size (1));
+  A (1, falloc_size_2, alloc_size (2));
+
+  A (1, falloc_size_2_4, alloc_size);
+  /* It would probably make more sense to have the built-in return
+     true only when both alloc_size arguments match, not just one
+     or the other.  */
+  A (0, falloc_size_2_4, alloc_size (1));
+  A (1, falloc_size_2_4, alloc_size (2));
+  A (0, falloc_size_2_4, alloc_size (3));
+  A (1, falloc_size_2_4, alloc_size (4));
+  A (1, falloc_size_2_4, alloc_size (2, 4));
+
+  extern ATTR (alloc_size (3))
+    void* fmalloc_size_3 (int, int, int);
+
+  A (1, fmalloc_size_3, alloc_size);
+  A (0, fmalloc_size_3, alloc_size (1));
+  A (0, fmalloc_size_3, alloc_size (2));
+  A (1, fmalloc_size_3, alloc_size (3));
+  A (0, fmalloc_size_3, malloc);
+
+  extern ATTR (malloc)
+    void* fmalloc_size_3 (int, int, int);
+
+  A (1, fmalloc_size_3, alloc_size (3));
+  A (1, fmalloc_size_3, malloc);
+}
+
+
+void test_alias (void)
+{
+  A (0, fnoreturn, alias);
+  A (1, falias, alias);
+  A (1, falias, alias ("fnoreturn"));
+  A (0, falias, alias ("falias"));
+  A (0, falias, alias ("fnone"));
+}
+
+
+void test_cold_hot (void)
+{
+  extern ATTR (cold) void fcold (void);
+  extern ATTR (hot) void fhot (void);
+
+  A (0, fnone, cold);
+  A (0, fnone, hot);
+
+  A (1, fcold, cold);
+  A (0, fcold, hot);
+
+  A (0, fhot, cold);
+  A (1, fhot, hot);
+}
+
+
+void test_const_leaf_pure (void)
+{
+  extern ATTR (const) int fconst (void);
+  extern ATTR (leaf) int fleaf (void);
+  extern ATTR (pure) int fpure (void);
+
+  A (0, fnone, const);
+  A (0, fnone, leaf);
+  A (0, fnone, pure);
+
+  A (1, fconst, const);
+  A (0, fconst, leaf);
+  A (0, fconst, pure);
+
+  A (0, fleaf, const);
+  A (1, fleaf, leaf);
+  A (0, fleaf, pure);
+
+  A (0, fpure, const);
+  A (0, fpure, leaf);
+  A (1, fpure, pure);
+
+  extern ATTR (const, leaf) int fconst_leaf (void);
+
+  A (1, fconst_leaf, const);
+  A (1, fconst_leaf, leaf);
+
+  extern ATTR (leaf, const) int fleaf_const (void);
+
+  A (1, fleaf_const, const);
+  A (1, fleaf_const, leaf);
+}
+
+
+void test_ctor_dtor (void)
+{
+  extern ATTR (constructor) void fctor (void);
+  extern ATTR (destructor) void fdtor (void);
+  extern ATTR (constructor, destructor) void fctor_dtor (void);
+
+  A (0, fnone, constructor);
+  A (0, fnone, destructor);
+
+  A (1, fctor, constructor);
+  A (1, fdtor, destructor);
+
+  extern ATTR (constructor) void fctor_dtor (void);
+  extern ATTR (destructor) void fctor_dtor (void);
+  extern ATTR (constructor, destructor) void fctor_dtor (void);
+
+  A (1, fctor_dtor, constructor);
+  A (1, fctor_dtor, destructor);
+
+  extern ATTR (constructor (123)) void fctor_123 (void);
+  A (1, fctor_123, constructor);
+  A (0, fctor_123, destructor);
+  A (1, fctor_123, constructor (123));
+  A (0, fctor_123, constructor (124));
+
+  extern ATTR (destructor (234)) void fctor_123 (void);
+  A (1, fctor_123, constructor (123));
+  A (1, fctor_123, destructor);
+  A (1, fctor_123, destructor (234));
+  A (0, fctor_123, destructor (235));
+}
+
+
+void test_externally_visible (void)
+{
+  extern void fexternally_visible (void);
+
+  A (0, fexternally_visible, externally_visible);
+
+  extern ATTR (externally_visible) void fexternally_visible (void);
+
+  A (1, fexternally_visible, externally_visible);
+}
+
+
+void test_flatten (void)
+{
+  extern void fflatten (void);
+
+  A (0, fflatten, flatten);
+
+  extern ATTR (flatten) void fflatten (void);
+
+  A (1, fflatten, flatten);
+
+  extern void fflatten (void);
+
+  A (1, fflatten, flatten);
+}
+
+
+ATTR (format (printf, 2, 4)) void
+fformat_printf_2_3 (int, const char*, int, ...);
+
+void test_format (void)
+{
+  A (0, fnone, format);
+  A (0, fnone, format (printf));
+  A (0, fnone, format (printf, 2));
+}
+
+
+inline void finline (void) { }
+inline ATTR (always_inline) void falways_inline (void) { }
+inline ATTR (always_inline, gnu_inline) void falways_gnu_inline (void) { }
+ATTR (noinline) void fnoinline () { }
+
+void test_inline (void)
+{
+  A (0, fnone, always_inline);
+  A (0, fnone, gnu_inline);
+  A (0, fnone, noinline);
+
+  A (0, finline, always_inline);
+  A (0, finline, gnu_inline);
+  A (0, finline, noinline);
+
+  A (1, falways_inline, always_inline);
+  A (0, falways_inline, gnu_inline);
+  A (0, falways_inline, noinline);
+
+  A (1, falways_gnu_inline, always_inline);
+  A (1, falways_gnu_inline, gnu_inline);
+  A (0, falways_gnu_inline, noinline);
+
+  A (0, fnoinline, always_inline);
+  A (0, fnoinline, gnu_inline);
+  A (1, fnoinline, noinline);
+}
+
+
+ATTR (no_instrument_function) void fno_instrument (void);
+
+ATTR (visibility ("default")) void fdefault (void);
+ATTR (visibility ("hidden")) void fhidden (void);
+ATTR (visibility ("internal")) void finternal (void);
+ATTR (visibility ("protected")) void fprotected (void);
+
+void test_visibility (void)
+{
+  A (0, fnone, visibility ("default"));
+  A (0, fnone, visibility ("hidden"));
+  A (0, fnone, visibility ("internal"));
+  A (0, fnone, visibility ("protected"));
+
+  A (1, fdefault, visibility ("default"));
+  A (0, fdefault, visibility ("hidden"));
+  A (0, fdefault, visibility ("internal"));
+  A (0, fdefault, visibility ("protected"));
+
+  A (0, fhidden, visibility ("default"));
+  A (1, fhidden, visibility ("hidden"));
+  A (0, fhidden, visibility ("internal"));
+  A (0, fhidden, visibility ("protected"));
+
+  A (0, finternal, visibility ("default"));
+  A (0, finternal, visibility ("hidden"));
+  A (1, finternal, visibility ("internal"));
+  A (0, finternal, visibility ("protected"));
+
+  A (0, fprotected, visibility ("default"));
+  A (0, fprotected, visibility ("hidden"));
+  A (0, fprotected, visibility ("internal"));
+  A (1, fprotected, visibility ("protected"));
+}
+
+/* { dg-prune-output "specifies less restrictive attribute" } */
diff --git a/gcc/testsuite/c-c++-common/builtin-has-attribute-4.c b/gcc/testsuite/c-c++-common/builtin-has-attribute-4.c
new file mode 100644
index 0000000..14bdd3f
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/builtin-has-attribute-4.c
@@ -0,0 +1,285 @@
+/* Verify __builtin_has_attribute return value for variables.
+   { dg-do compile }
+   { dg-options "-Wall -ftrack-macro-expansion=0" }
+   { dg-options "-Wall -Wno-narrowing -Wno-unused -ftrack-macro-expansion=0" { target c++ } }  */
+
+#define ATTR(...) __attribute__ ((__VA_ARGS__))
+
+#define A(expect, sym, attr)						\
+  typedef int Assert [1 - 2 * !(__builtin_has_attribute (sym, attr) == expect)]
+
+int vnone;
+
+ATTR (aligned) char valigned;
+ATTR (aligned (1)) char valigned_1;
+ATTR (aligned (2)) char valigned_2;
+ATTR (aligned (4)) char valigned_4;
+ATTR (aligned (8)) char valigned_8;
+
+void test_aligned (void)
+{
+  A (0, vnone, aligned);
+  A (0, vnone, aligned (0));
+  A (0, vnone, aligned (1));
+  A (0, vnone, aligned (2));
+  A (0, vnone, aligned (4));
+  A (0, vnone, aligned (8));
+  A (0, vnone, aligned (16));
+
+  A (1, valigned, aligned);
+  A (0, valigned, aligned (0));
+  A (0, valigned, aligned (1));
+  A (0, valigned, aligned (2));
+
+  A (1, valigned_1, aligned);
+  A (0, valigned_1, aligned (0));
+  A (1, valigned_1, aligned (1));
+  A (0, valigned_1, aligned (2));
+  A (0, valigned_1, aligned (4));
+
+  A (1, valigned_2, aligned);
+  A (0, valigned_2, aligned (0));
+  A (0, valigned_2, aligned (1));
+  A (1, valigned_2, aligned (2));
+  A (0, valigned_2, aligned (4));
+}
+
+
+int vtarget;
+extern ATTR (alias ("vtarget")) int valias;
+
+void test_alias (void)
+{
+  A (0, vnone, alias);
+  A (1, valias, alias);
+  A (1, valias, alias ("vtarget"));
+  A (0, valias, alias ("vnone"));
+}
+
+
+void test_cleanup (void)
+{
+  extern void fpv (void*);
+  extern void fcleanup (void*);
+
+  int var;
+  ATTR (cleanup (fcleanup)) int var_cleanup;
+  A (0, var, cleanup);
+  A (1, var_cleanup, cleanup);
+  A (1, var_cleanup, cleanup (fcleanup));
+  A (0, var_cleanup, cleanup (fpv));
+}
+
+
+ATTR (common) int vcommon;
+ATTR (nocommon) int vnocommon;
+
+void test_common (void)
+{
+  A (0, vnone, common);
+  A (0, vnone, nocommon);
+
+  A (1, vcommon, common);
+  A (0, vcommon, nocommon);
+
+  A (0, vnocommon, common);
+  A (1, vnocommon, nocommon);
+}
+
+
+void test_externally_visible (void)
+{
+  extern int vexternally_visible;
+
+  A (0, vexternally_visible, externally_visible);
+
+  extern ATTR (externally_visible) int vexternally_visible;
+
+  A (1, vexternally_visible, externally_visible);
+}
+
+
+int test_mode (void)
+{
+  ATTR (mode (byte)) int i8;
+  return __builtin_has_attribute (i8, mode);   /* { dg-warning ".mode. attribute not supported in .__builtin_has_attribute." } */
+}
+
+
+void test_nonstring (void)
+{
+  char arr[1];
+  char* ptr = arr;
+
+  ATTR (nonstring) char arr_nonstring[1];
+  ATTR (nonstring) char *ptr_nonstring =  arr_nonstring;
+
+  A (0, arr, nonstring);
+  A (0, ptr, nonstring);
+
+  A (1, arr_nonstring, nonstring);
+  A (1, ptr_nonstring, nonstring);
+}
+
+struct PackedMember
+{
+  char c;
+  short s;
+  int i;
+  ATTR (packed) int a[2];
+} gpak[2];
+
+void test_packed (struct PackedMember *p)
+{
+  int vunpacked;
+  ATTR (packed) int vpacked;   /* { dg-warning ".packed. attribute ignored" } */
+
+  A (0, vunpacked, packed);
+  A (0, vpacked, packed);
+
+  int arr_unpacked[2];
+  ATTR (packed) int arr_packed[2];   /* { dg-warning ".packed. attribute ignored" } */
+
+  A (0, arr_unpacked, packed);
+  A (0, arr_packed, packed);
+  A (0, arr_unpacked[0], packed);
+  A (0, arr_packed[0], packed);
+
+  A (0, gpak, packed);
+  A (0, gpak[0], packed);
+  A (0, *gpak, packed);
+  A (0, gpak[0].c, packed);
+  A (0, gpak[1].s, packed);
+  A (1, gpak->a, packed);
+  A (1, (*gpak).a[0], packed);
+
+  /* The following fails because in C it's represented as
+       INDIRECT_REF (POINTER_PLUS (NOP_EXPR (ADDR_EXPR (gpak)), ...))
+     with no reference to the member.  Avoid testing it.
+  A (1, *gpak[9].a, packed);  */
+
+  A (0, p->c, packed);
+  A (0, p->s, packed);
+  A (1, p->a, packed);
+  A (1, p->a[0], packed);
+  /* Similar to the comment above.
+   A (1, *p->a, packed);  */
+}
+
+
+ATTR (section ("sectA")) int var_sectA;
+ATTR (section ("sectB")) int var_sectB;
+
+void test_section (void)
+{
+  int var = 0;
+  A (0, var, section);
+  A (0, var, section ("sectA"));
+
+  A (1, var_sectA, section);
+  A (1, var_sectA, section ("sectA"));
+  A (0, var_sectA, section ("sectB"));
+
+  A (1, var_sectB, section);
+  A (0, var_sectB, section ("sectA"));
+  A (1, var_sectB, section ("sectB"));
+}
+
+
+void test_vector_size (void)
+{
+  char c;
+  extern int arrx[];
+  extern int arr1[1];
+
+  A (0, c, vector_size);
+  A (0, c, vector_size (1));
+  A (0, arrx, vector_size);
+  A (0, arrx, vector_size (4));
+  A (0, arr1, vector_size);
+  A (0, arr1, vector_size (8));
+
+  ATTR (vector_size (4)) char cv4;
+  ATTR (vector_size (16)) int iv16;
+
+  A (1, cv4, vector_size);
+  A (0, cv4, vector_size (1));
+  A (0, cv4, vector_size (2));
+  A (1, cv4, vector_size (4));
+  A (0, cv4, vector_size (8));
+
+  A (1, iv16, vector_size);
+  A (0, iv16, vector_size (1));
+  A (0, iv16, vector_size (8));
+  A (1, iv16, vector_size (16));
+  A (0, iv16, vector_size (32));
+
+  ATTR (vector_size (8)) float afv8[4];
+  A (1, afv8, vector_size);
+  A (0, afv8, vector_size (1));
+  A (0, afv8, vector_size (2));
+  A (0, afv8, vector_size (4));
+  A (1, afv8, vector_size (8));
+  A (0, afv8, vector_size (16));
+}
+
+
+ATTR (visibility ("default")) int vdefault;
+ATTR (visibility ("hidden")) int vhidden;
+ATTR (visibility ("internal")) int vinternal;
+ATTR (visibility ("protected")) int vprotected;
+
+void test_visibility (void)
+{
+  A (0, vnone, visibility ("default"));
+  A (0, vnone, visibility ("hidden"));
+  A (0, vnone, visibility ("internal"));
+  A (0, vnone, visibility ("protected"));
+
+  A (1, vdefault, visibility ("default"));
+  A (0, vdefault, visibility ("hidden"));
+  A (0, vdefault, visibility ("internal"));
+  A (0, vdefault, visibility ("protected"));
+
+  A (0, vhidden, visibility ("default"));
+  A (1, vhidden, visibility ("hidden"));
+  A (0, vhidden, visibility ("internal"));
+  A (0, vhidden, visibility ("protected"));
+
+  A (0, vinternal, visibility ("default"));
+  A (0, vinternal, visibility ("hidden"));
+  A (1, vinternal, visibility ("internal"));
+  A (0, vinternal, visibility ("protected"));
+
+  A (0, vprotected, visibility ("default"));
+  A (0, vprotected, visibility ("hidden"));
+  A (0, vprotected, visibility ("internal"));
+  A (1, vprotected, visibility ("protected"));
+}
+
+
+int var_init_strong = 123;
+int var_uninit_strong;
+static int var_extern_strong;
+static int var_static_strong;
+
+ATTR (weak) int var_init_weak = 234;
+ATTR (weak) int var_uninit_weak;
+
+void test_weak (void)
+{
+  int var_local = 0;
+  static int var_static_local = 0;
+
+  A (0, var_init_strong, weak);
+  A (0, var_uninit_strong, weak);
+  A (0, var_extern_strong, weak);
+  A (0, var_static_strong, weak);
+  A (0, var_local, weak);
+  A (0, var_static_local, weak);
+
+  A (1, var_init_weak, weak);
+  A (1, var_uninit_weak, weak);
+}
+
+/* { dg-prune-output "specifies less restrictive attribute" } */
diff --git a/gcc/testsuite/c-c++-common/builtin-has-attribute-5.c b/gcc/testsuite/c-c++-common/builtin-has-attribute-5.c
new file mode 100644
index 0000000..bf1ccbb
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/builtin-has-attribute-5.c
@@ -0,0 +1,53 @@
+/* Verify __builtin_has_attribute return value for enumerators
+   and labels.
+   { dg-do compile }
+   { dg-options "-Wall -ftrack-macro-expansion=0" }
+   { dg-options "-Wall -Wno-narrowing -Wno-unused -ftrack-macro-expansion=0" { target c++ } }  */
+
+#define ATTR(...) __attribute__ ((__VA_ARGS__))
+
+#define A(expect, sym, attr)						\
+  typedef int Assert [1 - 2 * !(__builtin_has_attribute (sym, attr) == expect)]
+
+enum {
+  e0,
+  e1_deprecated ATTR (deprecated),
+  e2
+};
+
+void test_deprecated (void)
+{
+  A (0, e0, deprecated);
+  /* The following fails in C where the enumerator is folded
+     into the constant 1 by the parser before it ever reaches
+     the built-in.  */
+  A (1, e1_deprecated, deprecated);   /* { dg-warning ".e1_deprecated. is deprecated" "pr?????" { xfail { ! c++ } } } */
+  A (0, e2, deprecated);
+}
+
+
+void test_lebel (void)
+{
+ l_none:
+ l_cold: ATTR (cold);
+ l_hot: ATTR (hot);
+ l_unused: ATTR (unused);
+ l_cold_unused: ATTR (cold, unused);
+
+  A (0, l_none, cold);
+  A (0, l_none, hot);
+  A (0, l_none, unused);
+
+  A (1, l_cold, cold);
+  A (0, l_cold, hot);
+  A (0, l_cold, unused);
+
+  A (0, l_hot, cold);
+  A (1, l_hot, hot);
+  A (0, l_hot, unused);
+
+  A (1, l_cold_unused, cold);
+  A (0, l_cold_unused, hot);
+  A (1, l_cold_unused, unused);
+
+}
diff --git a/gcc/testsuite/c-c++-common/builtin-has-attribute.c b/gcc/testsuite/c-c++-common/builtin-has-attribute.c
new file mode 100644
index 0000000..311c292
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/builtin-has-attribute.c
@@ -0,0 +1,60 @@
+/* Verify __builtin_has_attribute error handling.
+   { dg-do compile }
+   { dg-options "-Wall -ftrack-macro-expansion=0" }  */
+
+#define ATTR(list) __attribute__ (list)
+
+void fnone (void);
+
+ATTR ((aligned)) void faligned (void);
+ATTR ((aligned (8))) void faligned_8 (void);
+
+#define has_attr(x, attr)   __builtin_has_attribute (x, attr)
+
+#define A(expect, sym, attr)						\
+  typedef int Assert [1 - 2 * !(has_attr (sym, attr) == expect)]
+
+
+int b;
+
+/* Exercise syntactically invalid arguments.  */
+
+void test_bad_arguments (void)
+{
+  b = __builtin_has_attribute ();            /* { dg-error "expected \(primary-\)?expression|expected .,." } */
+  b = __builtin_has_attribute (1);           /* { dg-error "expected .,." } */
+  b = __builtin_has_attribute (void);        /* { dg-error "expected .,." } */
+  b = __builtin_has_attribute (foo);         /* { dg-error ".foo. \(undeclared|was not declared\)" } */
+  /* { dg-error "expected .,." "missing comma" { target *-*-* } .-1 } */
+
+  /* Verify the implementationm doesn't ICE.  */
+  b = __builtin_has_attribute (foobar, aligned);  /* { dg-error ".foobar. \(undeclared|was not declared\)" } */
+
+  b = __builtin_has_attribute (1, 2, 3);     /* { dg-error "expected identifier" } */
+  b = __builtin_has_attribute (int, 1 + 2);  /* { dg-error "expected identifier" } */
+  b = __builtin_has_attribute (2, "aligned"); /* { dg-error "expected identifier" } */
+}
+
+/* Exercise syntactically valid arguments applied in invalid ways.  */
+
+void test_invalid_arguments (void)
+{
+  b = has_attr (fnone, align);        /* { dg-error "unknown attribute .align." } */
+  b = has_attr (b, aligned__);        /* { dg-error "unknown attribute .aligned__." } */
+  b = has_attr (fnone, aligned (3));  /* { dg-error "alignment is not a positive power of 2" } */
+
+  /* Verify the out-of-bounds arguments are diagnosed and the result
+     of the built-in is false.  */
+  A (0, fnone, alloc_size (1));       /* { dg-warning "\\\[-Wattributes]" } */
+  A (0, fnone, alloc_size (2));       /* { dg-warning "\\\[-Wattributes]" } */
+
+  A (0, int, alloc_size (1));         /* { dg-warning ".alloc_size. attribute only applies to function types" } */
+
+  int i = 1;
+  A (0, i, alloc_size (1));           /* { dg-warning ".alloc_size. attribute only applies to function types" } */
+
+  A (0, faligned_8, aligned (i));     /* { dg-error "alignment is not an integer constant" } */
+
+  typedef ATTR ((aligned (2))) char CA2;
+  b = has_attr (CA2[2], aligned);     /* { dg-error "alignment of array elements is greater than element size" } */
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-has-attribute.c b/gcc/testsuite/gcc.dg/builtin-has-attribute.c
new file mode 100644
index 0000000..8c11cf8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-has-attribute.c
@@ -0,0 +1,45 @@
+/* Verify that defining a type in __builtin_has_attribute triggers
+   the expected -Wc++-compat warning and evaluates as expected.
+   Also verify that the expression in __builtin_has_attribute is
+   not evaluated.
+
+  { dg-do run }
+  { dg-options "-O2 -Wall -Wc++-compat" }  */
+
+#define ATTR(list) __attribute__ (list)
+
+#define A(expect, sym, attr)						\
+  typedef int Assert [1 - 2 * !(__builtin_has_attribute (sym, attr) == expect)]
+
+int nfails;
+
+#define assert(expr)						\
+  ((expr)							\
+   ? (void)0							\
+   : (__builtin_printf ("Assertion failed on line %i: %s\n",	\
+			__LINE__, #expr),			\
+      ++nfails))
+
+A (0, struct A { int i; }, aligned);   /* { dg-warning "expression is invalid in C\\\+\\\+" } */
+A (1, struct ATTR ((aligned)) B { int i; }, aligned);   /* { dg-warning "expression is invalid in C\\\+\\\+" } */
+
+
+int f (void)
+{
+  __builtin_abort ();
+}
+
+int n = 1;
+
+int main (void)
+{
+  assert (0 == __builtin_has_attribute (int[n++], aligned));
+  assert (1 == __builtin_has_attribute (ATTR ((aligned)) int[n++], aligned));
+  assert (1 == __builtin_has_attribute (ATTR ((aligned)) int[f ()], aligned));
+  assert (1 == 1);
+
+  if (nfails)
+    __builtin_abort ();
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/builtin-has-attribute.c b/gcc/testsuite/gcc.target/i386/builtin-has-attribute.c
new file mode 100644
index 0000000..2de1ba3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/builtin-has-attribute.c
@@ -0,0 +1,54 @@
+/* Verify __builtin_has_attribute return value for i386 function attributes.
+   { dg-do compile }
+   { dg-options "-Wall -ftrack-macro-expansion=0" }
+   { dg-options "-Wall -Wno-narrowing -Wno-unused -ftrack-macro-expansion=0" { target c++ } }  */
+
+#define ATTR(...) __attribute__ ((__VA_ARGS__))
+
+#define A(expect, sym, attr)						\
+  typedef int Assert [1 - 2 * !(__builtin_has_attribute (sym, attr) == expect)]
+
+void fnone (void);
+
+
+ATTR (interrupt) void finterrupt (void*);
+ATTR (interrupt, naked) void fnaked_interrupt (void*);
+
+A (0, fnone, interrupt);
+A (1, finterrupt, interrupt);
+A (1, fnaked_interrupt, interrupt);
+A (1, fnaked_interrupt, naked);
+
+
+ATTR (naked) void fnaked (void);
+
+A (0, fnone, naked);
+A (1, fnaked, naked);
+
+
+ATTR (no_caller_saved_registers) void fnsr (int);
+
+A (0, fnone, no_caller_saved_registers);
+A (1, fnsr, no_caller_saved_registers);
+
+
+ATTR (target ("abm")) void ftarget_abm (void);
+ATTR (target ("mmx")) void ftarget_mmx (void);
+ATTR (target ("mmx"), target ("sse")) void ftarget_mmx_sse (void);
+
+A (0, fnone, target);
+A (0, fnone, target ("abm"));
+A (0, fnone, target ("mmx"));
+
+A (1, ftarget_abm, target);
+A (0, ftarget_abm, target ("no-abm"));
+A (1, ftarget_abm, target ("abm"));
+
+A (1, ftarget_mmx, target);
+A (0, ftarget_mmx, target ("no-mmx"));
+A (1, ftarget_mmx, target ("mmx"));
+
+A (1, ftarget_mmx_sse, target);
+A (0, ftarget_mmx_sse, target ("no-mmx"));
+A (1, ftarget_mmx_sse, target ("mmx"));
+A (1, ftarget_mmx_sse, target ("sse"));
Jeff Law Oct. 16, 2018, 11:19 p.m. UTC | #6
On 10/13/18 6:19 PM, Martin Sebor wrote:
> Attached is an updated/enhanced patch with many more tests
> and the suggested documentation tweak.  It also restores
> the handling of empty attributes that the first revision
> inadvertently removed from the C parser.
> 
> The tests are much more comprehensive now but still not
> exhaustive.  I have added warning for the mode attribute
> that cannot be supported.  Enumerator attributes aren't
> detected in C because they are folded to constants before
> they reach the built-in, and label attributes aren't handled
> yet either in C or in C++.  Supporting those will take a minor
> enhancement.  I haven only added handful of test cases for
> x86_64 target attributes,  The built-in is not exercised
> for any other target yet.  I don't expect any surprises
> there.  Either it will work or (where the attributes aren't
> hanging off a node) it will return false.  Supporting those
> will have to wait until the later (I think the best way is
> to add a callback to struct attribute_spec to let each back
> end query a node for the properties unique to such attributes
> analogously to attribute vector_size).
> 
> I haven't done any work on supporting templates.  I would
> like to but I don't expect to be able to get it done before
> stage 1 is over (I have another feature I need to finish,
> the one that prompted this work to begin with).  I think
> the new built-in is quite useful even without template
> support.
> 
> I've kept the name __builtin_has_attribute: it is close to
> the __has_attribute macro, and I think that's fine because
> the built-in's purpose is very close to that of the macro.
> 
> Martin
> 
> 
> 
> gcc-builtin-has-attribute.diff
> 
> gcc/c/ChangeLog:
> 
> 	* c-parser.c (c_parser_has_attribute_expression): New function.
> 	(c_parser_attribute): New function.
> 	(c_parser_attributes): Move code into c_parser_attribute.
> 	(c_parser_unary_expression): Handle RID_HAS_ATTRIBUTE_EXPRESSION.
> 
> gcc/c-family/ChangeLog:
> 
> 	* c-attribs.c (type_for_vector_size): New function.
> 	(type_valid_for_vector_size): Same.
> 	(handle_vector_size_attribute): Move code to the functions above
> 	and call them.
> 	(validate_attribute, has_attribute): New functions.
> 	* c-common.h (has_attribute): Declare.
> 	(rid): Add RID_HAS_ATTRIBUTE_EXPRESSION.
> 	* c-common.c (c_common_resword): Same.
> 
> gcc/cp/ChangeLog:
> 
> 	* cp-tree.h (cp_check_const_attributes): Declare.
> 	* decl2.c (cp_check_const_attributes): Declare extern.
> 	* parser.c (cp_parser_has_attribute_expression): New function.
> 	(cp_parser_unary_expression): Handle RID_HAS_ATTRIBUTE_EXPRESSION.
> 	(cp_parser_gnu_attribute_list): Add argument.
> 
> gcc/ChangeLog:
> 
> 	* doc/extend.texi (Other Builtins): Add __builtin_has_attribute.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* c-c++-common/builtin-has-attribute-2.c: New test.
> 	* c-c++-common/builtin-has-attribute-3.c: New test.
> 	* c-c++-common/builtin-has-attribute-4.c: New test.
> 	* c-c++-common/builtin-has-attribute.c: New test.
> 	* gcc.dg/builtin-has-attribute.c: New test.
> 	* gcc/testsuite/gcc.target/i386/builtin-has-attribute.c: New test.
> 
Generally looks OK except for a nit mentioned below.  I don't mind
iterating a bit on the details here over time.  Give Jason a few days to
chime in on the C++ side.

> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 8ffb0cd..dcf4747 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -2649,8 +2649,9 @@ explicit @code{externally_visible} attributes are still necessary.
>  @cindex @code{flatten} function attribute
>  Generally, inlining into a function is limited.  For a function marked with
>  this attribute, every call inside this function is inlined, if possible.
> -Whether the function itself is considered for inlining depends on its size and
> -the current inlining parameters.
> +Functions declared with attribute @code{noinline} and similar are not
> +inlined.  Whether the function itself is considered for inlining depends
> +on its size and the current inlining parameters.
Guessing this was from another doc patch that I think has already been
approved :-)


> @@ -11726,6 +11728,33 @@ check its compatibility with @var{size}.
>  
>  @end deftypefn
>  
> +@deftypefn {Built-in Function} bool __builtin_has_attribute (@var{type-or-expression}, @var{attribute})
> +The @code{__builtin_has_attribute} function evaluates to an integer constant
> +expression equal to @code{true} if the symbol or type referenced by
> +the @var{type-or-expression} argument has been declared with
> +the @var{attribute} referenced by the second argument.  Neither argument
> +is valuated.  The @var{type-or-expression} argument is subject to the same
s/valuated/evaluated/ ?
Eric Gallager Oct. 17, 2018, 1:37 a.m. UTC | #7
On 10/13/18, Martin Sebor <msebor@gmail.com> wrote:
> Attached is an updated/enhanced patch with many more tests
> and the suggested documentation tweak.  It also restores
> the handling of empty attributes that the first revision
> inadvertently removed from the C parser.
>
> The tests are much more comprehensive now but still not
> exhaustive.  I have added warning for the mode attribute
> that cannot be supported.  Enumerator attributes aren't
> detected in C because they are folded to constants before
> they reach the built-in, and label attributes aren't handled
> yet either in C or in C++.  Supporting those will take a minor
> enhancement.  I haven only added handful of test cases for
> x86_64 target attributes,  The built-in is not exercised
> for any other target yet.  I don't expect any surprises
> there.  Either it will work or (where the attributes aren't
> hanging off a node) it will return false.  Supporting those
> will have to wait until the later (I think the best way is
> to add a callback to struct attribute_spec to let each back
> end query a node for the properties unique to such attributes
> analogously to attribute vector_size).
>
> I haven't done any work on supporting templates.  I would
> like to but I don't expect to be able to get it done before
> stage 1 is over (I have another feature I need to finish,
> the one that prompted this work to begin with).  I think
> the new built-in is quite useful even without template
> support.
>
> I've kept the name __builtin_has_attribute: it is close to
> the __has_attribute macro, and I think that's fine because
> the built-in's purpose is very close to that of the macro.

If you're going to keep the name, could you at least explain the
difference between the two in the documentation?

>
> Martin
>
> On 10/11/2018 08:54 AM, Martin Sebor wrote:
>> On 10/11/2018 06:04 AM, Joseph Myers wrote:
>>> On Thu, 11 Oct 2018, Martin Sebor wrote:
>>>
>>>> The attached patch introduces a built-in function called
>>>> __builtin_has_attribute that makes some of this possible.
>>>> See the documentation and tests for details.
>>>
>>> I see nothing in the documentation about handling of equivalent forms of
>>> an attribute - for example, specifying __aligned__ in the attribute but
>>> aligned in __builtin_has_attribute, or vice versa.  I'd expect that to
>>> be
>>> documented to work (both of those should return true), with associated
>>> tests.  (And likewise the semantics should allow for a format attribute
>>> using printf in one place and __printf__ in the other, for example, or
>>> the
>>> same constant argument represented with different expressions.)
>>
>> Yes, it occurred to me belatedly that I should add a test for those
>> as well.  I can also mention it in the documentation, although I'd
>> have thought it would be implicit in how attributes work in general.
>> (Or are there some differences between the underscored forms and
>> the one without it)?
>>
>>>
>>> What are the semantics of __builtin_has_attribute for attributes that
>>> can't be tested for?  (E.g. the mode attribute, which ends up
>>> resulting in
>>> some existing type with the required mode being used, so there's nothing
>>> to indicate the attribute was originally used to declare things.)
>>
>> With a few exceptions (like aligned) the built-in returns false
>> for attributes that aren't attached to a node.  I haven't exercised
>> nearly all the attributes yet, and this one could very well be among
>> those that aren't and perhaps can't be handled.  I suspect some
>> target attributes might be in the same group.  If there's no way
>> to tell it should probably be documented as a limitation of
>> the function, maybe also under the attribute itself that can't
>> be detected.  Alternatively, the built-in return type could be
>> changed to a tri-state: "don't know," false, true.  Can you
>> think of a better solution?
>>
>> Martin
>
>
Martin Sebor Oct. 22, 2018, 6:05 p.m. UTC | #8
Jason,

Do you have any suggestions for the C++ parts or are they good
enough to commit (with the expectation that I'll add template
handling later)?

The last patch is here:
   https://gcc.gnu.org/ml/gcc-patches/2018-10/msg00811.html

Martin

On 10/16/2018 05:19 PM, Jeff Law wrote:
> On 10/13/18 6:19 PM, Martin Sebor wrote:
>> Attached is an updated/enhanced patch with many more tests
>> and the suggested documentation tweak.  It also restores
>> the handling of empty attributes that the first revision
>> inadvertently removed from the C parser.
>>
>> The tests are much more comprehensive now but still not
>> exhaustive.  I have added warning for the mode attribute
>> that cannot be supported.  Enumerator attributes aren't
>> detected in C because they are folded to constants before
>> they reach the built-in, and label attributes aren't handled
>> yet either in C or in C++.  Supporting those will take a minor
>> enhancement.  I haven only added handful of test cases for
>> x86_64 target attributes,  The built-in is not exercised
>> for any other target yet.  I don't expect any surprises
>> there.  Either it will work or (where the attributes aren't
>> hanging off a node) it will return false.  Supporting those
>> will have to wait until the later (I think the best way is
>> to add a callback to struct attribute_spec to let each back
>> end query a node for the properties unique to such attributes
>> analogously to attribute vector_size).
>>
>> I haven't done any work on supporting templates.  I would
>> like to but I don't expect to be able to get it done before
>> stage 1 is over (I have another feature I need to finish,
>> the one that prompted this work to begin with).  I think
>> the new built-in is quite useful even without template
>> support.
>>
>> I've kept the name __builtin_has_attribute: it is close to
>> the __has_attribute macro, and I think that's fine because
>> the built-in's purpose is very close to that of the macro.
>>
>> Martin
>>
>>
>>
>> gcc-builtin-has-attribute.diff
>>
>> gcc/c/ChangeLog:
>>
>> 	* c-parser.c (c_parser_has_attribute_expression): New function.
>> 	(c_parser_attribute): New function.
>> 	(c_parser_attributes): Move code into c_parser_attribute.
>> 	(c_parser_unary_expression): Handle RID_HAS_ATTRIBUTE_EXPRESSION.
>>
>> gcc/c-family/ChangeLog:
>>
>> 	* c-attribs.c (type_for_vector_size): New function.
>> 	(type_valid_for_vector_size): Same.
>> 	(handle_vector_size_attribute): Move code to the functions above
>> 	and call them.
>> 	(validate_attribute, has_attribute): New functions.
>> 	* c-common.h (has_attribute): Declare.
>> 	(rid): Add RID_HAS_ATTRIBUTE_EXPRESSION.
>> 	* c-common.c (c_common_resword): Same.
>>
>> gcc/cp/ChangeLog:
>>
>> 	* cp-tree.h (cp_check_const_attributes): Declare.
>> 	* decl2.c (cp_check_const_attributes): Declare extern.
>> 	* parser.c (cp_parser_has_attribute_expression): New function.
>> 	(cp_parser_unary_expression): Handle RID_HAS_ATTRIBUTE_EXPRESSION.
>> 	(cp_parser_gnu_attribute_list): Add argument.
>>
>> gcc/ChangeLog:
>>
>> 	* doc/extend.texi (Other Builtins): Add __builtin_has_attribute.
>>
>> gcc/testsuite/ChangeLog:
>>
>> 	* c-c++-common/builtin-has-attribute-2.c: New test.
>> 	* c-c++-common/builtin-has-attribute-3.c: New test.
>> 	* c-c++-common/builtin-has-attribute-4.c: New test.
>> 	* c-c++-common/builtin-has-attribute.c: New test.
>> 	* gcc.dg/builtin-has-attribute.c: New test.
>> 	* gcc/testsuite/gcc.target/i386/builtin-has-attribute.c: New test.
>>
> Generally looks OK except for a nit mentioned below.  I don't mind
> iterating a bit on the details here over time.  Give Jason a few days to
> chime in on the C++ side.
>
>> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
>> index 8ffb0cd..dcf4747 100644
>> --- a/gcc/doc/extend.texi
>> +++ b/gcc/doc/extend.texi
>> @@ -2649,8 +2649,9 @@ explicit @code{externally_visible} attributes are still necessary.
>>  @cindex @code{flatten} function attribute
>>  Generally, inlining into a function is limited.  For a function marked with
>>  this attribute, every call inside this function is inlined, if possible.
>> -Whether the function itself is considered for inlining depends on its size and
>> -the current inlining parameters.
>> +Functions declared with attribute @code{noinline} and similar are not
>> +inlined.  Whether the function itself is considered for inlining depends
>> +on its size and the current inlining parameters.
> Guessing this was from another doc patch that I think has already been
> approved :-)
>
>
>> @@ -11726,6 +11728,33 @@ check its compatibility with @var{size}.
>>
>>  @end deftypefn
>>
>> +@deftypefn {Built-in Function} bool __builtin_has_attribute (@var{type-or-expression}, @var{attribute})
>> +The @code{__builtin_has_attribute} function evaluates to an integer constant
>> +expression equal to @code{true} if the symbol or type referenced by
>> +the @var{type-or-expression} argument has been declared with
>> +the @var{attribute} referenced by the second argument.  Neither argument
>> +is valuated.  The @var{type-or-expression} argument is subject to the same
> s/valuated/evaluated/ ?
>
Jason Merrill Oct. 22, 2018, 10:08 p.m. UTC | #9
On 10/13/18 8:19 PM, Martin Sebor wrote:
> +  oper = cp_parser_type_id (parser);
> +  parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
> +
> +  if (cp_parser_parse_definitely (parser))
> +    {
> +      /* If all went well, set OPER to the type.  */
> +      cp_decl_specifier_seq decl_specs;
> +
> +      /* Build a trivial decl-specifier-seq.  */
> +      clear_decl_specs (&decl_specs);
> +      decl_specs.type = oper;
> +
> +      /* Call grokdeclarator to figure out what type this is.  */
> +      oper = grokdeclarator (NULL,
> +			     &decl_specs,
> +			     TYPENAME,
> +			     /*initialized=*/0,
> +			     /*attrlist=*/NULL);
> +    }

Doesn't grokdeclarator here give you back the same type you already had 
from cp_parser_type_id?  The latter already calls grokdeclarator.

I don't know why cp_parser_sizeof_operand does this, either.  Try 
removing it from both places?

> +  /* Consume the comma if it's there.  */
> +  if (!cp_parser_require (parser, CPP_COMMA, RT_COMMA))
> +    {
> +      parens.require_close (parser);

I think you want cp_parser_skip_to_closing_parenthesis for error 
recovery, rather than require_close.

> +  if (tree attr = cp_parser_gnu_attribute_list (parser, /*exactly_one=*/true))
> +    {
> +      if (oper != error_mark_node)
> +	{
> +	  /* Fold constant expressions used in attributes first.  */
> +	  cp_check_const_attributes (attr);
> +
> +	  /* Finally, see if OPER has been declared with ATTR.  */
> +	  ret = has_attribute (atloc, oper, attr, default_conversion);
> +	}
> +    }
> +  else
> +    {
> +      error_at (atloc, "expected identifier");
> +      cp_parser_skip_to_closing_parenthesis (parser, true, false, false);
> +    }
> +
> +  parens.require_close (parser);

I think the require_close should be in the valid case, since *skip* 
already consumes a closing paren.

> +is valuated.  The @var{type-or-expression} argument is subject to the same

evaluated

Jason
Martin Sebor Oct. 23, 2018, 10:08 p.m. UTC | #10
On 10/22/2018 04:08 PM, Jason Merrill wrote:
> On 10/13/18 8:19 PM, Martin Sebor wrote:
>> +  oper = cp_parser_type_id (parser);
>> +  parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
>> +
>> +  if (cp_parser_parse_definitely (parser))
>> +    {
>> +      /* If all went well, set OPER to the type.  */
>> +      cp_decl_specifier_seq decl_specs;
>> +
>> +      /* Build a trivial decl-specifier-seq.  */
>> +      clear_decl_specs (&decl_specs);
>> +      decl_specs.type = oper;
>> +
>> +      /* Call grokdeclarator to figure out what type this is.  */
>> +      oper = grokdeclarator (NULL,
>> +                 &decl_specs,
>> +                 TYPENAME,
>> +                 /*initialized=*/0,
>> +                 /*attrlist=*/NULL);
>> +    }
>
> Doesn't grokdeclarator here give you back the same type you already had
> from cp_parser_type_id?  The latter already calls grokdeclarator.
>
> I don't know why cp_parser_sizeof_operand does this, either.  Try
> removing it from both places?

You're right, the call in cp_parser_has_attribute_expression
was unnecessary.  cp_parser_sizeof_operand still needs it.

>
>> +  /* Consume the comma if it's there.  */
>> +  if (!cp_parser_require (parser, CPP_COMMA, RT_COMMA))
>> +    {
>> +      parens.require_close (parser);
>
> I think you want cp_parser_skip_to_closing_parenthesis for error
> recovery, rather than require_close.

Thanks, the error messages look slightly better that way (there
are fewer of them), although still not as good as in C or other
compilers in some cases.

>
>> +  if (tree attr = cp_parser_gnu_attribute_list (parser,
>> /*exactly_one=*/true))
>> +    {
>> +      if (oper != error_mark_node)
>> +    {
>> +      /* Fold constant expressions used in attributes first.  */
>> +      cp_check_const_attributes (attr);
>> +
>> +      /* Finally, see if OPER has been declared with ATTR.  */
>> +      ret = has_attribute (atloc, oper, attr, default_conversion);
>> +    }
>> +    }
>> +  else
>> +    {
>> +      error_at (atloc, "expected identifier");
>> +      cp_parser_skip_to_closing_parenthesis (parser, true, false,
>> false);
>> +    }
>> +
>> +  parens.require_close (parser);
>
> I think the require_close should be in the valid case, since *skip*
> already consumes a closing paren.

Ah, I need to make it consume the paren by passing true as the last
argument.  With that it works.

>
>> +is valuated.  The @var{type-or-expression} argument is subject to the
>> same
>
> evaluated

Thanks for the review.

Attached is an updated patch with the fixes above.

Martin
gcc/c/ChangeLog:

	* c-parser.c (c_parser_has_attribute_expression): New function.
	(c_parser_attribute): New function.
	(c_parser_attributes): Move code into c_parser_attribute.
	(c_parser_unary_expression): Handle RID_HAS_ATTRIBUTE_EXPRESSION.

gcc/c-family/ChangeLog:

	* c-attribs.c (type_for_vector_size): New function.
	(type_valid_for_vector_size): Same.
	(handle_vector_size_attribute): Move code to the functions above
	and call them.
	(validate_attribute, has_attribute): New functions.
	* c-common.h (has_attribute): Declare.
	(rid): Add RID_HAS_ATTRIBUTE_EXPRESSION.
	* c-common.c (c_common_resword): Same.

gcc/cp/ChangeLog:

	* cp-tree.h (cp_check_const_attributes): Declare.
	* decl2.c (cp_check_const_attributes): Declare extern.
	* parser.c (cp_parser_has_attribute_expression): New function.
	(cp_parser_unary_expression): Handle RID_HAS_ATTRIBUTE_EXPRESSION.
	(cp_parser_gnu_attribute_list): Add argument.

gcc/ChangeLog:

	* doc/extend.texi (Other Builtins): Add __builtin_has_attribute.

gcc/testsuite/ChangeLog:

	* c-c++-common/builtin-has-attribute-2.c: New test.
	* c-c++-common/builtin-has-attribute-3.c: New test.
	* c-c++-common/builtin-has-attribute-4.c: New test.
	* c-c++-common/builtin-has-attribute.c: New test.
	* gcc.dg/builtin-has-attribute.c: New test.
	* gcc/testsuite/gcc.target/i386/builtin-has-attribute.c: New test.

diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 3a88766..c0a1bb5 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -3128,34 +3128,11 @@ handle_deprecated_attribute (tree *node, tree name,
   return NULL_TREE;
 }
 
-/* Handle a "vector_size" attribute; arguments as in
-   struct attribute_spec.handler.  */
-
+/* Return the "base" type from TYPE that is suitable to apply attribute
+   vector_size to by stripping arrays, function types, etc.  */
 static tree
-handle_vector_size_attribute (tree *node, tree name, tree args,
-			      int ARG_UNUSED (flags),
-			      bool *no_add_attrs)
+type_for_vector_size (tree type)
 {
-  unsigned HOST_WIDE_INT vecsize, nunits;
-  machine_mode orig_mode;
-  tree type = *node, new_type, size;
-
-  *no_add_attrs = true;
-
-  size = TREE_VALUE (args);
-  if (size && TREE_CODE (size) != IDENTIFIER_NODE
-      && TREE_CODE (size) != FUNCTION_DECL)
-    size = default_conversion (size);
-
-  if (!tree_fits_uhwi_p (size))
-    {
-      warning (OPT_Wattributes, "%qE attribute ignored", name);
-      return NULL_TREE;
-    }
-
-  /* Get the vector size (in bytes).  */
-  vecsize = tree_to_uhwi (size);
-
   /* We need to provide for vector pointers, vector arrays, and
      functions returning vectors.  For example:
 
@@ -3171,8 +3148,25 @@ handle_vector_size_attribute (tree *node, tree name, tree args,
 	 || TREE_CODE (type) == OFFSET_TYPE)
     type = TREE_TYPE (type);
 
+  return type;
+}
+
+/* Given TYPE, return the base type to which the vector_size attribute
+   ATNAME with ARGS, when non-null, can be applied, if one exists.
+   On success and when both ARGS and PTRNUNITS are non-null, set
+   *PTRNUNINTS to the number of vector units.  When PTRNUNITS is not
+   null, issue a warning when the attribute argument is not constant
+   and an error if there is no such type.  Otherwise issue a warning
+   in the latter case and return null.  */
+
+static tree
+type_valid_for_vector_size (tree type, tree atname, tree args,
+			    unsigned HOST_WIDE_INT *ptrnunits)
+{
+  bool error_p = ptrnunits != NULL;
+
   /* Get the mode of the type being modified.  */
-  orig_mode = TYPE_MODE (type);
+  machine_mode orig_mode = TYPE_MODE (type);
 
   if ((!INTEGRAL_TYPE_P (type)
        && !SCALAR_FLOAT_TYPE_P (type)
@@ -3183,14 +3177,38 @@ handle_vector_size_attribute (tree *node, tree name, tree args,
       || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))
       || TREE_CODE (type) == BOOLEAN_TYPE)
     {
-      error ("invalid vector type for attribute %qE", name);
+      if (error_p)
+	error ("invalid vector type for attribute %qE", atname);
+      else
+	warning (OPT_Wattributes, "invalid vector type for attribute %qE",
+		 atname);
       return NULL_TREE;
     }
 
+  /* When no argument has been provided this is just a request to validate
+     the type above.  Return TYPE to indicate success.  */
+  if (!args)
+    return type;
+
+  tree size = TREE_VALUE (args);
+  if (size && TREE_CODE (size) != IDENTIFIER_NODE
+      && TREE_CODE (size) != FUNCTION_DECL)
+    size = default_conversion (size);
+
+  if (!tree_fits_uhwi_p (size))
+    {
+      /* FIXME: make the error message more informative.  */
+      if (error_p)
+	warning (OPT_Wattributes, "%qE attribute ignored", atname);
+      return NULL_TREE;
+    }
+
+  unsigned HOST_WIDE_INT vecsize = tree_to_uhwi (size);
   if (vecsize % tree_to_uhwi (TYPE_SIZE_UNIT (type)))
     {
-      error ("vector size not an integral multiple of component size");
-      return NULL;
+      if (error_p)
+	error ("vector size not an integral multiple of component size");
+      return NULL_TREE;
     }
 
   if (vecsize == 0)
@@ -3200,14 +3218,44 @@ handle_vector_size_attribute (tree *node, tree name, tree args,
     }
 
   /* Calculate how many units fit in the vector.  */
-  nunits = vecsize / tree_to_uhwi (TYPE_SIZE_UNIT (type));
+  unsigned HOST_WIDE_INT nunits = vecsize / tree_to_uhwi (TYPE_SIZE_UNIT (type));
   if (nunits & (nunits - 1))
     {
-      error ("number of components of the vector not a power of two");
+      if (error_p)
+	error ("number of components of the vector not a power of two");
+      else
+	warning (OPT_Wattributes,
+		 "number of components of the vector not a power of two");
       return NULL_TREE;
     }
 
-  new_type = build_vector_type (type, nunits);
+  if (ptrnunits)
+    *ptrnunits = nunits;
+
+  return type;
+}
+
+/* Handle a "vector_size" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_vector_size_attribute (tree *node, tree name, tree args,
+			      int ARG_UNUSED (flags),
+			      bool *no_add_attrs)
+{
+  *no_add_attrs = true;
+
+  /* Determine the "base" type to apply the attribute to.  */
+  tree type = type_for_vector_size (*node);
+
+  /* Get the vector size (in bytes) and let the function compute
+     the number of vector units.  */
+  unsigned HOST_WIDE_INT nunits;
+  type = type_valid_for_vector_size (type, name, args, &nunits);
+  if (!type)
+    return NULL_TREE;
+
+  tree new_type = build_vector_type (type, nunits);
 
   /* Build back pointers if needed.  */
   *node = lang_hooks.types.reconstruct_complex_type (*node, new_type);
@@ -3678,3 +3726,324 @@ handle_patchable_function_entry_attribute (tree *, tree, tree, int, bool *)
   /* Nothing to be done here.  */
   return NULL_TREE;
 }
+
+/* Attempt to partially validate a single attribute ATTR as if
+   it were to be applied to an entity OPER.  */
+
+static bool
+validate_attribute (location_t atloc, tree oper, tree attr)
+{
+  /* Determine whether the name of the attribute is valid
+     and fail with an error if not.  */
+  tree atname = get_attribute_name (attr);
+  if (!lookup_attribute_spec (atname))
+    {
+      if (atloc != UNKNOWN_LOCATION)
+	error_at (atloc, "unknown attribute %qE", atname);
+      return false;
+    }
+
+  tree args = TREE_VALUE (attr);
+  if (!args)
+    return true;
+
+  /* FIXME: Do some validation.  */
+  const char *atstr = IDENTIFIER_POINTER (atname);
+  if (!strcmp (atstr, "format"))
+    return true;
+
+  /* Only when attribute arguments have been provided try to validate
+     the whole thing.  decl_attributes doesn't return an indication of
+     success or failure so proceed regardless.  */
+  const char tmpname[] = "__builtin_has_attribute_tmp.";
+  tree tmpid = get_identifier (tmpname);
+  tree tmpdecl;
+  if (!strcmp (atstr, "vector_size"))
+    {
+      tree type = TYPE_P (oper) ? oper : TREE_TYPE (oper);
+      /* Check for function type here since type_for_vector_size
+	 strips it while looking for a function's return type.  */
+      if (FUNC_OR_METHOD_TYPE_P (type))
+	{
+	  warning_at (atloc, OPT_Wattributes,
+		      "invalid operand type %qT for %qs", type, atstr);
+	  return false;
+	}
+
+      type = type_for_vector_size (type);
+      if (VECTOR_TYPE_P (type))
+	type = TREE_TYPE (type);
+      /* Avoid trying to apply attribute vector_size to OPER since
+	 it's overly restrictive.  Simply make sure it has the right
+	 type.  */
+      return type_valid_for_vector_size (type, atname, args, NULL);
+    }
+
+  if (TYPE_P (oper))
+    tmpdecl = build_decl (atloc, TYPE_DECL, tmpid, oper);
+  else
+    tmpdecl = build_decl (atloc, TREE_CODE (oper), tmpid, TREE_TYPE (oper));
+
+  /* Temporarily clear CURRENT_FUNCTION_DECL to make decl_attributes
+     believe the DECL declared above is at file scope.  (See bug 87526.)  */
+  tree save_curfunc = current_function_decl;
+  current_function_decl = NULL_TREE;
+  if (DECL_P (tmpdecl))
+    {
+      if (DECL_P (oper))
+	/* An alias cannot be a defintion so declare the symbol extern.  */
+	DECL_EXTERNAL (tmpdecl) = true;
+      /* Attribute visibility only applies to symbols visible from other
+	 translation units so make it "public."   */
+      TREE_PUBLIC (tmpdecl) = TREE_PUBLIC (oper);
+    }
+  decl_attributes (&tmpdecl, attr, 0);
+  current_function_decl = save_curfunc;
+
+  /* FIXME: Change decl_attributes to indicate success or failure (and
+     parameterize it to avoid failing with errors).  */
+  return true;
+}
+
+/* Return true if the DECL, EXPR, or TYPE t has been declared with
+   attribute ATTR.  For DECL, consider also its type.  For EXPR,
+   consider just its type.  */
+
+bool
+has_attribute (location_t atloc, tree t, tree attr, tree (*convert)(tree))
+{
+  if (!attr || !t || t == error_mark_node)
+    return false;
+
+  if (!validate_attribute (atloc, t, attr))
+    return false;
+
+  tree type = NULL_TREE;
+  tree expr = NULL_TREE;
+  if (TYPE_P (t))
+    type = t;
+  else
+    {
+      do
+	{
+	  /* Determine the array element/member declaration from
+	     an ARRAY/COMPONENT_REF.  */
+	  STRIP_NOPS (t);
+	  tree_code code = TREE_CODE (t);
+	  if (code == ARRAY_REF)
+	    t = TREE_OPERAND (t, 0);
+	  else if (code == COMPONENT_REF)
+	    t = TREE_OPERAND (t, 1);
+	  else
+	    break;
+	} while (true);
+      expr = t;
+    }
+
+  /* Set to true when an attribute is found in the referenced entity
+     that matches the specified attribute.  */
+  bool found_match = false;
+
+  tree atname = get_attribute_name (attr);
+  const char *namestr = IDENTIFIER_POINTER (atname);
+
+   /* Iterate once for a type and twice for a function or variable
+     declaration: once for the DECL and the second time for its
+     TYPE.  */
+  for (bool done = false; !found_match && !done; )
+    {
+      tree atlist;
+      if (type)
+	{
+	  if (type == error_mark_node)
+	    {
+	      /* This could be a label.  FIXME: add support for labels.  */
+	      warning_at (atloc, OPT_Wattributes,
+			  (TYPE_P (t)
+			   ? G_("%qs attribute not supported for %qT "
+				"in %<__builtin_has_attribute%>")
+			   : G_("%qs attribute not supported for %qE "
+				"in %<__builtin_has_attribute%>")),
+			  namestr, t);
+	      return false;
+	    }
+
+	  /* Clear EXPR to prevent considering it again below.  */
+	  atlist = TYPE_ATTRIBUTES (type);
+	  expr = NULL_TREE;
+	  done = true;
+	}
+      else if (DECL_P (expr))
+	{
+	  /* Set TYPE to the DECL's type to process it on the next
+	     iteration.  */
+	  atlist = DECL_ATTRIBUTES (expr);
+	  type = TREE_TYPE (expr);
+	}
+      else
+	{
+	  atlist = TYPE_ATTRIBUTES (TREE_TYPE (expr));
+	  done = true;
+	}
+
+     /* True when an attribute with the sought name (though not necessarily
+	 with the sought attributes) has been found on the attribute chain.  */
+      bool found_attr = false;
+
+      /* For attribute aligned ignore the attribute list and consider
+	 the tree node itself instead.  */
+      if (type && !strcmp ("aligned", namestr))
+	atlist = NULL_TREE;
+
+      /* When clear, the first mismatched attribute argument results
+	 in failure.  Otherwise, the first matched attribute argument
+	 results in success.  */
+      bool attr_nonnull = !strcmp ("nonnull", namestr);
+      bool ignore_mismatches = attr_nonnull;
+
+      /* Iterate over the instances of the sought attribute on the DECL or
+	 TYPE (there may be multiple instances with different arguments).  */
+      for (; (atlist = lookup_attribute (namestr, atlist));
+	   found_attr = true, atlist = TREE_CHAIN (atlist))
+	{
+	  /* If there are no arguments to match the result is true except
+	     for nonnull where the attribute with no arguments must match.  */
+	  if (!TREE_VALUE (attr))
+	    return attr_nonnull ? !TREE_VALUE (atlist) : true;
+
+	  /* Attribute nonnull with no arguments subsumes all values of
+	     the attribute.  FIXME: This is overly broad since it only
+	     applies to pointer arguments, but querying non-pointer
+	     arguments is diagnosed.  */
+	  if (!TREE_VALUE (atlist) && attr_nonnull)
+	    return true;
+
+	  /* Iterate over the DECL or TYPE attribute argument's values.  */
+	  for (tree val = TREE_VALUE (atlist); val; val = TREE_CHAIN (val))
+	    {
+	      /* Iterate over the arguments in the sought attribute comparing
+		 their values to those specified for the DECL or TYPE.  */
+	      for (tree arg = TREE_VALUE (attr); arg; arg = TREE_CHAIN (arg))
+		{
+		  tree v1 = TREE_VALUE (val);
+		  tree v2 = TREE_VALUE (arg);
+		  if (v1 == v2)
+		    return true;
+
+		  if (!v1 || !v2)
+		    break;
+
+		  if (TREE_CODE (v1) == IDENTIFIER_NODE
+		      || TREE_CODE (v2) == IDENTIFIER_NODE)
+		    /* Two identifiers are the same if their values are
+		       equal (that's handled above).  Otherwise ther are
+		       either not the same or oneis not an identifier.  */
+		    return false;
+
+		  /* Convert to make them equality-comparable.  */
+		  v1 = convert (v1);
+		  v2 = convert (v2);
+
+		  /* A positive value indicates equality, negative means
+		     "don't know."  */
+		  if (simple_cst_equal (v1, v2) == 1)
+		    return true;
+
+		  if (!ignore_mismatches)
+		    break;
+		}
+	    }
+	}
+
+      if (!found_attr)
+	{
+	  /* Some attributes are encoded directly in the tree node.  */
+	  if (!strcmp ("aligned", namestr))
+	    {
+	      if (tree arg = TREE_VALUE (attr))
+		{
+		  arg = convert (TREE_VALUE (arg));
+		  if (expr && DECL_P (expr)
+		      && DECL_USER_ALIGN (expr)
+		      && tree_fits_uhwi_p (arg))
+		    found_match = DECL_ALIGN_UNIT (expr) == tree_to_uhwi (arg);
+		  else if (type && TYPE_USER_ALIGN (type))
+		    found_match = TYPE_ALIGN_UNIT (type) == tree_to_uhwi (arg);
+		}
+	      else if (expr && DECL_P (expr))
+		found_match = DECL_USER_ALIGN (expr);
+	      else if (type)
+		found_match = TYPE_USER_ALIGN (type);
+	    }
+	  else if (!strcmp ("const", namestr))
+	    {
+	      if (expr && DECL_P (expr))
+		found_match = TREE_READONLY (expr);
+	    }
+	  else if (!strcmp ("const", namestr))
+	    {
+	      if (expr && DECL_P (expr))
+		found_match = DECL_PURE_P (expr);
+	    }
+	  else if (!strcmp ("deprecated", namestr))
+	    {
+	      found_match = TREE_DEPRECATED (expr ? expr : type);
+	      if (found_match)
+		return true;
+	    }
+	  else if (!strcmp ("vector_size", namestr))
+	    {
+	      if (!type)
+		continue;
+
+	      /* Determine the base type from arrays, pointers, and such.
+		 Fail if the base type is not a vector.  */
+	      type = type_for_vector_size (type);
+	      if (!VECTOR_TYPE_P (type))
+		return false;
+
+	      if (tree arg = TREE_VALUE (attr))
+		{
+		  /* Compare the vector size argument for equality.  */
+		  arg = convert (TREE_VALUE (arg));
+		  return tree_int_cst_equal (arg, TYPE_SIZE_UNIT (type)) == 1;
+		}
+	      else
+		return true;
+	    }
+	  else if (!strcmp ("warn_if_not_aligned", namestr))
+	    {
+	      if (tree arg = TREE_VALUE (attr))
+		{
+		  arg = convert (TREE_VALUE (arg));
+		  if (expr && DECL_P (expr))
+		    found_match = (DECL_WARN_IF_NOT_ALIGN (expr)
+				   == tree_to_uhwi (arg) * BITS_PER_UNIT);
+		  else if (type)
+		    found_match = (TYPE_WARN_IF_NOT_ALIGN (type)
+				   == tree_to_uhwi (arg) * BITS_PER_UNIT);
+		}
+	      else if (expr && DECL_P (expr))
+		found_match = DECL_WARN_IF_NOT_ALIGN (expr);
+	      else if (type)
+		found_match = TYPE_WARN_IF_NOT_ALIGN (type);
+	    }
+	  else if (!strcmp ("transparent_union", namestr))
+	    {
+	      if (type)
+		found_match = TYPE_TRANSPARENT_AGGR (type) != 0;
+	    }
+	  else if (!strcmp ("mode", namestr))
+	    {
+	      /* Finally issue a warning for attributes that cannot
+		 be supported in this context.  Attribute mode is not
+		 added to a symbol and cannot be determined from it.  */
+	      warning_at (atloc, OPT_Wattributes,
+			  "%qs attribute not supported in "
+			  "%<__builtin_has_attribute%>", namestr);
+	      break;
+	    }
+	}
+    }
+  return found_match;
+}
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 10a8bc2..7d139be 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -372,6 +372,7 @@ const struct c_common_resword c_common_reswords[] =
     RID_BUILTIN_CALL_WITH_STATIC_CHAIN, D_CONLY },
   { "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY },
   { "__builtin_complex", RID_BUILTIN_COMPLEX, D_CONLY },
+  { "__builtin_has_attribute", RID_BUILTIN_HAS_ATTRIBUTE, 0 },
   { "__builtin_launder", RID_BUILTIN_LAUNDER, D_CXXONLY },
   { "__builtin_shuffle", RID_BUILTIN_SHUFFLE, 0 },
   { "__builtin_tgmath", RID_BUILTIN_TGMATH, D_CONLY },
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 9e86876..6447ecb 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -103,6 +103,7 @@ enum rid
   RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL,      RID_CHOOSE_EXPR,
   RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,	     RID_BUILTIN_SHUFFLE,
   RID_BUILTIN_TGMATH,
+  RID_BUILTIN_HAS_ATTRIBUTE,
   RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,
 
   /* TS 18661-3 keywords, in the same sequence as the TI_* values.  */
@@ -1333,6 +1334,7 @@ extern void maybe_suggest_missing_token_insertion (rich_location *richloc,
 						   enum cpp_ttype token_type,
 						   location_t prev_token_loc);
 extern tree braced_list_to_string (tree, tree);
+extern bool has_attribute (location_t, tree, tree, tree (*)(tree));
 
 #if CHECKING_P
 namespace selftest {
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 1f173fc..090eef2 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -1439,6 +1439,8 @@ static vec<tree, va_gc> *c_parser_expr_list (c_parser *, bool, bool,
 					     vec<tree, va_gc> **, location_t *,
 					     tree *, vec<location_t> *,
 					     unsigned int * = NULL);
+static struct c_expr c_parser_has_attribute_expression (c_parser *);
+
 static void c_parser_oacc_declare (c_parser *);
 static void c_parser_oacc_enter_exit_data (c_parser *, bool);
 static void c_parser_oacc_update (c_parser *);
@@ -4290,7 +4292,126 @@ c_parser_attribute_any_word (c_parser *parser)
    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?  */
+   allow identifiers declared as types to start the arguments?
+   When EXPECT_COMMA is true, expect the attribute to be preceded
+   by a comma and fail if it isn't.
+   When EMPTY_OK is true, allow and consume any number of consecutive
+   commas with no attributes in between.  */
+
+static tree
+c_parser_attribute (c_parser *parser, tree attrs,
+		    bool expect_comma = false, bool empty_ok = true)
+{
+  bool comma_first = c_parser_next_token_is (parser, CPP_COMMA);
+  if (!comma_first
+      && !c_parser_next_token_is (parser, CPP_NAME)
+      && !c_parser_next_token_is (parser, CPP_KEYWORD))
+    return NULL_TREE;
+
+  while (c_parser_next_token_is (parser, CPP_COMMA))
+    {
+      c_parser_consume_token (parser);
+      if (!empty_ok)
+	return attrs;
+    }
+
+  tree attr_name = c_parser_attribute_any_word (parser);
+  if (attr_name == NULL_TREE)
+    return NULL_TREE;
+
+  attr_name = canonicalize_attr_name (attr_name);
+  c_parser_consume_token (parser);
+
+  tree attr;
+  if (c_parser_next_token_is_not (parser, CPP_OPEN_PAREN))
+    {
+      if (expect_comma && !comma_first)
+	{
+	  /* A comma is missing between the last attribute on the chain
+	     and this one.  */
+	  c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+				     "expected %<)%>");
+	  return error_mark_node;
+	}
+      attr = build_tree_list (attr_name, NULL_TREE);
+      /* Add this attribute to the list.  */
+      attrs = chainon (attrs, attr);
+      return attrs;
+    }
+  c_parser_consume_token (parser);
+
+  vec<tree, va_gc> *expr_list;
+  tree attr_args;
+  /* 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.
+     In objective-c the identifier may be a classname.  */
+  if (c_parser_next_token_is (parser, CPP_NAME)
+      && (c_parser_peek_token (parser)->id_kind == C_ID_ID
+	  || (c_dialect_objc ()
+	      && c_parser_peek_token (parser)->id_kind
+	      == C_ID_CLASSNAME))
+      && ((c_parser_peek_2nd_token (parser)->type == CPP_COMMA)
+	  || (c_parser_peek_2nd_token (parser)->type
+	      == CPP_CLOSE_PAREN))
+      && (attribute_takes_identifier_p (attr_name)
+	  || (c_dialect_objc ()
+	      && c_parser_peek_token (parser)->id_kind
+	      == C_ID_CLASSNAME)))
+    {
+      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
+	{
+	  tree tree_list;
+	  c_parser_consume_token (parser);
+	  expr_list = c_parser_expr_list (parser, false, true,
+					  NULL, NULL, NULL, NULL);
+	  tree_list = build_tree_list_vec (expr_list);
+	  attr_args = tree_cons (NULL_TREE, arg1, tree_list);
+	  release_tree_vector (expr_list);
+	}
+    }
+  else
+    {
+      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	attr_args = NULL_TREE;
+      else
+	{
+	  expr_list = c_parser_expr_list (parser, false, true,
+					  NULL, NULL, NULL, NULL);
+	  attr_args = build_tree_list_vec (expr_list);
+	  release_tree_vector (expr_list);
+	}
+    }
+
+  attr = build_tree_list (attr_name, attr_args);
+  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+    c_parser_consume_token (parser);
+  else
+    {
+      parser->lex_untranslated_string = false;
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+				 "expected %<)%>");
+      return error_mark_node;
+    }
+
+  if (expect_comma && !comma_first)
+    {
+      /* A comma is missing between the last attribute on the chain
+	 and this one.  */
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+				 "expected %<)%>");
+      return error_mark_node;
+    }
+
+  /* Add this attribute to the list.  */
+  attrs = chainon (attrs, attr);
+  return attrs;
+}
 
 static tree
 c_parser_attributes (c_parser *parser)
@@ -4315,97 +4436,19 @@ c_parser_attributes (c_parser *parser)
 	  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))
+      /* Parse the attribute list.  Require a comma between successive
+	 (possibly empty) attributes.  */
+      for (bool expect_comma = false; ; expect_comma = true)
 	{
-	  tree attr, attr_name, attr_args;
-	  vec<tree, va_gc> *expr_list;
-	  if (c_parser_next_token_is (parser, CPP_COMMA))
-	    {
-	      c_parser_consume_token (parser);
-	      continue;
-	    }
-
-	  attr_name = c_parser_attribute_any_word (parser);
-	  if (attr_name == NULL)
+	  /* Parse a single attribute.  */
+	  tree attr = c_parser_attribute (parser, attrs, expect_comma);
+	  if (attr == error_mark_node)
+	    return attrs;
+	  if (!attr)
 	    break;
-	  attr_name = canonicalize_attr_name (attr_name);
-	  c_parser_consume_token (parser);
-	  if (c_parser_next_token_is_not (parser, CPP_OPEN_PAREN))
-	    {
-	      attr = build_tree_list (attr_name, NULL_TREE);
-	      /* Add this attribute to the list.  */
-	      attrs = chainon (attrs, attr);
-	      /* If the next token isn't a comma, we're done.  */
-	      if (!c_parser_next_token_is (parser, CPP_COMMA))
-		break;
-	      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.  
-	     In objective-c the identifier may be a classname.  */
-	  if (c_parser_next_token_is (parser, CPP_NAME)
-	      && (c_parser_peek_token (parser)->id_kind == C_ID_ID
-		  || (c_dialect_objc ()
-		      && c_parser_peek_token (parser)->id_kind
-			 == C_ID_CLASSNAME))
-	      && ((c_parser_peek_2nd_token (parser)->type == CPP_COMMA)
-		  || (c_parser_peek_2nd_token (parser)->type
-		      == CPP_CLOSE_PAREN))
-	      && (attribute_takes_identifier_p (attr_name)
-		  || (c_dialect_objc ()
-		      && c_parser_peek_token (parser)->id_kind
-			 == C_ID_CLASSNAME)))
-	    {
-	      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
-		{
-		  tree tree_list;
-		  c_parser_consume_token (parser);
-		  expr_list = c_parser_expr_list (parser, false, true,
-						  NULL, NULL, NULL, NULL);
-		  tree_list = build_tree_list_vec (expr_list);
-		  attr_args = tree_cons (NULL_TREE, arg1, tree_list);
-		  release_tree_vector (expr_list);
-		}
-	    }
-	  else
-	    {
-	      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
-		attr_args = NULL_TREE;
-	      else
-		{
-		  expr_list = c_parser_expr_list (parser, false, true,
-						  NULL, NULL, NULL, NULL);
-		  attr_args = build_tree_list_vec (expr_list);
-		  release_tree_vector (expr_list);
-		}
-	    }
+	  attrs = attr;
+      }
 
-	  attr = build_tree_list (attr_name, attr_args);
-	  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
-	    c_parser_consume_token (parser);
-	  else
-	    {
-	      parser->lex_untranslated_string = false;
-	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
-					 "expected %<)%>");
-	      return attrs;
-	    }
-	  /* Add this attribute to the list.  */
-	  attrs = chainon (attrs, attr);
-	  /* If the next token isn't a comma, we're done.  */
-	  if (!c_parser_next_token_is (parser, CPP_COMMA))
-	    break;
-	}
       /* Look for the two `)' tokens.  */
       if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
 	c_parser_consume_token (parser);
@@ -7240,6 +7283,8 @@ c_parser_unary_expression (c_parser *parser)
 	  return c_parser_sizeof_expression (parser);
 	case RID_ALIGNOF:
 	  return c_parser_alignof_expression (parser);
+	case RID_BUILTIN_HAS_ATTRIBUTE:
+	  return c_parser_has_attribute_expression (parser);
 	case RID_EXTENSION:
 	  c_parser_consume_token (parser);
 	  ext = disable_extension_diagnostics ();
@@ -7439,6 +7484,123 @@ c_parser_alignof_expression (c_parser *parser)
     }
 }
 
+/* Parse the __builtin_has_attribute ([expr|type], attribute-spec)
+   expression.  */
+
+static struct c_expr
+c_parser_has_attribute_expression (c_parser *parser)
+{
+  gcc_assert (c_parser_next_token_is_keyword (parser,
+					      RID_BUILTIN_HAS_ATTRIBUTE));
+  c_parser_consume_token (parser);
+
+  c_inhibit_evaluation_warnings++;
+
+  matching_parens parens;
+  if (!parens.require_open (parser))
+    {
+      c_inhibit_evaluation_warnings--;
+      in_typeof--;
+
+      struct c_expr result;
+      result.set_error ();
+      result.original_code = ERROR_MARK;
+      result.original_type = NULL;
+      return result;
+    }
+
+  /* Treat the type argument the same way as in typeof for the purposes
+     of warnings.  FIXME: Generalize this so the warning refers to
+     __builtin_has_attribute rather than typeof.  */
+  in_typeof++;
+
+  /* The first operand: one of DECL, EXPR, or TYPE.  */
+  tree oper = NULL_TREE;
+  if (c_parser_next_tokens_start_typename (parser, cla_prefer_id))
+    {
+      struct c_type_name *tname = c_parser_type_name (parser);
+      in_typeof--;
+      if (tname)
+	{
+	  oper = groktypename (tname, NULL, NULL);
+	  pop_maybe_used (variably_modified_type_p (oper, NULL_TREE));
+	}
+    }
+  else
+    {
+      struct c_expr cexpr = c_parser_expr_no_commas (parser, NULL);
+      c_inhibit_evaluation_warnings--;
+      in_typeof--;
+      if (cexpr.value != error_mark_node)
+	{
+	  mark_exp_read (cexpr.value);
+	  oper = cexpr.value;
+	  tree etype = TREE_TYPE (oper);
+	  bool was_vm = variably_modified_type_p (etype, NULL_TREE);
+	  /* This is returned with the type so that when the type is
+	     evaluated, this can be evaluated.  */
+	  if (was_vm)
+	    oper = c_fully_fold (oper, false, NULL);
+	  pop_maybe_used (was_vm);
+	}
+    }
+
+  struct c_expr result;
+  result.original_code = ERROR_MARK;
+  result.original_type = NULL;
+
+  if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>"))
+    {
+      /* Consume the closing parenthesis if that's the next token
+	 in the likely case the built-in was invoked with fewer
+	 than two arguments.  */
+      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	c_parser_consume_token (parser);
+      c_inhibit_evaluation_warnings--;
+      result.set_error ();
+      return result;
+    }
+
+  parser->lex_untranslated_string = true;
+
+  location_t atloc = c_parser_peek_token (parser)->location;
+  /* Parse a single attribute.  Require no leading comma and do not
+     allow empty attributes.  */
+  tree attr = c_parser_attribute (parser, NULL_TREE, false, false);
+
+  parser->lex_untranslated_string = false;
+
+  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+    c_parser_consume_token (parser);
+  else
+    {
+      c_parser_error (parser, "expected identifier");
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+
+      result.set_error ();
+      return result;
+    }
+
+  if (!attr)
+    {
+      error_at (atloc, "expected identifier");
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+				 "expected %<)%>");
+      result.set_error ();
+      return result;
+    }
+
+  result.original_code = INTEGER_CST;
+  result.original_type = boolean_type_node;
+
+  if (has_attribute (atloc, oper, attr, default_conversion))
+    result.value = boolean_true_node;
+  else
+    result.value =  boolean_false_node;
+
+  return result;
+}
+
 /* Helper function to read arguments of builtins which are interfaces
    for the middle-end nodes like COMPLEX_EXPR, VEC_PERM_EXPR and
    others.  The name of the builtin is passed using BNAME parameter.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index efbdad8..8e8af94 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6428,6 +6428,7 @@ extern int parm_index                           (tree);
 extern tree vtv_start_verification_constructor_init_function (void);
 extern tree vtv_finish_verification_constructor_init_function (tree);
 extern bool cp_omp_mappable_type		(tree);
+extern void cp_check_const_attributes (tree);
 
 /* in error.c */
 extern const char *type_as_string		(tree, int);
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index a5ad0ee..baf303f 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -1378,7 +1378,7 @@ cp_reconstruct_complex_type (tree type, tree bottom)
 /* Replaces any constexpr expression that may be into the attributes
    arguments with their reduced value.  */
 
-static void
+void
 cp_check_const_attributes (tree attributes)
 {
   if (attributes == error_mark_node)
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 6696f17..f07df78 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2049,6 +2049,8 @@ static cp_expr cp_parser_unary_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false, bool = false);
 static enum tree_code cp_parser_unary_operator
   (cp_token *);
+static tree cp_parser_has_attribute_expression
+  (cp_parser *);
 static tree cp_parser_new_expression
   (cp_parser *);
 static vec<tree, va_gc> *cp_parser_new_placement
@@ -2380,7 +2382,7 @@ static tree cp_parser_attributes_opt
 static tree cp_parser_gnu_attributes_opt
   (cp_parser *);
 static tree cp_parser_gnu_attribute_list
-  (cp_parser *);
+  (cp_parser *, bool = false);
 static tree cp_parser_std_attribute
   (cp_parser *, tree);
 static tree cp_parser_std_attribute_spec
@@ -8065,6 +8067,9 @@ cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 	    return ret_expr;
 	  }
 
+	case RID_BUILTIN_HAS_ATTRIBUTE:
+	  return cp_parser_has_attribute_expression (parser);
+
 	case RID_NEW:
 	  return cp_parser_new_expression (parser);
 
@@ -8362,6 +8367,129 @@ cp_parser_unary_operator (cp_token* token)
     }
 }
 
+/* Parse a __builtin_has_attribute([expr|type], attribute-spec) expression.
+   Returns a representation of the expression.  */
+
+static tree
+cp_parser_has_attribute_expression (cp_parser *parser)
+{
+  location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
+
+  /* Consume the __builtin_has_attribute token.  */
+  cp_lexer_consume_token (parser->lexer);
+
+  matching_parens parens;
+  if (!parens.require_open (parser))
+    return error_mark_node;
+
+  /* Types cannot be defined in a `sizeof' expression.  Save away the
+     old message.  */
+  const char *saved_message = parser->type_definition_forbidden_message;
+  /* And create the new one.  */
+  const int kwd = RID_BUILTIN_HAS_ATTRIBUTE;
+  char *tmp = concat ("types may not be defined in %<",
+		      IDENTIFIER_POINTER (ridpointers[kwd]),
+		      "%> expressions", NULL);
+  parser->type_definition_forbidden_message = tmp;
+
+  /* The restrictions on constant-expressions do not apply inside
+     sizeof expressions.  */
+  bool saved_integral_constant_expression_p
+    = parser->integral_constant_expression_p;
+  bool saved_non_integral_constant_expression_p
+    = parser->non_integral_constant_expression_p;
+  parser->integral_constant_expression_p = false;
+
+  /* Do not actually evaluate the expression.  */
+  ++cp_unevaluated_operand;
+  ++c_inhibit_evaluation_warnings;
+
+  tree oper = NULL_TREE;
+
+  /* We can't be sure yet whether we're looking at a type-id or an
+     expression.  */
+  cp_parser_parse_tentatively (parser);
+
+  bool saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p;
+  parser->in_type_id_in_expr_p = true;
+  /* Look for the type-id.  */
+  oper = cp_parser_type_id (parser);
+  parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
+
+  if (cp_parser_parse_definitely (parser))
+    {
+      /* If all went well, set OPER to the type.  */
+      cp_decl_specifier_seq decl_specs;
+
+      /* Build a trivial decl-specifier-seq.  */
+      clear_decl_specs (&decl_specs);
+      decl_specs.type = oper;
+    }
+
+  /* If the type-id production did not work out, then we must be
+     looking at the unary-expression production.  */
+  if (!oper || oper == error_mark_node)
+    oper = cp_parser_unary_expression (parser);
+
+  /* Go back to evaluating expressions.  */
+  --cp_unevaluated_operand;
+  --c_inhibit_evaluation_warnings;
+
+  /* Free the message we created.  */
+  free (tmp);
+  /* And restore the old one.  */
+  parser->type_definition_forbidden_message = saved_message;
+  parser->integral_constant_expression_p
+    = saved_integral_constant_expression_p;
+  parser->non_integral_constant_expression_p
+    = saved_non_integral_constant_expression_p;
+
+  /* Consume the comma if it's there.  */
+  if (!cp_parser_require (parser, CPP_COMMA, RT_COMMA))
+    {
+      cp_parser_skip_to_closing_parenthesis (parser, false, false,
+					     /*consume_paren=*/true);
+      return error_mark_node;
+    }
+
+  /* Parse the attribute specification.  */
+  bool ret = false;
+  location_t atloc = cp_lexer_peek_token (parser->lexer)->location;
+  if (tree attr = cp_parser_gnu_attribute_list (parser, /*exactly_one=*/true))
+    {
+      if (oper != error_mark_node)
+	{
+	  /* Fold constant expressions used in attributes first.  */
+	  cp_check_const_attributes (attr);
+
+	  /* Finally, see if OPER has been declared with ATTR.  */
+	  ret = has_attribute (atloc, oper, attr, default_conversion);
+	}
+
+      parens.require_close (parser);
+    }
+  else
+    {
+      error_at (atloc, "expected identifier");
+      cp_parser_skip_to_closing_parenthesis (parser, true, false, true);
+    }
+
+  /* Construct a location e.g. :
+     __builtin_has_attribute (oper, attr)
+     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     with start == caret at the start of the built-in token,
+     and with the endpoint at the final closing paren.  */
+  location_t finish_loc
+    = cp_lexer_previous_token (parser->lexer)->location;
+  location_t compound_loc
+    = make_location (start_loc, start_loc, finish_loc);
+
+  cp_expr ret_expr (ret ? boolean_true_node : boolean_false_node);
+  ret_expr.set_location (compound_loc);
+  ret_expr = ret_expr.maybe_add_location_wrapper ();
+  return ret_expr;
+}
+
 /* Parse a new-expression.
 
    new-expression:
@@ -25154,7 +25282,7 @@ cp_parser_gnu_attributes_opt (cp_parser* parser)
    the arguments, if any.  */
 
 static tree
-cp_parser_gnu_attribute_list (cp_parser* parser)
+cp_parser_gnu_attribute_list (cp_parser* parser, bool exactly_one /* = false */)
 {
   tree attribute_list = NULL_TREE;
   bool save_translate_strings_p = parser->translate_strings_p;
@@ -25221,9 +25349,9 @@ cp_parser_gnu_attribute_list (cp_parser* parser)
 
 	  token = cp_lexer_peek_token (parser->lexer);
 	}
-      /* Now, look for more attributes.  If the next token isn't a
-	 `,', we're done.  */
-      if (token->type != CPP_COMMA)
+      /* Unless EXACTLY_ONE is set look for more attributes.
+	 If the next token isn't a `,', we're done.  */
+      if (exactly_one || token->type != CPP_COMMA)
 	break;
 
       /* Consume the comma and keep going.  */
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8ffb0cd..05eb0ac 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -11069,6 +11069,7 @@ is called and the @var{flag} argument passed to it.
 @findex __builtin_call_with_static_chain
 @findex __builtin_extend_pointer
 @findex __builtin_fpclassify
+@findex __builtin_has_attribute
 @findex __builtin_isfinite
 @findex __builtin_isnormal
 @findex __builtin_isgreater
@@ -11726,6 +11727,45 @@ check its compatibility with @var{size}.
 
 @end deftypefn
 
+@deftypefn {Built-in Function} bool __builtin_has_attribute (@var{type-or-expression}, @var{attribute})
+The @code{__builtin_has_attribute} function evaluates to an integer constant
+expression equal to @code{true} if the symbol or type referenced by
+the @var{type-or-expression} argument has been declared with
+the @var{attribute} referenced by the second argument.  Neither argument
+is evaluated.  The @var{type-or-expression} argument is subject to the same
+restrictions as the argument to @code{typeof} (@pxref{Typeof}).  The
+@var{attribute} argument is an attribute name optionally followed by
+arguments.  Both forms of attribute names---with and without double
+leading and trailing underscores---are recognized.  See @xref{Attribute Syntax}
+for details.  When no attribute arguments are specified for an attribute
+that expects one or more arguments the function returns @code{true} if
+@var{type-or-expression} has been declared with the attribute regardless
+of the attribute argument values.  Arguments provided for an attribute
+that expects some are validated and matched up to the provided number.
+The function returns true if all provided arguments match.  For example,
+the first call to the function below evaluates to @code{true} because
+@code{x} is declared with the @code{aligned} attribute but the second
+call evaluates to @code{false} because @code{x} is declared
+@code{aligned (8)} and not @code{aligned (4)}.
+
+@smallexample
+__attribute__ ((aligned (8))) int x;
+_Static_assert (__builtin_has_attribute (x, aligned), "aligned");
+_Static_assert (!__builtin_has_attribute (x, aligned (4)), "aligned (4)");
+@end smallexample
+
+Due to a limitation the @code{__builtin_has_attribute} function returns
+@code{false} for the @code{mode} attribute even if the type or variable
+referenced by the @var{type-or-expression} argument was declared with one.
+The function is also not supported with labels, and in C with enumerators.
+
+Note that unlike the @code{__has_attribute} preprocessor operator which
+is suitable for use in @code{#if} preprocessing directives
+@code{__builtin_has_attribute} is an intrinsic function that is not
+recognized in such contexts.
+
+@end deftypefn
+
 @deftypefn {Built-in Function} @var{type} __builtin_speculation_safe_value (@var{type} val, @var{type} failval)
 
 This built-in function can be used to help mitigate against unsafe
Jason Merrill Oct. 24, 2018, 6:33 p.m. UTC | #11
On 10/23/18 6:08 PM, Martin Sebor wrote:
> +  if (cp_parser_parse_definitely (parser))
> +    {
> +      /* If all went well, set OPER to the type.  */
> +      cp_decl_specifier_seq decl_specs;
> +
> +      /* Build a trivial decl-specifier-seq.  */
> +      clear_decl_specs (&decl_specs);
> +      decl_specs.type = oper;
> +    }
> +
> +  /* If the type-id production did not work out, then we must be
> +     looking at the unary-expression production.  */
> +  if (!oper || oper == error_mark_node)
> +    oper = cp_parser_unary_expression (parser);

The decl_specs stuff is still unneeded; I think all this can be 
simplified to

if (!cp_parser_parse_definitely (parser))
   oper = cp_parser_unary_expression (parser);

The C++ bits are OK with that change.

You probably want a follow-on patch to handle 
template-argument-dependent cases, like when the alignment argument 
depends on a template parameter.

Jason
Martin Sebor Oct. 31, 2018, 4:27 p.m. UTC | #12
Ping: https://gcc.gnu.org/ml/gcc-patches/2018-10/msg01473.html

With the C++ bits approved I'm still looking for a review/approval
of the remaining changes: the C front end and the shared c-family
handling of the new built-in.

On 10/23/2018 04:08 PM, Martin Sebor wrote:
> On 10/22/2018 04:08 PM, Jason Merrill wrote:
>> On 10/13/18 8:19 PM, Martin Sebor wrote:
>>> +  oper = cp_parser_type_id (parser);
>>> +  parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
>>> +
>>> +  if (cp_parser_parse_definitely (parser))
>>> +    {
>>> +      /* If all went well, set OPER to the type.  */
>>> +      cp_decl_specifier_seq decl_specs;
>>> +
>>> +      /* Build a trivial decl-specifier-seq.  */
>>> +      clear_decl_specs (&decl_specs);
>>> +      decl_specs.type = oper;
>>> +
>>> +      /* Call grokdeclarator to figure out what type this is.  */
>>> +      oper = grokdeclarator (NULL,
>>> +                 &decl_specs,
>>> +                 TYPENAME,
>>> +                 /*initialized=*/0,
>>> +                 /*attrlist=*/NULL);
>>> +    }
>>
>> Doesn't grokdeclarator here give you back the same type you already had
>> from cp_parser_type_id?  The latter already calls grokdeclarator.
>>
>> I don't know why cp_parser_sizeof_operand does this, either.  Try
>> removing it from both places?
>
> You're right, the call in cp_parser_has_attribute_expression
> was unnecessary.  cp_parser_sizeof_operand still needs it.
>
>>
>>> +  /* Consume the comma if it's there.  */
>>> +  if (!cp_parser_require (parser, CPP_COMMA, RT_COMMA))
>>> +    {
>>> +      parens.require_close (parser);
>>
>> I think you want cp_parser_skip_to_closing_parenthesis for error
>> recovery, rather than require_close.
>
> Thanks, the error messages look slightly better that way (there
> are fewer of them), although still not as good as in C or other
> compilers in some cases.
>
>>
>>> +  if (tree attr = cp_parser_gnu_attribute_list (parser,
>>> /*exactly_one=*/true))
>>> +    {
>>> +      if (oper != error_mark_node)
>>> +    {
>>> +      /* Fold constant expressions used in attributes first.  */
>>> +      cp_check_const_attributes (attr);
>>> +
>>> +      /* Finally, see if OPER has been declared with ATTR.  */
>>> +      ret = has_attribute (atloc, oper, attr, default_conversion);
>>> +    }
>>> +    }
>>> +  else
>>> +    {
>>> +      error_at (atloc, "expected identifier");
>>> +      cp_parser_skip_to_closing_parenthesis (parser, true, false,
>>> false);
>>> +    }
>>> +
>>> +  parens.require_close (parser);
>>
>> I think the require_close should be in the valid case, since *skip*
>> already consumes a closing paren.
>
> Ah, I need to make it consume the paren by passing true as the last
> argument.  With that it works.
>
>>
>>> +is valuated.  The @var{type-or-expression} argument is subject to the
>>> same
>>
>> evaluated
>
> Thanks for the review.
>
> Attached is an updated patch with the fixes above.
>
> Martin
>
Jeff Law Nov. 9, 2018, 7:59 p.m. UTC | #13
On 10/31/18 10:27 AM, Martin Sebor wrote:
> Ping: https://gcc.gnu.org/ml/gcc-patches/2018-10/msg01473.html
> 
> With the C++ bits approved I'm still looking for a review/approval
> of the remaining changes: the C front end and the shared c-family
> handling of the new built-in.
I thought I acked those with just a couple minor doc nits:

> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 8ffb0cd..dcf4747 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -2649,8 +2649,9 @@ explicit @code{externally_visible} attributes
are still necessary.
>  @cindex @code{flatten} function attribute
>  Generally, inlining into a function is limited.  For a function
marked with
>  this attribute, every call inside this function is inlined, if possible.
> -Whether the function itself is considered for inlining depends on its
size and
> -the current inlining parameters.
> +Functions declared with attribute @code{noinline} and similar are not
> +inlined.  Whether the function itself is considered for inlining depends
> +on its size and the current inlining parameters.
Guessing this was from another doc patch that I think has already been
approved


> @@ -11726,6 +11728,33 @@ check its compatibility with @var{size}.
>
>  @end deftypefn
>
> +@deftypefn {Built-in Function} bool __builtin_has_attribute
(@var{type-or-expression}, @var{attribute})
> +The @code{__builtin_has_attribute} function evaluates to an integer
constant
> +expression equal to @code{true} if the symbol or type referenced by
> +the @var{type-or-expression} argument has been declared with
> +the @var{attribute} referenced by the second argument.  Neither argument
> +is valuated.  The @var{type-or-expression} argument is subject to the
same
s/valuated/evaluated/ ?


Did the implementation change significantly requiring another review
iteration?

jeff
Martin Sebor Nov. 9, 2018, 11:43 p.m. UTC | #14
On 11/09/2018 12:59 PM, Jeff Law wrote:
> On 10/31/18 10:27 AM, Martin Sebor wrote:
>> Ping: https://gcc.gnu.org/ml/gcc-patches/2018-10/msg01473.html
>>
>> With the C++ bits approved I'm still looking for a review/approval
>> of the remaining changes: the C front end and the shared c-family
>> handling of the new built-in.
> I thought I acked those with just a couple minor doc nits:

I don't see a formal approval for the rest in my Inbox or in
the archive.

>> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
>> index 8ffb0cd..dcf4747 100644
>> --- a/gcc/doc/extend.texi
>> +++ b/gcc/doc/extend.texi
>> @@ -2649,8 +2649,9 @@ explicit @code{externally_visible} attributes
> are still necessary.
>>  @cindex @code{flatten} function attribute
>>  Generally, inlining into a function is limited.  For a function
> marked with
>>  this attribute, every call inside this function is inlined, if possible.
>> -Whether the function itself is considered for inlining depends on its
> size and
>> -the current inlining parameters.
>> +Functions declared with attribute @code{noinline} and similar are not
>> +inlined.  Whether the function itself is considered for inlining depends
>> +on its size and the current inlining parameters.
> Guessing this was from another doc patch that I think has already been
> approved

Yes.  It shouldn't be in the latest patch at the link above.

>> @@ -11726,6 +11728,33 @@ check its compatibility with @var{size}.
>>
>>  @end deftypefn
>>
>> +@deftypefn {Built-in Function} bool __builtin_has_attribute
> (@var{type-or-expression}, @var{attribute})
>> +The @code{__builtin_has_attribute} function evaluates to an integer
> constant
>> +expression equal to @code{true} if the symbol or type referenced by
>> +the @var{type-or-expression} argument has been declared with
>> +the @var{attribute} referenced by the second argument.  Neither argument
>> +is valuated.  The @var{type-or-expression} argument is subject to the
> same
> s/valuated/evaluated/ ?

This should also be fixed in the latest patch at the link above.

> Did the implementation change significantly requiring another review
> iteration?

I don't think it changed too significantly between the last two
revisions but I don't have a record of anyone having approved
the C FE and the middle-end bits.  (Sorry if I missed it.) Other
than this response from you all I see in the archive is this:

   https://gcc.gnu.org/ml/gcc-patches/2018-10/msg00606.html

Please let me if the last revision is okay to commit.

Thanks
Martin
Martin Sebor Nov. 16, 2018, 3:06 a.m. UTC | #15
Ping: https://gcc.gnu.org/ml/gcc-patches/2018-10/msg01473.html
(Still looking for an approval.)

On 11/09/2018 04:43 PM, Martin Sebor wrote:
> On 11/09/2018 12:59 PM, Jeff Law wrote:
>> On 10/31/18 10:27 AM, Martin Sebor wrote:
>>> Ping: https://gcc.gnu.org/ml/gcc-patches/2018-10/msg01473.html
>>>
>>> With the C++ bits approved I'm still looking for a review/approval
>>> of the remaining changes: the C front end and the shared c-family
>>> handling of the new built-in.
>> I thought I acked those with just a couple minor doc nits:
>
> I don't see a formal approval for the rest in my Inbox or in
> the archive.
>
>>> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
>>> index 8ffb0cd..dcf4747 100644
>>> --- a/gcc/doc/extend.texi
>>> +++ b/gcc/doc/extend.texi
>>> @@ -2649,8 +2649,9 @@ explicit @code{externally_visible} attributes
>> are still necessary.
>>>  @cindex @code{flatten} function attribute
>>>  Generally, inlining into a function is limited.  For a function
>> marked with
>>>  this attribute, every call inside this function is inlined, if
>>> possible.
>>> -Whether the function itself is considered for inlining depends on its
>> size and
>>> -the current inlining parameters.
>>> +Functions declared with attribute @code{noinline} and similar are not
>>> +inlined.  Whether the function itself is considered for inlining
>>> depends
>>> +on its size and the current inlining parameters.
>> Guessing this was from another doc patch that I think has already been
>> approved
>
> Yes.  It shouldn't be in the latest patch at the link above.
>
>>> @@ -11726,6 +11728,33 @@ check its compatibility with @var{size}.
>>>
>>>  @end deftypefn
>>>
>>> +@deftypefn {Built-in Function} bool __builtin_has_attribute
>> (@var{type-or-expression}, @var{attribute})
>>> +The @code{__builtin_has_attribute} function evaluates to an integer
>> constant
>>> +expression equal to @code{true} if the symbol or type referenced by
>>> +the @var{type-or-expression} argument has been declared with
>>> +the @var{attribute} referenced by the second argument.  Neither
>>> argument
>>> +is valuated.  The @var{type-or-expression} argument is subject to the
>> same
>> s/valuated/evaluated/ ?
>
> This should also be fixed in the latest patch at the link above.
>
>> Did the implementation change significantly requiring another review
>> iteration?
>
> I don't think it changed too significantly between the last two
> revisions but I don't have a record of anyone having approved
> the C FE and the middle-end bits.  (Sorry if I missed it.) Other
> than this response from you all I see in the archive is this:
>
>   https://gcc.gnu.org/ml/gcc-patches/2018-10/msg00606.html
>
> Please let me if the last revision is okay to commit.
>
> Thanks
> Martin
Jeff Law Nov. 16, 2018, 4:07 p.m. UTC | #16
On 11/15/18 8:06 PM, Martin Sebor wrote:
> Ping: https://gcc.gnu.org/ml/gcc-patches/2018-10/msg01473.html
> (Still looking for an approval.)
> 
> On 11/09/2018 04:43 PM, Martin Sebor wrote:
>> On 11/09/2018 12:59 PM, Jeff Law wrote:
>>> On 10/31/18 10:27 AM, Martin Sebor wrote:
>>>> Ping: https://gcc.gnu.org/ml/gcc-patches/2018-10/msg01473.html
>>>>
>>>> With the C++ bits approved I'm still looking for a review/approval
>>>> of the remaining changes: the C front end and the shared c-family
>>>> handling of the new built-in.
>>> I thought I acked those with just a couple minor doc nits:
>>
>> I don't see a formal approval for the rest in my Inbox or in
>> the archive.
>>
>>>> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
>>>> index 8ffb0cd..dcf4747 100644
>>>> --- a/gcc/doc/extend.texi
>>>> +++ b/gcc/doc/extend.texi
>>>> @@ -2649,8 +2649,9 @@ explicit @code{externally_visible} attributes
>>> are still necessary.
>>>>  @cindex @code{flatten} function attribute
>>>>  Generally, inlining into a function is limited.  For a function
>>> marked with
>>>>  this attribute, every call inside this function is inlined, if
>>>> possible.
>>>> -Whether the function itself is considered for inlining depends on its
>>> size and
>>>> -the current inlining parameters.
>>>> +Functions declared with attribute @code{noinline} and similar are not
>>>> +inlined.  Whether the function itself is considered for inlining
>>>> depends
>>>> +on its size and the current inlining parameters.
>>> Guessing this was from another doc patch that I think has already been
>>> approved
>>
>> Yes.  It shouldn't be in the latest patch at the link above.
>>
>>>> @@ -11726,6 +11728,33 @@ check its compatibility with @var{size}.
>>>>
>>>>  @end deftypefn
>>>>
>>>> +@deftypefn {Built-in Function} bool __builtin_has_attribute
>>> (@var{type-or-expression}, @var{attribute})
>>>> +The @code{__builtin_has_attribute} function evaluates to an integer
>>> constant
>>>> +expression equal to @code{true} if the symbol or type referenced by
>>>> +the @var{type-or-expression} argument has been declared with
>>>> +the @var{attribute} referenced by the second argument.  Neither
>>>> argument
>>>> +is valuated.  The @var{type-or-expression} argument is subject to the
>>> same
>>> s/valuated/evaluated/ ?
>>
>> This should also be fixed in the latest patch at the link above.
>>
>>> Did the implementation change significantly requiring another review
>>> iteration?
>>
>> I don't think it changed too significantly between the last two
>> revisions but I don't have a record of anyone having approved
>> the C FE and the middle-end bits.  (Sorry if I missed it.) Other
>> than this response from you all I see in the archive is this:
>>
>>   https://gcc.gnu.org/ml/gcc-patches/2018-10/msg00606.html
>>
>> Please let me if the last revision is okay to commit.
Yes.  It's fine to commit.
jeff
Christophe Lyon Nov. 22, 2018, 12:28 p.m. UTC | #17
On Sat, 10 Nov 2018 at 00:43, Martin Sebor <msebor@gmail.com> wrote:
>
> On 11/09/2018 12:59 PM, Jeff Law wrote:
> > On 10/31/18 10:27 AM, Martin Sebor wrote:
> >> Ping: https://gcc.gnu.org/ml/gcc-patches/2018-10/msg01473.html
> >>
> >> With the C++ bits approved I'm still looking for a review/approval
> >> of the remaining changes: the C front end and the shared c-family
> >> handling of the new built-in.
> > I thought I acked those with just a couple minor doc nits:
>
> I don't see a formal approval for the rest in my Inbox or in
> the archive.
>
> >> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> >> index 8ffb0cd..dcf4747 100644
> >> --- a/gcc/doc/extend.texi
> >> +++ b/gcc/doc/extend.texi
> >> @@ -2649,8 +2649,9 @@ explicit @code{externally_visible} attributes
> > are still necessary.
> >>  @cindex @code{flatten} function attribute
> >>  Generally, inlining into a function is limited.  For a function
> > marked with
> >>  this attribute, every call inside this function is inlined, if possible.
> >> -Whether the function itself is considered for inlining depends on its
> > size and
> >> -the current inlining parameters.
> >> +Functions declared with attribute @code{noinline} and similar are not
> >> +inlined.  Whether the function itself is considered for inlining depends
> >> +on its size and the current inlining parameters.
> > Guessing this was from another doc patch that I think has already been
> > approved
>
> Yes.  It shouldn't be in the latest patch at the link above.
>
> >> @@ -11726,6 +11728,33 @@ check its compatibility with @var{size}.
> >>
> >>  @end deftypefn
> >>
> >> +@deftypefn {Built-in Function} bool __builtin_has_attribute
> > (@var{type-or-expression}, @var{attribute})
> >> +The @code{__builtin_has_attribute} function evaluates to an integer
> > constant
> >> +expression equal to @code{true} if the symbol or type referenced by
> >> +the @var{type-or-expression} argument has been declared with
> >> +the @var{attribute} referenced by the second argument.  Neither argument
> >> +is valuated.  The @var{type-or-expression} argument is subject to the
> > same
> > s/valuated/evaluated/ ?
>
> This should also be fixed in the latest patch at the link above.
>
> > Did the implementation change significantly requiring another review
> > iteration?
>
> I don't think it changed too significantly between the last two
> revisions but I don't have a record of anyone having approved
> the C FE and the middle-end bits.  (Sorry if I missed it.) Other
> than this response from you all I see in the archive is this:
>
>    https://gcc.gnu.org/ml/gcc-patches/2018-10/msg00606.html
>
> Please let me if the last revision is okay to commit.
>

Hi,

It seems you committed this yesterday as r266335, and I have noticed
new failures:
on both aarch64 and arm:
FAIL: c-c++-common/builtin-has-attribute-3.c  -Wc++-compat  (test for
excess errors)

gcc.log says:
Excess errors:
/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:11:25: error:
alignment for 'faligned_1' must be at least 4
/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:12:25: error:
alignment for 'faligned_2' must be at least 4
/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:39:3: error:
alignment for '__builtin_has_attribute_tmp.' must be at least 4
/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:40:3: error:
alignment for '__builtin_has_attribute_tmp.' must be at least 4
/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:47:3: error:
alignment for '__builtin_has_attribute_tmp.' must be at least 4
/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:48:3: error:
alignment for '__builtin_has_attribute_tmp.' must be at least 4
/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:50:3: error:
size of array 'Assert' is negative
/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:52:3: error:
alignment for '__builtin_has_attribute_tmp.' must be at least 4
/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:52:3: error:
size of array 'Assert' is negative
/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:53:3: error:
alignment for '__builtin_has_attribute_tmp.' must be at least 4
/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:56:3: error:
size of array 'Assert' is negative
/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:58:3: error:
alignment for '__builtin_has_attribute_tmp.' must be at least 4
/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:59:3: error:
alignment for '__builtin_has_attribute_tmp.' must be at least 4
/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:59:3: error:
size of array 'Assert' is negative

on arm only:
gcc.dg/builtin-has-attribute.c (test for excess errors)
gdb.log says:
Excess errors:
/gcc/testsuite/gcc.dg/builtin-has-attribute.c:12:15: error: size of
array 'Assert' is negative

Christophe

> Thanks
> Martin
Rainer Orth Nov. 22, 2018, 1:09 p.m. UTC | #18
Hi Christophe,

> On Sat, 10 Nov 2018 at 00:43, Martin Sebor <msebor@gmail.com> wrote:
>>
>> On 11/09/2018 12:59 PM, Jeff Law wrote:
>> > On 10/31/18 10:27 AM, Martin Sebor wrote:
>> >> Ping: https://gcc.gnu.org/ml/gcc-patches/2018-10/msg01473.html
>> >>
>> >> With the C++ bits approved I'm still looking for a review/approval
>> >> of the remaining changes: the C front end and the shared c-family
>> >> handling of the new built-in.
>> > I thought I acked those with just a couple minor doc nits:
>>
>> I don't see a formal approval for the rest in my Inbox or in
>> the archive.
>>
>> >> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
>> >> index 8ffb0cd..dcf4747 100644
>> >> --- a/gcc/doc/extend.texi
>> >> +++ b/gcc/doc/extend.texi
>> >> @@ -2649,8 +2649,9 @@ explicit @code{externally_visible} attributes
>> > are still necessary.
>> >>  @cindex @code{flatten} function attribute
>> >>  Generally, inlining into a function is limited.  For a function
>> > marked with
>> >>  this attribute, every call inside this function is inlined, if possible.
>> >> -Whether the function itself is considered for inlining depends on its
>> > size and
>> >> -the current inlining parameters.
>> >> +Functions declared with attribute @code{noinline} and similar are not
>> >> +inlined.  Whether the function itself is considered for inlining depends
>> >> +on its size and the current inlining parameters.
>> > Guessing this was from another doc patch that I think has already been
>> > approved
>>
>> Yes.  It shouldn't be in the latest patch at the link above.
>>
>> >> @@ -11726,6 +11728,33 @@ check its compatibility with @var{size}.
>> >>
>> >>  @end deftypefn
>> >>
>> >> +@deftypefn {Built-in Function} bool __builtin_has_attribute
>> > (@var{type-or-expression}, @var{attribute})
>> >> +The @code{__builtin_has_attribute} function evaluates to an integer
>> > constant
>> >> +expression equal to @code{true} if the symbol or type referenced by
>> >> +the @var{type-or-expression} argument has been declared with
>> >> +the @var{attribute} referenced by the second argument.  Neither argument
>> >> +is valuated.  The @var{type-or-expression} argument is subject to the
>> > same
>> > s/valuated/evaluated/ ?
>>
>> This should also be fixed in the latest patch at the link above.
>>
>> > Did the implementation change significantly requiring another review
>> > iteration?
>>
>> I don't think it changed too significantly between the last two
>> revisions but I don't have a record of anyone having approved
>> the C FE and the middle-end bits.  (Sorry if I missed it.) Other
>> than this response from you all I see in the archive is this:
>>
>>    https://gcc.gnu.org/ml/gcc-patches/2018-10/msg00606.html
>>
>> Please let me if the last revision is okay to commit.
>>
>
> Hi,
>
> It seems you committed this yesterday as r266335, and I have noticed
> new failures:
> on both aarch64 and arm:
> FAIL: c-c++-common/builtin-has-attribute-3.c  -Wc++-compat  (test for
> excess errors)
>
> gcc.log says:
> Excess errors:
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:11:25: error:
> alignment for 'faligned_1' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:12:25: error:
> alignment for 'faligned_2' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:39:3: error:
> alignment for '__builtin_has_attribute_tmp.' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:40:3: error:
> alignment for '__builtin_has_attribute_tmp.' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:47:3: error:
> alignment for '__builtin_has_attribute_tmp.' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:48:3: error:
> alignment for '__builtin_has_attribute_tmp.' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:50:3: error:
> size of array 'Assert' is negative
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:52:3: error:
> alignment for '__builtin_has_attribute_tmp.' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:52:3: error:
> size of array 'Assert' is negative
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:53:3: error:
> alignment for '__builtin_has_attribute_tmp.' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:56:3: error:
> size of array 'Assert' is negative
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:58:3: error:
> alignment for '__builtin_has_attribute_tmp.' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:59:3: error:
> alignment for '__builtin_has_attribute_tmp.' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:59:3: error:
> size of array 'Assert' is negative
>
> on arm only:
> gcc.dg/builtin-has-attribute.c (test for excess errors)
> gdb.log says:
> Excess errors:
> /gcc/testsuite/gcc.dg/builtin-has-attribute.c:12:15: error: size of
> array 'Assert' is negative

I'm seeing the same on sparc-sun-solaris2.11, plus

+FAIL: c-c++-common/builtin-has-attribute-3.c  -std=gnu++14 (test for excess errors)
+FAIL: c-c++-common/builtin-has-attribute-3.c  -std=gnu++17 (test for excess errors)
+FAIL: c-c++-common/builtin-has-attribute-3.c  -std=gnu++98 (test for excess errors)

According to gcc-testresults postings, several other targets are also
affected:

	aarch64-unknown-linux-gnu
        armv8l-unknown-linux-gnueabihf
        m68k-unknown-linux-gnu
        mips64el-unknown-linux-gnu
        moxie-unknown-elf
        powerpc64-unknown-linux-gnu
        powerpc64le-unknown-linux-gnu

	Rainer
Christophe Lyon Nov. 22, 2018, 2:40 p.m. UTC | #19
On Thu, 22 Nov 2018 at 14:09, Rainer Orth <ro@cebitec.uni-bielefeld.de> wrote:
>
> Hi Christophe,
>
> > On Sat, 10 Nov 2018 at 00:43, Martin Sebor <msebor@gmail.com> wrote:
> >>
> >> On 11/09/2018 12:59 PM, Jeff Law wrote:
> >> > On 10/31/18 10:27 AM, Martin Sebor wrote:
> >> >> Ping: https://gcc.gnu.org/ml/gcc-patches/2018-10/msg01473.html
> >> >>
> >> >> With the C++ bits approved I'm still looking for a review/approval
> >> >> of the remaining changes: the C front end and the shared c-family
> >> >> handling of the new built-in.
> >> > I thought I acked those with just a couple minor doc nits:
> >>
> >> I don't see a formal approval for the rest in my Inbox or in
> >> the archive.
> >>
> >> >> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> >> >> index 8ffb0cd..dcf4747 100644
> >> >> --- a/gcc/doc/extend.texi
> >> >> +++ b/gcc/doc/extend.texi
> >> >> @@ -2649,8 +2649,9 @@ explicit @code{externally_visible} attributes
> >> > are still necessary.
> >> >>  @cindex @code{flatten} function attribute
> >> >>  Generally, inlining into a function is limited.  For a function
> >> > marked with
> >> >>  this attribute, every call inside this function is inlined, if possible.
> >> >> -Whether the function itself is considered for inlining depends on its
> >> > size and
> >> >> -the current inlining parameters.
> >> >> +Functions declared with attribute @code{noinline} and similar are not
> >> >> +inlined.  Whether the function itself is considered for inlining depends
> >> >> +on its size and the current inlining parameters.
> >> > Guessing this was from another doc patch that I think has already been
> >> > approved
> >>
> >> Yes.  It shouldn't be in the latest patch at the link above.
> >>
> >> >> @@ -11726,6 +11728,33 @@ check its compatibility with @var{size}.
> >> >>
> >> >>  @end deftypefn
> >> >>
> >> >> +@deftypefn {Built-in Function} bool __builtin_has_attribute
> >> > (@var{type-or-expression}, @var{attribute})
> >> >> +The @code{__builtin_has_attribute} function evaluates to an integer
> >> > constant
> >> >> +expression equal to @code{true} if the symbol or type referenced by
> >> >> +the @var{type-or-expression} argument has been declared with
> >> >> +the @var{attribute} referenced by the second argument.  Neither argument
> >> >> +is valuated.  The @var{type-or-expression} argument is subject to the
> >> > same
> >> > s/valuated/evaluated/ ?
> >>
> >> This should also be fixed in the latest patch at the link above.
> >>
> >> > Did the implementation change significantly requiring another review
> >> > iteration?
> >>
> >> I don't think it changed too significantly between the last two
> >> revisions but I don't have a record of anyone having approved
> >> the C FE and the middle-end bits.  (Sorry if I missed it.) Other
> >> than this response from you all I see in the archive is this:
> >>
> >>    https://gcc.gnu.org/ml/gcc-patches/2018-10/msg00606.html
> >>
> >> Please let me if the last revision is okay to commit.
> >>
> >
> > Hi,
> >
> > It seems you committed this yesterday as r266335, and I have noticed
> > new failures:
> > on both aarch64 and arm:
> > FAIL: c-c++-common/builtin-has-attribute-3.c  -Wc++-compat  (test for
> > excess errors)
> >
> > gcc.log says:
> > Excess errors:
> > /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:11:25: error:
> > alignment for 'faligned_1' must be at least 4
> > /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:12:25: error:
> > alignment for 'faligned_2' must be at least 4
> > /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:39:3: error:
> > alignment for '__builtin_has_attribute_tmp.' must be at least 4
> > /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:40:3: error:
> > alignment for '__builtin_has_attribute_tmp.' must be at least 4
> > /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:47:3: error:
> > alignment for '__builtin_has_attribute_tmp.' must be at least 4
> > /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:48:3: error:
> > alignment for '__builtin_has_attribute_tmp.' must be at least 4
> > /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:50:3: error:
> > size of array 'Assert' is negative
> > /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:52:3: error:
> > alignment for '__builtin_has_attribute_tmp.' must be at least 4
> > /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:52:3: error:
> > size of array 'Assert' is negative
> > /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:53:3: error:
> > alignment for '__builtin_has_attribute_tmp.' must be at least 4
> > /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:56:3: error:
> > size of array 'Assert' is negative
> > /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:58:3: error:
> > alignment for '__builtin_has_attribute_tmp.' must be at least 4
> > /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:59:3: error:
> > alignment for '__builtin_has_attribute_tmp.' must be at least 4
> > /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:59:3: error:
> > size of array 'Assert' is negative
> >
> > on arm only:
> > gcc.dg/builtin-has-attribute.c (test for excess errors)
> > gdb.log says:
> > Excess errors:
> > /gcc/testsuite/gcc.dg/builtin-has-attribute.c:12:15: error: size of
> > array 'Assert' is negative
>
> I'm seeing the same on sparc-sun-solaris2.11, plus
>
> +FAIL: c-c++-common/builtin-has-attribute-3.c  -std=gnu++14 (test for excess errors)
> +FAIL: c-c++-common/builtin-has-attribute-3.c  -std=gnu++17 (test for excess errors)
> +FAIL: c-c++-common/builtin-has-attribute-3.c  -std=gnu++98 (test for excess errors)
>

Right, me too.

As I split reports in one column for gcc, one for g++, one for
libstdc++ and one for gfortran,
I forgot to report the g++ regressions.
Thanks

> According to gcc-testresults postings, several other targets are also
> affected:
>
>         aarch64-unknown-linux-gnu
>         armv8l-unknown-linux-gnueabihf
>         m68k-unknown-linux-gnu
>         mips64el-unknown-linux-gnu
>         moxie-unknown-elf
>         powerpc64-unknown-linux-gnu
>         powerpc64le-unknown-linux-gnu
>
>         Rainer
>
> --
> -----------------------------------------------------------------------------
> Rainer Orth, Center for Biotechnology, Bielefeld University
Martin Sebor Nov. 24, 2018, 2:19 a.m. UTC | #20
On 11/22/18 5:28 AM, Christophe Lyon wrote:
> On Sat, 10 Nov 2018 at 00:43, Martin Sebor <msebor@gmail.com> wrote:
>>
>> On 11/09/2018 12:59 PM, Jeff Law wrote:
>>> On 10/31/18 10:27 AM, Martin Sebor wrote:
>>>> Ping: https://gcc.gnu.org/ml/gcc-patches/2018-10/msg01473.html
>>>>
>>>> With the C++ bits approved I'm still looking for a review/approval
>>>> of the remaining changes: the C front end and the shared c-family
>>>> handling of the new built-in.
>>> I thought I acked those with just a couple minor doc nits:
>>
>> I don't see a formal approval for the rest in my Inbox or in
>> the archive.
>>
>>>> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
>>>> index 8ffb0cd..dcf4747 100644
>>>> --- a/gcc/doc/extend.texi
>>>> +++ b/gcc/doc/extend.texi
>>>> @@ -2649,8 +2649,9 @@ explicit @code{externally_visible} attributes
>>> are still necessary.
>>>>   @cindex @code{flatten} function attribute
>>>>   Generally, inlining into a function is limited.  For a function
>>> marked with
>>>>   this attribute, every call inside this function is inlined, if possible.
>>>> -Whether the function itself is considered for inlining depends on its
>>> size and
>>>> -the current inlining parameters.
>>>> +Functions declared with attribute @code{noinline} and similar are not
>>>> +inlined.  Whether the function itself is considered for inlining depends
>>>> +on its size and the current inlining parameters.
>>> Guessing this was from another doc patch that I think has already been
>>> approved
>>
>> Yes.  It shouldn't be in the latest patch at the link above.
>>
>>>> @@ -11726,6 +11728,33 @@ check its compatibility with @var{size}.
>>>>
>>>>   @end deftypefn
>>>>
>>>> +@deftypefn {Built-in Function} bool __builtin_has_attribute
>>> (@var{type-or-expression}, @var{attribute})
>>>> +The @code{__builtin_has_attribute} function evaluates to an integer
>>> constant
>>>> +expression equal to @code{true} if the symbol or type referenced by
>>>> +the @var{type-or-expression} argument has been declared with
>>>> +the @var{attribute} referenced by the second argument.  Neither argument
>>>> +is valuated.  The @var{type-or-expression} argument is subject to the
>>> same
>>> s/valuated/evaluated/ ?
>>
>> This should also be fixed in the latest patch at the link above.
>>
>>> Did the implementation change significantly requiring another review
>>> iteration?
>>
>> I don't think it changed too significantly between the last two
>> revisions but I don't have a record of anyone having approved
>> the C FE and the middle-end bits.  (Sorry if I missed it.) Other
>> than this response from you all I see in the archive is this:
>>
>>     https://gcc.gnu.org/ml/gcc-patches/2018-10/msg00606.html
>>
>> Please let me if the last revision is okay to commit.
>>
> 
> Hi,
> 
> It seems you committed this yesterday as r266335, and I have noticed
> new failures:
> on both aarch64 and arm:
> FAIL: c-c++-common/builtin-has-attribute-3.c  -Wc++-compat  (test for
> excess errors)
> 
> gcc.log says:
> Excess errors:
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:11:25: error:
> alignment for 'faligned_1' must be at least 4

Test fails on targets whose default function alignment is 4 (or
any other value greater than 1) because the attribute handler
rejects attribute aligned with less restrictive arguments.

That seems unnecessary to me because less restrictive alignments
are trivially satisfied by more restrictive ones.  Clang accepts
the code and I'd say it makes sense for GCC to do the same.
The GNU assembler for aarch64 and sparc-solaris2.11 don't seem
to mind it (though it doesn't look like GCC actually emits .align
directives for small alignments; that may need a separate change
from one to relax the handler).

> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:12:25: error:
> alignment for 'faligned_2' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:39:3: error:
> alignment for '__builtin_has_attribute_tmp.' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:40:3: error:
> alignment for '__builtin_has_attribute_tmp.' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:47:3: error:
> alignment for '__builtin_has_attribute_tmp.' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:48:3: error:
> alignment for '__builtin_has_attribute_tmp.' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:50:3: error:
> size of array 'Assert' is negative
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:52:3: error:
> alignment for '__builtin_has_attribute_tmp.' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:52:3: error:
> size of array 'Assert' is negative
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:53:3: error:
> alignment for '__builtin_has_attribute_tmp.' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:56:3: error:
> size of array 'Assert' is negative
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:58:3: error:
> alignment for '__builtin_has_attribute_tmp.' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:59:3: error:
> alignment for '__builtin_has_attribute_tmp.' must be at least 4
> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:59:3: error:
> size of array 'Assert' is negative
> 
> on arm only:
> gcc.dg/builtin-has-attribute.c (test for excess errors)
> gdb.log says:
> Excess errors:
> /gcc/testsuite/gcc.dg/builtin-has-attribute.c:12:15: error: size of
> array 'Assert' is negative

This failure is caused by the TYPE_USER_ALIGN bit getting cleared
for a type:

   struct __attribute__ ((aligned (8))) S { int i; }
   _Static_assert (__builtin_has_attribute (struct S, aligned));

The assertion passes with the aarch64 and x86_64 back crosses but
not with arm or sparc.  The bit is cleared in finalize_type_size:

static void
finalize_type_size (tree type)
{
   /* Normally, use the alignment corresponding to the mode chosen.
      However, where strict alignment is not required, avoid
      over-aligning structures, since most compilers do not do this
      alignment.  */
   if (TYPE_MODE (type) != BLKmode
       && TYPE_MODE (type) != VOIDmode
       && (STRICT_ALIGNMENT || !AGGREGATE_TYPE_P (type)))
     {
       unsigned mode_align = GET_MODE_ALIGNMENT (TYPE_MODE (type));

       /* Don't override a larger alignment requirement coming from a user
	 alignment of one of the fields.  */
       if (mode_align >= TYPE_ALIGN (type))
	{
	  SET_TYPE_ALIGN (type, mode_align);
	  TYPE_USER_ALIGN (type) = 0;
	}
     }

   /* Do machine-dependent extra alignment.  */
#ifdef ROUND_TYPE_ALIGN
   SET_TYPE_ALIGN (type,
                   ROUND_TYPE_ALIGN (type, TYPE_ALIGN (type), 
BITS_PER_UNIT));
#endif
   ...

has_attribute needs to handle this.

Martin
Martin Sebor Nov. 24, 2018, 2:32 a.m. UTC | #21
On 11/22/18 6:09 AM, Rainer Orth wrote:
> Hi Christophe,
> 
>> On Sat, 10 Nov 2018 at 00:43, Martin Sebor <msebor@gmail.com> wrote:
>>>
>>> On 11/09/2018 12:59 PM, Jeff Law wrote:
>>>> On 10/31/18 10:27 AM, Martin Sebor wrote:
>>>>> Ping: https://gcc.gnu.org/ml/gcc-patches/2018-10/msg01473.html
>>>>>
>>>>> With the C++ bits approved I'm still looking for a review/approval
>>>>> of the remaining changes: the C front end and the shared c-family
>>>>> handling of the new built-in.
>>>> I thought I acked those with just a couple minor doc nits:
>>>
>>> I don't see a formal approval for the rest in my Inbox or in
>>> the archive.
>>>
>>>>> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
>>>>> index 8ffb0cd..dcf4747 100644
>>>>> --- a/gcc/doc/extend.texi
>>>>> +++ b/gcc/doc/extend.texi
>>>>> @@ -2649,8 +2649,9 @@ explicit @code{externally_visible} attributes
>>>> are still necessary.
>>>>>   @cindex @code{flatten} function attribute
>>>>>   Generally, inlining into a function is limited.  For a function
>>>> marked with
>>>>>   this attribute, every call inside this function is inlined, if possible.
>>>>> -Whether the function itself is considered for inlining depends on its
>>>> size and
>>>>> -the current inlining parameters.
>>>>> +Functions declared with attribute @code{noinline} and similar are not
>>>>> +inlined.  Whether the function itself is considered for inlining depends
>>>>> +on its size and the current inlining parameters.
>>>> Guessing this was from another doc patch that I think has already been
>>>> approved
>>>
>>> Yes.  It shouldn't be in the latest patch at the link above.
>>>
>>>>> @@ -11726,6 +11728,33 @@ check its compatibility with @var{size}.
>>>>>
>>>>>   @end deftypefn
>>>>>
>>>>> +@deftypefn {Built-in Function} bool __builtin_has_attribute
>>>> (@var{type-or-expression}, @var{attribute})
>>>>> +The @code{__builtin_has_attribute} function evaluates to an integer
>>>> constant
>>>>> +expression equal to @code{true} if the symbol or type referenced by
>>>>> +the @var{type-or-expression} argument has been declared with
>>>>> +the @var{attribute} referenced by the second argument.  Neither argument
>>>>> +is valuated.  The @var{type-or-expression} argument is subject to the
>>>> same
>>>> s/valuated/evaluated/ ?
>>>
>>> This should also be fixed in the latest patch at the link above.
>>>
>>>> Did the implementation change significantly requiring another review
>>>> iteration?
>>>
>>> I don't think it changed too significantly between the last two
>>> revisions but I don't have a record of anyone having approved
>>> the C FE and the middle-end bits.  (Sorry if I missed it.) Other
>>> than this response from you all I see in the archive is this:
>>>
>>>     https://gcc.gnu.org/ml/gcc-patches/2018-10/msg00606.html
>>>
>>> Please let me if the last revision is okay to commit.
>>>
>>
>> Hi,
>>
>> It seems you committed this yesterday as r266335, and I have noticed
>> new failures:
>> on both aarch64 and arm:
>> FAIL: c-c++-common/builtin-has-attribute-3.c  -Wc++-compat  (test for
>> excess errors)
>>
>> gcc.log says:
>> Excess errors:
>> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:11:25: error:
>> alignment for 'faligned_1' must be at least 4
>> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:12:25: error:
>> alignment for 'faligned_2' must be at least 4
>> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:39:3: error:
>> alignment for '__builtin_has_attribute_tmp.' must be at least 4
>> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:40:3: error:
>> alignment for '__builtin_has_attribute_tmp.' must be at least 4
>> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:47:3: error:
>> alignment for '__builtin_has_attribute_tmp.' must be at least 4
>> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:48:3: error:
>> alignment for '__builtin_has_attribute_tmp.' must be at least 4
>> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:50:3: error:
>> size of array 'Assert' is negative
>> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:52:3: error:
>> alignment for '__builtin_has_attribute_tmp.' must be at least 4
>> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:52:3: error:
>> size of array 'Assert' is negative
>> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:53:3: error:
>> alignment for '__builtin_has_attribute_tmp.' must be at least 4
>> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:56:3: error:
>> size of array 'Assert' is negative
>> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:58:3: error:
>> alignment for '__builtin_has_attribute_tmp.' must be at least 4
>> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:59:3: error:
>> alignment for '__builtin_has_attribute_tmp.' must be at least 4
>> /gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:59:3: error:
>> size of array 'Assert' is negative
>>
>> on arm only:
>> gcc.dg/builtin-has-attribute.c (test for excess errors)
>> gdb.log says:
>> Excess errors:
>> /gcc/testsuite/gcc.dg/builtin-has-attribute.c:12:15: error: size of
>> array 'Assert' is negative
> 
> I'm seeing the same on sparc-sun-solaris2.11, plus
> 
> +FAIL: c-c++-common/builtin-has-attribute-3.c  -std=gnu++14 (test for excess errors)
> +FAIL: c-c++-common/builtin-has-attribute-3.c  -std=gnu++17 (test for excess errors)
> +FAIL: c-c++-common/builtin-has-attribute-3.c  -std=gnu++98 (test for excess errors)

I see these errors with my sparc-sun-solaris2.11 cross-compiler:

/ssd/src/gcc/svn/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:197:3: 
error: constructor priorities are not supported

Apparently the sparc-sun-solaris2.11 target doesn't like ctor and
dtor priorities.  The error comes from here:

static priority_type
get_priority (tree args, bool is_destructor)
{
   HOST_WIDE_INT pri;
   tree arg;

   if (!args)
     return DEFAULT_INIT_PRIORITY;

   if (!SUPPORTS_INIT_PRIORITY)
     {
       if (is_destructor)
	error ("destructor priorities are not supported");
       else
	error ("constructor priorities are not supported");
       return DEFAULT_INIT_PRIORITY;
     }

The manual doesn't mention anything about this.  It makes it sound
like ctor/dtor priorities are supported everywhere.

Does Solaris really not support these even with Binutils?  If not,
I'll adjust the test (and the manual to make it clear the attribute
isn't supported everywhere).

> According to gcc-testresults postings, several other targets are also
> affected:
> 
> 	aarch64-unknown-linux-gnu
>          armv8l-unknown-linux-gnueabihf
>          m68k-unknown-linux-gnu
>          mips64el-unknown-linux-gnu
>          moxie-unknown-elf
>          powerpc64-unknown-linux-gnu
>          powerpc64le-unknown-linux-gnu

AFAICS, powerpc64le-unknown-linux-gnu does accept priorities so
the test failure there must be something due to something else.
I haven't checked what yet.

Martin
Rainer Orth Nov. 25, 2018, 2:11 p.m. UTC | #22
Hi Martin,

>> I'm seeing the same on sparc-sun-solaris2.11, plus
>>
>> +FAIL: c-c++-common/builtin-has-attribute-3.c -std=gnu++14 (test for
>> excess errors)
>> +FAIL: c-c++-common/builtin-has-attribute-3.c -std=gnu++17 (test for
>> excess errors)
>> +FAIL: c-c++-common/builtin-has-attribute-3.c -std=gnu++98 (test for
>> excess errors)
>
> I see these errors with my sparc-sun-solaris2.11 cross-compiler:
>
> /ssd/src/gcc/svn/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:197:3:
> error: constructor priorities are not supported
>
> Apparently the sparc-sun-solaris2.11 target doesn't like ctor and
> dtor priorities.  The error comes from here:
[...]
> The manual doesn't mention anything about this.  It makes it sound
> like ctor/dtor priorities are supported everywhere.

They're not, obviously, and we do have an effective-target keyword for
this: init_priority.

> Does Solaris really not support these even with Binutils?  If not,
> I'll adjust the test (and the manual to make it clear the attribute
> isn't supported everywhere).

The test is in gcc/configure.ac (gcc_AC_INITFINI_ARRAY), defined in
acinclude.m4.  It requires not only target binutils, but also target
headers to work; however, you can override it with
--enable-initfini-array.  You may have pass the same option when
configuring binutils at least on Solaris/SPARC (don't remember the
details offhand).

However, that wasn't the problem in the Solaris 11.4+ results I reported:
it *does* support constructor priority even with as/ld and the failures
have nothing to do with that issue.  Here are the errors/warnings I'm
seeing:

+FAIL: c-c++-common/builtin-has-attribute-3.c  -std=gnu++14 (test for excess errors)
+FAIL: c-c++-common/builtin-has-attribute-3.c  -std=gnu++17 (test for excess errors)
+FAIL: c-c++-common/builtin-has-attribute-3.c  -std=gnu++98 (test for excess errors)

Excess errors:
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:11:25: error: alignment for 'void faligned_1()' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:12:25: error: alignment for 'void faligned_2()' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:39:3: error: alignment for '__builtin_has_attribute_tmp.' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:40:3: error: alignment for '__builtin_has_attribute_tmp.' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:47:3: error: alignment for '__builtin_has_attribute_tmp.' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:48:3: error: alignment for '__builtin_has_attribute_tmp.' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:50:3: error: size of array 'Assert' is negative
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:52:3: error: alignment for '__builtin_has_attribute_tmp.' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:52:3: error: size of array 'Assert' is negative
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:53:3: error: alignment for '__builtin_has_attribute_tmp.' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:56:3: error: size of array 'Assert' is negative
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:58:3: error: alignment for '__builtin_has_attribute_tmp.' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:59:3: error: alignment for '__builtin_has_attribute_tmp.' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:59:3: error: size of array 'Assert' is negative

+FAIL: c-c++-common/builtin-has-attribute-3.c  -Wc++-compat  (test for excess errors)

Excess errors:
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:11:25: error: alignment for 'faligned_1' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:12:25: error: alignment for 'faligned_2' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:39:3: error: alignment for '__builtin_has_attribute_tmp.' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:40:3: error: alignment for '__builtin_has_attribute_tmp.' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:47:3: error: alignment for '__builtin_has_attribute_tmp.' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:48:3: error: alignment for '__builtin_has_attribute_tmp.' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:50:3: error: size of array 'Assert' is negative
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:52:3: error: alignment for '__builtin_has_attribute_tmp.' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:52:3: error: size of array 'Assert' is negative
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:53:3: error: alignment for '__builtin_has_attribute_tmp.' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:56:3: error: size of array 'Assert' is negative
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:58:3: error: alignment for '__builtin_has_attribute_tmp.' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:59:3: error: alignment for '__builtin_has_attribute_tmp.' must be at least 4
/vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:59:3: error: size of array 'Assert' is negative

+FAIL: gcc.dg/builtin-has-attribute.c (test for excess errors)

Excess errors:
/vol/gcc/src/hg/trunk/local/gcc/testsuite/gcc.dg/builtin-has-attribute.c:12:15: error: size of array 'Assert' is negative

+UNRESOLVED: gcc.dg/builtin-has-attribute.c compilation failed to produce executable

The error about missing construcot priority support does occur, however,
on Solaris 10 up to 11.3 with ld, in addition to the ones above.

As for the test, it would be best to split it into two: one for the bulk
that doesn't need constructor priority support and can work everywhere,
and another smaller one for the rest, guarded by
dg-require-effective-target init_priority.

	Rainer
Martin Sebor Nov. 25, 2018, 8:40 p.m. UTC | #23
On 11/25/18 7:11 AM, Rainer Orth wrote:
> Hi Martin,
> 
>>> I'm seeing the same on sparc-sun-solaris2.11, plus
>>>
>>> +FAIL: c-c++-common/builtin-has-attribute-3.c -std=gnu++14 (test for
>>> excess errors)
>>> +FAIL: c-c++-common/builtin-has-attribute-3.c -std=gnu++17 (test for
>>> excess errors)
>>> +FAIL: c-c++-common/builtin-has-attribute-3.c -std=gnu++98 (test for
>>> excess errors)
>>
>> I see these errors with my sparc-sun-solaris2.11 cross-compiler:
>>
>> /ssd/src/gcc/svn/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:197:3:
>> error: constructor priorities are not supported
>>
>> Apparently the sparc-sun-solaris2.11 target doesn't like ctor and
>> dtor priorities.  The error comes from here:
> [...]
>> The manual doesn't mention anything about this.  It makes it sound
>> like ctor/dtor priorities are supported everywhere.
> 
> They're not, obviously, and we do have an effective-target keyword for
> this: init_priority.
> 
>> Does Solaris really not support these even with Binutils?  If not,
>> I'll adjust the test (and the manual to make it clear the attribute
>> isn't supported everywhere).
> 
> The test is in gcc/configure.ac (gcc_AC_INITFINI_ARRAY), defined in
> acinclude.m4.  It requires not only target binutils, but also target
> headers to work; however, you can override it with
> --enable-initfini-array.  You may have pass the same option when
> configuring binutils at least on Solaris/SPARC (don't remember the
> details offhand).
> 
> However, that wasn't the problem in the Solaris 11.4+ results I reported:
> it *does* support constructor priority even with as/ld and the failures
> have nothing to do with that issue.  Here are the errors/warnings I'm
> seeing:
> 
> +FAIL: c-c++-common/builtin-has-attribute-3.c  -std=gnu++14 (test for excess errors)
> +FAIL: c-c++-common/builtin-has-attribute-3.c  -std=gnu++17 (test for excess errors)
> +FAIL: c-c++-common/builtin-has-attribute-3.c  -std=gnu++98 (test for excess errors)
> 
> Excess errors:
> /vol/gcc/src/hg/trunk/local/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c:11:25: error: alignment for 'void faligned_1()' must be at least 4

Right, these are the same issue as those noted by Christophe on
aarch64 and arm.  I mentioned this issue in my question to
the gcc list:  https://gcc.gnu.org/ml/gcc/2018-11/msg00127.html
I have a patch to let GCC accept less restrictive alignments that
I plan to submit (unless someone knows of a reason that makes
rejecting this code is necessary).

> +FAIL: gcc.dg/builtin-has-attribute.c (test for excess errors)
> 
> Excess errors:
> /vol/gcc/src/hg/trunk/local/gcc/testsuite/gcc.dg/builtin-has-attribute.c:12:15: error: size of array 'Assert' is negative
> 
> +UNRESOLVED: gcc.dg/builtin-has-attribute.c compilation failed to produce executable

This one also affects other targets.  I explained what causes
it in my response here:
   https://gcc.gnu.org/ml/gcc-patches/2018-11/msg02013.html
Fixing it will need a small tweak to the builtin.  I'll take
care of it this week.

> 
> The error about missing construcot priority support does occur, however,
> on Solaris 10 up to 11.3 with ld, in addition to the ones above.
> 
> As for the test, it would be best to split it into two: one for the bulk
> that doesn't need constructor priority support and can work everywhere,
> and another smaller one for the rest, guarded by
> dg-require-effective-target init_priority.

I agree.  I'll take care of this as well this week.

Martin
diff mbox series

Patch

gcc/c/ChangeLog:

	* c-parser.c (c_parser_has_attribute_expression): New function.
	(c_parser_attribute): New function.
	(c_parser_attributes): Move code into c_parser_attribute.
	(c_parser_unary_expression): Handle RID_HAS_ATTRIBUTE_EXPRESSION.

gcc/c-family/ChangeLog:

	* c-attribs.c (validate_attribute, has_attribute): New functions.
	* c-common.h (has_attribute): Declare.
	* c-common.c (c_common_resword): Add RID_HAS_ATTRIBUTE_EXPRESSION.
	* c-common.h (rid): Same.

gcc/cp/ChangeLog:

	* cp-tree.h (cp_check_const_attributes): Declare.
	* decl2.c (cp_check_const_attributes): Declare extern.
	* parser.c (cp_parser_has_attribute_expression): New function.
	(cp_parser_unary_expression): Handle RID_HAS_ATTRIBUTE_EXPRESSION.
	(cp_parser_gnu_attribute_list): Add argument.

gcc/ChangeLog:

	* doc/extend.texi (Other Builtins): Add __builtin_has_attribute.

gcc/testsuite/ChangeLog:

	* c-c++-common/builtin-has-attribute-2.c: New test.
	* c-c++-common/builtin-has-attribute-3.c: New test.
	* c-c++-common/builtin-has-attribute.c: New test.
	* gcc.dg/builtin-has-attribute.c: New test.
diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c
index 3a88766..56967fa 100644
--- a/gcc/c-family/c-attribs.c
+++ b/gcc/c-family/c-attribs.c
@@ -3678,3 +3678,220 @@  handle_patchable_function_entry_attribute (tree *, tree, tree, int, bool *)
   /* Nothing to be done here.  */
   return NULL_TREE;
 }
+
+/* Attempt to partially validate a single attribute ATTR if it were
+   to be applied to an entity OPER.  */
+
+static bool
+validate_attribute (location_t atloc, tree oper, tree attr)
+{
+  /* Determine whether the name of the attribute is valid
+     and fail with an error if not.  */
+  tree atname = get_attribute_name (attr);
+  if (!lookup_attribute_spec (atname))
+    {
+      if (atloc != UNKNOWN_LOCATION)
+	error_at (atloc, "unknown attribute %qE", atname);
+      return false;
+    }
+
+  if (!TREE_VALUE (attr))
+    return true;
+
+  /* FIXME: Do some validation.  */
+  if (!strcmp (IDENTIFIER_POINTER (atname), "format"))
+    return true;
+
+  /* Only when attribute arguments have been provided try to validate
+     the whole thing.  decl_attributes doesn't return an indication of
+     success or failure so proceed regardless.  */
+  const char tmpname[] = "__builtin_has_attribute_tmp.";
+  tree tmpid = get_identifier (tmpname);
+  tree tmpdecl;
+  if (TYPE_P (oper))
+    tmpdecl = build_decl (atloc, TYPE_DECL,
+			  tmpid, oper);
+  else
+    tmpdecl = build_decl (atloc, TREE_CODE (oper),
+			  tmpid, TREE_TYPE (oper));
+
+  /* Temporarily clear CURRENT_FUNCTION_DECL to make decl_attributes
+     believe the DECL declared above is at file scope.  (See bug 87526.)  */
+  tree save_curfunc = current_function_decl;
+  current_function_decl = NULL_TREE;
+  if (DECL_P (tmpdecl))
+    {
+      if (DECL_P (oper))
+	DECL_EXTERNAL (tmpdecl) = DECL_EXTERNAL (oper);
+      TREE_PUBLIC (tmpdecl) = TREE_PUBLIC (oper);
+    }
+  decl_attributes (&tmpdecl, attr, 0);
+  current_function_decl = save_curfunc;
+
+  /* FIXME: Change decl_attributes to indicate success or failure.  */
+  return true;
+}
+
+/* Return true if the DECL, EXPR, or TYPE t has been declared with
+   attribute ATTR.  For DECL, consider also its type.  For EXPR,
+   consider just its type.  */
+
+bool
+has_attribute (location_t atloc, tree t, tree attr, tree (*convert)(tree))
+{
+  if (!attr || !t || t == error_mark_node)
+    return false;
+
+  if (!validate_attribute (atloc, t, attr))
+    return false;
+
+  tree type = NULL_TREE;
+  tree expr = NULL_TREE;
+  if (TYPE_P (t))
+    type = t;
+  else
+    expr = t;
+
+  /* Set to true when an attribute is found in the referenced entity
+     that matches the specified attribute.  */
+  bool found_match = false;
+
+  /* Iterate once for a type and twice for a function or variable
+     declaration: once for the DECL and the second time for its
+     TYPE.  */
+  for (bool done = false; !found_match && !done; )
+    {
+      tree atlist;
+      if (type)
+	{
+	  /* Clear EXPR to prevent considering it again below.  */
+	  atlist = TYPE_ATTRIBUTES (type);
+	  expr = NULL_TREE;
+	  done = true;
+	}
+      else if (DECL_P (expr))
+	{
+	  /* Set TYPE to the DECL's type to process it on the next
+	     iteration.  */
+	  atlist = DECL_ATTRIBUTES (expr);
+	  type = TREE_TYPE (expr);
+	}
+      else
+	{
+	  atlist = TYPE_ATTRIBUTES (TREE_TYPE (expr));
+	  done = true;
+	}
+
+      tree atname = get_attribute_name (attr);
+      const char *namestr = IDENTIFIER_POINTER (atname);
+
+      /* True when an attribute with the sought name (though not necessarily
+	 with the sought attributes) has been found on the attribute chain.  */
+      bool found_attr = false;
+
+      /* For attribute aligned ignore the attribute list and consider
+	 the tree node itself instead.  */
+      if (type && !strcmp ("aligned", namestr))
+	atlist = NULL_TREE;
+
+      /* When clear, the first mismatched attribute argument results
+	 in failure.  Otherwise, the first matched attribute argument
+	 results in success.  */
+      bool attr_nonnull = !strcmp ("nonnull", namestr);
+      bool ignore_mismatches = attr_nonnull;
+
+      /* Iterate over the instances of the sought attribute on the DECL or
+	 TYPE (there may be multiple instances with different arguments).  */
+      for (; (atlist = lookup_attribute (namestr, atlist));
+	   found_attr = true, atlist = TREE_CHAIN (atlist))
+	{
+	  /* If there are no arguments to match the result is true except
+	     for nonnull where the attribute with no arguments must match.  */
+	  if (!TREE_VALUE (attr))
+	    return attr_nonnull ? !TREE_VALUE (atlist) : true;
+
+	  /* Attribute nonnull with no arguments subsumes all values of
+	     the attribute.  FIXME: This is overly broad since it only
+	     applies to pointer arguments, but querying non-pointer
+	     arguments is diagnosed.  */
+	  if (!TREE_VALUE (atlist) && attr_nonnull)
+	    return true;
+
+	  /* Iterate over the DECL or TYPE attribute argument's values.  */
+	  for (tree val = TREE_VALUE (atlist); val; val = TREE_CHAIN (val))
+	    {
+	      /* Iterate over the arguments in the sought attribute comparing
+		 their values to those specified for the DECL or TYPE.  */
+	      for (tree arg = TREE_VALUE (attr); arg; arg = TREE_CHAIN (arg))
+		{
+		  tree v1 = TREE_VALUE (val);
+		  tree v2 = TREE_VALUE (arg);
+		  if (v1 == v2)
+		    return true;
+
+		  if (!v1 || !v2)
+		    break;
+
+		  if (TREE_CODE (v1) != IDENTIFIER_NODE
+		      && TREE_CODE (v2) != IDENTIFIER_NODE)
+		    {
+		      /* Convert to make them equality-comparable.  */
+		      v1 = convert (v1);
+		      v2 = convert (v2);
+		    }
+
+		  if (simple_cst_equal (v1, v2))
+		    return true;
+
+		  if (!ignore_mismatches)
+		    break;
+		}
+	    }
+	}
+
+      if (!found_attr)
+	{
+	  /* Some attributes are encoded directly in the tree node.  */
+	  if (!strcmp ("aligned", namestr))
+	    {
+	      if (tree arg = TREE_VALUE (attr))
+		{
+		  arg = convert (TREE_VALUE (arg));
+		  if (expr && DECL_P (expr)
+		      && DECL_USER_ALIGN (expr)
+		      && tree_fits_uhwi_p (arg))
+		    found_match = DECL_ALIGN_UNIT (expr) == tree_to_uhwi (arg);
+		  else if (type && TYPE_USER_ALIGN (type))
+		    found_match = TYPE_ALIGN_UNIT (type) == tree_to_uhwi (arg);
+		}
+	      else if (expr && DECL_P (expr))
+		found_match = DECL_USER_ALIGN (expr);
+	      else if (type)
+		found_match = TYPE_USER_ALIGN (type);
+	    }
+	  else if (!strcmp ("warn_if_not_aligned", namestr))
+	    {
+	      if (tree arg = TREE_VALUE (attr))
+		{
+		  arg = convert (TREE_VALUE (arg));
+		  if (expr && DECL_P (expr))
+		    found_match = (DECL_WARN_IF_NOT_ALIGN (expr)
+				   == tree_to_uhwi (arg) * BITS_PER_UNIT);
+		  else if (type)
+		    found_match = (TYPE_WARN_IF_NOT_ALIGN (type)
+				   == tree_to_uhwi (arg) * BITS_PER_UNIT);
+		}
+	      else if (expr && DECL_P (expr))
+		found_match = DECL_WARN_IF_NOT_ALIGN (expr);
+	      else if (type)
+		found_match = TYPE_WARN_IF_NOT_ALIGN (type);
+	    }
+	  else if (!strcmp ("transparent_union", namestr))
+	    {
+	      if (type)
+		found_match = TYPE_TRANSPARENT_AGGR (type) != 0;
+	    }
+	}
+    }
+  return found_match;
+}
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 10a8bc2..7d139be 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -372,6 +372,7 @@  const struct c_common_resword c_common_reswords[] =
     RID_BUILTIN_CALL_WITH_STATIC_CHAIN, D_CONLY },
   { "__builtin_choose_expr", RID_CHOOSE_EXPR, D_CONLY },
   { "__builtin_complex", RID_BUILTIN_COMPLEX, D_CONLY },
+  { "__builtin_has_attribute", RID_BUILTIN_HAS_ATTRIBUTE, 0 },
   { "__builtin_launder", RID_BUILTIN_LAUNDER, D_CXXONLY },
   { "__builtin_shuffle", RID_BUILTIN_SHUFFLE, 0 },
   { "__builtin_tgmath", RID_BUILTIN_TGMATH, D_CONLY },
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 9e86876..6447ecb 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -103,6 +103,7 @@  enum rid
   RID_EXTENSION, RID_IMAGPART, RID_REALPART, RID_LABEL,      RID_CHOOSE_EXPR,
   RID_TYPES_COMPATIBLE_P,      RID_BUILTIN_COMPLEX,	     RID_BUILTIN_SHUFFLE,
   RID_BUILTIN_TGMATH,
+  RID_BUILTIN_HAS_ATTRIBUTE,
   RID_DFLOAT32, RID_DFLOAT64, RID_DFLOAT128,
 
   /* TS 18661-3 keywords, in the same sequence as the TI_* values.  */
@@ -1333,6 +1334,7 @@  extern void maybe_suggest_missing_token_insertion (rich_location *richloc,
 						   enum cpp_ttype token_type,
 						   location_t prev_token_loc);
 extern tree braced_list_to_string (tree, tree);
+extern bool has_attribute (location_t, tree, tree, tree (*)(tree));
 
 #if CHECKING_P
 namespace selftest {
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 1f173fc..cf31ab8 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -1439,6 +1439,8 @@  static vec<tree, va_gc> *c_parser_expr_list (c_parser *, bool, bool,
 					     vec<tree, va_gc> **, location_t *,
 					     tree *, vec<location_t> *,
 					     unsigned int * = NULL);
+static struct c_expr c_parser_has_attribute_expression (c_parser *);
+
 static void c_parser_oacc_declare (c_parser *);
 static void c_parser_oacc_enter_exit_data (c_parser *, bool);
 static void c_parser_oacc_update (c_parser *);
@@ -4293,6 +4295,100 @@  c_parser_attribute_any_word (c_parser *parser)
    allow identifiers declared as types to start the arguments?  */
 
 static tree
+c_parser_attribute (c_parser *parser, tree attrs)
+{
+  if (!c_parser_next_token_is (parser, CPP_COMMA)
+      && !c_parser_next_token_is (parser, CPP_NAME)
+      && !c_parser_next_token_is (parser, CPP_KEYWORD))
+    return NULL_TREE;
+
+  vec<tree, va_gc> *expr_list;
+  if (c_parser_next_token_is (parser, CPP_COMMA))
+    {
+      c_parser_consume_token (parser);
+      return attrs;
+    }
+
+  tree attr_name = c_parser_attribute_any_word (parser);
+  if (attr_name == NULL_TREE)
+    return NULL_TREE;
+
+  attr_name = canonicalize_attr_name (attr_name);
+  c_parser_consume_token (parser);
+
+  tree attr;
+  if (c_parser_next_token_is_not (parser, CPP_OPEN_PAREN))
+    {
+      attr = build_tree_list (attr_name, NULL_TREE);
+      /* Add this attribute to the list.  */
+      attrs = chainon (attrs, attr);
+      return attrs;
+    }
+  c_parser_consume_token (parser);
+
+  tree attr_args;
+  /* 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.
+     In objective-c the identifier may be a classname.  */
+  if (c_parser_next_token_is (parser, CPP_NAME)
+      && (c_parser_peek_token (parser)->id_kind == C_ID_ID
+	  || (c_dialect_objc ()
+	      && c_parser_peek_token (parser)->id_kind
+	      == C_ID_CLASSNAME))
+      && ((c_parser_peek_2nd_token (parser)->type == CPP_COMMA)
+	  || (c_parser_peek_2nd_token (parser)->type
+	      == CPP_CLOSE_PAREN))
+      && (attribute_takes_identifier_p (attr_name)
+	  || (c_dialect_objc ()
+	      && c_parser_peek_token (parser)->id_kind
+	      == C_ID_CLASSNAME)))
+    {
+      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
+	{
+	  tree tree_list;
+	  c_parser_consume_token (parser);
+	  expr_list = c_parser_expr_list (parser, false, true,
+					  NULL, NULL, NULL, NULL);
+	  tree_list = build_tree_list_vec (expr_list);
+	  attr_args = tree_cons (NULL_TREE, arg1, tree_list);
+	  release_tree_vector (expr_list);
+	}
+    }
+  else
+    {
+      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	attr_args = NULL_TREE;
+      else
+	{
+	  expr_list = c_parser_expr_list (parser, false, true,
+					  NULL, NULL, NULL, NULL);
+	  attr_args = build_tree_list_vec (expr_list);
+	  release_tree_vector (expr_list);
+	}
+    }
+
+  attr = build_tree_list (attr_name, attr_args);
+  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+    c_parser_consume_token (parser);
+  else
+    {
+      parser->lex_untranslated_string = false;
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+				 "expected %<)%>");
+      return error_mark_node;
+    }
+  /* Add this attribute to the list.  */
+  attrs = chainon (attrs, attr);
+  return attrs;
+}
+
+static tree
 c_parser_attributes (c_parser *parser)
 {
   tree attrs = NULL_TREE;
@@ -4316,96 +4412,17 @@  c_parser_attributes (c_parser *parser)
 	  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))
+      while (true)
 	{
-	  tree attr, attr_name, attr_args;
-	  vec<tree, va_gc> *expr_list;
-	  if (c_parser_next_token_is (parser, CPP_COMMA))
-	    {
-	      c_parser_consume_token (parser);
-	      continue;
-	    }
-
-	  attr_name = c_parser_attribute_any_word (parser);
-	  if (attr_name == NULL)
+	  /* Parse a single attribute.  */
+	  tree attr = c_parser_attribute (parser, attrs);
+	  if (attr == error_mark_node)
+	    return attrs;
+	  if (!attr)
 	    break;
-	  attr_name = canonicalize_attr_name (attr_name);
-	  c_parser_consume_token (parser);
-	  if (c_parser_next_token_is_not (parser, CPP_OPEN_PAREN))
-	    {
-	      attr = build_tree_list (attr_name, NULL_TREE);
-	      /* Add this attribute to the list.  */
-	      attrs = chainon (attrs, attr);
-	      /* If the next token isn't a comma, we're done.  */
-	      if (!c_parser_next_token_is (parser, CPP_COMMA))
-		break;
-	      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.  
-	     In objective-c the identifier may be a classname.  */
-	  if (c_parser_next_token_is (parser, CPP_NAME)
-	      && (c_parser_peek_token (parser)->id_kind == C_ID_ID
-		  || (c_dialect_objc ()
-		      && c_parser_peek_token (parser)->id_kind
-			 == C_ID_CLASSNAME))
-	      && ((c_parser_peek_2nd_token (parser)->type == CPP_COMMA)
-		  || (c_parser_peek_2nd_token (parser)->type
-		      == CPP_CLOSE_PAREN))
-	      && (attribute_takes_identifier_p (attr_name)
-		  || (c_dialect_objc ()
-		      && c_parser_peek_token (parser)->id_kind
-			 == C_ID_CLASSNAME)))
-	    {
-	      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
-		{
-		  tree tree_list;
-		  c_parser_consume_token (parser);
-		  expr_list = c_parser_expr_list (parser, false, true,
-						  NULL, NULL, NULL, NULL);
-		  tree_list = build_tree_list_vec (expr_list);
-		  attr_args = tree_cons (NULL_TREE, arg1, tree_list);
-		  release_tree_vector (expr_list);
-		}
-	    }
-	  else
-	    {
-	      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
-		attr_args = NULL_TREE;
-	      else
-		{
-		  expr_list = c_parser_expr_list (parser, false, true,
-						  NULL, NULL, NULL, NULL);
-		  attr_args = build_tree_list_vec (expr_list);
-		  release_tree_vector (expr_list);
-		}
-	    }
+	  attrs = attr;
+      }
 
-	  attr = build_tree_list (attr_name, attr_args);
-	  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
-	    c_parser_consume_token (parser);
-	  else
-	    {
-	      parser->lex_untranslated_string = false;
-	      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
-					 "expected %<)%>");
-	      return attrs;
-	    }
-	  /* Add this attribute to the list.  */
-	  attrs = chainon (attrs, attr);
-	  /* If the next token isn't a comma, we're done.  */
-	  if (!c_parser_next_token_is (parser, CPP_COMMA))
-	    break;
-	}
       /* Look for the two `)' tokens.  */
       if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
 	c_parser_consume_token (parser);
@@ -7240,6 +7257,8 @@  c_parser_unary_expression (c_parser *parser)
 	  return c_parser_sizeof_expression (parser);
 	case RID_ALIGNOF:
 	  return c_parser_alignof_expression (parser);
+	case RID_BUILTIN_HAS_ATTRIBUTE:
+	  return c_parser_has_attribute_expression (parser);
 	case RID_EXTENSION:
 	  c_parser_consume_token (parser);
 	  ext = disable_extension_diagnostics ();
@@ -7439,6 +7458,121 @@  c_parser_alignof_expression (c_parser *parser)
     }
 }
 
+/* Parse the __builtin_has_attribute ([expr|type], attribute-spec)
+   expression.  */
+
+static struct c_expr
+c_parser_has_attribute_expression (c_parser *parser)
+{
+  gcc_assert (c_parser_next_token_is_keyword (parser,
+					      RID_BUILTIN_HAS_ATTRIBUTE));
+  c_parser_consume_token (parser);
+
+  c_inhibit_evaluation_warnings++;
+
+  matching_parens parens;
+  if (!parens.require_open (parser))
+    {
+      c_inhibit_evaluation_warnings--;
+      in_typeof--;
+
+      struct c_expr result;
+      result.set_error ();
+      result.original_code = ERROR_MARK;
+      result.original_type = NULL;
+      return result;
+    }
+
+  /* Treat the type argument the same way as in typeof for the purposes
+     of warnings.  FIXME: Generalize this so the warning refers to
+     __builtin_has_attribute rather than typeof.  */
+  in_typeof++;
+
+  /* The first operand: one of DECL, EXPR, or TYPE.  */
+  tree oper = NULL_TREE;
+  if (c_parser_next_tokens_start_typename (parser, cla_prefer_id))
+    {
+      struct c_type_name *tname = c_parser_type_name (parser);
+      in_typeof--;
+      if (tname)
+	{
+	  oper = groktypename (tname, NULL, NULL);
+	  pop_maybe_used (variably_modified_type_p (oper, NULL_TREE));
+	}
+    }
+  else
+    {
+      struct c_expr cexpr = c_parser_expr_no_commas (parser, NULL);
+      c_inhibit_evaluation_warnings--;
+      in_typeof--;
+      if (cexpr.value != error_mark_node)
+	{
+	  mark_exp_read (cexpr.value);
+	  oper = cexpr.value;
+	  tree etype = TREE_TYPE (oper);
+	  bool was_vm = variably_modified_type_p (etype, NULL_TREE);
+	  /* This is returned with the type so that when the type is
+	     evaluated, this can be evaluated.  */
+	  if (was_vm)
+	    oper = c_fully_fold (oper, false, NULL);
+	  pop_maybe_used (was_vm);
+	}
+    }
+
+  struct c_expr result;
+  result.original_code = ERROR_MARK;
+  result.original_type = NULL;
+
+  if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>"))
+    {
+      /* Consume the closing parenthesis if that's the next token
+	 in the likely case the built-in was invoked with fewer
+	 than two arguments.  */
+      if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+	c_parser_consume_token (parser);
+      c_inhibit_evaluation_warnings--;
+      result.set_error ();
+      return result;
+    }
+
+  parser->lex_untranslated_string = true;
+
+  location_t atloc = c_parser_peek_token (parser)->location;
+  tree attr = c_parser_attribute (parser, NULL_TREE);
+
+  parser->lex_untranslated_string = false;
+
+  if (c_parser_next_token_is (parser, CPP_CLOSE_PAREN))
+    c_parser_consume_token (parser);
+  else
+    {
+      c_parser_error (parser, "expected identifier");
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, NULL);
+
+      result.set_error ();
+      return result;
+    }
+
+  if (!attr)
+    {
+      error_at (atloc, "expected identifier");
+      c_parser_skip_until_found (parser, CPP_CLOSE_PAREN,
+				 "expected %<)%>");
+      result.set_error ();
+      return result;
+    }
+
+  result.original_code = INTEGER_CST;
+  result.original_type = boolean_type_node;
+
+  if (has_attribute (atloc, oper, attr, default_conversion))
+    result.value = boolean_true_node;
+  else
+    result.value =  boolean_false_node;
+
+  return result;
+}
+
 /* Helper function to read arguments of builtins which are interfaces
    for the middle-end nodes like COMPLEX_EXPR, VEC_PERM_EXPR and
    others.  The name of the builtin is passed using BNAME parameter.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index efbdad8..8e8af94 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6428,6 +6428,7 @@  extern int parm_index                           (tree);
 extern tree vtv_start_verification_constructor_init_function (void);
 extern tree vtv_finish_verification_constructor_init_function (tree);
 extern bool cp_omp_mappable_type		(tree);
+extern void cp_check_const_attributes (tree);
 
 /* in error.c */
 extern const char *type_as_string		(tree, int);
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index a5ad0ee..baf303f 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -1378,7 +1378,7 @@  cp_reconstruct_complex_type (tree type, tree bottom)
 /* Replaces any constexpr expression that may be into the attributes
    arguments with their reduced value.  */
 
-static void
+void
 cp_check_const_attributes (tree attributes)
 {
   if (attributes == error_mark_node)
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 6696f17..aff098e 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -2049,6 +2049,8 @@  static cp_expr cp_parser_unary_expression
   (cp_parser *, cp_id_kind * = NULL, bool = false, bool = false, bool = false);
 static enum tree_code cp_parser_unary_operator
   (cp_token *);
+static tree cp_parser_has_attribute_expression
+  (cp_parser *);
 static tree cp_parser_new_expression
   (cp_parser *);
 static vec<tree, va_gc> *cp_parser_new_placement
@@ -2380,7 +2382,7 @@  static tree cp_parser_attributes_opt
 static tree cp_parser_gnu_attributes_opt
   (cp_parser *);
 static tree cp_parser_gnu_attribute_list
-  (cp_parser *);
+  (cp_parser *, bool = false);
 static tree cp_parser_std_attribute
   (cp_parser *, tree);
 static tree cp_parser_std_attribute_spec
@@ -8065,6 +8067,9 @@  cp_parser_unary_expression (cp_parser *parser, cp_id_kind * pidk,
 	    return ret_expr;
 	  }
 
+	case RID_BUILTIN_HAS_ATTRIBUTE:
+	  return cp_parser_has_attribute_expression (parser);
+
 	case RID_NEW:
 	  return cp_parser_new_expression (parser);
 
@@ -8362,6 +8367,135 @@  cp_parser_unary_operator (cp_token* token)
     }
 }
 
+/* Parse a __builtin_has_attribute([expr|type], attribute-spec) expression.
+   Returns a representation of the expression.  */
+
+static tree
+cp_parser_has_attribute_expression (cp_parser *parser)
+{
+  location_t start_loc = cp_lexer_peek_token (parser->lexer)->location;
+
+  /* Consume the __builtin_has_attribute token.  */
+  cp_lexer_consume_token (parser->lexer);
+
+  matching_parens parens;
+  if (!parens.require_open (parser))
+    return error_mark_node;
+
+  /* Types cannot be defined in a `sizeof' expression.  Save away the
+     old message.  */
+  const char *saved_message = parser->type_definition_forbidden_message;
+  /* And create the new one.  */
+  const int kwd = RID_BUILTIN_HAS_ATTRIBUTE;
+  char *tmp = concat ("types may not be defined in %<",
+		      IDENTIFIER_POINTER (ridpointers[kwd]),
+		      "%> expressions", NULL);
+  parser->type_definition_forbidden_message = tmp;
+
+  /* The restrictions on constant-expressions do not apply inside
+     sizeof expressions.  */
+  bool saved_integral_constant_expression_p
+    = parser->integral_constant_expression_p;
+  bool saved_non_integral_constant_expression_p
+    = parser->non_integral_constant_expression_p;
+  parser->integral_constant_expression_p = false;
+
+  /* Do not actually evaluate the expression.  */
+  ++cp_unevaluated_operand;
+  ++c_inhibit_evaluation_warnings;
+
+  tree oper = NULL_TREE;
+
+  /* We can't be sure yet whether we're looking at a type-id or an
+     expression.  */
+  cp_parser_parse_tentatively (parser);
+
+  bool saved_in_type_id_in_expr_p = parser->in_type_id_in_expr_p;
+  parser->in_type_id_in_expr_p = true;
+  /* Look for the type-id.  */
+  oper = cp_parser_type_id (parser);
+  parser->in_type_id_in_expr_p = saved_in_type_id_in_expr_p;
+
+  if (cp_parser_parse_definitely (parser))
+    {
+      /* If all went well, set OPER to the type.  */
+      cp_decl_specifier_seq decl_specs;
+
+      /* Build a trivial decl-specifier-seq.  */
+      clear_decl_specs (&decl_specs);
+      decl_specs.type = oper;
+
+      /* Call grokdeclarator to figure out what type this is.  */
+      oper = grokdeclarator (NULL,
+			     &decl_specs,
+			     TYPENAME,
+			     /*initialized=*/0,
+			     /*attrlist=*/NULL);
+    }
+
+  /* If the type-id production did not work out, then we must be
+     looking at the unary-expression production.  */
+  if (!oper || oper == error_mark_node)
+    oper = cp_parser_unary_expression (parser);
+
+  /* Go back to evaluating expressions.  */
+  --cp_unevaluated_operand;
+  --c_inhibit_evaluation_warnings;
+
+  /* Free the message we created.  */
+  free (tmp);
+  /* And restore the old one.  */
+  parser->type_definition_forbidden_message = saved_message;
+  parser->integral_constant_expression_p
+    = saved_integral_constant_expression_p;
+  parser->non_integral_constant_expression_p
+    = saved_non_integral_constant_expression_p;
+
+  /* Consume the comma if it's there.  */
+  if (!cp_parser_require (parser, CPP_COMMA, RT_COMMA))
+    {
+      parens.require_close (parser);
+      return error_mark_node;
+    }
+
+  /* Parse the attribute specification.  */
+  bool ret = false;
+  location_t atloc = cp_lexer_peek_token (parser->lexer)->location;
+  if (tree attr = cp_parser_gnu_attribute_list (parser, /*exactly_one=*/true))
+    {
+      if (oper != error_mark_node)
+	{
+	  /* Fold constant expressions used in attributes first.  */
+	  cp_check_const_attributes (attr);
+
+	  /* Finally, see if OPER has been declared with ATTR.  */
+	  ret = has_attribute (atloc, oper, attr, default_conversion);
+	}
+    }
+  else
+    {
+      error_at (atloc, "expected identifier");
+      cp_parser_skip_to_closing_parenthesis (parser, true, false, false);
+    }
+
+  parens.require_close (parser);
+
+  /* Construct a location e.g. :
+     __builtin_has_attribute (oper, attr)
+     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+     with start == caret at the start of the built-in token,
+     and with the endpoint at the final closing paren.  */
+  location_t finish_loc
+    = cp_lexer_previous_token (parser->lexer)->location;
+  location_t compound_loc
+    = make_location (start_loc, start_loc, finish_loc);
+
+  cp_expr ret_expr (ret ? boolean_true_node : boolean_false_node);
+  ret_expr.set_location (compound_loc);
+  ret_expr = ret_expr.maybe_add_location_wrapper ();
+  return ret_expr;
+}
+
 /* Parse a new-expression.
 
    new-expression:
@@ -25154,7 +25288,7 @@  cp_parser_gnu_attributes_opt (cp_parser* parser)
    the arguments, if any.  */
 
 static tree
-cp_parser_gnu_attribute_list (cp_parser* parser)
+cp_parser_gnu_attribute_list (cp_parser* parser, bool exactly_one /* = false */)
 {
   tree attribute_list = NULL_TREE;
   bool save_translate_strings_p = parser->translate_strings_p;
@@ -25221,9 +25355,9 @@  cp_parser_gnu_attribute_list (cp_parser* parser)
 
 	  token = cp_lexer_peek_token (parser->lexer);
 	}
-      /* Now, look for more attributes.  If the next token isn't a
-	 `,', we're done.  */
-      if (token->type != CPP_COMMA)
+      /* Unless EXACTLY_ONE is set look for more attributes.
+	 If the next token isn't a `,', we're done.  */
+      if (exactly_one || token->type != CPP_COMMA)
 	break;
 
       /* Consume the comma and keep going.  */
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8ffb0cd..3c64a289 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -11069,6 +11069,7 @@  is called and the @var{flag} argument passed to it.
 @findex __builtin_call_with_static_chain
 @findex __builtin_extend_pointer
 @findex __builtin_fpclassify
+@findex __builtin_has_attribute
 @findex __builtin_isfinite
 @findex __builtin_isnormal
 @findex __builtin_isgreater
@@ -11726,6 +11727,31 @@  check its compatibility with @var{size}.
 
 @end deftypefn
 
+@deftypefn {Built-in Function} bool __builtin_has_attribute (@var{type-or-expression}, @var{attribute})
+The @code{__builtin_has_attribute} function evaluates to an integer constant
+expression equal to @code{true} if the symbol or type referenced by
+the @var{type-or-expression} argument has been declared with
+the @var{attribute} referenced by the second argument.  Neither argument
+is evaluated.  The @var{type-or-expression} argument is subject to the same
+restrictions as the argument to @code{typeof} (@pxref{Typeof}).  The
+@var{attribute} argument is an attribute name optionally followed by
+arguments.  When no attribute arguments are specified for an attribute
+that expects one or more arguments the function returns @code{true} if
+@var{type-or-expression} has been declared with the attribute regardless
+of the attribute argument values.  For example, the first call to
+the function below evaluates to @code{true} because @code{x} is
+declared with the @code{aligned} attribute but the second call evaluates
+to @code{false} because @code{x} is declared @code{aligned (8)} and
+not @code{aligned (4)}.
+
+@smallexample
+__attribute__ ((aligned (8))) int x;
+_Static_assert (__builtin_has_attribute (x, aligned), "aligned");
+_Static_assert (!__builtin_has_attribute (x, aligned (4)), "aligned (4)");
+@end smallexample
+
+@end deftypefn
+
 @deftypefn {Built-in Function} @var{type} __builtin_speculation_safe_value (@var{type} val, @var{type} failval)
 
 This built-in function can be used to help mitigate against unsafe
diff --git a/gcc/testsuite/c-c++-common/builtin-has-attribute-2.c b/gcc/testsuite/c-c++-common/builtin-has-attribute-2.c
new file mode 100644
index 0000000..7e77df2
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/builtin-has-attribute-2.c
@@ -0,0 +1,199 @@ 
+/* Verify __builtin_has_attribute return value for types.
+   { dg-do compile }
+   { dg-options "-Wall -ftrack-macro-expansion=0" }
+   { dg-options "-Wall -Wno-narrowing -Wno-unused-local-typedefs -ftrack-macro-expansion=0" { target c++ } }  */
+
+#define ATTR(...) __attribute__ ((__VA_ARGS__))
+
+#define A(expect, sym, attr)						\
+  typedef int Assert [1 - 2 * !(__builtin_has_attribute (sym, attr) == expect)]
+
+struct ATTR (packed) Packed { char c; int i; };
+
+void fvoid (void);
+struct Packed fpacked (void);
+
+union OrdinaryUnion { void *p; int i; };
+union ATTR (transparent_union) TransparentUnion { void *p; int i; };
+
+/* Exercise __builtin_has_attribute with the first argument that
+   is a type.  */
+
+void test_type (int n)
+{
+  A (0, int, aligned);
+  A (0, int, aligned (1));
+  A (0, int, aligned (2));
+  A (0, int[1], aligned);
+  A (0, int[1], aligned (2));
+  A (0, int[n], aligned);
+  A (0, int[n], aligned (4));
+
+  A (1, ATTR (aligned) char, aligned);
+  A (1, ATTR (aligned (2)) short, aligned);
+  A (1, ATTR (aligned (4)) int, aligned);
+
+  A (0, int ATTR (aligned (4)), aligned (2));
+  A (0, int ATTR (aligned (2)), aligned (4));
+  /* GCC retains both attributes in the */
+  A (0, int ATTR (aligned (2), aligned (4)), aligned (2));
+  A (1, int ATTR (aligned (2), aligned (4)), aligned (4));
+  /* The following fails due to bug 87524.
+     A (1, int ATTR (aligned (4), aligned (2))), aligned (4)); */
+  A (0, int ATTR (aligned (4), aligned (2)), aligned (8));
+
+  A (1, int ATTR (aligned (8)), aligned (1 + 7));
+
+  enum { eight = 8 };
+  A (1, int ATTR (aligned (8)), aligned (eight));
+  A (1, int ATTR (aligned (eight)), aligned (1 + 7));
+
+  struct NotPacked { char c; int i; };
+  A (0, struct NotPacked, packed);
+  A (1, struct Packed, packed);
+
+  /* Exercise types returned from a function.  */
+  A (0, fvoid (), packed);
+  A (1, fpacked (), packed);
+
+  struct ATTR (aligned (2), packed) Aligned2Packed { char c; int i; };
+  A (1, struct Aligned2Packed, aligned);
+  A (1, struct Aligned2Packed, aligned (2));
+  A (0, struct Aligned2Packed, aligned (4));
+  A (1, struct Aligned2Packed, packed);
+
+  A (0, int, may_alias);
+  A (1, ATTR (may_alias) int, may_alias);
+
+  A (0, char, warn_if_not_aligned (1));
+  A (0, char, warn_if_not_aligned (2));
+
+  A (1, ATTR (warn_if_not_aligned (2)) char, warn_if_not_aligned);
+  A (0, ATTR (warn_if_not_aligned (2)) char, warn_if_not_aligned (1));
+  A (1, ATTR (warn_if_not_aligned (2)) char, warn_if_not_aligned (2));
+  A (0, ATTR (warn_if_not_aligned (2)) char, warn_if_not_aligned (4));
+
+  A (0, union OrdinaryUnion, transparent_union);
+
+  A (1, union TransparentUnion, transparent_union);
+  A (1, const union TransparentUnion, transparent_union);
+}
+
+/* Exercise __builtin_has_attribute with the first argument that
+   is a typedef.  */
+
+void test_typedef (int n)
+{
+  typedef char A1[1];
+  A (0, A1, aligned);
+  A (0, A1, aligned (1));
+  A (0, A1, aligned (2));
+
+  typedef char An[n];
+  A (0, An, aligned);
+  A (0, An, aligned (1));
+  A (0, An, aligned (2));
+
+  typedef ATTR (aligned (8)) short AI8;
+  A (1, AI8, aligned);
+  A (0, AI8, aligned (4));
+  A (1, AI8, aligned (8));
+  A (0, AI8, aligned (16));
+
+  A (1, const AI8, aligned);
+  A (1, const volatile AI8, aligned);
+
+  typedef ATTR (aligned (2), aligned (8), aligned (16)) int AI16;
+  A (1, AI16, aligned);
+  A (0, AI16, aligned (1));
+  A (0, AI16, aligned (2));
+  A (0, AI16, aligned (4));
+  A (0, AI16, aligned (8));
+  A (1, AI16, aligned (16));
+  A (0, AI16, aligned (32));
+
+  typedef const AI16 CAI16;
+  A (1, CAI16, aligned);
+  A (0, CAI16, aligned (1));
+  A (1, CAI16, aligned (16));
+
+  typedef int I;
+  A (0, I, may_alias);
+  A (0, AI8, may_alias);
+
+  typedef ATTR (may_alias) int MAI;
+  A (1, MAI, may_alias);
+
+  typedef ATTR (aligned (4), may_alias) char A4MAC;
+  A (0, A4MAC, aligned (0));
+  A (0, A4MAC, aligned (1));
+  A (0, A4MAC, aligned (2));
+  A (1, A4MAC, aligned (4));
+  A (0, A4MAC, aligned (8));
+  A (1, A4MAC, may_alias);
+
+  typedef ATTR (may_alias, aligned (8)) char A8MAC;
+  A (1, A8MAC, aligned);
+  A (0, A8MAC, aligned (0));
+  A (0, A8MAC, aligned (1));
+  A (0, A8MAC, aligned (2));
+  A (0, A8MAC, aligned (4));
+  A (1, A8MAC, aligned (8));
+  A (0, A8MAC, aligned (16));
+  A (1, A8MAC, may_alias);
+
+  typedef ATTR (may_alias) const AI8 CMAI8;
+  A (1, CMAI8, aligned);
+  A (1, CMAI8, may_alias);
+  A (0, CMAI8, aligned (4));
+  A (1, CMAI8, aligned (8));
+
+  typedef void Fnull (void*, void*, void*);
+  A (0, Fnull, nonnull);
+  A (0, Fnull, nonnull (1));
+  A (0, Fnull, nonnull (2));
+  A (0, Fnull, nonnull (3));
+
+  typedef ATTR (nonnull) Fnull Fnonnull;
+  A (1, Fnonnull, nonnull);
+  A (1, Fnonnull, nonnull (1));
+  A (1, Fnonnull, nonnull (2));
+  A (1, Fnonnull, nonnull (3));
+
+  typedef ATTR (nonnull (2)) void Fnonnull_2 (void*, void*, void*);
+  A (0, Fnonnull_2, nonnull);
+  A (0, Fnonnull_2, nonnull (1));
+  A (1, Fnonnull_2, nonnull (2));
+  A (0, Fnonnull_2, nonnull (3));
+
+  typedef ATTR (nonnull (1), nonnull (2), nonnull (3))
+    void Fnonnull_1_2_3 (void*, void*, void*);
+
+  /* The following fails because  the built-in doesn't recognize that
+     a single nonnull with no arguments is the same as one nonnull for
+     each function parameter.  Disable the testing for now.
+     A (1, Fnonnull_1_2_3, nonnull);
+  */
+  A (1, Fnonnull_1_2_3, nonnull (1));
+  A (1, Fnonnull_1_2_3, nonnull (2));
+  A (1, Fnonnull_1_2_3, nonnull (3));
+
+  typedef void Freturns (void);
+  A (0, Fnull, noreturn);
+  A (0, Freturns, noreturn);
+
+  typedef ATTR (warn_if_not_aligned (8)) char CWA8;
+  A (0, CWA8, warn_if_not_aligned (2));
+  A (0, CWA8, warn_if_not_aligned (4));
+  A (1, CWA8, warn_if_not_aligned (8));
+  A (0, CWA8, warn_if_not_aligned (16));
+
+  typedef union OrdinaryUnion OrdUnion;
+  A (0, OrdUnion, transparent_union);
+
+  /* The attribute is ignored on typedefs but GCC fails to diagnose
+     it (see bug ).  */
+  typedef union ATTR (transparent_union)
+    OrdinaryUnion TransUnion;   /* { dg-warning "\\\[-Wattributes" "pr87578" { xfail { ! { c++ } } } } */
+  A (0, TransUnion, transparent_union);
+}
diff --git a/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c b/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c
new file mode 100644
index 0000000..7331478
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/builtin-has-attribute-3.c
@@ -0,0 +1,143 @@ 
+/* Verify __builtin_has_attribute return value for functions.
+   { dg-do compile }
+   { dg-options "-Wall -ftrack-macro-expansion=0" }
+   { dg-options "-Wall -Wno-narrowing -Wno-unused-local-typedefs -ftrack-macro-expansion=0" { target c++ } }  */
+
+#define ATTR(...) __attribute__ ((__VA_ARGS__))
+
+void fnone (void);
+
+ATTR (aligned) void faligned (void);
+ATTR (aligned (1)) void faligned_1 (void);
+ATTR (aligned (2)) void faligned_2 (void);
+ATTR (aligned (4)) void faligned_4 (void);
+ATTR (aligned (8)) void faligned_8 (void);
+
+ATTR (alloc_size (1)) void* falloc_size_1 (int, int);
+ATTR (alloc_size (2)) void* falloc_size_2 (int, int);
+ATTR (alloc_size (2)) void* falloc_size_2_4 (int, int, int, int);
+
+ATTR (alloc_align (1)) void* falloc_align_1 (int, int);
+ATTR (alloc_align (2)) void* falloc_align_2 (int, int);
+ATTR (alloc_align (1), alloc_size (2)) void* falloc_align_1_size_2 (int, int);
+ATTR (alloc_align (2), alloc_size (1)) void* falloc_align_2_size_1 (int, int);
+
+#if __cplusplus
+extern "C"
+#endif
+ATTR (noreturn) void fnoreturn (void) { __builtin_abort (); }
+
+ATTR (alias ("fnoreturn")) void falias (void);
+
+#define A(expect, sym, attr)						\
+  typedef int Assert [1 - 2 * !(__builtin_has_attribute (sym, attr) == expect)]
+
+void test_aligned (void)
+{
+  A (0, fnone, aligned);
+  A (0, fnone, aligned (0));
+  A (0, fnone, aligned (1));
+  A (0, fnone, aligned (2));
+  A (0, fnone, aligned (4));
+  A (0, fnone, aligned (8));
+  A (0, fnone, aligned (16));
+
+  A (1, faligned, aligned);
+  A (0, faligned, aligned (0));
+  A (0, faligned, aligned (1));
+  A (0, faligned, aligned (2));
+
+  A (1, faligned_1, aligned);
+  A (0, faligned_1, aligned (0));
+  A (1, faligned_1, aligned (1));
+  A (0, faligned_1, aligned (2));
+  A (0, faligned_1, aligned (4));
+
+  A (1, faligned_2, aligned);
+  A (0, faligned_2, aligned (0));
+  A (0, faligned_2, aligned (1));
+  A (1, faligned_2, aligned (2));
+  A (0, faligned_2, aligned (4));
+}
+
+void test_alloc_slign (void)
+{
+  A (0, fnone, alloc_align);
+  A (0, falloc_size_1, alloc_align);
+  A (1, falloc_align_1, alloc_align);
+  A (1, falloc_align_2, alloc_align);
+
+  A (0, fnone, alloc_align (1));        /* { dg-warning "\\\[-Wattributes" } */
+  A (0, falloc_size_1, alloc_align (1));
+  A (1, falloc_align_1, alloc_align (1));
+  A (0, falloc_align_2, alloc_align (1));
+  A (1, falloc_align_2, alloc_align (2));
+}
+
+void test_alloc_size (void)
+{
+  A (0, fnone, alloc_size);
+  A (0, fnone, alloc_size (1));         /* { dg-warning "\\\[-Wattributes" } */
+  A (0, fnone, alloc_size (2));         /* { dg-warning "\\\[-Wattributes" } */
+  A (0, falloc_align_1, alloc_size (1));
+  A (0, falloc_align_2, alloc_size (1));
+  A (1, falloc_size_1, alloc_size (1));
+  A (0, falloc_size_1, alloc_size (2));
+  A (0, falloc_size_2, alloc_size (1));
+  A (1, falloc_size_2, alloc_size (2));
+}
+
+void test_alias (void)
+{
+  A (0, fnoreturn, alias);
+  A (1, falias, alias);
+  A (1, falias, alias ("fnoreturn"));
+  A (0, falias, alias ("falias"));
+  A (0, falias, alias ("fnone"));
+}
+
+ATTR (format (printf, 2, 4)) void
+fformat_printf_2_3 (int, const char*, int, ...);
+
+void test_format (void)
+{
+  A (0, fnone, format);
+  A (0, fnone, format (printf));
+  A (0, fnone, format (printf, 2));
+}
+
+
+ATTR (visibility ("default")) void fdefault (void);
+ATTR (visibility ("hidden")) void fhidden (void);
+ATTR (visibility ("internal")) void finternal (void);
+ATTR (visibility ("protected")) void fprotected (void);
+
+void test_visibility (void)
+{
+  A (0, fnone, visibility ("default"));
+  A (0, fnone, visibility ("hidden"));
+  A (0, fnone, visibility ("internal"));
+  A (0, fnone, visibility ("protected"));
+
+  A (1, fdefault, visibility ("default"));
+  A (0, fdefault, visibility ("hidden"));
+  A (0, fdefault, visibility ("internal"));
+  A (0, fdefault, visibility ("protected"));
+
+  A (0, fhidden, visibility ("default"));
+  A (1, fhidden, visibility ("hidden"));
+  A (0, fhidden, visibility ("internal"));
+  A (0, fhidden, visibility ("protected"));
+
+  A (0, finternal, visibility ("default"));
+  A (0, finternal, visibility ("hidden"));
+  A (1, finternal, visibility ("internal"));
+  A (0, finternal, visibility ("protected"));
+
+  A (0, fprotected, visibility ("default"));
+  A (0, fprotected, visibility ("hidden"));
+  A (0, fprotected, visibility ("internal"));
+  A (1, fprotected, visibility ("protected"));
+}
+
+/* { dg-prune-output "specifies less restrictive attribute" } */
diff --git a/gcc/testsuite/c-c++-common/builtin-has-attribute.c b/gcc/testsuite/c-c++-common/builtin-has-attribute.c
new file mode 100644
index 0000000..50c8aa0
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/builtin-has-attribute.c
@@ -0,0 +1,59 @@ 
+/* Verify __builtin_has_attribute error handling.
+   { dg-do compile }
+   { dg-options "-Wall -ftrack-macro-expansion=0" }  */
+
+#define ATTR(list) __attribute__ (list)
+
+void fnone (void);
+
+ATTR ((aligned)) void faligned (void);
+ATTR ((aligned (8))) void faligned_8 (void);
+
+#define has_attr(x, attr)   __builtin_has_attribute (x, attr)
+
+#define A(expect, sym, attr)						\
+  typedef int Assert [1 - 2 * !(has_attr (sym, attr) == expect)]
+
+
+int b;
+
+/* Exercise syntactically invalid arguments.  */
+
+void test_bad_arguments (void)
+{
+  b = __builtin_has_attribute ();            /* { dg-error "expected \(primary-\)?expression|expected .,." } */
+  b = __builtin_has_attribute (1);           /* { dg-error "expected .,." } */
+  b = __builtin_has_attribute (void);        /* { dg-error "expected .,." } */
+  b = __builtin_has_attribute (foo);         /* { dg-error ".foo. \(undeclared|was not declared\)" } */
+  /* { dg-error "expected .,." "missing comma" { target *-*-* } .-1 } */
+
+  /* Verify the implementationm doesn't ICE.  */
+  b = __builtin_has_attribute (foobar, aligned);  /* { dg-error ".foobar. \(undeclared|was not declared\)" } */
+
+  b = __builtin_has_attribute (1, 2, 3);     /* { dg-error "expected identifier" } */
+  b = __builtin_has_attribute (int, 1 + 2);  /* { dg-error "expected identifier" } */
+  b = __builtin_has_attribute (2, "aligned"); /* { dg-error "expected identifier" } */
+}
+
+/* Exercise syntactically valid arguments applied in invalid ways.  */
+
+void test_invalid_arguments (void)
+{
+  b = has_attr (fnone, align);        /* { dg-error "unknown attribute .align." } */
+  b = has_attr (fnone, aligned (3));  /* { dg-error "alignment is not a positive power of 2" } */
+
+  /* Verify the out-of-bounds arguments are diagnosed and the result
+     of the built-in is false.  */
+  A (0, fnone, alloc_size (1));       /* { dg-warning "\\\[-Wattributes]" } */
+  A (0, fnone, alloc_size (2));       /* { dg-warning "\\\[-Wattributes]" } */
+
+  A (0, int, alloc_size (1));         /* { dg-warning ".alloc_size. attribute only applies to function types" } */
+
+  int i = 1;
+  A (0, i, alloc_size (1));           /* { dg-warning ".alloc_size. attribute only applies to function types" } */
+
+  A (0, faligned_8, aligned (i));     /* { dg-error "alignment is not an integer constant" } */
+
+  typedef ATTR ((aligned (2))) char CA2;
+  b = has_attr (CA2[2], aligned);     /* { dg-error "alignment of array elements is greater than element size" } */
+}
diff --git a/gcc/testsuite/gcc.dg/builtin-has-attribute.c b/gcc/testsuite/gcc.dg/builtin-has-attribute.c
new file mode 100644
index 0000000..8c11cf8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/builtin-has-attribute.c
@@ -0,0 +1,45 @@ 
+/* Verify that defining a type in __builtin_has_attribute triggers
+   the expected -Wc++-compat warning and evaluates as expected.
+   Also verify that the expression in __builtin_has_attribute is
+   not evaluated.
+
+  { dg-do run }
+  { dg-options "-O2 -Wall -Wc++-compat" }  */
+
+#define ATTR(list) __attribute__ (list)
+
+#define A(expect, sym, attr)						\
+  typedef int Assert [1 - 2 * !(__builtin_has_attribute (sym, attr) == expect)]
+
+int nfails;
+
+#define assert(expr)						\
+  ((expr)							\
+   ? (void)0							\
+   : (__builtin_printf ("Assertion failed on line %i: %s\n",	\
+			__LINE__, #expr),			\
+      ++nfails))
+
+A (0, struct A { int i; }, aligned);   /* { dg-warning "expression is invalid in C\\\+\\\+" } */
+A (1, struct ATTR ((aligned)) B { int i; }, aligned);   /* { dg-warning "expression is invalid in C\\\+\\\+" } */
+
+
+int f (void)
+{
+  __builtin_abort ();
+}
+
+int n = 1;
+
+int main (void)
+{
+  assert (0 == __builtin_has_attribute (int[n++], aligned));
+  assert (1 == __builtin_has_attribute (ATTR ((aligned)) int[n++], aligned));
+  assert (1 == __builtin_has_attribute (ATTR ((aligned)) int[f ()], aligned));
+  assert (1 == 1);
+
+  if (nfails)
+    __builtin_abort ();
+
+  return 0;
+}