Hi, this is the updated version of patch with testsuite compensation and some changes: - As discussed with Richard on IRC, I added -Wlto-type-mismatch to control the warnings (enabled by default) and split ODR warnings from warnings about incompatible types (in a sense of weak structural equality tested by types_compatible_p at LTO time).
Both -Wlto-type-mismatch and -Wodr are on by default. -Wodr points will output warnings only on conflicts between C++ programs that are positively undefined by the standard (modulo implementation bugs) and -Wlto-type-mismatch positives points out likely wrong code since the declarations are not compatible to -fstrit-aliasing. The testuiste fallout of Fortran was not that hard to fix, so I hope -Wlto-type-mismatch is weak enough to make sense for mixed language units. In fact I can use the ODR type matching tocde to implement more strict checks for separate flag (-Wlto-strict-type-mismatch) that would be off by default or perhaps just warn between C and C++ units. - I got Firefox building and noticed a false positives on functions when forward declaration and prototype was mixed. THis is because useless_type_conversion compares function types but requires the outer type to be the one more specified (prototype). types_compatible_p checks that the types are convertible both directions so it return false. This whole code does not make much sense to be. I do not see why useless_type_conversion needs to care about funcition types - we never convert these. I also do not see why it match TYPE_METHOD_BASETYPE when this field is never used for codegen. It is used by ubsan and dbxout only. I think useless_type_conversion can jsut ICE on function/method types and types_compatible_p can be extended by same code as I have in warn_types_mismatch minus the TYPE_METHOD_BASETYPE matching. On a positive note, the patch also finds some real bugs in Firefox :) - I also noticed that we may miss some ODR warnings when the declaration is of compound type, not named type. (i.e. odr_type a[4];). To make these working I added odr_or_derived_type_p and exported the functionality to make structural compare from ipa-devirt. The way of walking compount types was discussed with Jason here: https://gcc.gnu.org/ml/gcc-patches/2014-07/msg00382.html Bootstrapped/regtested x86_64-linux, ppc64 running. Also tested with ltobootstrap. OK? * ipa-utils.h (warn_types_mismatch, odr_or_derived_type_p, odr_types_equivalent_p): Declare. (odr_type_p): Use gcc_checking_assert. * common.opt (Wlto-type-mismatch): New warning. * ipa-devirt.c (compound_type_base): New function. (odr_or_derived_type_p): New function. (odr_types_equivalent_p): New function. (add_type_duplicate): Simplify. * lto-symtab.c (warn_type_compatibility_p): Break out from ...; compare ODR types (if available) and function types. (lto_symtab_merge): ... here; output ODR violation warnings and call warn_types_mismatch. * gfortran.dg/lto/20091028-2_1.c: Fix return value. * gfortran.dg/lto/pr41576_1.f90: Add interface. * gfortran.dg/lto/pr41521_0.f90: Disable lto-type-mismatch * gfortran.dg/lto/pr60635_0.f90: Disable lto-type-mismatch. * gfortran.dg/lto/20091028-1_1.c: Fix return type. * gcc.dg/lto/20120723_0.c: Disbale lto-type-mismatch. Index: ipa-utils.h =================================================================== --- ipa-utils.h (revision 222991) +++ ipa-utils.h (working copy) @@ -84,6 +84,9 @@ bool types_must_be_same_for_odr (tree, t bool types_odr_comparable (tree, tree, bool strict = false); cgraph_node *try_speculative_devirtualization (tree, HOST_WIDE_INT, ipa_polymorphic_call_context); +void warn_types_mismatch (tree t1, tree t2); +bool odr_or_derived_type_p (const_tree t); +bool odr_types_equivalent_p (tree type1, tree type2); /* Return vector containing possible targets of polymorphic call E. If COMPLETEP is non-NULL, store true if the list is complete. @@ -164,7 +167,7 @@ odr_type_p (const_tree t) return true; /* We do not have this information when not in LTO, but we do not need to care, since it is used only for type merging. */ - gcc_assert (in_lto_p || flag_lto); + gcc_checking_assert (in_lto_p || flag_lto); return (TYPE_NAME (t) && (DECL_ASSEMBLER_NAME_SET_P (TYPE_NAME (t)))); Index: common.opt =================================================================== --- common.opt (revision 222991) +++ common.opt (working copy) @@ -607,6 +607,10 @@ Woverflow Common Var(warn_overflow) Init(1) Warning Warn about overflow in arithmetic expressions +Wlto-type-mismatch +Common Var(warn_lto_type_mismatch) Init(1) Warning +During link time optimization warn about mismatched types of global declarations + Wpacked Common Var(warn_packed) Warning Warn when the packed attribute has no effect on struct layout Index: lto/lto-symtab.c =================================================================== --- lto/lto-symtab.c (revision 222991) +++ lto/lto-symtab.c (working copy) @@ -56,6 +56,7 @@ along with GCC; see the file COPYING3. #include "ipa-prop.h" #include "ipa-inline.h" #include "builtins.h" +#include "print-tree.h" /* Replace the cgraph node NODE with PREVAILING_NODE in the cgraph, merging all edges and removing the old node. */ @@ -203,45 +204,49 @@ lto_varpool_replace_node (varpool_node * vnode->remove (); } -/* Merge two variable or function symbol table entries PREVAILING and ENTRY. - Return false if the symbols are not fully compatible and a diagnostic - should be emitted. */ +/* Return non-zero if we want to output waring about T1 and T2. + Return value is a bitmask of reasons of violation: + Bit 0 indicates that types are not compatible of memory layout. + Bot 1 indicates that types are not compatible because of C++ ODR rule. */ -static bool -lto_symtab_merge (symtab_node *prevailing, symtab_node *entry) +static int +warn_type_compatibility_p (tree prevailing_type, tree type) { - tree prevailing_decl = prevailing->decl; - tree decl = entry->decl; - tree prevailing_type, type; - - if (prevailing_decl == decl) - return true; - - /* Merge decl state in both directions, we may still end up using - the new decl. */ - TREE_ADDRESSABLE (prevailing_decl) |= TREE_ADDRESSABLE (decl); - TREE_ADDRESSABLE (decl) |= TREE_ADDRESSABLE (prevailing_decl); - - /* The linker may ask us to combine two incompatible symbols. - Detect this case and notify the caller of required diagnostics. */ - - if (TREE_CODE (decl) == FUNCTION_DECL) - { - if (!types_compatible_p (TREE_TYPE (prevailing_decl), - TREE_TYPE (decl))) - /* If we don't have a merged type yet...sigh. The linker - wouldn't complain if the types were mismatched, so we - probably shouldn't either. Just use the type from - whichever decl appears to be associated with the - definition. If for some odd reason neither decl is, the - older one wins. */ - (void) 0; - - return true; + int lev = 0; + /* C++ provide a robust way to check for type compatibility via the ODR + rule. */ + if (odr_or_derived_type_p (prevailing_type) && odr_type_p (type) + && !odr_types_equivalent_p (prevailing_type, type)) + lev = 2; + + /* Function types needs special care, because types_compatible_p never + thinks prototype is compatible to non-prototype. */ + if ((TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE) + && TREE_CODE (type) == TREE_CODE (prevailing_type)) + { + lev |= warn_type_compatibility_p (TREE_TYPE (prevailing_type), + TREE_TYPE (type)); + if (TREE_CODE (type) == METHOD_TYPE) + lev |= warn_type_compatibility_p (TYPE_METHOD_BASETYPE (prevailing_type), + TYPE_METHOD_BASETYPE (type)); + if (prototype_p (prevailing_type) && prototype_p (type) + && TYPE_ARG_TYPES (prevailing_type) != TYPE_ARG_TYPES (type)) + { + tree parm1, parm2; + for (parm1 = TYPE_ARG_TYPES (prevailing_type), + parm2 = TYPE_ARG_TYPES (type); + parm1 && parm2; + parm1 = TREE_CHAIN (prevailing_type), + parm2 = TREE_CHAIN (type)) + lev |= warn_type_compatibility_p (TREE_VALUE (parm1), + TREE_VALUE (parm2)); + if (parm1 || parm2) + lev = 3; + } + if (comp_type_attributes (prevailing_type, type) == 0) + lev = 3; + return lev; } - - /* Now we exclusively deal with VAR_DECLs. */ - /* Sharing a global symbol is a strong hint that two types are compatible. We could use this information to complete incomplete pointed-to types more aggressively here, ignoring @@ -254,19 +259,22 @@ lto_symtab_merge (symtab_node *prevailin ??? In principle we might want to only warn for structurally incompatible types here, but unless we have protective measures for TBAA in place that would hide useful information. */ - prevailing_type = TYPE_MAIN_VARIANT (TREE_TYPE (prevailing_decl)); - type = TYPE_MAIN_VARIANT (TREE_TYPE (decl)); + prevailing_type = TYPE_MAIN_VARIANT (prevailing_type); + type = TYPE_MAIN_VARIANT (type); if (!types_compatible_p (prevailing_type, type)) { - if (COMPLETE_TYPE_P (type)) - return false; + if (TREE_CODE (prevailing_type) == FUNCTION_TYPE + || TREE_CODE (type) == METHOD_TYPE) + return 1 | lev; + if (COMPLETE_TYPE_P (type) && COMPLETE_TYPE_P (prevailing_type)) + return 1 | lev; /* If type is incomplete then avoid warnings in the cases that TBAA handles just fine. */ if (TREE_CODE (prevailing_type) != TREE_CODE (type)) - return false; + return 1 | lev; if (TREE_CODE (prevailing_type) == ARRAY_TYPE) { @@ -280,10 +288,10 @@ lto_symtab_merge (symtab_node *prevailin } if (TREE_CODE (tem1) != TREE_CODE (tem2)) - return false; + return 1 | lev; if (!types_compatible_p (tem1, tem2)) - return false; + return 1 | lev; } /* Fallthru. Compatible enough. */ @@ -292,6 +300,43 @@ lto_symtab_merge (symtab_node *prevailin /* ??? We might want to emit a warning here if type qualification differences were spotted. Do not do this unconditionally though. */ + return lev; +} + +/* Merge two variable or function symbol table entries PREVAILING and ENTRY. + Return false if the symbols are not fully compatible and a diagnostic + should be emitted. */ + +static bool +lto_symtab_merge (symtab_node *prevailing, symtab_node *entry) +{ + tree prevailing_decl = prevailing->decl; + tree decl = entry->decl; + + if (prevailing_decl == decl) + return true; + + /* Merge decl state in both directions, we may still end up using + the new decl. */ + TREE_ADDRESSABLE (prevailing_decl) |= TREE_ADDRESSABLE (decl); + TREE_ADDRESSABLE (decl) |= TREE_ADDRESSABLE (prevailing_decl); + + /* The linker may ask us to combine two incompatible symbols. + Detect this case and notify the caller of required diagnostics. */ + + if (TREE_CODE (decl) == FUNCTION_DECL) + { + if (warn_type_compatibility_p (TREE_TYPE (prevailing_decl), + TREE_TYPE (decl))) + return false; + + return true; + } + + if (warn_type_compatibility_p (TREE_TYPE (prevailing_decl), + TREE_TYPE (decl))) + return false; + /* There is no point in comparing too many details of the decls here. The type compatibility checks or the completing of types has properly dealt with most issues. */ @@ -483,24 +528,39 @@ lto_symtab_merge_decls_2 (symtab_node *f /* Diagnose all mismatched re-declarations. */ FOR_EACH_VEC_ELT (mismatches, i, decl) { - if (!types_compatible_p (TREE_TYPE (prevailing->decl), - TREE_TYPE (decl))) - diagnosed_p |= warning_at (DECL_SOURCE_LOCATION (decl), 0, - "type of %qD does not match original " - "declaration", decl); - + int level = warn_type_compatibility_p (TREE_TYPE (prevailing->decl), + TREE_TYPE (decl)); + if (level) + { + bool diag = false; + if (level > 1) + diag = warning_at (DECL_SOURCE_LOCATION (decl), + OPT_Wodr, + "%qD violates the C++ One Definition Rule ", + decl); + if (!diag && (level & 1)) + diag = warning_at (DECL_SOURCE_LOCATION (decl), + OPT_Wlto_type_mismatch, + "type of %qD does not match original " + "declaration", decl); + if (diag) + warn_types_mismatch (TREE_TYPE (prevailing->decl), + TREE_TYPE (decl)); + diagnosed_p |= diag; + } else if ((DECL_USER_ALIGN (prevailing->decl) && DECL_USER_ALIGN (decl)) && DECL_ALIGN (prevailing->decl) < DECL_ALIGN (decl)) { - diagnosed_p |= warning_at (DECL_SOURCE_LOCATION (decl), 0, + diagnosed_p |= warning_at (DECL_SOURCE_LOCATION (decl), + OPT_Wlto_type_mismatch, "alignment of %qD is bigger than " "original declaration", decl); } } if (diagnosed_p) inform (DECL_SOURCE_LOCATION (prevailing->decl), - "previously declared here"); + "%qD was previously declared here", prevailing->decl); mismatches.release (); } Index: ipa-devirt.c =================================================================== --- ipa-devirt.c (revision 222991) +++ ipa-devirt.c (working copy) @@ -549,6 +549,59 @@ types_must_be_same_for_odr (tree t1, tre return TYPE_MAIN_VARIANT (t1) == TYPE_MAIN_VARIANT (t2); } +/* If T is compound type, return type it is based on. */ + +static tree +compound_type_base (const_tree t) +{ + if (TREE_CODE (t) == ARRAY_TYPE + || POINTER_TYPE_P (t) + || TREE_CODE (t) == COMPLEX_TYPE + || VECTOR_TYPE_P (t)) + return TREE_TYPE (t); + if (TREE_CODE (t) == METHOD_TYPE) + return TYPE_METHOD_BASETYPE (t); + if (TREE_CODE (t) == OFFSET_TYPE) + return TYPE_OFFSET_BASETYPE (t); + return NULL_TREE; +} + +/* Return true if T is either ODR type or compound type based from it. + If the function return true, we know that T is a type originating from C++ + source even at link-time. */ + +bool +odr_or_derived_type_p (const_tree t) +{ + do + { + if (odr_type_p (t)) + return true; + /* Function type is a tricky one. Basically we can consider it + ODR derived if return type or any of the parameters is. + We need to check all parameters because LTO streaming merges + common types (such as void) and they are not considered ODR then. */ + if (TREE_CODE (t) == FUNCTION_TYPE) + { + if (TYPE_METHOD_BASETYPE (t)) + t = TYPE_METHOD_BASETYPE (t); + else + { + if (TREE_TYPE (t) && odr_or_derived_type_p (TREE_TYPE (t))) + return true; + for (t = TYPE_ARG_TYPES (t); t; t = TREE_CHAIN (t)) + if (odr_or_derived_type_p (TREE_VALUE (t))) + return true; + return false; + } + } + else + t = compound_type_base (t); + } + while (t); + return t; +} + /* Compare types T1 and T2 and return true if they are equivalent. */ @@ -1577,6 +1642,20 @@ odr_types_equivalent_p (tree t1, tree t2 return true; } +/* Return true if TYPE1 and TYPE2 are equivalent for One Definition Rule. */ + +bool +odr_types_equivalent_p (tree type1, tree type2) +{ + hash_set<type_pair,pair_traits> visited; + +#ifdef ENABLE_CHECKING + gcc_assert (odr_or_derived_type_p (type1) && odr_or_derived_type_p (type2)); +#endif + return odr_types_equivalent_p (type1, type2, false, NULL, + &visited); +} + /* TYPE is equivalent to VAL by ODR, but its tree representation differs from VAL->type. This may happen in LTO where tree merging did not merge all variants of the same type or due to ODR violation. @@ -1701,12 +1780,8 @@ add_type_duplicate (odr_type val, tree t base_mismatch = true; } else - { - hash_set<type_pair,pair_traits> visited; - if (!odr_types_equivalent_p (type1, type2, false, NULL, - &visited)) - base_mismatch = true; - } + if (!odr_types_equivalent_p (type1, type2)) + base_mismatch = true; if (base_mismatch) { if (!warned && !val->odr_violated) Index: testsuite/gfortran.dg/lto/20091028-2_1.c =================================================================== --- testsuite/gfortran.dg/lto/20091028-2_1.c (revision 222991) +++ testsuite/gfortran.dg/lto/20091028-2_1.c (working copy) @@ -1,9 +1,9 @@ extern void *memcpy(void *dest, const void *src, __SIZE_TYPE__ n); char *p; -int int_gen_ti_header_c_ (char * hdrbuf, int * hdrbufsize, - int * itypesize, int * typesize, - int * DataHandle, char * Data, - int * Count, int * code) +void int_gen_ti_header_c_ (char * hdrbuf, int * hdrbufsize, + int * itypesize, int * typesize, + int * DataHandle, char * Data, + int * Count, int * code) { memcpy (typesize, p, sizeof(int)) ; memcpy (Data, p, *Count * *typesize) ; Index: testsuite/gfortran.dg/lto/pr41576_1.f90 =================================================================== --- testsuite/gfortran.dg/lto/pr41576_1.f90 (revision 222991) +++ testsuite/gfortran.dg/lto/pr41576_1.f90 (working copy) @@ -5,3 +5,8 @@ program test if (c/=1 .or. d/=2) call abort end program test +interface + subroutine foo() + end subroutine +end interface + Index: testsuite/gfortran.dg/lto/pr41521_0.f90 =================================================================== --- testsuite/gfortran.dg/lto/pr41521_0.f90 (revision 222991) +++ testsuite/gfortran.dg/lto/pr41521_0.f90 (working copy) @@ -1,9 +1,9 @@ ! { dg-lto-do link } -! { dg-lto-options {{-g -flto} {-g -O -flto}} } +! { dg-lto-options {{-g -flto -Wno-lto-type-mismatch} {-g -O -flto -Wno-lto-type-mismatch}} } program species integer spk(2) real eval(2) spk = 2 call atom(1.1,spk,eval) end program Index: testsuite/gfortran.dg/lto/pr60635_0.f90 =================================================================== --- testsuite/gfortran.dg/lto/pr60635_0.f90 (revision 222991) +++ testsuite/gfortran.dg/lto/pr60635_0.f90 (working copy) @@ -1,4 +1,5 @@ ! { dg-lto-do link } +! { dg-lto-options {{ -Wno-lto-type-mismatch }} } program test use iso_fortran_env Index: testsuite/gfortran.dg/lto/pr41576_0.f90 =================================================================== --- testsuite/gfortran.dg/lto/pr41576_0.f90 (revision 222991) +++ testsuite/gfortran.dg/lto/pr41576_0.f90 (working copy) @@ -1,5 +1,5 @@ ! { dg-lto-do run } -! { dg-lto-options { { -O2 -flto -Werror } } } +! { dg-lto-options { { -O2 -flto -Werror -Wno-lto-type-mismatch } } } subroutine foo common /bar/ a, b Index: testsuite/gfortran.dg/lto/20091028-1_1.c =================================================================== --- testsuite/gfortran.dg/lto/20091028-1_1.c (revision 222991) +++ testsuite/gfortran.dg/lto/20091028-1_1.c (working copy) @@ -1,9 +1,9 @@ extern void bcopy(const void *, void *, __SIZE_TYPE__ n); char *p; -int int_gen_ti_header_c_ (char * hdrbuf, int * hdrbufsize, - int * itypesize, int * typesize, - int * DataHandle, char * Data, - int * Count, int * code) +void int_gen_ti_header_c_ (char * hdrbuf, int * hdrbufsize, + int * itypesize, int * typesize, + int * DataHandle, char * Data, + int * Count, int * code) { bcopy (typesize, p, sizeof(int)) ; bcopy (Data, p, *Count * *typesize) ; Index: testsuite/gcc.dg/lto/20120723_0.c =================================================================== --- testsuite/gcc.dg/lto/20120723_0.c (revision 222991) +++ testsuite/gcc.dg/lto/20120723_0.c (working copy) @@ -3,7 +3,7 @@ ??? This testcase is invalid C and can only pass on specific platforms. */ /* { dg-lto-do run } */ /* { dg-skip-if "" { { sparc*-*-* } && ilp32 } { "*" } { "" } } */ -/* { dg-lto-options { {-O3 -fno-early-inlining -flto}} } */ +/* { dg-lto-options { {-O3 -fno-early-inlining -flto -Wno-lto-type-mismatch}} } */ extern void abort (void);