commit 65b7f772bb3240688aae6af41d0a1200972885ae
Author: Jason Merrill <jason@redhat.com>
Date:   Tue Dec 2 17:11:37 2014 -0500

    	* cp-tree.h (NAMESPACE_ABI_TAG): New.
    	* name-lookup.c (handle_namespace_attrs): Set it.
    	* class.c (check_tag): Split out from find_abi_tags_r.
    	(find_abi_tags_r): Also check namespace tags.
    	(mark_type_abi_tags): Also mark namespace tags.

diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index c83c8ad..07bbc69 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -1352,18 +1352,73 @@ handle_using_decl (tree using_decl, tree t)
     alter_access (t, decl, access);
 }
 
+/* Data structure for find_abi_tags_r, below.  */
+
+struct abi_tag_data
+{
+  tree t;		// The type that we're checking for missing tags.
+  tree subob;		// The subobject of T that we're getting tags from.
+  tree tags; // error_mark_node for diagnostics, or a list of missing tags.
+};
+
+/* Subroutine of find_abi_tags_r. Handle a single TAG found on the class TP
+   in the context of P.  TAG can be either an identifier (the DECL_NAME of
+   a tag NAMESPACE_DECL) or a STRING_CST (a tag attribute).  */
+
+static void
+check_tag (tree tag, tree *tp, abi_tag_data *p)
+{
+  tree id;
+
+  if (TREE_CODE (tag) == STRING_CST)
+    id = get_identifier (TREE_STRING_POINTER (tag));
+  else
+    {
+      id = tag;
+      tag = NULL_TREE;
+    }
+
+  if (!IDENTIFIER_MARKED (id))
+    {
+      if (!tag)
+	tag = build_string (IDENTIFIER_LENGTH (id) + 1,
+			    IDENTIFIER_POINTER (id));
+      if (p->tags != error_mark_node)
+	{
+	  /* We're collecting tags from template arguments.  */
+	  p->tags = tree_cons (NULL_TREE, tag, p->tags);
+	  ABI_TAG_IMPLICIT (p->tags) = true;
+
+	  /* Don't inherit this tag multiple times.  */
+	  IDENTIFIER_MARKED (id) = true;
+	}
+
+      /* Otherwise we're diagnosing missing tags.  */
+      else if (TYPE_P (p->subob))
+	{
+	  if (warning (OPT_Wabi_tag, "%qT does not have the %E abi tag "
+		       "that base %qT has", p->t, tag, p->subob))
+	    inform (location_of (p->subob), "%qT declared here",
+		    p->subob);
+	}
+      else
+	{
+	  if (warning (OPT_Wabi_tag, "%qT does not have the %E abi tag "
+		       "that %qT (used in the type of %qD) has",
+		       p->t, tag, *tp, p->subob))
+	    {
+	      inform (location_of (p->subob), "%qD declared here",
+		      p->subob);
+	      inform (location_of (*tp), "%qT declared here", *tp);
+	    }
+	}
+    }
+}
+
 /* walk_tree callback for check_abi_tags: if the type at *TP involves any
    types with abi tags, add the corresponding identifiers to the VEC in
    *DATA and set IDENTIFIER_MARKED.  */
 
-struct abi_tag_data
-{
-  tree t;
-  tree subob;
-  // error_mark_node to get diagnostics; otherwise collect missing tags here
-  tree tags;
-};
-
 static tree
 find_abi_tags_r (tree *tp, int *walk_subtrees, void *data)
 {
@@ -1374,48 +1429,21 @@ find_abi_tags_r (tree *tp, int *walk_subtrees, void *data)
      anyway, but let's make sure of it.  */
   *walk_subtrees = false;
 
+  abi_tag_data *p = static_cast<struct abi_tag_data*>(data);
+
+  for (tree ns = decl_namespace_context (*tp);
+       ns != global_namespace;
+       ns = CP_DECL_CONTEXT (ns))
+    if (NAMESPACE_ABI_TAG (ns))
+      check_tag (DECL_NAME (ns), tp, p);
+
   if (tree attributes = lookup_attribute ("abi_tag", TYPE_ATTRIBUTES (*tp)))
     {
-      struct abi_tag_data *p = static_cast<struct abi_tag_data*>(data);
       for (tree list = TREE_VALUE (attributes); list;
 	   list = TREE_CHAIN (list))
 	{
 	  tree tag = TREE_VALUE (list);
-	  tree id = get_identifier (TREE_STRING_POINTER (tag));
-	  if (!IDENTIFIER_MARKED (id))
-	    {
-	      if (p->tags != error_mark_node)
-		{
-		  /* We're collecting tags from template arguments.  */
-		  tree str = build_string (IDENTIFIER_LENGTH (id),
-					   IDENTIFIER_POINTER (id));
-		  p->tags = tree_cons (NULL_TREE, str, p->tags);
-		  ABI_TAG_IMPLICIT (p->tags) = true;
-
-		  /* Don't inherit this tag multiple times.  */
-		  IDENTIFIER_MARKED (id) = true;
-		}
-
-	      /* Otherwise we're diagnosing missing tags.  */
-	      else if (TYPE_P (p->subob))
-		{
-		  if (warning (OPT_Wabi_tag, "%qT does not have the %E abi tag "
-			       "that base %qT has", p->t, tag, p->subob))
-		    inform (location_of (p->subob), "%qT declared here",
-			    p->subob);
-		}
-	      else
-		{
-		  if (warning (OPT_Wabi_tag, "%qT does not have the %E abi tag "
-			       "that %qT (used in the type of %qD) has",
-			       p->t, tag, *tp, p->subob))
-		    {
-		      inform (location_of (p->subob), "%qD declared here",
-			      p->subob);
-		      inform (location_of (*tp), "%qT declared here", *tp);
-		    }
-		}
-	    }
+	  check_tag (tag, tp, p);
 	}
     }
   return NULL_TREE;
@@ -1427,6 +1455,12 @@ find_abi_tags_r (tree *tp, int *walk_subtrees, void *data)
 static void
 mark_type_abi_tags (tree t, bool val)
 {
+  for (tree ns = decl_namespace_context (t);
+       ns != global_namespace;
+       ns = CP_DECL_CONTEXT (ns))
+    if (NAMESPACE_ABI_TAG (ns))
+      IDENTIFIER_MARKED (DECL_NAME (ns)) = val;
+
   tree attributes = lookup_attribute ("abi_tag", TYPE_ATTRIBUTES (t));
   if (attributes)
     {
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index ad1cc71..e851660 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -151,6 +151,7 @@ c-common.h, not after.
       DECL_MUTABLE_P (in FIELD_DECL)
       DECL_DEPENDENT_P (in USING_DECL)
       LABEL_DECL_BREAK (in LABEL_DECL)
+      NAMESPACE_ABI_TAG (in NAMESPACE_DECL)
    1: C_TYPEDEF_EXPLICITLY_SIGNED (in TYPE_DECL).
       DECL_TEMPLATE_INSTANTIATED (in a VAR_DECL or a FUNCTION_DECL)
       DECL_MEMBER_TEMPLATE_P (in TEMPLATE_DECL)
@@ -2642,6 +2643,11 @@ struct GTY(()) lang_decl {
 #define LOCAL_CLASS_P(NODE)				\
   (decl_function_context (TYPE_MAIN_DECL (NODE)) != NULL_TREE)
 
+/* 1 iff this NAMESPACE_DECL should also be treated as an ABI tag for
+   -Wabi-tag.  */
+#define NAMESPACE_ABI_TAG(NODE)				\
+  DECL_LANG_FLAG_0 (NAMESPACE_DECL_CHECK (NODE))
+
 /* For a NAMESPACE_DECL: the list of using namespace directives
    The PURPOSE is the used namespace, the value is the namespace
    that is the common ancestor.  */
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 71b359e..b982451 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -3610,10 +3610,8 @@ current_decl_namespace (void)
   return result;
 }
 
-/* Process any ATTRIBUTES on a namespace definition.  Currently only
-   attribute visibility is meaningful, which is a property of the syntactic
-   block rather than the namespace as a whole, so we don't touch the
-   NAMESPACE_DECL at all.  Returns true if attribute visibility is seen.  */
+/* Process any ATTRIBUTES on a namespace definition.  Returns true if
+   attribute visibility is seen.  */
 
 bool
 handle_namespace_attrs (tree ns, tree attributes)
@@ -3628,6 +3626,9 @@ handle_namespace_attrs (tree ns, tree attributes)
 
       if (is_attribute_p ("visibility", name))
 	{
+	  /* attribute visibility is a property of the syntactic block
+	     rather than the namespace as a whole, so we don't touch the
+	     NAMESPACE_DECL at all.  */
 	  tree x = args ? TREE_VALUE (args) : NULL_TREE;
 	  if (x == NULL_TREE || TREE_CODE (x) != STRING_CST || TREE_CHAIN (args))
 	    {
@@ -3645,6 +3646,10 @@ handle_namespace_attrs (tree ns, tree attributes)
 	  push_visibility (TREE_STRING_POINTER (x), 1);
 	  saw_vis = true;
 	}
+      else if (is_attribute_p ("abi_tag", name))
+	{
+	  NAMESPACE_ABI_TAG (ns) = true;
+	}
       else
 	{
 	  warning (OPT_Wattributes, "%qD attribute directive ignored",
diff --git a/gcc/testsuite/g++.dg/abi/abi-tag12.C b/gcc/testsuite/g++.dg/abi/abi-tag12.C
new file mode 100644
index 0000000..74dffa0
--- /dev/null
+++ b/gcc/testsuite/g++.dg/abi/abi-tag12.C
@@ -0,0 +1,8 @@
+// { dg-options "-Wabi-tag" }
+
+inline namespace A __attribute ((abi_tag)) {
+  struct Foo { };		// { dg-message "declared here" }
+  struct Baz: Foo { };
+}
+
+struct Bar: Foo { };		// { dg-warning "tag" }
