OSDN Git Service

gcc/ChangeLog:
authoraoliva <aoliva@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 27 Aug 2007 20:40:00 +0000 (20:40 +0000)
committeraoliva <aoliva@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 27 Aug 2007 20:40:00 +0000 (20:40 +0000)
* 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.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@127839 138bc75d-0d04-0410-961f-82ee72b054a4

14 files changed:
gcc/ChangeLog
gcc/cp/ChangeLog
gcc/cp/decl.c
gcc/doc/extend.texi
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/ext/gnu-inline-anon-namespace.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/gnu-inline-class-static.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/gnu-inline-class.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/gnu-inline-common.h [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/gnu-inline-global-reject.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/gnu-inline-global.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/gnu-inline-namespace.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/gnu-inline-template-class.C [new file with mode: 0644]
gcc/testsuite/g++.dg/ext/gnu-inline-template-func.C [new file with mode: 0644]

index 995a7d4..82c559d 100644 (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
index d073d38..ca09bf4 100644 (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.
index f054b66..ef63ffd 100644 (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))
index 95d25ca..f0a8b4d 100644 (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
index 3795be3..7839eaa 100644 (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
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 (file)
index 0000000..b33629d
--- /dev/null
@@ -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 (file)
index 0000000..f22a23c
--- /dev/null
@@ -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 (file)
index 0000000..71a0b1e
--- /dev/null
@@ -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 (file)
index 0000000..87455ae
--- /dev/null
@@ -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 (file)
index 0000000..2f2b8f2
--- /dev/null
@@ -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 (file)
index 0000000..f628073
--- /dev/null
@@ -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 (file)
index 0000000..ce3fea6
--- /dev/null
@@ -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 (file)
index 0000000..9bf36a8
--- /dev/null
@@ -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 (file)
index 0000000..fb88a2a
--- /dev/null
@@ -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);