PR c/82301 - Updated test case g++.dg/ext/attr-ifunc-1.C (and others) in r253041...
authorMartin Sebor <msebor@redhat.com>
Thu, 12 Oct 2017 17:37:56 +0000 (17:37 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Thu, 12 Oct 2017 17:37:56 +0000 (11:37 -0600)
PR c/82301 - Updated test case g++.dg/ext/attr-ifunc-1.C (and others) in r253041 segfault on powerpc64
PR c/82435 - new __attribute__((alias)) warning gets in the way

gcc/ChangeLog:

PR other/82301
PR c/82435
* cgraphunit.c (maybe_diag_incompatible_alias): New function.
(handle_alias_pairs): Call it.
* common.opt (-Wattribute-alias): New option.
* doc/extend.texi (ifunc attribute): Discuss C++ specifics.
* doc/invoke.texi (-Wattribute-alias): Document.

gcc/testsuite/ChangeLog:

PR other/82301
PR c/82435
* g++.dg/ext/attr-ifunc-1.C: Update.
* g++.dg/ext/attr-ifunc-2.C: Same.
* g++.dg/ext/attr-ifunc-3.C: Same.
* g++.dg/ext/attr-ifunc-4.C: Same.
* g++.dg/ext/attr-ifunc-5.C: Same.
* g++.dg/ext/attr-ifunc-6.C: New test.
* g++.old-deja/g++.abi/vtable2.C: Update.
* gcc.dg/attr-ifunc-6.c: New test.
* gcc.dg/attr-ifunc-7.c: New test.
* gcc.dg/pr81854.c: Update.
* lib/target-supports.exp: Update.

From-SVN: r253688

14 files changed:
gcc/ChangeLog
gcc/cgraphunit.c
gcc/common.opt
gcc/doc/extend.texi
gcc/doc/invoke.texi
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/ext/attr-ifunc-1.C
gcc/testsuite/g++.dg/ext/attr-ifunc-2.C
gcc/testsuite/g++.dg/ext/attr-ifunc-3.C
gcc/testsuite/g++.dg/ext/attr-ifunc-4.C
gcc/testsuite/g++.dg/ext/attr-ifunc-5.C
gcc/testsuite/g++.old-deja/g++.abi/vtable2.C
gcc/testsuite/gcc.dg/pr81854.c
gcc/testsuite/lib/target-supports.exp

index 430f3c9096b50ff39b55f86b507e734278c9bb84..dc17b705025daeceff773667b17caeba50f8439c 100644 (file)
@@ -1,3 +1,13 @@
+2017-10-12  Martin Sebor  <msebor@redhat.com>
+
+       PR other/82301
+       PR c/82435
+       * cgraphunit.c (maybe_diag_incompatible_alias): New function.
+       (handle_alias_pairs): Call it.
+       * common.opt (-Wattribute-alias): New option.
+       * doc/extend.texi (ifunc attribute): Discuss C++ specifics.
+       * doc/invoke.texi (-Wattribute-alias): Document.
+
 2017-10-12  Vladimir Makarov  <vmakarov@redhat.com>
 
        Revert
index 8c1acf770b9166937175a84c4354ef4ad2081792..9385dc825ab286276a627955d004b7aa462dda12 100644 (file)
@@ -1296,6 +1296,93 @@ analyze_functions (bool first_time)
   input_location = saved_loc;
 }
 
+/* Check declaration of the type of ALIAS for compatibility with its TARGET
+   (which may be an ifunc resolver) and issue a diagnostic when they are
+   not compatible according to language rules (plus a C++ extension for
+   non-static member functions).  */
+
+static void
+maybe_diag_incompatible_alias (tree alias, tree target)
+{
+  tree altype = TREE_TYPE (alias);
+  tree targtype = TREE_TYPE (target);
+
+  bool ifunc = lookup_attribute ("ifunc", DECL_ATTRIBUTES (alias));
+  tree funcptr = altype;
+
+  if (ifunc)
+    {
+      /* Handle attribute ifunc first.  */
+      if (TREE_CODE (altype) == METHOD_TYPE)
+       {
+         /* Set FUNCPTR to the type of the alias target.  If the type
+            is a non-static member function of class C, construct a type
+            of an ordinary function taking C* as the first argument,
+            followed by the member function argument list, and use it
+            instead to check for incompatibility.  This conversion is
+            not defined by the language but an extension provided by
+            G++.  */
+
+         tree rettype = TREE_TYPE (altype);
+         tree args = TYPE_ARG_TYPES (altype);
+         altype = build_function_type (rettype, args);
+         funcptr = altype;
+       }
+
+      targtype = TREE_TYPE (targtype);
+
+      if (POINTER_TYPE_P (targtype))
+       {
+         targtype = TREE_TYPE (targtype);
+
+         /* Only issue Wattribute-alias for conversions to void* with
+            -Wextra.  */
+         if (VOID_TYPE_P (targtype) && !extra_warnings)
+           return;
+
+         /* Proceed to handle incompatible ifunc resolvers below.  */
+       }
+      else
+       {
+         funcptr = build_pointer_type (funcptr);
+
+         error_at (DECL_SOURCE_LOCATION (target),
+                   "%<ifunc%> resolver for %qD must return %qT",
+                alias, funcptr);
+         inform (DECL_SOURCE_LOCATION (alias),
+                 "resolver indirect function declared here");
+         return;
+       }
+    }
+
+  if ((!FUNC_OR_METHOD_TYPE_P (targtype)
+       || (prototype_p (altype)
+          && prototype_p (targtype)
+          && !types_compatible_p (altype, targtype))))
+    {
+      /* Warn for incompatibilities.  Avoid warning for functions
+        without a prototype to make it possible to declare aliases
+        without knowing the exact type, as libstdc++ does.  */
+      if (ifunc)
+       {
+         funcptr = build_pointer_type (funcptr);
+
+         if (warning_at (DECL_SOURCE_LOCATION (target),
+                         OPT_Wattribute_alias,
+                         "%<ifunc%> resolver for %qD should return %qT",
+                         alias, funcptr))
+           inform (DECL_SOURCE_LOCATION (alias),
+                   "resolver indirect function declared here");
+       }
+      else if (warning_at (DECL_SOURCE_LOCATION (alias),
+                          OPT_Wattribute_alias,
+                          "%qD alias between functions of incompatible "
+                          "types %qT and %qT", alias, altype, targtype))
+       inform (DECL_SOURCE_LOCATION (target),
+               "aliased declaration here");
+    }
+}
+
 /* Translate the ugly representation of aliases as alias pairs into nice
    representation in callgraph.  We don't handle all cases yet,
    unfortunately.  */
@@ -1305,7 +1392,7 @@ handle_alias_pairs (void)
 {
   alias_pair *p;
   unsigned i;
-  
+
   for (i = 0; alias_pairs && alias_pairs->iterate (i, &p);)
     {
       symtab_node *target_node = symtab_node::get_for_asmname (p->target);
@@ -1352,65 +1439,7 @@ handle_alias_pairs (void)
       if (TREE_CODE (p->decl) == FUNCTION_DECL
           && target_node && is_a <cgraph_node *> (target_node))
        {
-         tree t1 = TREE_TYPE (p->decl);
-         tree t2 = TREE_TYPE (target_node->decl);
-
-         if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (p->decl)))
-           {
-             t2 = TREE_TYPE (t2);
-             if (POINTER_TYPE_P (t2))
-               {
-                 t2 = TREE_TYPE (t2);
-                 if (!FUNC_OR_METHOD_TYPE_P (t2))
-                   {
-                     if (warning_at (DECL_SOURCE_LOCATION (p->decl),
-                                     OPT_Wattributes,
-                                     "%q+D %<ifunc%> resolver should return "
-                                     "a function pointer",
-                                     p->decl))
-                       inform (DECL_SOURCE_LOCATION (target_node->decl),
-                               "resolver declaration here");
-
-                     t2 = NULL_TREE;
-                   }
-               }
-             else
-               {
-                 /* Deal with static member function pointers.  */
-                 if (TREE_CODE (t2) == RECORD_TYPE
-                     && TYPE_FIELDS (t2)
-                     && TREE_CODE (TREE_TYPE (TYPE_FIELDS (t2))) == POINTER_TYPE
-                     && (TREE_CODE (TREE_TYPE (TREE_TYPE (TYPE_FIELDS (t2))))
-                         == METHOD_TYPE))
-                   t2 = TREE_TYPE (TREE_TYPE (TYPE_FIELDS (t2)));
-                 else
-                   {
-                     error ("%q+D %<ifunc%> resolver must return a function "
-                            "pointer",
-                            p->decl);
-                     inform (DECL_SOURCE_LOCATION (target_node->decl),
-                             "resolver declaration here");
-
-                     t2 = NULL_TREE;
-                   }
-               }
-           }
-
-         if (t2
-             && (!FUNC_OR_METHOD_TYPE_P (t2)
-                 || (prototype_p (t1)
-                     && prototype_p (t2)
-                     && !types_compatible_p (t1, t2))))
-           {
-             /* Warn for incompatibilities.  Avoid warning for functions
-                without a prototype to make it possible to declare aliases
-                without knowing the exact type, as libstdc++ does.  */
-             if (warning_at (DECL_SOURCE_LOCATION (p->decl), OPT_Wattributes,
-                             "%q+D alias between functions of incompatible "
-                             "types %qT and %qT", p->decl, t1, t2))
-               inform (DECL_SOURCE_LOCATION (target_node->decl),
-                       "aliased declaration here");
-           }
+         maybe_diag_incompatible_alias (p->decl, target_node->decl);
 
          cgraph_node *src_node = cgraph_node::get (p->decl);
          if (src_node && src_node->definition)
index ce8194b58fafc7d6181ec10cd811761c77848006..c95da64017465a9ca09f393d3c6e0c8ab085df59 100644 (file)
@@ -562,6 +562,10 @@ Wattributes
 Common Var(warn_attributes) Init(1) Warning
 Warn about inappropriate attribute usage.
 
+Wattribute-alias
+Common Var(warn_attributes) Init(1) Warning
+Warn about type safety and similar errors in attribute alias and related.
+
 Wcast-align
 Common Var(warn_cast_align) Warning
 Warn about pointer casts which increase alignment.
index 0014490e8692d3ac63f278567f6a939fdcbdbbed..a196b596fa9337189b019f83d2bb9a24bfafb4ab 100644 (file)
@@ -2801,7 +2801,7 @@ void *my_memcpy (void *dst, const void *src, size_t len)
 
 static void * (*resolve_memcpy (void))(void *, const void *, size_t)
 @{
-  return my_memcpy; // we'll just always select this routine
+  return my_memcpy; // we will just always select this routine
 @}
 @end smallexample
 
@@ -2814,15 +2814,56 @@ extern void *memcpy (void *, const void *, size_t);
 @end smallexample
 
 @noindent
-allowing the user to call this as a regular function, unaware of the
-implementation.  Finally, the indirect function needs to be defined in
-the same translation unit as the resolver function:
+allowing the user to call @code{memcpy} as a regular function, unaware of
+the actual implementation.  Finally, the indirect function needs to be
+defined in the same translation unit as the resolver function:
 
 @smallexample
 void *memcpy (void *, const void *, size_t)
      __attribute__ ((ifunc ("resolve_memcpy")));
 @end smallexample
 
+In C++, the @code{ifunc} attribute takes a string that is the mangled name
+of the resolver function.  A C++ resolver for a non-static member function
+of class @code{C} should be declared to return a pointer to a non-member
+function taking pointer to @code{C} as the first argument, followed by
+the same arguments as of the implementation function.  G++ checks
+the signatures of the two functions and issues
+a @option{-Wattribute-alias} warning for mismatches.  To suppress a warning
+for the necessary cast from a pointer to the implementation member function
+to the type of the corresponding non-member function use
+the @option{-Wno-pmf-conversions} option.  For example:
+
+@smallexample
+class S
+@{
+private:
+  int debug_impl (int);
+  int optimized_impl (int);
+
+  typedef int Func (S*, int);
+
+  static Func* resolver ();
+public:
+
+  int interface (int);
+@};
+
+int S::debug_impl (int) @{ /* @r{@dots{}} */ @}
+int S::optimized_impl (int) @{ /* @r{@dots{}} */ @}
+
+S::Func* S::resolver ()
+@{
+  int (S::*pimpl) (int)
+    = getenv ("DEBUG") ? &S::debug_impl : &S::optimized_impl;
+
+  // Cast triggers -Wno-pmf-conversions.
+  return reinterpret_cast<Func*>(pimpl);
+@}
+
+int S::interface (int) __attribute__ ((ifunc ("_ZN1S8resolverEv")));
+@end smallexample
+
 Indirect functions cannot be weak.  Binutils version 2.20.1 or higher
 and GNU C Library version 2.11.1 are required to use this feature.
 
index 9ad1fb339babe2ce8f45ecac2fa93d7b9ae5fd30..4e7dfb33c310ef30ebf904bce8f66ef994b41da2 100644 (file)
@@ -5402,6 +5402,11 @@ pointers. This warning level may give a larger number of
 false positives and is deactivated by default.
 @end table
 
+@item -Wattribute-alias
+Warn about declarations using the @code{alias} and similar attributes whose
+target is incompatible with the type of the alias.  @xref{Function Attributes,
+,Declaring Attributes of Functions}.
+
 @item -Wbool-compare
 @opindex Wno-bool-compare
 @opindex Wbool-compare
index 47b3f78c9a3a7243cc7d982ab5145affde89657f..a910f8aeac166e3bb0459cde32ba7938b06f0d2d 100644 (file)
@@ -1,3 +1,19 @@
+2017-10-12  Martin Sebor  <msebor@redhat.com>
+
+       PR other/82301
+       PR c/82435
+       * g++.dg/ext/attr-ifunc-1.C: Update.
+       * g++.dg/ext/attr-ifunc-2.C: Same.
+       * g++.dg/ext/attr-ifunc-3.C: Same.
+       * g++.dg/ext/attr-ifunc-4.C: Same.
+       * g++.dg/ext/attr-ifunc-5.C: Same.
+       * g++.dg/ext/attr-ifunc-6.C: New test.
+       * g++.old-deja/g++.abi/vtable2.C: Update.
+       * gcc.dg/attr-ifunc-6.c: New test.
+       * gcc.dg/attr-ifunc-7.c: New test.
+       * gcc.dg/pr81854.c: Update.
+       * lib/target-supports.exp: Update.
+
 2017-10-12  David Malcolm  <dmalcolm@redhat.com>
 
        * g++.dg/parse/pragma2.C: Update to reflect reinstatement of the
index 2c7bba129597aa63a1c59e45e54ad65acb553797..4a29e8bb4d69aef2aff03266b16c7d00dd2f1c19 100644 (file)
@@ -4,26 +4,33 @@
 
 struct Klass
 {
+  int a[4];
+
   int implementation ();
   int magic ();
 
-  typedef int (Klass::*MemFuncPtr)();
+  /* An ifunc resolver must return a pointer to an ordinary (non-member)
+     function.  To make it possible to use ifunc with member functions,
+     the resolver must convert a member function pointer to an ordinary
+     function pointer (slicing off the high word).  */
+  typedef int Func (Klass*);
 
-  static MemFuncPtr resolver ();
+  static Func* resolver ();
 };
 
-Klass::MemFuncPtr p = &Klass::implementation;
-
-int Klass::implementation (void)
+int Klass::implementation ()
 {
   __builtin_printf ("'ere I am JH\n");
-  return 1234;
+  return a[0] + a[1] + a[2] + a[3];
 }
 
-
-Klass::MemFuncPtr Klass::resolver (void)
+Klass::Func* Klass::resolver (void)
 {
-  return &Klass::implementation;
+  /* GCC guarantees this conversion to be safe and the resulting pointer
+     usable to call the member function using ordinary (i.e., non-member)
+     function call syntax.  */
+
+  return reinterpret_cast<Func*>(&Klass::implementation);
 }
 
 int f (void) __attribute__ ((ifunc ("foo")));
@@ -32,11 +39,16 @@ typedef int (F)(void);
 extern "C" F* foo () { return 0; }
 
 
-int Klass::magic (void) __attribute__ ((ifunc ("_ZN5Klass8resolverEv")));
+int Klass::magic () __attribute__ ((ifunc ("_ZN5Klass8resolverEv")));
 
 int main ()
 {
   Klass obj;
 
-  return !(obj.magic () == 1234);
+  obj.a[0] = 1;
+  obj.a[1] = 2;
+  obj.a[2] = 3;
+  obj.a[3] = 4;
+
+  return !(obj.magic () == 10);
 }
index 1fc940bb7ddf0f4d8462d23e30c1809ca01ef18b..e5be3d29aba096f1cb7414966a91db7833330899 100644 (file)
@@ -9,9 +9,9 @@ struct Klass
   int implementation ();
   int magic ();
 
-  typedef int (Klass::*MemFuncPtr)();
+  typedef int Func (Klass*);
 
-  static MemFuncPtr resolver ();
+  static Func* resolver ();
 };
 
 int Klass::implementation (void)
@@ -20,9 +20,13 @@ int Klass::implementation (void)
   return 0;
 }
 
-Klass::MemFuncPtr Klass::resolver (void)
+Klass::Func* Klass::resolver (void)
 {
-  return &Klass::implementation;
+  /* GCC guarantees this conversion to be safe and the resulting pointer
+     usable to call the member function using ordinary (i.e., non-member)
+     function call syntax.  */
+
+  return reinterpret_cast<Func*>(&Klass::implementation);
 }
 
 int Klass::magic (void) __attribute__ ((ifunc ("_ZN5Klass8resolverEv")));
index 04206a126e8e290981226a1a9a92402bf42a6fd1..6d494244331278a257f75514cfec2097d3538f45 100644 (file)
@@ -6,23 +6,29 @@
 
 struct Klass
 {
+  int a[4];
+
   int implementation ();
   int magic ();
 
-  typedef int (Klass::*MemFuncPtr)();
+  typedef int Func (Klass*);
 
-  static MemFuncPtr resolver ();
+  static Func* resolver ();
 };
 
 int Klass::implementation (void)
 {
   printf ("'ere I am JH\n");
-  return 0;
+  return a[0] + a[1] + a[2] + a[3];
 }
 
-Klass::MemFuncPtr Klass::resolver (void)
+Klass::Func* Klass::resolver ()
 {
-  return &Klass::implementation;
+  /* GCC guarantees this conversion to be safe and the resulting pointer
+     usable to call the member function using ordinary (i.e., non-member)
+     function call syntax.  */
+
+  return reinterpret_cast<Func*>(&Klass::implementation);
 }
 
 int Klass::magic (void) __attribute__ ((ifunc ("_ZN5Klass8resolverEv")));
@@ -36,5 +42,10 @@ int main ()
 {
   Klass obj;
 
-  return Foo (obj, &Klass::magic) != 0;
+  obj.a[0] = 1;
+  obj.a[1] = 2;
+  obj.a[2] = 3;
+  obj.a[3] = 4;
+
+  return Foo (obj, &Klass::magic) != 10;
 }
index 3127193147e16d191f017cce8822278110406d62..f71dc3b9ba9f44d6e5f08c1d6d193a79f9af6a90 100644 (file)
@@ -14,9 +14,9 @@ struct Klassier : Klass
   int implementation ();
   int magic ();
 
-  typedef int (Klassier::*MemFuncPtr)();
+  typedef int Func (Klass*);
 
-  static MemFuncPtr resolver ();
+  static Func* resolver ();
 };
 
 int Klassier::implementation (void)
@@ -25,9 +25,13 @@ int Klassier::implementation (void)
   return 0;
 }
 
-Klassier::MemFuncPtr Klassier::resolver (void)
+Klassier::Func* Klassier::resolver ()
 {
-  return &Klassier::implementation;
+  /* GCC guarantees this conversion to be safe and the resulting pointer
+     usable to call the member function using ordinary (i.e., non-member)
+     function call syntax.  */
+
+  return reinterpret_cast<Func*>(&Klassier::implementation);
 }
 
 int Klassier::magic (void) __attribute__ ((ifunc ("_ZN8Klassier8resolverEv")));
index 05855dd20c0cc95f94865def734f3620e7bf915f..fd8bcff79b79bdff46d1c6f78f5fbf5a46399e04 100644 (file)
@@ -1,15 +1,21 @@
 // PR c/81854 - weak alias of an incompatible symbol accepted
 // { dg-do compile }
 // { dg-require-ifunc "" } */
+// { dg-options "-Wextra -Wno-pmf-conversions" }
 
 struct Klass
 {
   int implementation ();
-  const char* magic ();
+  int good_magic ();
+  int iffy_magic ();
+  const char* bad_magic ();
 
+  typedef int (Func)(Klass*);
   typedef int (Klass::*MemFuncPtr)();
 
-  static MemFuncPtr resolver ();
+  static Func* good_resolver ();
+  static void* iffy_resolver ();
+  static MemFuncPtr bad_resolver ();
 };
 
 int Klass::implementation (void)
@@ -17,13 +23,42 @@ int Klass::implementation (void)
   return 0;
 }
 
-const char* __attribute__ ((ifunc ("_ZN5Klass8resolverEv")))
-  Klass::magic ();   // { dg-warning "alias between functions of incompatible types" }
+// Verify no warning for the expected/compatible declaration.
 
+int __attribute__ ((ifunc ("_ZN5Klass13good_resolverEv")))
+Klass::good_magic ();
+
+Klass::Func*
+Klass::good_resolver (void)
+{
+  MemFuncPtr mfp = &Klass::implementation;
+
+  return reinterpret_cast<Func*>(mfp);
+}
+
+
+// Verify a warning for the unsafe declaration.
+
+int __attribute__ ((ifunc ("_ZN5Klass13iffy_resolverEv")))
+Klass::iffy_magic ();    // { dg-message "resolver indirect function declared here" }
+
+void*
+Klass::iffy_resolver (void)   // { dg-warning ".ifunc. resolver for .int Klass::iffy_magic\\(\\). should return .int \\(\\*\\)\\(Klass\\*\\)." }
+{
+  MemFuncPtr mfp = &Klass::implementation;
+
+  return reinterpret_cast<void*>(mfp);
+}
+
+
+// Verify an error for an incompatible declaration.
+
+const char* __attribute__ ((ifunc ("_ZN5Klass12bad_resolverEv")))
+Klass::bad_magic ();   // { dg-message "resolver indirect function declared here" }
 
 
 Klass::MemFuncPtr
-Klass::resolver (void) // { dg-message "aliased declaration here" }
+Klass::bad_resolver (void)   // { dg-error ".ifunc. resolver for .const char\\* Klass::bad_magic\\(\\). must return .const char\\* \\(\\*\\)\\(Klass\\*\\)." }
 {
   return &Klass::implementation;
 }
index 2c88a95800bc0e2b827f954a6ceebbf113869808..96533e09218e5c94c510853ac59a12a5b6661782 100644 (file)
@@ -1,5 +1,5 @@
 // { dg-do run  }
-// { dg-options "-Wno-attributes -fno-strict-aliasing" }
+// { dg-options "-Wno-attribute-alias -fno-strict-aliasing" }
 // Origin: Mark Mitchell <mark@codesourcery.com>
 
 #if defined (__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100
index b8499f8b130930c1889f33d8f07e1ceab1b0754d..1021a811be40103dd06894f8841e5e56a847c161 100644 (file)
@@ -1,6 +1,7 @@
 /* PR c/81854 - weak alias of an incompatible symbol accepted
    { dg-do compile }
-   { dg-require-ifunc "" } */
+   { dg-require-ifunc "" }
+   { dg-options "-Wextra" } */
 
 const char* __attribute__ ((weak, alias ("f0_target")))
 f0 (void);          /* { dg-error "alias between function and variable" } */
@@ -26,39 +27,37 @@ const char* f2_target (int i)   /* { dg-message "aliased declaration here" } */
   return 0;
 }
 
-
 int __attribute__ ((ifunc ("f3_resolver")))
-f3 (void);          /* { dg-error ".ifunc. resolver must return a function pointer" } */
+f3 (void);          /* { dg-message "resolver indirect function declared here" } */
 
-int f3_resolver (void)   /* { dg-message "resolver declaration here" } */
+void* f3_resolver (void) /* { dg-warning "ifunc. resolver for .f3. should return .int \\(\\*\\)\\(void\\)." } */
 {
   return 0;
 }
 
 
 int __attribute__ ((ifunc ("f4_resolver")))
-f4 (void);          /* { dg-warning ".ifunc. resolver should return a function pointer" } */
+f4 (void);          /* { dg-message "resolver indirect function declared here" } */
 
-void* f4_resolver (void) /* { dg-message "resolver declaration here" } */
+typedef void F4 (void);
+F4* f4_resolver (void) /* { dg-warning ".ifunc. resolver for .f4. should return .int \\(\\*\\)\\(void\\)" } */
 {
   return 0;
 }
 
+const char* __attribute__ ((ifunc ("f5_resolver")))
+f5 (void);
 
-int __attribute__ ((ifunc ("f5_resolver")))
-f5 (void);          /* { dg-warning "alias between functions of incompatible types" } */
-
-typedef void F5 (void);
-F5* f5_resolver (void) /* { dg-message "aliased declaration here" } */
+typedef const char* F5 (void);
+F5* f5_resolver (void)
 {
   return 0;
 }
 
-const char* __attribute__ ((ifunc ("f6_resolver")))
-f6 (void);
+int __attribute__ ((ifunc ("f6_resolver")))
+f6 (void);          /* { dg-message "resolver indirect function declared here" } */
 
-typedef const char* F6 (void);
-F6* f6_resolver (void)
+int f6_resolver (void)   /* { dg-error ".ifunc. resolver for 'f6' must return .int \\(\\*\\)\\(void\\)." } */
 {
   return 0;
 }
index fbe8f2a76af739c8e211c708f79717a60a72b87a..4f9bf460c220aa88a1f1236e17ca6f93d6ca8a88 100644 (file)
@@ -440,8 +440,8 @@ proc check_ifunc_available { } {
        extern "C" {
        #endif
        typedef void F (void);
-       F* g() {}
-       void f() __attribute__((ifunc("g")));
+       F* g (void) {}
+       void f () __attribute__ ((ifunc ("g")));
        #ifdef __cplusplus
        }
        #endif