diff options
-rw-r--r-- | gcc/ChangeLog | 5 | ||||
-rw-r--r-- | gcc/cp/ChangeLog | 8 | ||||
-rw-r--r-- | gcc/cp/decl.c | 126 | ||||
-rw-r--r-- | gcc/doc/extend.texi | 8 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 12 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ext/gnu-inline-anon-namespace.C | 11 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ext/gnu-inline-class-static.C | 20 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ext/gnu-inline-class.C | 19 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ext/gnu-inline-common.h | 24 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ext/gnu-inline-global-reject.C | 55 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ext/gnu-inline-global.C | 50 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ext/gnu-inline-namespace.C | 11 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ext/gnu-inline-template-class.C | 22 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ext/gnu-inline-template-func.C | 17 |
14 files changed, 372 insertions, 16 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 995a7d4eaee..82c559d4cb2 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2007-08-27 Alexandre Oliva <aoliva@redhat.com> + + * doc/extend.texi (gnu_inline funtion attribute): Document C++ + behavior. + 2007-08-27 Jason Merrill <jason@redhat.com> PR c++/31337 diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index d073d38e152..ca09bf4ea31 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,11 @@ +2007-08-27 Alexandre Oliva <aoliva@redhat.com> + + * decl.c (GNU_INLINE_P): New. + (duplicate_decls): Handle gnu_inline. Merge attributes and + some flags in overriding definitions. + (redeclaration_error_message): Handle gnu_inline. + (start_preparsed_function): Likewise. + 2007-08-25 Kaveh R. Ghazi <ghazi@caip.rutgers.edu> * call.c (sufficient_parms_p): Constify. diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index f054b662c26..ef63ffd83ac 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -1098,6 +1098,10 @@ check_redeclaration_exception_specification (tree new_decl, } } +#define GNU_INLINE_P(fn) (DECL_DECLARED_INLINE_P (fn) \ + && lookup_attribute ("gnu_inline", \ + DECL_ATTRIBUTES (fn))) + /* If NEWDECL is a redeclaration of OLDDECL, merge the declarations. If the redeclaration is invalid, a diagnostic is issued, and the error_mark_node is returned. Otherwise, OLDDECL is returned. @@ -1634,20 +1638,46 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend) = chainon (DECL_TEMPLATE_SPECIALIZATIONS (olddecl), DECL_TEMPLATE_SPECIALIZATIONS (newdecl)); + DECL_ATTRIBUTES (old_result) + = (*targetm.merge_decl_attributes) (old_result, new_result); + if (DECL_FUNCTION_TEMPLATE_P (newdecl)) { - DECL_INLINE (old_result) - |= DECL_INLINE (new_result); - DECL_DECLARED_INLINE_P (old_result) - |= DECL_DECLARED_INLINE_P (new_result); - check_redeclaration_exception_specification (newdecl, olddecl); + if (GNU_INLINE_P (old_result) != GNU_INLINE_P (new_result) + && DECL_INITIAL (new_result)) + { + if (DECL_INITIAL (old_result)) + { + DECL_INLINE (old_result) = 0; + DECL_UNINLINABLE (old_result) = 1; + } + else + { + DECL_INLINE (old_result) = DECL_INLINE (new_result); + DECL_UNINLINABLE (old_result) = DECL_UNINLINABLE (new_result); + } + DECL_EXTERNAL (old_result) = DECL_EXTERNAL (new_result); + DECL_NOT_REALLY_EXTERN (old_result) + = DECL_NOT_REALLY_EXTERN (new_result); + DECL_INTERFACE_KNOWN (old_result) + = DECL_INTERFACE_KNOWN (new_result); + DECL_DECLARED_INLINE_P (old_result) + = DECL_DECLARED_INLINE_P (new_result); + } + else + { + DECL_INLINE (old_result) + |= DECL_INLINE (new_result); + DECL_DECLARED_INLINE_P (old_result) + |= DECL_DECLARED_INLINE_P (new_result); + check_redeclaration_exception_specification (newdecl, olddecl); + } } /* If the new declaration is a definition, update the file and line information on the declaration, and also make the old declaration the same definition. */ - if (DECL_INITIAL (old_result) == NULL_TREE - && DECL_INITIAL (new_result) != NULL_TREE) + if (DECL_INITIAL (new_result) != NULL_TREE) { DECL_SOURCE_LOCATION (olddecl) = DECL_SOURCE_LOCATION (old_result) @@ -1805,9 +1835,30 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend) new_template = NULL_TREE; if (DECL_LANG_SPECIFIC (newdecl) && DECL_LANG_SPECIFIC (olddecl)) { - DECL_INTERFACE_KNOWN (newdecl) |= DECL_INTERFACE_KNOWN (olddecl); - DECL_NOT_REALLY_EXTERN (newdecl) |= DECL_NOT_REALLY_EXTERN (olddecl); - DECL_COMDAT (newdecl) |= DECL_COMDAT (olddecl); + bool old_decl_gnu_inline; + + if ((DECL_INTERFACE_KNOWN (olddecl) + && TREE_CODE (olddecl) == FUNCTION_DECL) + || (TREE_CODE (olddecl) == TEMPLATE_DECL + && TREE_CODE (DECL_TEMPLATE_RESULT (olddecl)) == FUNCTION_DECL)) + { + tree fn = olddecl; + + if (TREE_CODE (fn) == TEMPLATE_DECL) + fn = DECL_TEMPLATE_RESULT (olddecl); + + old_decl_gnu_inline = GNU_INLINE_P (fn) && DECL_INITIAL (fn); + } + else + old_decl_gnu_inline = false; + + if (!old_decl_gnu_inline) + { + DECL_INTERFACE_KNOWN (newdecl) |= DECL_INTERFACE_KNOWN (olddecl); + DECL_INTERFACE_KNOWN (newdecl) |= DECL_INTERFACE_KNOWN (olddecl); + DECL_NOT_REALLY_EXTERN (newdecl) |= DECL_NOT_REALLY_EXTERN (olddecl); + DECL_COMDAT (newdecl) |= DECL_COMDAT (olddecl); + } DECL_TEMPLATE_INSTANTIATED (newdecl) |= DECL_TEMPLATE_INSTANTIATED (olddecl); @@ -1881,6 +1932,13 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend) /* [temp.expl.spec/14] We don't inline explicit specialization just because the primary template says so. */ } + else if (new_defines_function && DECL_INITIAL (olddecl)) + { + /* C++ is always in in unit-at-a-time mode, so we never + inline re-defined extern inline functions. */ + DECL_INLINE (newdecl) = 0; + DECL_UNINLINABLE (newdecl) = 1; + } else { if (DECL_PENDING_INLINE_INFO (newdecl) == 0) @@ -2123,9 +2181,25 @@ redeclaration_error_message (tree newdecl, tree olddecl) { if (DECL_NAME (olddecl) == NULL_TREE) return "%q#D not declared in class"; - else + else if (!GNU_INLINE_P (olddecl) + || GNU_INLINE_P (newdecl)) return "redefinition of %q#D"; } + + if (DECL_DECLARED_INLINE_P (olddecl) && DECL_DECLARED_INLINE_P (newdecl)) + { + bool olda = GNU_INLINE_P (olddecl); + bool newa = GNU_INLINE_P (newdecl); + + if (olda != newa) + { + if (newa) + return "%q+D redeclared inline with %<gnu_inline%> attribute"; + else + return "%q+D redeclared inline without %<gnu_inline%> attribute"; + } + } + return NULL; } else if (TREE_CODE (newdecl) == TEMPLATE_DECL) @@ -2151,9 +2225,24 @@ redeclaration_error_message (tree newdecl, tree olddecl) ot = DECL_TEMPLATE_RESULT (olddecl); if (DECL_TEMPLATE_INFO (ot)) ot = DECL_TEMPLATE_RESULT (template_for_substitution (ot)); - if (DECL_INITIAL (nt) && DECL_INITIAL (ot)) + if (DECL_INITIAL (nt) && DECL_INITIAL (ot) + && (!GNU_INLINE_P (ot) || GNU_INLINE_P (nt))) return "redefinition of %q#D"; + if (DECL_DECLARED_INLINE_P (ot) && DECL_DECLARED_INLINE_P (nt)) + { + bool olda = GNU_INLINE_P (ot); + bool newa = GNU_INLINE_P (nt); + + if (olda != newa) + { + if (newa) + return "%q+D redeclared inline with %<gnu_inline%> attribute"; + else + return "%q+D redeclared inline without %<gnu_inline%> attribute"; + } + } + /* Core issue #226 (C++0x): If a friend function template declaration specifies a @@ -10786,6 +10875,14 @@ start_preparsed_function (tree decl1, tree attrs, int flags) && lookup_attribute ("noinline", attrs)) warning (0, "inline function %q+D given attribute noinline", decl1); + /* Handle gnu_inline attribute. */ + if (GNU_INLINE_P (decl1)) + { + DECL_EXTERNAL (decl1) = 1; + DECL_NOT_REALLY_EXTERN (decl1) = 0; + DECL_INTERFACE_KNOWN (decl1) = 1; + } + if (DECL_MAYBE_IN_CHARGE_CONSTRUCTOR_P (decl1)) /* This is a constructor, we must ensure that any default args introduced by this definition are propagated to the clones @@ -11071,8 +11168,9 @@ start_preparsed_function (tree decl1, tree attrs, int flags) else { /* This is a definition, not a reference. - So clear DECL_EXTERNAL. */ - DECL_EXTERNAL (decl1) = 0; + So clear DECL_EXTERNAL, unless this is a GNU extern inline. */ + if (!GNU_INLINE_P (decl1)) + DECL_EXTERNAL (decl1) = 0; if ((DECL_DECLARED_INLINE_P (decl1) || DECL_TEMPLATE_INSTANTIATION (decl1)) diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 95d25caa235..f0a8b4d3f4c 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -1718,8 +1718,8 @@ refer to the single copy in the library. Note that the two definitions of the functions need not be precisely the same, although if they do not have the same effect your program may behave oddly. -If the function is neither @code{extern} nor @code{static}, then the -function is compiled as a standalone function, as well as being +In C, if the function is neither @code{extern} nor @code{static}, then +the function is compiled as a standalone function, as well as being inlined where possible. This is how GCC traditionally handled functions declared @@ -1731,6 +1731,10 @@ preprocessor macros @code{__GNUC_GNU_INLINE__} or @code{__GNUC_STDC_INLINE__} are defined. @xref{Inline,,An Inline Function is As Fast As a Macro}. +In C++, this attribute does not depend on @code{extern} in any way, +but it still requires the @code{inline} keyword to enable its special +behavior. + @cindex @code{flatten} function attribute @item flatten Generally, inlining into a function is limited. For a function marked with diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 3795be363da..7839eaa831a 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,15 @@ +2007-08-27 Alexandre Oliva <aoliva@redhat.com> + + * g++.dg/ext/gnu-inline-common.h: New. + * g++.dg/ext/gnu-inline-global-reject.C: New. + * g++.dg/ext/gnu-inline-global.C: New. + * g++.dg/ext/gnu-inline-namespace.C: New. + * g++.dg/ext/gnu-inline-anon-namespace.C: New. + * g++.dg/ext/gnu-inline-class.C: New. + * g++.dg/ext/gnu-inline-class-static.C: New. + * g++.dg/ext/gnu-inline-template-class.C: New. + * g++.dg/ext/gnu-inline-template-func.C: New. + 2007-08-27 Jason Merrill <jason@redhat.com> PR c++/31337 diff --git a/gcc/testsuite/g++.dg/ext/gnu-inline-anon-namespace.C b/gcc/testsuite/g++.dg/ext/gnu-inline-anon-namespace.C new file mode 100644 index 00000000000..b33629dcfff --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/gnu-inline-anon-namespace.C @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-options "-O" } */ // such that static functions are optimized out +/* { dg-final { scan-assembler-not "func1" } } */ +/* { dg-final { scan-assembler-not "func2" } } */ +/* { dg-final { scan-assembler-not "func3" } } */ +/* { dg-final { scan-assembler-not "func4" } } */ +/* { dg-final { scan-assembler-not "func5" } } */ + +namespace { +#include "gnu-inline-global.C" +} diff --git a/gcc/testsuite/g++.dg/ext/gnu-inline-class-static.C b/gcc/testsuite/g++.dg/ext/gnu-inline-class-static.C new file mode 100644 index 00000000000..f22a23c0ba6 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/gnu-inline-class-static.C @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-O" } */ // such that static functions are optimized out +/* { dg-final { scan-assembler "func1" } } */ +/* { dg-final { scan-assembler "func2" } } */ +/* { dg-final { scan-assembler-not "func3" } } */ +/* { dg-final { scan-assembler "func4" } } */ +/* { dg-final { scan-assembler "func5" } } */ + +#undef IN_CLASS +#define IN_CLASS gnu_test_static + +struct IN_CLASS { + static int func1(void); + static int func2(void); + static int func3(void); + static int func4(void); + static int func5(void); +}; + +#include "gnu-inline-global.C" diff --git a/gcc/testsuite/g++.dg/ext/gnu-inline-class.C b/gcc/testsuite/g++.dg/ext/gnu-inline-class.C new file mode 100644 index 00000000000..71a0b1e36c1 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/gnu-inline-class.C @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-O" } */ // such that static functions are optimized out +/* { dg-final { scan-assembler "func1" } } */ +/* { dg-final { scan-assembler "func2" } } */ +/* { dg-final { scan-assembler-not "func3" } } */ +/* { dg-final { scan-assembler "func4" } } */ +/* { dg-final { scan-assembler "func5" } } */ + +#define IN_CLASS gnu_test + +struct IN_CLASS { + int func1(void); + int func2(void); + int func3(void); + int func4(void); + int func5(void); +}; + +#include "gnu-inline-global.C" diff --git a/gcc/testsuite/g++.dg/ext/gnu-inline-common.h b/gcc/testsuite/g++.dg/ext/gnu-inline-common.h new file mode 100644 index 00000000000..87455ae7a6c --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/gnu-inline-common.h @@ -0,0 +1,24 @@ +#ifndef gnu +# define gnu_inline __attribute__((gnu_inline)) inline +#endif + +#define declspec(spec, name) spec int name (void) +#ifdef IN_CLASS +# define decl(spec, name) +#else +# define decl(spec, name) defpfx declspec(spec, name); +#endif +#define def(spec, name, ret) defpfx declspec(spec, name) { return ret; } +#define gnuindef(name, ret) def(gnu_inline, name, ret) + +#ifndef pfx +# ifdef IN_CLASS +# define pfx(x) IN_CLASS::x +# else +# define pfx(x) x +# endif +#endif + +#ifndef defpfx +# define defpfx +#endif diff --git a/gcc/testsuite/g++.dg/ext/gnu-inline-global-reject.C b/gcc/testsuite/g++.dg/ext/gnu-inline-global-reject.C new file mode 100644 index 00000000000..2f2b8f29a08 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/gnu-inline-global-reject.C @@ -0,0 +1,55 @@ +/* Test __attribute__((gnu_inline)). + + Check that we reject various forms of duplicate definitions. +*/ + +/* { dg-do compile } */ + +#include "gnu-inline-common.h" + +#undef fn +#define fn pfx(func_decl_inline_before) +decl(inline, fn) // { dg-error "previous" "" } +gnuindef(fn, 0) // { dg-error "redeclared" "" } + +#undef fn +#define fn pfx(func_decl_inline_after) +gnuindef(fn, 0) // { dg-error "previous" "" } +decl(inline, fn) // { dg-error "redeclared" "" } + +#undef fn +#define fn pfx(func_def_gnuin_redef) +gnuindef(fn, 0) // { dg-error "previous" "" } +gnuindef(fn, 1) // { dg-error "redefinition" "" } + +#undef fn +#define fn pfx(func_def_inline_redef) +def(inline, fn, 0) // { dg-error "previous" "" } +def(inline, fn, 1) // { dg-error "redefinition" "" } + +#undef fn +#define fn pfx(func_def_inline_after) +gnuindef(fn, 0) // { dg-error "previous" "" } +def(inline, fn, 1) // { dg-error "redeclare" "" } + +#undef fn +#define fn pfx(func_def_inline_before) +def(inline, fn, 0) // { dg-error "previous" "" } +gnuindef(fn, 1) // { dg-error "redefinition" "" } + +#undef fn +#define fn pfx(func_def_before) +def(, fn, 0) // { dg-error "previous" "" } +gnuindef(fn, 1) // { dg-error "redefinition" "" } + +#undef fn +#define fn pfx(func_decl_static_inline_before) +decl(static inline, fn) // { dg-error "previous" "" } +gnuindef(fn, 0) // { dg-error "redeclared" "" } + +#undef fn +#define fn pfx(func_def_static_inline_after) +decl(static, fn) +gnuindef(fn, 0) // { dg-error "previous" "" } +decl(static, fn) +def(static inline, fn, 1) // { dg-error "redeclare" "" } diff --git a/gcc/testsuite/g++.dg/ext/gnu-inline-global.C b/gcc/testsuite/g++.dg/ext/gnu-inline-global.C new file mode 100644 index 00000000000..f628073f9fe --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/gnu-inline-global.C @@ -0,0 +1,50 @@ +/* Test __attribute__((gnu_inline)). + + Check that __attribute__((gnu_inline)) has no effect, in the + absence of extern and/or inline. + + Check that we don't get out-of-line definitions for extern inline + gnu_inline functions, regardless of declarations or definitions. + + Check that such functions can be overridden by out-of-line + definitions. + + */ + +/* { dg-do compile } */ +/* { dg-options "-O" } */ // such that static functions are optimized out +/* { dg-final { scan-assembler "func1" } } */ +/* { dg-final { scan-assembler "func2" } } */ +/* { dg-final { scan-assembler-not "func3" } } */ +/* { dg-final { scan-assembler "func4" } } */ +/* { dg-final { scan-assembler-not "func5" } } */ + +#include "gnu-inline-common.h" + +#undef fn +#define fn pfx(func1) // must be emitted out-of-line +gnuindef(fn, 0) +def(, fn, 2) + +#undef fn +#define fn pfx(func2) // must be emitted out-of-line +decl(extern, fn) +gnuindef(fn, 0) +def(, fn, 2) + +#undef fn +#define fn pfx(func3) // must not be emitted +decl(extern, fn) +gnuindef(fn, 0) + +#undef fn +#define fn pfx(func4) // must be emitted out-of-line +decl(extern, fn) +gnuindef(fn, 0) +def(, fn, 1) + +#undef fn +#define fn pfx(func5) // must NOT be emitted, because it's static and unused +decl(static, fn) +gnuindef(fn, 0) +def(, fn, 1) diff --git a/gcc/testsuite/g++.dg/ext/gnu-inline-namespace.C b/gcc/testsuite/g++.dg/ext/gnu-inline-namespace.C new file mode 100644 index 00000000000..ce3fea6558d --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/gnu-inline-namespace.C @@ -0,0 +1,11 @@ +/* { dg-do compile } */ +/* { dg-options "-O" } */ // such that static functions are optimized out +/* { dg-final { scan-assembler "func1" } } */ +/* { dg-final { scan-assembler "func2" } } */ +/* { dg-final { scan-assembler-not "func3" } } */ +/* { dg-final { scan-assembler "func4" } } */ +/* { dg-final { scan-assembler-not "func5" } } */ + +namespace gnu_test { +#include "gnu-inline-global.C" +} diff --git a/gcc/testsuite/g++.dg/ext/gnu-inline-template-class.C b/gcc/testsuite/g++.dg/ext/gnu-inline-template-class.C new file mode 100644 index 00000000000..9bf36a8d670 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/gnu-inline-template-class.C @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-O" } */ // such that static functions are optimized out +/* { dg-final { scan-assembler "func1" } } */ +/* { dg-final { scan-assembler "func2" } } */ +/* { dg-final { scan-assembler-not "func3" } } */ +/* { dg-final { scan-assembler "func4" } } */ +/* { dg-final { scan-assembler "func5" } } */ + +template <typename T> struct gnu_test { + int func1(void); + int func2(void); + int func3(void); + int func4(void); + int func5(void); +}; + +#define defpfx template <typename T> +#define IN_CLASS gnu_test<T> + +#include "gnu-inline-global.C" + +template struct gnu_test<int>; diff --git a/gcc/testsuite/g++.dg/ext/gnu-inline-template-func.C b/gcc/testsuite/g++.dg/ext/gnu-inline-template-func.C new file mode 100644 index 00000000000..fb88a2a91f7 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/gnu-inline-template-func.C @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-O" } */ // such that static functions are optimized out +/* { dg-final { scan-assembler "func1" } } */ +/* { dg-final { scan-assembler "func2" } } */ +/* { dg-final { scan-assembler-not "func3" } } */ +/* { dg-final { scan-assembler "func4" } } */ +/* { dg-final { scan-assembler-not "func5" } } */ + +#define defpfx template <typename T> + +#include "gnu-inline-global.C" + +template int func1<int>(void); +template int func2<int>(void); +template int func3<int>(void); +template int func4<int>(void); +template int func5<int>(void); |