From c65e18d3331aa9995ddab8527132de706f6d9236 Mon Sep 17 00:00:00 2001 From: Bernd Edlinger Date: Thu, 14 Dec 2017 18:59:24 +0000 Subject: [PATCH] invoke.texi: Document -Wcast-function-type. gcc: 2017-12-14 Bernd Edlinger * doc/invoke.texi: Document -Wcast-function-type. * recog.h (stored_funcptr): Change signature. * tree-dump.c (dump_node): Avoid warning. * typed-splay-tree.h (typed_splay_tree): Avoid warning. libcpp: 2017-12-14 Bernd Edlinger * internal.h (maybe_print_line): Change signature. c-family: 2017-12-14 Bernd Edlinger * c.opt (Wcast-function-type): New warning option. * c-lex.c (get_fileinfo): Avoid warning. * c-ppoutput.c (scan_translation_unit_directives_only): Remove cast. c: 2017-12-14 Bernd Edlinger * c-typeck.c (c_safe_arg_type_equiv_p, c_safe_function_type_cast_p): New function. (build_c_cast): Implement -Wcast-function-type. cp: 2017-12-14 Bernd Edlinger * decl2.c (start_static_storage_duration_function): Avoid warning. * typeck.c (cxx_safe_arg_type_equiv_p, cxx_safe_function_type_cast_p): New function. (build_reinterpret_cast_1): Implement -Wcast-function-type. testsuite: 2017-12-14 Bernd Edlinger * c-c++-common/Wcast-function-type.c: New test. * g++.dg/Wcast-function-type.C: New test. From-SVN: r255661 --- gcc/ChangeLog | 7 ++ gcc/c-family/ChangeLog | 6 ++ gcc/c-family/c-lex.c | 6 +- gcc/c-family/c-ppoutput.c | 2 +- gcc/c-family/c.opt | 4 + gcc/c/ChangeLog | 6 ++ gcc/c/c-typeck.c | 63 +++++++++++++++ gcc/cp/ChangeLog | 7 ++ gcc/cp/decl2.c | 3 +- gcc/cp/typeck.c | 77 ++++++++++++++++++- gcc/doc/invoke.texi | 18 ++++- gcc/recog.h | 2 +- gcc/testsuite/ChangeLog | 5 ++ .../c-c++-common/Wcast-function-type.c | 31 ++++++++ gcc/testsuite/g++.dg/Wcast-function-type.C | 17 ++++ gcc/tree-dump.c | 3 +- gcc/typed-splay-tree.h | 9 ++- libcpp/ChangeLog | 4 + libcpp/internal.h | 2 +- 19 files changed, 258 insertions(+), 14 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/Wcast-function-type.c create mode 100644 gcc/testsuite/g++.dg/Wcast-function-type.C diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 0192d672823..d57e06b0f0e 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,10 @@ +2017-12-14 Bernd Edlinger + + * doc/invoke.texi: Document -Wcast-function-type. + * recog.h (stored_funcptr): Change signature. + * tree-dump.c (dump_node): Avoid warning. + * typed-splay-tree.h (typed_splay_tree): Avoid warning. + 2017-12-14 Qing Zhao PR middle_end/79538 diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index abe53019aa6..9f87d008382 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,9 @@ +2017-12-14 Bernd Edlinger + + * c.opt (Wcast-function-type): New warning option. + * c-lex.c (get_fileinfo): Avoid warning. + * c-ppoutput.c (scan_translation_unit_directives_only): Remove cast. + 2017-12-14 Qing Zhao PR middle_end/79538 diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c index 8342800303a..d45dc4b6840 100644 --- a/gcc/c-family/c-lex.c +++ b/gcc/c-family/c-lex.c @@ -101,9 +101,11 @@ get_fileinfo (const char *name) struct c_fileinfo *fi; if (!file_info_tree) - file_info_tree = splay_tree_new ((splay_tree_compare_fn) strcmp, + file_info_tree = splay_tree_new ((splay_tree_compare_fn) + (void (*) (void)) strcmp, 0, - (splay_tree_delete_value_fn) free); + (splay_tree_delete_value_fn) + (void (*) (void)) free); n = splay_tree_lookup (file_info_tree, (splay_tree_key) name); if (n) diff --git a/gcc/c-family/c-ppoutput.c b/gcc/c-family/c-ppoutput.c index d1c92379f62..32176ed122e 100644 --- a/gcc/c-family/c-ppoutput.c +++ b/gcc/c-family/c-ppoutput.c @@ -299,7 +299,7 @@ scan_translation_unit_directives_only (cpp_reader *pfile) struct _cpp_dir_only_callbacks cb; cb.print_lines = print_lines_directives_only; - cb.maybe_print_line = (void (*) (source_location)) maybe_print_line; + cb.maybe_print_line = maybe_print_line; _cpp_preprocess_dir_only (pfile, &cb); } diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index b2548105736..31b50ee56c9 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -384,6 +384,10 @@ Wc++17-compat C++ ObjC++ Var(warn_cxx17_compat) Warning LangEnabledBy(C++ ObjC++,Wall) Warn about C++ constructs whose meaning differs between ISO C++ 2014 and ISO C++ 2017. +Wcast-function-type +C ObjC C++ ObjC++ Var(warn_cast_function_type) Warning EnabledBy(Wextra) +Warn about casts between incompatible function types. + Wcast-qual C ObjC C++ ObjC++ Var(warn_cast_qual) Warning Warn about casts which discard qualifiers. diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index 3f5414bc18d..d486018e3a2 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,9 @@ +2017-12-14 Bernd Edlinger + + * c-typeck.c (c_safe_arg_type_equiv_p, + c_safe_function_type_cast_p): New function. + (build_c_cast): Implement -Wcast-function-type. + 2017-12-14 Richard Biener PR c/83415 diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c index 13b26845d97..541fb61ef08 100644 --- a/gcc/c/c-typeck.c +++ b/gcc/c/c-typeck.c @@ -5472,6 +5472,59 @@ handle_warn_cast_qual (location_t loc, tree type, tree otype) while (TREE_CODE (in_type) == POINTER_TYPE); } +/* Heuristic check if two parameter types can be considered ABI-equivalent. */ + +static bool +c_safe_arg_type_equiv_p (tree t1, tree t2) +{ + t1 = TYPE_MAIN_VARIANT (t1); + t2 = TYPE_MAIN_VARIANT (t2); + + if (TREE_CODE (t1) == POINTER_TYPE + && TREE_CODE (t2) == POINTER_TYPE) + return true; + + /* The signedness of the parameter matters only when an integral + type smaller than int is promoted to int, otherwise only the + precision of the parameter matters. + This check should make sure that the callee does not see + undefined values in argument registers. */ + if (INTEGRAL_TYPE_P (t1) + && INTEGRAL_TYPE_P (t2) + && TYPE_PRECISION (t1) == TYPE_PRECISION (t2) + && (TYPE_UNSIGNED (t1) == TYPE_UNSIGNED (t2) + || !targetm.calls.promote_prototypes (NULL_TREE) + || TYPE_PRECISION (t1) >= TYPE_PRECISION (integer_type_node))) + return true; + + return comptypes (t1, t2); +} + +/* Check if a type cast between two function types can be considered safe. */ + +static bool +c_safe_function_type_cast_p (tree t1, tree t2) +{ + if (TREE_TYPE (t1) == void_type_node && + TYPE_ARG_TYPES (t1) == void_list_node) + return true; + + if (TREE_TYPE (t2) == void_type_node && + TYPE_ARG_TYPES (t2) == void_list_node) + return true; + + if (!c_safe_arg_type_equiv_p (TREE_TYPE (t1), TREE_TYPE (t2))) + return false; + + for (t1 = TYPE_ARG_TYPES (t1), t2 = TYPE_ARG_TYPES (t2); + t1 && t2; + t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2)) + if (!c_safe_arg_type_equiv_p (TREE_VALUE (t1), TREE_VALUE (t2))) + return false; + + return true; +} + /* Build an expression representing a cast to type TYPE of expression EXPR. LOC is the location of the cast-- typically the open paren of the cast. */ @@ -5665,6 +5718,16 @@ build_c_cast (location_t loc, tree type, tree expr) pedwarn (loc, OPT_Wpedantic, "ISO C forbids " "conversion of object pointer to function pointer type"); + if (TREE_CODE (type) == POINTER_TYPE + && TREE_CODE (otype) == POINTER_TYPE + && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE + && TREE_CODE (TREE_TYPE (otype)) == FUNCTION_TYPE + && !c_safe_function_type_cast_p (TREE_TYPE (type), + TREE_TYPE (otype))) + warning_at (loc, OPT_Wcast_function_type, + "cast between incompatible function types" + " from %qT to %qT", otype, type); + ovalue = value; value = convert (type, value); diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index ac6a8e1bfff..d7a1dde686f 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,10 @@ +2017-12-14 Bernd Edlinger + + * decl2.c (start_static_storage_duration_function): Avoid warning. + * typeck.c (cxx_safe_arg_type_equiv_p, + cxx_safe_function_type_cast_p): New function. + (build_reinterpret_cast_1): Implement -Wcast-function-type. + 2017-12-14 Jakub Jelinek PR c++/79650 diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 5d30369e80f..89a940ac330 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -3558,7 +3558,8 @@ start_static_storage_duration_function (unsigned count) priority_info_map = splay_tree_new (splay_tree_compare_ints, /*delete_key_fn=*/0, /*delete_value_fn=*/ - (splay_tree_delete_value_fn) &free); + (splay_tree_delete_value_fn) + (void (*) (void)) free); /* We always need to generate functions for the DEFAULT_INIT_PRIORITY so enter it now. That way when we walk diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 8f3302f1933..390aa1580bd 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -1173,6 +1173,59 @@ comp_template_parms_position (tree t1, tree t2) return true; } +/* Heuristic check if two parameter types can be considered ABI-equivalent. */ + +static bool +cxx_safe_arg_type_equiv_p (tree t1, tree t2) +{ + t1 = TYPE_MAIN_VARIANT (t1); + t2 = TYPE_MAIN_VARIANT (t2); + + if (TREE_CODE (t1) == POINTER_TYPE + && TREE_CODE (t2) == POINTER_TYPE) + return true; + + /* The signedness of the parameter matters only when an integral + type smaller than int is promoted to int, otherwise only the + precision of the parameter matters. + This check should make sure that the callee does not see + undefined values in argument registers. */ + if (INTEGRAL_TYPE_P (t1) + && INTEGRAL_TYPE_P (t2) + && TYPE_PRECISION (t1) == TYPE_PRECISION (t2) + && (TYPE_UNSIGNED (t1) == TYPE_UNSIGNED (t2) + || !targetm.calls.promote_prototypes (NULL_TREE) + || TYPE_PRECISION (t1) >= TYPE_PRECISION (integer_type_node))) + return true; + + return same_type_p (t1, t2); +} + +/* Check if a type cast between two function types can be considered safe. */ + +static bool +cxx_safe_function_type_cast_p (tree t1, tree t2) +{ + if (TREE_TYPE (t1) == void_type_node && + TYPE_ARG_TYPES (t1) == void_list_node) + return true; + + if (TREE_TYPE (t2) == void_type_node && + TYPE_ARG_TYPES (t2) == void_list_node) + return true; + + if (!cxx_safe_arg_type_equiv_p (TREE_TYPE (t1), TREE_TYPE (t2))) + return false; + + for (t1 = TYPE_ARG_TYPES (t1), t2 = TYPE_ARG_TYPES (t2); + t1 && t2; + t1 = TREE_CHAIN (t1), t2 = TREE_CHAIN (t2)) + if (!cxx_safe_arg_type_equiv_p (TREE_VALUE (t1), TREE_VALUE (t2))) + return false; + + return true; +} + /* Subroutine in comptypes. */ static bool @@ -7326,9 +7379,27 @@ build_reinterpret_cast_1 (tree type, tree expr, bool c_cast_p, && same_type_p (type, intype)) /* DR 799 */ return rvalue (expr); - else if ((TYPE_PTRFN_P (type) && TYPE_PTRFN_P (intype)) - || (TYPE_PTRMEMFUNC_P (type) && TYPE_PTRMEMFUNC_P (intype))) - return build_nop (type, expr); + else if (TYPE_PTRFN_P (type) && TYPE_PTRFN_P (intype)) + { + if ((complain & tf_warning) + && !cxx_safe_function_type_cast_p (TREE_TYPE (type), + TREE_TYPE (intype))) + warning (OPT_Wcast_function_type, + "cast between incompatible function types" + " from %qH to %qI", intype, type); + return build_nop (type, expr); + } + else if (TYPE_PTRMEMFUNC_P (type) && TYPE_PTRMEMFUNC_P (intype)) + { + if ((complain & tf_warning) + && !cxx_safe_function_type_cast_p + (TREE_TYPE (TYPE_PTRMEMFUNC_FN_TYPE_RAW (type)), + TREE_TYPE (TYPE_PTRMEMFUNC_FN_TYPE_RAW (intype)))) + warning (OPT_Wcast_function_type, + "cast between incompatible pointer to member types" + " from %qH to %qI", intype, type); + return build_nop (type, expr); + } else if ((TYPE_PTRDATAMEM_P (type) && TYPE_PTRDATAMEM_P (intype)) || (TYPE_PTROBV_P (type) && TYPE_PTROBV_P (intype))) { diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 0d565b48f21..001bbeae577 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -267,7 +267,7 @@ Objective-C and Objective-C++ Dialects}. -Wno-builtin-declaration-mismatch @gol -Wno-builtin-macro-redefined -Wc90-c99-compat -Wc99-c11-compat @gol -Wc++-compat -Wc++11-compat -Wc++14-compat @gol --Wcast-align -Wcast-align=strict -Wcast-qual @gol +-Wcast-align -Wcast-align=strict -Wcast-function-type -Wcast-qual @gol -Wchar-subscripts -Wchkp -Wcatch-value -Wcatch-value=@var{n} @gol -Wclobbered -Wcomment -Wconditionally-supported @gol -Wconversion -Wcoverage-mismatch -Wno-cpp -Wdangling-else -Wdate-time @gol @@ -3904,6 +3904,7 @@ This enables some extra warning flags that are not enabled by name is still supported, but the newer name is more descriptive.) @gccoptlist{-Wclobbered @gol +-Wcast-function-type @gol -Wempty-body @gol -Wignored-qualifiers @gol -Wimplicit-fallthrough=3 @gol @@ -6041,6 +6042,21 @@ Warn whenever a pointer is cast such that the required alignment of the target is increased. For example, warn if a @code{char *} is cast to an @code{int *} regardless of the target machine. +@item -Wcast-function-type +@opindex Wcast-function-type +@opindex Wno-cast-function-type +Warn when a function pointer is cast to an incompatible function pointer. +In a cast involving function types with a variable argument list only +the types of initial arguments that are provided are considered. +Any parameter of pointer-type matches any other pointer-type. Any benign +differences in integral types are ignored, like @code{int} vs. @code{long} +on ILP32 targets. Likewise type qualifiers are ignored. The function +type @code{void (*) (void)} is special and matches everything, which can +be used to suppress this warning. +In a cast involving pointer to member types this warning warns whenever +the type cast is changing the pointer to member type. +This warning is enabled by @option{-Wextra}. + @item -Wwrite-strings @opindex Wwrite-strings @opindex Wno-write-strings diff --git a/gcc/recog.h b/gcc/recog.h index 07c60feffc6..2ce979932a2 100644 --- a/gcc/recog.h +++ b/gcc/recog.h @@ -294,7 +294,7 @@ struct insn_gen_fn typedef rtx_insn * (*f15) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx); typedef rtx_insn * (*f16) (rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx, rtx); - typedef f0 stored_funcptr; + typedef void (*stored_funcptr) (void); rtx_insn * operator () (void) const { return ((f0)func) (); } rtx_insn * operator () (rtx a0) const { return ((f1)func) (a0); } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6cedc130701..f39da0bde03 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2017-12-14 Bernd Edlinger + + * c-c++-common/Wcast-function-type.c: New test. + * g++.dg/Wcast-function-type.C: New test. + 2017-12-14 Qing Zhao PR middle_end/79538 diff --git a/gcc/testsuite/c-c++-common/Wcast-function-type.c b/gcc/testsuite/c-c++-common/Wcast-function-type.c new file mode 100644 index 00000000000..81105762ef7 --- /dev/null +++ b/gcc/testsuite/c-c++-common/Wcast-function-type.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-Wcast-function-type" } */ + +int f(long); + +typedef int (f1)(long); +typedef int (f2)(void*); +#ifdef __cplusplus +typedef int (f3)(...); +typedef void (f4)(...); +#else +typedef int (f3)(); +typedef void (f4)(); +#endif +typedef void (f5)(void); + +f1 *a; +f2 *b; +f3 *c; +f4 *d; +f5 *e; + +void +foo (void) +{ + a = (f1 *) f; /* { dg-bogus "incompatible function types" } */ + b = (f2 *) f; /* { dg-warning "incompatible function types" } */ + c = (f3 *) f; /* { dg-bogus "incompatible function types" } */ + d = (f4 *) f; /* { dg-warning "incompatible function types" } */ + e = (f5 *) f; /* { dg-bogus "incompatible function types" } */ +} diff --git a/gcc/testsuite/g++.dg/Wcast-function-type.C b/gcc/testsuite/g++.dg/Wcast-function-type.C new file mode 100644 index 00000000000..c6494051791 --- /dev/null +++ b/gcc/testsuite/g++.dg/Wcast-function-type.C @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-Wcast-function-type" } */ + +struct S +{ + void foo (int*); + void bar (int); +}; + +typedef void (S::*MF)(int); + +void +foo (void) +{ + MF p1 = (MF)&S::foo; /* { dg-warning "pointer to member" } */ + MF p2 = (MF)&S::bar; /* { dg-bogus "pointer to member" } */ +} diff --git a/gcc/tree-dump.c b/gcc/tree-dump.c index d691278bbb2..74813303a52 100644 --- a/gcc/tree-dump.c +++ b/gcc/tree-dump.c @@ -736,7 +736,8 @@ dump_node (const_tree t, dump_flags_t flags, FILE *stream) di.flags = flags; di.node = t; di.nodes = splay_tree_new (splay_tree_compare_pointers, 0, - (splay_tree_delete_value_fn) &free); + (splay_tree_delete_value_fn) + (void (*) (void)) free); /* Queue up the first node. */ queue (&di, t, DUMP_NONE); diff --git a/gcc/typed-splay-tree.h b/gcc/typed-splay-tree.h index b41ff7ae645..032ae4fd9d6 100644 --- a/gcc/typed-splay-tree.h +++ b/gcc/typed-splay-tree.h @@ -75,9 +75,12 @@ inline typed_splay_tree:: delete_key_fn delete_key_fn, delete_value_fn delete_value_fn) { - m_inner = splay_tree_new ((splay_tree_compare_fn)compare_fn, - (splay_tree_delete_key_fn)delete_key_fn, - (splay_tree_delete_value_fn)delete_value_fn); + m_inner = splay_tree_new ((splay_tree_compare_fn) + (void (*) (void)) compare_fn, + (splay_tree_delete_key_fn) + (void (*) (void)) delete_key_fn, + (splay_tree_delete_value_fn) + (void (*) (void)) delete_value_fn); } /* Destructor for typed_splay_tree . */ diff --git a/libcpp/ChangeLog b/libcpp/ChangeLog index 76f10fb2af7..5a41cf4b5f5 100644 --- a/libcpp/ChangeLog +++ b/libcpp/ChangeLog @@ -1,3 +1,7 @@ +2017-12-14 Bernd Edlinger + + * internal.h (maybe_print_line): Change signature. + 2017-12-05 Jakub Jelinek PR c++/79228 diff --git a/libcpp/internal.h b/libcpp/internal.h index 0a33abafd43..ee47a53c33e 100644 --- a/libcpp/internal.h +++ b/libcpp/internal.h @@ -709,7 +709,7 @@ struct _cpp_dir_only_callbacks { /* Called to print a block of lines. */ void (*print_lines) (int, const void *, size_t); - void (*maybe_print_line) (source_location); + bool (*maybe_print_line) (source_location); }; extern void _cpp_preprocess_dir_only (cpp_reader *, -- 2.30.2