diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 37f35339849..fb19e1f51eb 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,10 @@ +2006-03-20 Jason Merrill + + PR c++/21764 + * c-pragma.c (visstack): Move out of handle_pragma_visibility. + (push_visibility, pop_visibility): Likewise. + * c-pragma.h: Declare them. + 2006-03-20 Kaz Kojima * config/sh/sh.c (untangle_mova): Initialize n_addr and n_target. diff --git a/gcc/c-pragma.c b/gcc/c-pragma.c index 39c459528e0..b62352c2e6b 100644 --- a/gcc/c-pragma.c +++ b/gcc/c-pragma.c @@ -593,9 +593,42 @@ static void handle_pragma_visibility (cpp_reader *); typedef enum symbol_visibility visibility; DEF_VEC_I (visibility); DEF_VEC_ALLOC_I (visibility, heap); +static VEC (visibility, heap) *visstack; + +/* Push the visibility indicated by STR onto the top of the #pragma + visibility stack. */ + +void +push_visibility (const char *str) +{ + VEC_safe_push (visibility, heap, visstack, + default_visibility); + if (!strcmp (str, "default")) + default_visibility = VISIBILITY_DEFAULT; + else if (!strcmp (str, "internal")) + default_visibility = VISIBILITY_INTERNAL; + else if (!strcmp (str, "hidden")) + default_visibility = VISIBILITY_HIDDEN; + else if (!strcmp (str, "protected")) + default_visibility = VISIBILITY_PROTECTED; + else + GCC_BAD ("#pragma GCC visibility push() must specify default, internal, hidden or protected"); + visibility_options.inpragma = 1; +} + +/* Pop a level of the #pragma visibility stack. */ + +void +pop_visibility (void) +{ + default_visibility = VEC_pop (visibility, visstack); + visibility_options.inpragma + = VEC_length (visibility, visstack) != 0; +} /* Sets the default visibility for symbols to something other than that specified on the command line. */ + static void handle_pragma_visibility (cpp_reader *dummy ATTRIBUTE_UNUSED) { @@ -603,7 +636,6 @@ handle_pragma_visibility (cpp_reader *dummy ATTRIBUTE_UNUSED) tree x; enum cpp_ttype token; enum { bad, push, pop } action = bad; - static VEC (visibility, heap) *visstack; token = pragma_lex (&x); if (token == CPP_NAME) @@ -621,15 +653,9 @@ handle_pragma_visibility (cpp_reader *dummy ATTRIBUTE_UNUSED) if (pop == action) { if (!VEC_length (visibility, visstack)) - { - GCC_BAD ("no matching push for %<#pragma GCC visibility pop%>"); - } + GCC_BAD ("no matching push for %<#pragma GCC visibility pop%>"); else - { - default_visibility = VEC_pop (visibility, visstack); - visibility_options.inpragma - = VEC_length (visibility, visstack) != 0; - } + pop_visibility (); } else { @@ -637,28 +663,9 @@ handle_pragma_visibility (cpp_reader *dummy ATTRIBUTE_UNUSED) GCC_BAD ("missing %<(%> after %<#pragma GCC visibility push%> - ignored"); token = pragma_lex (&x); if (token != CPP_NAME) - { - GCC_BAD ("malformed #pragma GCC visibility push"); - } + GCC_BAD ("malformed #pragma GCC visibility push"); else - { - const char *str = IDENTIFIER_POINTER (x); - VEC_safe_push (visibility, heap, visstack, - default_visibility); - if (!strcmp (str, "default")) - default_visibility = VISIBILITY_DEFAULT; - else if (!strcmp (str, "internal")) - default_visibility = VISIBILITY_INTERNAL; - else if (!strcmp (str, "hidden")) - default_visibility = VISIBILITY_HIDDEN; - else if (!strcmp (str, "protected")) - default_visibility = VISIBILITY_PROTECTED; - else - { - GCC_BAD ("#pragma GCC visibility push() must specify default, internal, hidden or protected"); - } - visibility_options.inpragma = 1; - } + push_visibility (IDENTIFIER_POINTER (x)); if (pragma_lex (&x) != CPP_CLOSE_PAREN) GCC_BAD ("missing %<(%> after %<#pragma GCC visibility push%> - ignored"); } diff --git a/gcc/c-pragma.h b/gcc/c-pragma.h index 5868f07251c..6ebb08b685c 100644 --- a/gcc/c-pragma.h +++ b/gcc/c-pragma.h @@ -75,6 +75,8 @@ extern struct cpp_reader* parse_in; visibility is not supported on the host OS platform the statements are ignored. */ #define HANDLE_PRAGMA_VISIBILITY 1 +extern void push_visibility (const char *); +extern void pop_visibility (void); extern void init_pragma (void); diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index b3f411146eb..983165551be 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,7 +1,28 @@ +2006-03-20 Jason Merrill + + PR c++/21764, c++/19238 + * decl.c (cp_finish_decl): Call determine_visibility later. + (start_preparsed_function): Likewise. + * cp-tree.h (CP_TYPE_CONTEXT, TYPE_NAMESPACE_SCOPE_P): New macros. + (TYPE_CLASS_SCOPE_P, TYPE_FUNCTION_SCOPE_P): New macros. + * name-lookup.h (struct cp_binding_level): Add has_visibility + bitfield. + * name-lookup.c: Include c-pragma.h. + (push_namespace_with_attribs): Split out from push_namespace. + Push visibility if appropriate. Set TREE_PUBLIC on namespaces. + (leave_scope): Pop visibility if appropriate. + * decl2.c (determine_visibility_from_class): Split out from... + (determine_visibility): ...here. Handle function scope and + nested classes. + (import_export_decl): Move visibility handling to + determine_visibility_from_class. + * parser.c (cp_parser_declaration, cp_parser_namespace_name): Allow + attributes on namespace declarations. + 2006-03-15 Volker Reichelt PR c++/6634 - decl.c (grokdeclarator): Do not accept long long double. + * decl.c (grokdeclarator): Do not accept long long double. Reorganize checks for invalid (combinations of) type modifiers. Quote modifiers in messages. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 69d64667502..4b155735427 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -1964,6 +1964,8 @@ struct lang_decl GTY(()) /* NULL_TREE in DECL_CONTEXT represents the global namespace. */ #define CP_DECL_CONTEXT(NODE) \ (DECL_CONTEXT (NODE) ? DECL_CONTEXT (NODE) : global_namespace) +#define CP_TYPE_CONTEXT(NODE) \ + (TYPE_CONTEXT (NODE) ? TYPE_CONTEXT (NODE) : global_namespace) #define FROB_CONTEXT(NODE) ((NODE) == global_namespace ? NULL_TREE : (NODE)) /* 1 iff NODE has namespace scope, including the global namespace. */ @@ -1971,15 +1973,25 @@ struct lang_decl GTY(()) (!DECL_TEMPLATE_PARM_P (NODE) \ && TREE_CODE (CP_DECL_CONTEXT (NODE)) == NAMESPACE_DECL) +#define TYPE_NAMESPACE_SCOPE_P(NODE) \ + (TREE_CODE (CP_TYPE_CONTEXT (NODE)) == NAMESPACE_DECL) + /* 1 iff NODE is a class member. */ #define DECL_CLASS_SCOPE_P(NODE) \ (DECL_CONTEXT (NODE) && TYPE_P (DECL_CONTEXT (NODE))) +#define TYPE_CLASS_SCOPE_P(NODE) \ + (TYPE_CONTEXT (NODE) && TYPE_P (TYPE_CONTEXT (NODE))) + /* 1 iff NODE is function-local. */ #define DECL_FUNCTION_SCOPE_P(NODE) \ (DECL_CONTEXT (NODE) \ && TREE_CODE (DECL_CONTEXT (NODE)) == FUNCTION_DECL) +#define TYPE_FUNCTION_SCOPE_P(NODE) \ + (TYPE_CONTEXT (NODE) \ + && TREE_CODE (TYPE_CONTEXT (NODE)) == FUNCTION_DECL) + /* 1 iff VAR_DECL node NODE is a type-info decl. This flag is set for both the primary typeinfo object and the associated NTBS name. */ #define DECL_TINFO_P(NODE) TREE_LANG_FLAG_4 (VAR_DECL_CHECK (NODE)) diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index bb6a59c4c55..45feb9f0c58 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -5178,9 +5178,6 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, the class specifier. */ if (!DECL_EXTERNAL (decl)) var_definition_p = true; - /* The variable is being defined, so determine its - visibility. */ - determine_visibility (decl); } /* If the variable has an array type, lay out the type, even if there is no initializer. It is valid to index through the @@ -5244,6 +5241,10 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, initialize_local_var (decl, init); } + /* The variable is being defined, so determine its visibility. + This needs to happen after the linkage is set. */ + determine_visibility (decl); + /* If a variable is defined, and then a subsequent definition with external linkage is encountered, we will get here twice for the same variable. We want to avoid @@ -10422,12 +10423,6 @@ start_preparsed_function (tree decl1, tree attrs, int flags) maybe_apply_pragma_weak (decl1); } - /* Determine the ELF visibility attribute for the function. We must - not do this before calling "pushdecl", as we must allow - "duplicate_decls" to merge any attributes appropriately. */ - if (!DECL_CLONED_FUNCTION_P (decl1)) - determine_visibility (decl1); - /* Reset these in case the call to pushdecl changed them. */ current_function_decl = decl1; cfun->decl = decl1; @@ -10546,6 +10541,13 @@ start_preparsed_function (tree decl1, tree attrs, int flags) DECL_INTERFACE_KNOWN (decl1) = 1; } + /* Determine the ELF visibility attribute for the function. We must not + do this before calling "pushdecl", as we must allow "duplicate_decls" + to merge any attributes appropriately. We also need to wait until + linkage is set. */ + if (!DECL_CLONED_FUNCTION_P (decl1)) + determine_visibility (decl1); + begin_scope (sk_function_parms, decl1); ++function_depth; diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index b332e6e9113..88d7e8e95f3 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -82,6 +82,7 @@ static tree prune_vars_needing_no_initialization (tree *); static void write_out_vars (tree); static void import_export_class (tree); static tree get_guard_bits (tree); +static void determine_visibility_from_class (tree, tree); /* A list of static class variables. This is needed, because a static class variable can be declared inside the class without @@ -1566,13 +1567,27 @@ maybe_emit_vtables (tree ctype) } /* Like c_determine_visibility, but with additional C++-specific - behavior. */ + behavior. + + Function-scope entities can rely on the function's visibility because + it is set in start_preparsed_function. + + Class-scope entities cannot rely on the class's visibility until the end + of the enclosing class definition. + + Note that because namespaces have multiple independent definitions, + namespace visibility is handled elsewhere using the #pragma visibility + machinery rather than by decorating the namespace declaration. */ void determine_visibility (tree decl) { tree class_type; + /* Only relevant for names with external linkage. */ + if (!TREE_PUBLIC (decl)) + return; + /* Cloned constructors and destructors get the same visibility as the underlying function. That should be set up in maybe_clone_body. */ @@ -1596,6 +1611,14 @@ determine_visibility (tree decl) so they are automatically handled above. */ gcc_assert (TREE_CODE (decl) != VAR_DECL || !DECL_VTABLE_OR_VTT_P (decl)); + + if (DECL_FUNCTION_SCOPE_P (decl)) + { + tree fn = DECL_CONTEXT (decl); + DECL_VISIBILITY (decl) = DECL_VISIBILITY (fn); + DECL_VISIBILITY_SPECIFIED (decl) = DECL_VISIBILITY_SPECIFIED (fn); + } + /* Entities not associated with any class just get the visibility specified by their attributes. */ return; @@ -1605,33 +1628,62 @@ determine_visibility (tree decl) the visibility of their containing class. */ if (class_type) { - if (TARGET_DLLIMPORT_DECL_ATTRIBUTES - && lookup_attribute ("dllexport", TYPE_ATTRIBUTES (class_type))) + determine_visibility_from_class (decl, class_type); + + /* Give the target a chance to override the visibility associated + with DECL. */ + if (TREE_CODE (decl) == VAR_DECL + && (DECL_TINFO_P (decl) + || (DECL_VTABLE_OR_VTT_P (decl) + /* Construction virtual tables are not exported because + they cannot be referred to from other object files; + their name is not standardized by the ABI. */ + && !DECL_CONSTRUCTION_VTABLE_P (decl))) + && TREE_PUBLIC (decl) + && !DECL_REALLY_EXTERN (decl) + && DECL_VISIBILITY_SPECIFIED (decl) + && (!class_type || !CLASSTYPE_VISIBILITY_SPECIFIED (class_type))) + targetm.cxx.determine_class_data_visibility (decl); + } +} + +static void +determine_visibility_from_class (tree decl, tree class_type) +{ + if (TARGET_DLLIMPORT_DECL_ATTRIBUTES + && lookup_attribute ("dllexport", TYPE_ATTRIBUTES (class_type))) + { + DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT; + DECL_VISIBILITY_SPECIFIED (decl) = 1; + } + else if (TREE_CODE (decl) == FUNCTION_DECL + && DECL_DECLARED_INLINE_P (decl) + && visibility_options.inlines_hidden) + { + /* Don't change it if it has been set explicitly by user. */ + if (!DECL_VISIBILITY_SPECIFIED (decl)) { - DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT; + DECL_VISIBILITY (decl) = VISIBILITY_HIDDEN; DECL_VISIBILITY_SPECIFIED (decl) = 1; } - else if (TREE_CODE (decl) == FUNCTION_DECL - && DECL_DECLARED_INLINE_P (decl) - && visibility_options.inlines_hidden) - { - /* Don't change it if it has been set explicitly by user. */ - if (!DECL_VISIBILITY_SPECIFIED (decl)) - { - DECL_VISIBILITY (decl) = VISIBILITY_HIDDEN; - DECL_VISIBILITY_SPECIFIED (decl) = 1; - } - } - else if (CLASSTYPE_VISIBILITY_SPECIFIED (class_type)) - { - DECL_VISIBILITY (decl) = CLASSTYPE_VISIBILITY (class_type); - DECL_VISIBILITY_SPECIFIED (decl) = 1; - } - else if (!DECL_VISIBILITY_SPECIFIED (decl)) - { - DECL_VISIBILITY (decl) = CLASSTYPE_VISIBILITY (class_type); - DECL_VISIBILITY_SPECIFIED (decl) = 0; - } + } + else if (CLASSTYPE_VISIBILITY_SPECIFIED (class_type)) + { + DECL_VISIBILITY (decl) = CLASSTYPE_VISIBILITY (class_type); + DECL_VISIBILITY_SPECIFIED (decl) = 1; + } + else if (TYPE_CLASS_SCOPE_P (class_type)) + determine_visibility_from_class (decl, TYPE_CONTEXT (class_type)); + else if (TYPE_FUNCTION_SCOPE_P (class_type)) + { + tree fn = TYPE_CONTEXT (class_type); + DECL_VISIBILITY (decl) = DECL_VISIBILITY (fn); + DECL_VISIBILITY_SPECIFIED (decl) = DECL_VISIBILITY_SPECIFIED (fn); + } + else if (!DECL_VISIBILITY_SPECIFIED (decl)) + { + DECL_VISIBILITY (decl) = CLASSTYPE_VISIBILITY (class_type); + DECL_VISIBILITY_SPECIFIED (decl) = 0; } } @@ -1905,21 +1957,6 @@ import_export_decl (tree decl) comdat_linkage (decl); } - /* Give the target a chance to override the visibility associated - with DECL. */ - if (TREE_CODE (decl) == VAR_DECL - && (DECL_TINFO_P (decl) - || (DECL_VTABLE_OR_VTT_P (decl) - /* Construction virtual tables are not exported because - they cannot be referred to from other object files; - their name is not standardized by the ABI. */ - && !DECL_CONSTRUCTION_VTABLE_P (decl))) - && TREE_PUBLIC (decl) - && !DECL_REALLY_EXTERN (decl) - && DECL_VISIBILITY_SPECIFIED (decl) - && (!class_type || !CLASSTYPE_VISIBILITY_SPECIFIED (class_type))) - targetm.cxx.determine_class_data_visibility (decl); - DECL_INTERFACE_KNOWN (decl) = 1; } diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index ebf8a3365ed..9b10fb4a9d7 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -31,6 +31,7 @@ Boston, MA 02110-1301, USA. */ #include "toplev.h" #include "diagnostic.h" #include "debug.h" +#include "c-pragma.h" /* The bindings for a particular name in a particular scope. */ @@ -1330,11 +1331,16 @@ leave_scope (void) is_class_level = 0; } +#ifdef HANDLE_PRAGMA_VISIBILITY + if (scope->has_visibility) + pop_visibility (); +#endif + /* Move one nesting level up. */ current_binding_level = scope->level_chain; /* Namespace-scopes are left most probably temporarily, not - completely; they can be reopen later, e.g. in namespace-extension + completely; they can be reopened later, e.g. in namespace-extension or any name binding activity that requires us to resume a namespace. For classes, we cache some binding levels. For other scopes, we just make the structure available for reuse. */ @@ -2957,6 +2963,15 @@ current_decl_namespace (void) void push_namespace (tree name) +{ + push_namespace_with_attribs (name, NULL_TREE); +} + +/* Same, but specify attributes to apply to the namespace. The attributes + only apply to the current namespace-body, not to any later extensions. */ + +void +push_namespace_with_attribs (tree name, tree attributes) { tree d = NULL_TREE; int need_new = 1; @@ -3004,6 +3019,7 @@ push_namespace (tree name) /* Make a new namespace, binding the name to it. */ d = build_lang_decl (NAMESPACE_DECL, name, void_type_node); DECL_CONTEXT (d) = FROB_CONTEXT (current_namespace); + TREE_PUBLIC (d) = 1; pushdecl (d); if (anon) { @@ -3021,6 +3037,36 @@ push_namespace (tree name) /* Enter the name space. */ current_namespace = d; +#ifdef HANDLE_PRAGMA_VISIBILITY + /* Clear has_visibility in case a previous namespace-definition had a + visibility attribute and this one doesn't. */ + current_binding_level->has_visibility = 0; + for (d = attributes; d; d = TREE_CHAIN (d)) + { + tree name = TREE_PURPOSE (d); + tree args = TREE_VALUE (d); + tree x; + + if (! is_attribute_p ("visibility", name)) + { + warning (OPT_Wattributes, "%qs attribute directive ignored", + IDENTIFIER_POINTER (name)); + continue; + } + + x = args ? TREE_VALUE (args) : NULL_TREE; + if (x == NULL_TREE || TREE_CODE (x) != STRING_CST) + { + warning (OPT_Wattributes, "%qs attribute requires an NTBS argument", + IDENTIFIER_POINTER (name)); + continue; + } + + current_binding_level->has_visibility = 1; + push_visibility (TREE_STRING_POINTER (x)); + } +#endif + timevar_pop (TV_NAME_LOOKUP); } diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index 20c82255a4d..ede7747d00d 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -259,7 +259,11 @@ struct cp_binding_level GTY(()) unsigned more_cleanups_ok : 1; unsigned have_cleanups : 1; - /* 22 bits left to fill a 32-bit word. */ + /* Nonzero if this level has associated visibility which we should pop + when leaving the scope. */ + unsigned has_visibility : 1; + + /* 23 bits left to fill a 32-bit word. */ }; /* The binding level currently in effect. */ @@ -307,6 +311,7 @@ extern void pop_inner_scope (tree, tree); extern void push_binding_level (struct cp_binding_level *); extern void push_namespace (tree); +extern void push_namespace_with_attribs (tree, tree); extern void pop_namespace (void); extern void push_nested_namespace (tree); extern void pop_nested_namespace (tree); diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index b5c7fc011a5..e04a8e7f0d6 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -7064,7 +7064,7 @@ cp_parser_declaration (cp_parser* parser) && (/* A named namespace definition. */ (token2.type == CPP_NAME && (cp_lexer_peek_nth_token (parser->lexer, 3)->type - == CPP_OPEN_BRACE)) + != CPP_EQ)) /* An unnamed namespace definition. */ || token2.type == CPP_OPEN_BRACE)) cp_parser_namespace_definition (parser); @@ -10470,7 +10470,7 @@ cp_parser_namespace_name (cp_parser* parser) static void cp_parser_namespace_definition (cp_parser* parser) { - tree identifier; + tree identifier, attribs; /* Look for the `namespace' keyword. */ cp_parser_require_keyword (parser, RID_NAMESPACE, "`namespace'"); @@ -10484,10 +10484,13 @@ cp_parser_namespace_definition (cp_parser* parser) else identifier = NULL_TREE; + /* Parse any specified attributes. */ + attribs = cp_parser_attributes_opt (parser); + /* Look for the `{' to start the namespace. */ cp_parser_require (parser, CPP_OPEN_BRACE, "`{'"); /* Start the namespace. */ - push_namespace (identifier); + push_namespace_with_attribs (identifier, attribs); /* Parse the body of the namespace. */ cp_parser_namespace_body (parser); /* Finish the namespace. */ diff --git a/gcc/testsuite/g++.dg/ext/visibility/local1.C b/gcc/testsuite/g++.dg/ext/visibility/local1.C new file mode 100644 index 00000000000..4871009ded2 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/local1.C @@ -0,0 +1,25 @@ +// PR c++/19238 +// Test that hidden visibility on an inline function is inherited by static +// local variables and local classes. + +// { dg-do compile { target i?86-*-linux* x86_64-*-linux* powerpc*-*-linux* } } +// { dg-final { scan-assembler "hidden\[ \t\]*_Z1fv" } } +// { dg-final { scan-assembler "hidden\[ \t\]*_ZZ1fvE1i" } } +// { dg-final { scan-assembler "hidden\[ \t\]*_ZZ1fvEN1A1fEv" } } + +__attribute ((visibility ("hidden"))) inline int +f() +{ + static int i = 2; + struct A + { + void f () { } + } a; + a.f(); + return i; +} + +int main() +{ + f(); +} diff --git a/gcc/testsuite/g++.dg/ext/visibility/namespace1.C b/gcc/testsuite/g++.dg/ext/visibility/namespace1.C new file mode 100644 index 00000000000..903a1f2524c --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/visibility/namespace1.C @@ -0,0 +1,30 @@ +// PR c++/21764 +// Test for namespace visibility attribute semantics. + +// { dg-do compile { target i?86-*-linux* x86_64-*-linux* powerpc*-*-linux* } } +// { dg-final { scan-assembler "hidden\[ \t\]*_ZN3foo1fEv" } } +// { dg-final { scan-assembler "hidden\[ \t\]*_ZN3foo1gEv" } } +// { dg-final { scan-assembler "hidden\[ \t\]*_ZN3foo1A1mEv" } } +// { dg-final { scan-assembler "hidden\[ \t\]*_ZN3foo1tIiEEvv" } } +// { dg-final { scan-assembler-not "hidden\[ \t\]*_ZN3foo1hEv" } } + +namespace foo __attribute ((visibility ("hidden"))) +{ + int f() { } + void g(); + template void t() { } + class A + { + void m (); + }; +} + +namespace foo +{ + void h() {} +} + +void foo::g() { t (); } + +void foo::A::m() { } +