extend.texi (gnu_inline funtion attribute): Document C++ behavior.

gcc/ChangeLog:
* doc/extend.texi (gnu_inline funtion attribute): Document C++
behavior.
gcc/cp/ChangeLog:
* 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.
gcc/testsuite/ChangeLog:
* 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.

From-SVN: r127839
This commit is contained in:
Alexandre Oliva 2007-08-27 20:40:00 +00:00 committed by Alexandre Oliva
parent 83d7e8f06e
commit 3a47c4e4f4
14 changed files with 372 additions and 16 deletions

View File

@ -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

View File

@ -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.

View File

@ -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))

View File

@ -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

View File

@ -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

View File

@ -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"
}

View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -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" "" }

View File

@ -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)

View File

@ -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"
}

View File

@ -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>;

View File

@ -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);