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"));

Reply via email to