+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
+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.
}
}
+#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.
= 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)
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);
/* [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)
{
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)
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
&& 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
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))
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
@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
+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
--- /dev/null
+/* { 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"
+}
--- /dev/null
+/* { 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"
--- /dev/null
+/* { 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"
--- /dev/null
+#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
--- /dev/null
+/* 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" "" }
--- /dev/null
+/* 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)
--- /dev/null
+/* { 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"
+}
--- /dev/null
+/* { 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>;
--- /dev/null
+/* { 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);