diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog
index 7b30b3bfc43cdaed068cb69e24d1fb6a5b34cd35..04134ec444db622f3e1295e18a3b2675779de54a 100644
--- a/gcc/c-family/ChangeLog
+++ b/gcc/c-family/ChangeLog
@@ -1,3 +1,8 @@
+2011-11-07  Jason Merrill  <jason@redhat.com>
+
+	PR c++/35688
+	* c-common.c, c-common.h: Revert yesterday's changes.
+
 2011-11-06  Jason Merrill  <jason@redhat.com>
 
 	PR c++/35688
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 92fb363552bb38135318b08d63699997f45e69dd..0329bc7e01cb58456095b663971e4cbc3325305f 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -7073,22 +7073,6 @@ handle_visibility_attribute (tree *node, tree name, tree args,
   return NULL_TREE;
 }
 
-/* Returns true iff DECL actually has visibility specified by an attribute.
-   We check for an explicit attribute, rather than just checking
-   DECL_VISIBILITY_SPECIFIED, to distinguish the use of an attribute from
-   the use of a "#pragma GCC visibility push(...)"; in the latter case we
-   still want other considerations to be able to overrule the #pragma.  */
-
-bool
-decl_has_visibility_attr (tree decl)
-{
-  tree attrs = DECL_ATTRIBUTES (decl);
-  return (lookup_attribute ("visibility", attrs)
-	  || (TARGET_DLLIMPORT_DECL_ATTRIBUTES
-	      && (lookup_attribute ("dllimport", attrs)
-		  || lookup_attribute ("dllexport", attrs))));
-}
-
 /* Determine the ELF symbol visibility for DECL, which is either a
    variable or a function.  It is an error to use this function if a
    definition of DECL is not available in this translation unit.
@@ -7104,8 +7088,15 @@ c_determine_visibility (tree decl)
 
   /* If the user explicitly specified the visibility with an
      attribute, honor that.  DECL_VISIBILITY will have been set during
-     the processing of the attribute.  */
-  if (decl_has_visibility_attr (decl))
+     the processing of the attribute.  We check for an explicit
+     attribute, rather than just checking DECL_VISIBILITY_SPECIFIED,
+     to distinguish the use of an attribute from the use of a "#pragma
+     GCC visibility push(...)"; in the latter case we still want other
+     considerations to be able to overrule the #pragma.  */
+  if (lookup_attribute ("visibility", DECL_ATTRIBUTES (decl))
+      || (TARGET_DLLIMPORT_DECL_ATTRIBUTES
+	  && (lookup_attribute ("dllimport", DECL_ATTRIBUTES (decl))
+	      || lookup_attribute ("dllexport", DECL_ATTRIBUTES (decl)))))
     return true;
 
   /* Set default visibility to whatever the user supplied with
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index f914d494f87e555827502805d7fb609b3bd66775..bff6956cc1442614acaf04bd39d05febe70016a9 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -775,7 +775,6 @@ extern void overflow_warning (location_t, tree);
 extern void warn_logical_operator (location_t, enum tree_code, tree,
 				   enum tree_code, tree, enum tree_code, tree);
 extern void check_main_parameter_types (tree decl);
-extern bool decl_has_visibility_attr (tree);
 extern bool c_determine_visibility (tree);
 extern bool same_scalar_type_ignoring_signedness (tree, tree);
 extern void mark_valid_location_for_stdc_pragma (bool);
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 41922ae3dccfbb87ff8c387e814794426f522870..c66c9dc8d73e6f3ae8ca745d28466aee7d56ef5f 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,5 +1,11 @@
 2011-11-07  Jason Merrill  <jason@redhat.com>
 
+	PR c++/35688
+	* decl2.c (constrain_visibility): Return void.  Add tmpl parm
+	which gives the constraint priority over an attribute.
+	(constrain_visibility_for_template, determine_visibility): Adjust.
+	* pt.c (instantiate_class_template_1): Call determine_visibility.
+
 	PR c++/33255
 	* decl.c (save_function_data): Clear local_typedefs.
 
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 80fb0c397d4786f925c4a03eb7ca135e6fd1eb65..17be3ad7137a685a77db2cee385625645f79682d 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -1954,10 +1954,12 @@ type_visibility (tree type)
 }
 
 /* Limit the visibility of DECL to VISIBILITY, if not explicitly
-   specified (or if VISIBILITY is static).  */
+   specified (or if VISIBILITY is static).  If TMPL is true, this
+   constraint is for a template argument, and takes precedence
+   over explicitly-specified visibility on the template.  */
 
-static bool
-constrain_visibility (tree decl, int visibility)
+static void
+constrain_visibility (tree decl, int visibility, bool tmpl)
 {
   if (visibility == VISIBILITY_ANON)
     {
@@ -1974,16 +1976,11 @@ constrain_visibility (tree decl, int visibility)
 	    DECL_NOT_REALLY_EXTERN (decl) = 1;
 	}
     }
-  /* We check decl_has_visibility_attr rather than
-     DECL_VISIBILITY_SPECIFIED here because we want other considerations
-     to override visibility from a namespace or #pragma.  */
   else if (visibility > DECL_VISIBILITY (decl)
-	   && !decl_has_visibility_attr (decl))
+	   && (tmpl || !DECL_VISIBILITY_SPECIFIED (decl)))
     {
       DECL_VISIBILITY (decl) = (enum symbol_visibility) visibility;
-      return true;
     }
-  return false;
 }
 
 /* Constrain the visibility of DECL based on the visibility of its template
@@ -2019,7 +2016,7 @@ constrain_visibility_for_template (tree decl, tree targs)
 	    }
 	}
       if (vis)
-	constrain_visibility (decl, vis);
+	constrain_visibility (decl, vis, true);
     }
 }
 
@@ -2132,7 +2129,7 @@ determine_visibility (tree decl)
 	  if (underlying_vis == VISIBILITY_ANON
 	      || (CLASS_TYPE_P (underlying_type)
 		  && CLASSTYPE_VISIBILITY_SPECIFIED (underlying_type)))
-	    constrain_visibility (decl, underlying_vis);
+	    constrain_visibility (decl, underlying_vis, false);
 	  else
 	    DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT;
 	}
@@ -2140,7 +2137,7 @@ determine_visibility (tree decl)
 	{
 	  /* tinfo visibility is based on the type it's for.  */
 	  constrain_visibility
-	    (decl, type_visibility (TREE_TYPE (DECL_NAME (decl))));
+	    (decl, type_visibility (TREE_TYPE (DECL_NAME (decl))), false);
 
 	  /* Give the target a chance to override the visibility associated
 	     with DECL.  */
@@ -2207,14 +2204,14 @@ determine_visibility (tree decl)
   if (decl_anon_ns_mem_p (decl))
     /* Names in an anonymous namespace get internal linkage.
        This might change once we implement export.  */
-    constrain_visibility (decl, VISIBILITY_ANON);
+    constrain_visibility (decl, VISIBILITY_ANON, false);
   else if (TREE_CODE (decl) != TYPE_DECL)
     {
       /* Propagate anonymity from type to decl.  */
       int tvis = type_visibility (TREE_TYPE (decl));
       if (tvis == VISIBILITY_ANON
 	  || ! DECL_VISIBILITY_SPECIFIED (decl))
-	constrain_visibility (decl, tvis);
+	constrain_visibility (decl, tvis, false);
     }
   else if (no_linkage_check (TREE_TYPE (decl), /*relaxed_p=*/true))
     /* DR 757: A type without linkage shall not be used as the type of a
@@ -2225,7 +2222,7 @@ determine_visibility (tree decl)
 
        Since non-extern "C" decls need to be defined in the same
        translation unit, we can make the type internal.  */
-    constrain_visibility (decl, VISIBILITY_ANON);
+    constrain_visibility (decl, VISIBILITY_ANON, false);
 
   /* If visibility changed and DECL already has DECL_RTL, ensure
      symbol flags are updated.  */
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 493e3e6bcc311e2e6679c341f6592cc0f9192a4e..52f4d4730e30a1ea75e84094136d249b234e0dca 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -8600,6 +8600,8 @@ instantiate_class_template_1 (tree type)
     {
       CLASSTYPE_VISIBILITY_SPECIFIED (type) = 1;
       CLASSTYPE_VISIBILITY (type) = CLASSTYPE_VISIBILITY (pattern);
+      /* Adjust visibility for template arguments.  */
+      determine_visibility (TYPE_MAIN_DECL (type));
     }
   CLASSTYPE_FINAL (type) = CLASSTYPE_FINAL (pattern);
 
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 46f702a66b54889a690a80197a0befee3aae3954..b9c39d30537e49a8ff9d8b527b0cb7a052852ebb 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,8 @@
+2011-11-07  Jason Merrill  <jason@redhat.com>
+
+	PR c++/35688
+	* g++.dg/ext/visibility/template8.C: New.
+
 2011-11-07  Jakub Jelinek  <jakub@redhat.com>
 
 	PR tree-optimization/50789
diff --git a/gcc/testsuite/g++.dg/ext/visibility/template8.C b/gcc/testsuite/g++.dg/ext/visibility/template8.C
new file mode 100644
index 0000000000000000000000000000000000000000..e491882e05773c00921415f8756f958066a04cd8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/visibility/template8.C
@@ -0,0 +1,26 @@
+// PR c++/35688
+// { dg-require-visibility "" }
+// { dg-options "-fvisibility=hidden" }
+
+// { dg-final { scan-hidden "_Z1gI1BEvT_" } }
+// { dg-final { scan-hidden "_Z1gI1AI1BEEvT_" } }
+
+// Test that template argument visibility takes priority even over an
+// explicit visibility attribute on a template.
+
+template <class T>
+struct __attribute ((visibility ("default"))) A { };
+template <class T>
+void g(T) __attribute ((visibility ("default")));
+
+struct B { };
+
+template <class T>
+void g(T)
+{ }
+
+int main()
+{
+  g(B());
+  g(A<B>());
+}