On Mon, 16 Jan 2012, Richard Guenther wrote: > On Mon, 16 Jan 2012, Jakub Jelinek wrote: > > > On Mon, Jan 16, 2012 at 09:35:08AM +0100, Richard Guenther wrote: > > > But that's of course intended. Attributes or redirection on the > > > extern inline variant are completely meaningless. > > > > > > > If you want to keep olddecl as is, then IMHO we should add a new bool > > > > argument to merge_decls and if the flag is set, make sure we only ever > > > > modify newdecl and not olddecl. > > > > > > I don't think that is necesary nor warranted. What legitimate use > > > would break you think? > > > > The extern inline decl gets also all the previous decl attributes > > accumulated. Asm redirection on the extern inline is meaningless. > > Consider: > > /* foo.h */ > > extern int foo (int, int) __attribute__((regparm (2))) __asm ("bar"); > > ... > > /* foo-inlines.h */ > > extern inline __attribute__((gnu_inline, always_inline)) void > > foo (int x, int y) > > { > > return something (x, y); > > } > > ... > > /* foo.c */ > > int > > foo (int x, int y) > > { > > return something (x, y); > > } > > > > You really want to have __asm ("bar") on the foo out-of-line definition, > > wouldn't surprise me if this broke glibc or other packages that heavily > > use asm redirection (in glibc e.g. LFS). And the regparm attribute > > is needed too. The gnu_inline attribute isn't needed, but can be safely > > copied over and ignored, the always_inline attribute and maybe artificial > > attributes are the only ones that should be not copied over to the > > out-of-line definition. > > Ok, I suppose that requires even more adjustments of the scope/lookup > stuff for extern inlines when we see the redefinition - thus, lookup > a previous decl and merge with that instead of doing no merging. > We don't seem to have a testcase for this at least. I'll add yours > as follows (works with 4.6, fails with my patch) > > extern int foo (int, int) __asm__ ("bar"); > extern inline __attribute__((gnu_inline, always_inline)) int > foo (int x, int y) > { > return 0; > } > int > foo (int x, int y) > { > return 0; > } > int main() > { > return bar (); > } > > Before I spend too much time on this age-old bug - Joseph, can you > agree with the proposed partial fix sofar and then with looking > through the I_SYMBOL_BINDING chain to skip the extern inline decl > to find the decl to merge with?
The following makes all testcases collected sofar pass. Richard. 2012-01-13 Richard Guenther <rguent...@suse.de> PR c/33763 * c-decl.c (redeclared_extern_inline_fn_p): New function. (duplicate_decls): Do not merge re-declared extern inline function decls with their re-declaration. Do not merge extern inline functions with any declarations either. (pushdecl): Do not put extern inlines into external_scope. * gcc.dg/torture/pr33763-1.c: New testcase. * gcc.dg/torture/pr33763-2.c: Likewise. * gcc.dg/torture/pr33763-3.c: Likewise. * gcc.dg/torture/pr33763-4.c: Likewise. Index: gcc/testsuite/gcc.dg/torture/pr33763-1.c =================================================================== *** gcc/testsuite/gcc.dg/torture/pr33763-1.c (revision 0) --- gcc/testsuite/gcc.dg/torture/pr33763-1.c (revision 0) *************** *** 0 **** --- 1,43 ---- + /* { dg-do run } */ + + extern void abort (void); + + extern __inline __attribute__ ((__always_inline__)) + int foo () + { + return 1; + } + int test1 () + { + /* Name-lookup should find foo that returns 1. */ + return foo (); + } + int foo () + { + return 0; + } + + extern __inline __attribute__ ((__always_inline__)) + int bar () + { + return 1; + } + int bar () + { + return 0; + } + int test2 () + { + /* Name-lookup should find bar that returns 0. */ + return bar (); + } + + int + main() + { + if (test1 () != 1) + abort (); + if (test2 () != 0) + abort (); + return 0; + } Index: gcc/testsuite/gcc.dg/torture/pr33763-2.c =================================================================== *** gcc/testsuite/gcc.dg/torture/pr33763-2.c (revision 0) --- gcc/testsuite/gcc.dg/torture/pr33763-2.c (revision 0) *************** *** 0 **** --- 1,30 ---- + /* { dg-do compile } */ + + extern int foo (const char *, int); + extern int baz (const char *, int); + + extern inline __attribute__ ((always_inline, gnu_inline)) int + baz (const char *x, int y) + { + return 2; + } + + int + baz (const char *x, int y) + { + return 1; + } + + int xa, xb; + + static int + inl (const char *x, int y) + { + return baz (x, y); + } + + int + foo (const char *x, int y) + { + return inl (x, y); + } Index: gcc/testsuite/gcc.dg/torture/pr33763-3.c =================================================================== *** gcc/testsuite/gcc.dg/torture/pr33763-3.c (revision 0) --- gcc/testsuite/gcc.dg/torture/pr33763-3.c (revision 0) *************** *** 0 **** --- 1,58 ---- + /* { dg-do compile } */ + + typedef struct + { + void *a; + void *b; + } T; + extern void *foo (const char *, const char *); + extern void *bar (void *, const char *, T); + extern int baz (const char *, int); + + extern inline __attribute__ ((always_inline, gnu_inline)) int + baz (const char *x, int y) + { + return 2; + } + + int + baz (const char *x, int y) + { + return 1; + } + + int xa, xb; + + static void * + inl (const char *x, const char *y) + { + T t = { &xa, &xb }; + int *f = (int *) __builtin_malloc (sizeof (int)); + const char *z; + int o = 0; + void *r = 0; + + for (z = y; *z; z++) + { + if (*z == 'r') + o |= 1; + if (*z == 'w') + o |= 2; + } + if (o == 1) + *f = baz (x, 0); + if (o == 2) + *f = baz (x, 1); + if (o == 3) + *f = baz (x, 2); + + if (o && *f > 0) + r = bar (f, "w", t); + return r; + } + + void * + foo (const char *x, const char *y) + { + return inl (x, y); + } Index: gcc/testsuite/gcc.dg/torture/pr33763-4.c =================================================================== *** gcc/testsuite/gcc.dg/torture/pr33763-4.c (revision 0) --- gcc/testsuite/gcc.dg/torture/pr33763-4.c (revision 0) *************** *** 0 **** --- 1,17 ---- + /* { dg-do run } */ + + extern int foo (int, int) __asm__ ("bar"); + extern inline __attribute__((gnu_inline, always_inline)) int + foo (int x, int y) + { + return 0; + } + int + foo (int x, int y) + { + return 0; + } + int main() + { + return bar (); + } Index: gcc/c-decl.c =================================================================== *** gcc/c-decl.c (revision 183205) --- gcc/c-decl.c (working copy) *************** locate_old_decl (tree decl) *** 1627,1632 **** --- 1627,1652 ---- inform (input_location, "previous declaration of %q+D was here", decl); } + #define DECL_EXTERN_INLINE(DECL) (DECL_DECLARED_INLINE_P (DECL) \ + && DECL_EXTERNAL (DECL)) + + /* Return whether NEWDECL is a redeclaration of an extern inline declared + function OLDDECL. */ + + static bool + redeclared_extern_inline_fn_p (tree olddecl, tree newdecl) + { + return !(!DECL_EXTERN_INLINE (olddecl) + || DECL_EXTERN_INLINE (newdecl) + || (!flag_gnu89_inline + && (!DECL_DECLARED_INLINE_P (olddecl) + || !lookup_attribute ("gnu_inline", + DECL_ATTRIBUTES (olddecl))) + && (!DECL_DECLARED_INLINE_P (newdecl) + || !lookup_attribute ("gnu_inline", + DECL_ATTRIBUTES (newdecl))))); + } + /* Subroutine of duplicate_decls. Compare NEWDECL to OLDDECL. Returns true if the caller should proceed to merge the two, false if OLDDECL should simply be discarded. As a side effect, issues *************** diagnose_mismatched_decls (tree newdecl, *** 1644,1652 **** bool warned = false; bool retval = true; - #define DECL_EXTERN_INLINE(DECL) (DECL_DECLARED_INLINE_P (DECL) \ - && DECL_EXTERNAL (DECL)) - /* If we have error_mark_node for either decl or type, just discard the previous decl - we're in an error cascade already. */ if (olddecl == error_mark_node || newdecl == error_mark_node) --- 1664,1669 ---- *************** diagnose_mismatched_decls (tree newdecl, *** 1858,1873 **** isn't overriding an extern inline reject the new decl. In c99, no overriding is allowed in the same translation unit. */ ! if ((!DECL_EXTERN_INLINE (olddecl) ! || DECL_EXTERN_INLINE (newdecl) ! || (!flag_gnu89_inline ! && (!DECL_DECLARED_INLINE_P (olddecl) ! || !lookup_attribute ("gnu_inline", ! DECL_ATTRIBUTES (olddecl))) ! && (!DECL_DECLARED_INLINE_P (newdecl) ! || !lookup_attribute ("gnu_inline", ! DECL_ATTRIBUTES (newdecl)))) ! ) && same_translation_unit_p (newdecl, olddecl)) { error ("redefinition of %q+D", newdecl); --- 1875,1881 ---- isn't overriding an extern inline reject the new decl. In c99, no overriding is allowed in the same translation unit. */ ! if (!redeclared_extern_inline_fn_p (olddecl, newdecl) && same_translation_unit_p (newdecl, olddecl)) { error ("redefinition of %q+D", newdecl); *************** diagnose_mismatched_decls (tree newdecl, *** 2132,2142 **** if (warned || pedwarned) locate_old_decl (olddecl); - #undef DECL_EXTERN_INLINE - return retval; } /* Subroutine of duplicate_decls. NEWDECL has been found to be consistent with OLDDECL, but carries new information. Merge the new information into OLDDECL. This function issues no --- 2140,2150 ---- if (warned || pedwarned) locate_old_decl (olddecl); return retval; } + #undef DECL_EXTERN_INLINE + /* Subroutine of duplicate_decls. NEWDECL has been found to be consistent with OLDDECL, but carries new information. Merge the new information into OLDDECL. This function issues no *************** duplicate_decls (tree newdecl, tree oldd *** 2512,2517 **** --- 2520,2544 ---- return false; } + if (TREE_CODE (newdecl) == FUNCTION_DECL) + { + /* If we have a redeclared extern inline function simply drop olddecl + on the floor instead of merging it with newdecl. */ + if (DECL_INITIAL (newdecl) + && DECL_INITIAL (olddecl) + && redeclared_extern_inline_fn_p (olddecl, newdecl)) + return false; + + /* Do not merge an extern inline decl with any other decl either. */ + if ((DECL_DECLARED_INLINE_P (newdecl) + && DECL_EXTERNAL (newdecl)) + || (!flag_gnu89_inline + && (DECL_DECLARED_INLINE_P (newdecl) + || lookup_attribute ("gnu_inline", + DECL_ATTRIBUTES (newdecl))))) + return false; + } + merge_decls (newdecl, olddecl, newtype, oldtype); return true; } *************** pushdecl (tree x) *** 2775,2781 **** nested = true; x = visdecl; } ! else { bind (name, x, external_scope, /*invisible=*/true, /*nested=*/false, locus); --- 2802,2810 ---- nested = true; x = visdecl; } ! else if (TREE_CODE (x) != FUNCTION_DECL ! || !(DECL_DECLARED_INLINE_P (x) ! && DECL_EXTERNAL (x))) { bind (name, x, external_scope, /*invisible=*/true, /*nested=*/false, locus);