This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
correct attribute ifunc C++ type safety (PR 82301)
- From: Martin Sebor <msebor at gmail dot com>
- To: Gcc Patch List <gcc-patches at gcc dot gnu dot org>
- Date: Sun, 24 Sep 2017 19:03:16 -0600
- Subject: correct attribute ifunc C++ type safety (PR 82301)
- Authentication-results: sourceware.org; auth=none
r253041 enhanced type checking for alias and ifunc attributes to
detect declarations of incompatible aliases, or ifunc resolvers
that return pointers to functions of an incompatible type. More
extensive testing exposed a bug in the implementation of the ifunc
attribute handling in C++ where the checker expected the ifunc
resolver to return a pointer to a member function when the
implementation actually expects it return a pointer to a non-
member function.
In a discussion of the test suite failures, Jakub also suggested
to break the enhanced warning out of -Wattributes and issue it
under a different option.
The attached patch corrects the C++ problem and moves the warning
under -Wincompatible-pointer-types. Since this is a C-only option,
the patch also enables for it C++. Since the option is enabled by
default, the patch further requires -Wextra to issue the warning
for ifunc resolvers returning void*. However, the patched checker
diagnoses other incompatibilities without it.
Martin
PR c/82301 - Updated test case g++.dg/ext/attr-ifunc-1.C (and others) in r253041 segfault on powerpc64
gcc/ChangeLog:
PR other/82301
* cgraphunit.c (maybe_diag_incompatible_alias): New function.
(handle_alias_pairs): Call it.
* doc/extend.texi (ifunc attribute): Discuss C++ specifics.
* doc/invoke.texi (-Wincompatible-pointer-types): Ditto.
gcc/c-family/ChangeLog:
PR other/82301
* c.opt (-Wincompatible-pointer-types): Enable for C++.
gcc/testsuite/ChangeLog:
PR other/82301
* 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.
* 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.
Index: gcc/c-family/c.opt
===================================================================
--- gcc/c-family/c.opt (revision 253099)
+++ gcc/c-family/c.opt (working copy)
@@ -592,7 +592,7 @@ C C++ Var(warn_ignored_attributes) Init(1) Warning
Warn whenever attributes are ignored.
Wincompatible-pointer-types
-C ObjC Var(warn_incompatible_pointer_types) Init(1) Warning
+C C++ ObjC ObjcC++ Var(warn_incompatible_pointer_types) Init(1) Warning
Warn when there is a conversion between pointers that have incompatible types.
Winit-self
Index: gcc/cgraphunit.c
===================================================================
--- gcc/cgraphunit.c (revision 253099)
+++ gcc/cgraphunit.c (working copy)
@@ -1296,6 +1296,106 @@ 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));
+ if (ifunc)
+ {
+ /* Handle attribute ifunc first. */
+
+ tree funcptr = altype;
+
+ /* 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 compatibilties. FUNCPTR is used only
+ in diagnostics. */
+
+ if (TREE_CODE (altype) == METHOD_TYPE)
+ {
+ 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 Wincompatible-pointer-types for conversions
+ to void* with -Wextra. */
+ if (VOID_TYPE_P (targtype) && !extra_warnings)
+ return;
+
+ if ((!FUNC_OR_METHOD_TYPE_P (targtype)
+ || (prototype_p (altype)
+ && prototype_p (targtype)
+ && !types_compatible_p (altype, targtype))))
+ {
+ funcptr = build_pointer_type (funcptr);
+
+ if (warning_at (DECL_SOURCE_LOCATION (target),
+ OPT_Wincompatible_pointer_types,
+ "%<ifunc%> resolver for %qD should return %qT",
+ alias, funcptr))
+ inform (DECL_SOURCE_LOCATION (alias),
+ "resolver indirect function declared here");
+ }
+ }
+ else
+ {
+ /* Deal with static member function pointers. */
+ if (TREE_CODE (targtype) != RECORD_TYPE
+ || TYPE_FIELDS (targtype)
+ || TREE_CODE (TREE_TYPE (TYPE_FIELDS (targtype))) != POINTER_TYPE
+ || (TREE_CODE (TREE_TYPE (TREE_TYPE (TYPE_FIELDS (targtype))))
+ != METHOD_TYPE))
+ {
+ funcptr = build_pointer_type (funcptr);
+
+ error ("%<ifunc%> resolver for %qD must return %qT",
+ alias, funcptr);
+
+ inform (DECL_SOURCE_LOCATION (alias),
+ "resolver indirect function declared here");
+ }
+ }
+
+ return;
+ }
+
+ /* Handle attribute alias. */
+
+ 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 (warning_at (DECL_SOURCE_LOCATION (alias),
+ OPT_Wincompatible_pointer_types,
+ "%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 +1405,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,66 +1452,8 @@ 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);
+ maybe_diag_incompatible_alias (p->decl, 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");
- }
-
cgraph_node *src_node = cgraph_node::get (p->decl);
if (src_node && src_node->definition)
src_node->reset ();
Index: gcc/doc/extend.texi
===================================================================
--- gcc/doc/extend.texi (revision 253099)
+++ gcc/doc/extend.texi (working copy)
@@ -2801,7 +2801,7 @@ void *my_memcpy (void *dst, const void *src, size_
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,9 +2814,9 @@ 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)
@@ -2823,6 +2823,47 @@ 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{-Wincompatible-pointer-types} 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: gcc/doc/invoke.texi
===================================================================
--- gcc/doc/invoke.texi (revision 253099)
+++ gcc/doc/invoke.texi (working copy)
@@ -5456,13 +5456,16 @@ are being discarded. Typically, the compiler warns
takes a @code{int (*)[]} parameter. This option can be used to
suppress such a warning.
-@item -Wno-incompatible-pointer-types @r{(C and Objective-C only)}
+@item -Wno-incompatible-pointer-types
@opindex Wno-incompatible-pointer-types
@opindex Wincompatible-pointer-types
Do not warn when there is a conversion between pointers that have incompatible
types. This warning is for cases not covered by @option{-Wno-pointer-sign},
which warns for pointer argument passing or assignment with different
-signedness.
+signedness. In C++ where incompatible pointer conversions ordinarily cause
+errors, the warning detects such conversions in GCC extensions that aren't
+part of the standard language. An example of a construct that might perform
+such a conversion is the @code{alias} attribute. @xref{Function Attributes,,Declaring Attributes of Functions}.
@item -Wno-int-conversion @r{(C and Objective-C only)}
@opindex Wno-int-conversion
Index: gcc/testsuite/g++.dg/ext/attr-ifunc-1.C
===================================================================
--- gcc/testsuite/g++.dg/ext/attr-ifunc-1.C (revision 253099)
+++ gcc/testsuite/g++.dg/ext/attr-ifunc-1.C (working copy)
@@ -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::Func* Klass::resolver (void)
+{
+ /* 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. */
-Klass::MemFuncPtr Klass::resolver (void)
-{
- return &Klass::implementation;
+ 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: gcc/testsuite/g++.dg/ext/attr-ifunc-2.C
===================================================================
--- gcc/testsuite/g++.dg/ext/attr-ifunc-2.C (revision 253099)
+++ gcc/testsuite/g++.dg/ext/attr-ifunc-2.C (working copy)
@@ -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: gcc/testsuite/g++.dg/ext/attr-ifunc-3.C
===================================================================
--- gcc/testsuite/g++.dg/ext/attr-ifunc-3.C (revision 253099)
+++ gcc/testsuite/g++.dg/ext/attr-ifunc-3.C (working copy)
@@ -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: gcc/testsuite/g++.dg/ext/attr-ifunc-4.C
===================================================================
--- gcc/testsuite/g++.dg/ext/attr-ifunc-4.C (revision 253099)
+++ gcc/testsuite/g++.dg/ext/attr-ifunc-4.C (working copy)
@@ -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: gcc/testsuite/g++.dg/ext/attr-ifunc-5.C
===================================================================
--- gcc/testsuite/g++.dg/ext/attr-ifunc-5.C (revision 253099)
+++ gcc/testsuite/g++.dg/ext/attr-ifunc-5.C (working copy)
@@ -1,15 +1,19 @@
// PR c/81854 - weak alias of an incompatible symbol accepted
// { dg-do compile }
// { dg-require-ifunc "" } */
+// { dg-options "-Wno-pmf-conversions" }
struct Klass
{
int implementation ();
- const char* magic ();
+ int good_magic ();
+ const char* bad_magic ();
+ typedef int (Func)(Klass*);
typedef int (Klass::*MemFuncPtr)();
- static MemFuncPtr resolver ();
+ static Func* good_resolver ();
+ static MemFuncPtr bad_resolver ();
};
int Klass::implementation (void)
@@ -17,13 +21,28 @@ int Klass::implementation (void)
return 0;
}
-const char* __attribute__ ((ifunc ("_ZN5Klass8resolverEv")))
- Klass::magic (); // { dg-warning "alias between functions of incompatible types" }
+int __attribute__ ((ifunc ("_ZN5Klass13good_resolverEv")))
+Klass::good_magic ();
+Klass::Func*
+Klass::good_resolver (void)
+{
+ MemFuncPtr mfp = &Klass::implementation;
+
+ return reinterpret_cast<Func*>(mfp);
+}
+
+
+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)
{
return &Klass::implementation;
-}
+ // The location of this error is unfortunate. It would be a lot better
+ // if it were on the next to the prototype...
+} // { dg-error ".ifunc. resolver for .const char\\* Klass::bad_magic\\(\\). must return .const char\\* \\(\\*\\)\\(Klass\\*\\)." }
Index: gcc/testsuite/g++.dg/ext/attr-ifunc-6.C
===================================================================
--- gcc/testsuite/g++.dg/ext/attr-ifunc-6.C (nonexistent)
+++ gcc/testsuite/g++.dg/ext/attr-ifunc-6.C (working copy)
@@ -0,0 +1,81 @@
+// PR c/81854 - weak alias of an incompatible symbol accepted
+// { dg-do run }
+// { dg-require-ifunc "" }
+// { dg-options "-Wno-pmf-conversions" }
+
+struct Klass
+{
+ int a[4];
+
+ int implementation (int);
+ int implementation (int, long, const char*);
+
+ int magic (int);
+ int magic (int, long, const char *);
+
+ /* 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 Func1 (Klass*, int);
+ typedef int Func3 (Klass*, int, long, const char*);
+
+ template <class Func>
+ static Func* resolver ();
+};
+
+const char *str;
+
+int Klass::implementation (int x)
+{
+ return a[0] + a[1] + a[2] + a[3] + x;
+}
+
+int Klass::implementation (int x, long y, const char *s)
+{
+ return a[0] + a[1] + a[2] + a[3] + x + y + !(s == str);
+}
+
+template <>
+Klass::Func1* Klass::resolver<Klass::Func1> ()
+{
+ int (Klass::*pmf)(int) = &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. The cast triggers -Wno-pmf-conversions which
+ the test suppresses. */
+
+ return reinterpret_cast<Func1*>(pmf);
+}
+
+template <>
+Klass::Func3* Klass::resolver<Klass::Func3> ()
+{
+ int (Klass::*pmf)(int, long, const char*) = &Klass::implementation;
+
+ return reinterpret_cast<Func3*>(pmf);
+}
+
+__attribute__ ((ifunc ("_ZN5Klass8resolverIFiPS_iEEEPT_v")))
+int Klass::magic (int);
+
+__attribute__ ((ifunc ("_ZN5Klass8resolverIFiPS_ilPKcEEEPT_v")))
+int Klass::magic (int, long, const char *);
+
+int main ()
+{
+ Klass obj;
+
+ obj.a[0] = 1;
+ obj.a[1] = 2;
+ obj.a[2] = 3;
+ obj.a[3] = 4;
+
+ str = "teststring";
+
+ // Verify that data members and function arguments are correctly
+ // retrieved.
+ if (obj.magic (5) != 15 || obj.magic (5, 6, str) != 21)
+ __builtin_abort ();
+}
Index: gcc/testsuite/gcc.dg/attr-ifunc-6.c
===================================================================
--- gcc/testsuite/gcc.dg/attr-ifunc-6.c (nonexistent)
+++ gcc/testsuite/gcc.dg/attr-ifunc-6.c (working copy)
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "" } */
+
+static int implementation (void)
+{
+ return 0;
+}
+
+/* Verify that a resolver that returns void* is not diagnosed without
+ -Wextra (see attr-ifunc-7.c for a test that verifies that -Wextra
+ triggers the warning). */
+static void* resolver_nowarn (void)
+{
+ return implementation;
+}
+
+extern int
+indirect_nowarn (void) __attribute__ ((ifunc ("resolver_nowarn")));
+
+
+/* Verify that a resolver that returns a T* that's not void* is diagnosed
+ even without -Wextra. */
+static int* resolver_warn (void) /* { dg-warning ".ifunc. resolver for .indirect_warn. should return 'int \\(\\*\\)\\(void\\)." } */
+{
+ return (int *)implementation;
+}
+
+extern int
+indirect_warn (void) /* { dg-message "" } */
+__attribute__ ((ifunc ("resolver_warn")));
Index: gcc/testsuite/gcc.dg/attr-ifunc-7.c
===================================================================
--- gcc/testsuite/gcc.dg/attr-ifunc-7.c (nonexistent)
+++ gcc/testsuite/gcc.dg/attr-ifunc-7.c (working copy)
@@ -0,0 +1,30 @@
+/* { dg-do compile } */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-Wextra" } */
+
+static int implementation (void)
+{
+ return 0;
+}
+
+/* Verify that a resolver that returns void* is diagnosed with -Wextra. */
+static void* resolver_warn (void) /* { dg-warning ".ifunc. resolver for .indirect_warn. should return .int \\(\\*\\)\\(void\\)." } */
+{
+ return implementation;
+}
+
+extern __attribute__ ((ifunc ("resolver_warn"))) int
+indirect_warn (void); /* { dg-message "resolver indirect function declared here" } */
+
+
+
+/* Verify that a resolver that returns int* is still diagnosed, even
+ with -Wextra. */
+static int* resolver_warn_2 (void) /* { dg-warning ".ifunc. resolver for .indirect_warn_2. should return .int \\(\\*\\)\\(void\\)." } */
+{
+ return (int *)implementation;
+}
+
+extern __attribute__ ((ifunc ("resolver_warn_2"))) int
+indirect_warn_2 (void); /* { dg-message "resolver indirect function declared here" } */
+
Index: gcc/testsuite/gcc.dg/pr81854.c
===================================================================
--- gcc/testsuite/gcc.dg/pr81854.c (revision 253099)
+++ gcc/testsuite/gcc.dg/pr81854.c (working copy)
@@ -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,11 +27,10 @@ const char* f2_target (int i) /* { dg-message "a
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;
}
@@ -37,28 +37,32 @@ int __attribute__ ((ifunc ("f3_resolver")))
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)
{
return 0;
}
+
+/* Unfortunately, the diagnostic location is either the closing curly
+ of the resolver defintion or the last declaration that comes after
+ it (if one does). */
+int f6_resolver (void); /* { dg-error ".ifunc. resolver for 'f6' must return .int \\(\\*\\)\\(void\\)." } */
Index: gcc/testsuite/lib/target-supports.exp
===================================================================
--- gcc/testsuite/lib/target-supports.exp (revision 253099)
+++ gcc/testsuite/lib/target-supports.exp (working copy)
@@ -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