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;
+}
