diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 65b835a337c..0b9caa31f2e 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,32 @@ +2006-10-31 Geoffrey Keating + + * c-decl.c (grokdeclarator): Don't set DECL_EXTERNAL on + inline static functions in c99 mode. + + PR 16622 + * doc/extend.texi (Inline): Update. + * c-tree.h (struct language_function): Remove field 'extern_inline'. + * c-decl.c (current_extern_inline): Delete. + (pop_scope): Adjust test for an undefined nested function. + Add warning about undeclared inline function. + (diagnose_mismatched_decls): Update comments. Disallow overriding + of inline functions in a translation unit in C99. Allow inline + declarations in C99 at any time. + (merge_decls): Boolize variables. Handle C99 'extern inline' + semantics. + (grokdeclarator): Set DECL_EXTERNAL here for functions. Handle + C99 inline semantics. + (start_function): Don't clear current_extern_inline. Don't set + DECL_EXTERNAL. + (c_push_function_context): Don't push current_extern_inline. + (c_pop_function_context): Don't restore current_extern_inline. + + PR 11377 + * c-typeck.c (build_external_ref): Warn about static variables + used in extern inline functions. + * c-decl.c (start_decl): Warn about static variables declared + in extern inline functions. + 2006-10-31 Roger Sayle PR middle-end/23470 diff --git a/gcc/c-decl.c b/gcc/c-decl.c index 7ebb8b9c4f1..34709df5cfe 100644 --- a/gcc/c-decl.c +++ b/gcc/c-decl.c @@ -154,10 +154,6 @@ int current_function_returns_abnormally; static int warn_about_return_type; -/* Nonzero when starting a function declared `extern inline'. */ - -static int current_extern_inline; - /* Nonzero when the current toplevel function contains a declaration of a nested function which is never defined. */ @@ -797,11 +793,22 @@ pop_scope (void) && DECL_ABSTRACT_ORIGIN (p) != p) TREE_ADDRESSABLE (DECL_ABSTRACT_ORIGIN (p)) = 1; if (!DECL_EXTERNAL (p) - && DECL_INITIAL (p) == 0) + && !DECL_INITIAL (p) + && scope != file_scope + && scope != external_scope) { error ("nested function %q+D declared but never defined", p); undef_nested_function = true; } + /* C99 6.7.4p6: "a function with external linkage... declared + with an inline function specifier ... shall also be defined in the + same translation unit." */ + else if (DECL_DECLARED_INLINE_P (p) + && TREE_PUBLIC (p) + && !DECL_INITIAL (p) + && flag_isoc99) + pedwarn ("inline function %q+D declared but never defined", p); + goto common_symbol; case VAR_DECL: @@ -1292,10 +1299,11 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl, /* Function declarations can either be 'static' or 'extern' (no qualifier is equivalent to 'extern' - C99 6.2.2p5) and therefore - can never conflict with each other on account of linkage (6.2.2p4). - Multiple definitions are not allowed (6.9p3,5) but GCC permits - two definitions if one is 'extern inline' and one is not. The non- - extern-inline definition supersedes the extern-inline definition. */ + can never conflict with each other on account of linkage + (6.2.2p4). Multiple definitions are not allowed (6.9p3,5) but + gnu89 mode permits two definitions if one is 'extern inline' and + one is not. The non- extern-inline definition supersedes the + extern-inline definition. */ else if (TREE_CODE (newdecl) == FUNCTION_DECL) { @@ -1321,16 +1329,11 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl, { /* If both decls are in the same TU and the new declaration isn't overriding an extern inline reject the new decl. - When we handle c99 style inline rules we'll want to reject - the following: - - DECL_EXTERN_INLINE (olddecl) - && !DECL_EXTERN_INLINE (newdecl) - - if they're in the same translation unit. Until we implement - the full semantics we accept the construct. */ - if (!(DECL_EXTERN_INLINE (olddecl) - && !DECL_EXTERN_INLINE (newdecl)) + In c99, no overriding is allowed in the same translation + unit. */ + if ((!DECL_EXTERN_INLINE (olddecl) + || DECL_EXTERN_INLINE (newdecl) + || flag_isoc99) && same_translation_unit_p (newdecl, olddecl)) { error ("redefinition of %q+D", newdecl); @@ -1521,9 +1524,13 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl, ??? Should we still warn about this now we have unit-at-a-time mode and can get it right? Definitely don't complain if the decls are in different translation - units. */ + units. + C99 permits this, so don't warn in that case. (The function + may not be inlined everywhere in function-at-a-time mode, but + we still shouldn't warn.) */ if (DECL_DECLARED_INLINE_P (newdecl) && !DECL_DECLARED_INLINE_P (olddecl) - && same_translation_unit_p (olddecl, newdecl)) + && same_translation_unit_p (olddecl, newdecl) + && ! flag_isoc99) { if (TREE_USED (olddecl)) { @@ -1600,12 +1607,13 @@ diagnose_mismatched_decls (tree newdecl, tree olddecl, static void merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype) { - int new_is_definition = (TREE_CODE (newdecl) == FUNCTION_DECL - && DECL_INITIAL (newdecl) != 0); - int new_is_prototype = (TREE_CODE (newdecl) == FUNCTION_DECL - && TYPE_ARG_TYPES (TREE_TYPE (newdecl)) != 0); - int old_is_prototype = (TREE_CODE (olddecl) == FUNCTION_DECL - && TYPE_ARG_TYPES (TREE_TYPE (olddecl)) != 0); + bool new_is_definition = (TREE_CODE (newdecl) == FUNCTION_DECL + && DECL_INITIAL (newdecl) != 0); + bool new_is_prototype = (TREE_CODE (newdecl) == FUNCTION_DECL + && TYPE_ARG_TYPES (TREE_TYPE (newdecl)) != 0); + bool old_is_prototype = (TREE_CODE (olddecl) == FUNCTION_DECL + && TYPE_ARG_TYPES (TREE_TYPE (olddecl)) != 0); + bool extern_changed = false; /* For real parm decl following a forward decl, rechain the old decl in its new location and clear TREE_ASM_WRITTEN (it's not a @@ -1752,6 +1760,18 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype) } } + /* In c99, 'extern' declaration before (or after) 'inline' means this + function is not DECL_EXTERNAL. */ + if (TREE_CODE (newdecl) == FUNCTION_DECL + && (DECL_DECLARED_INLINE_P (newdecl) + || DECL_DECLARED_INLINE_P (olddecl)) + && (!DECL_DECLARED_INLINE_P (newdecl) + || !DECL_DECLARED_INLINE_P (olddecl) + || !DECL_EXTERNAL (olddecl)) + && DECL_EXTERNAL (newdecl) + && flag_isoc99) + DECL_EXTERNAL (newdecl) = 0; + if (DECL_EXTERNAL (newdecl)) { TREE_STATIC (newdecl) = TREE_STATIC (olddecl); @@ -1844,6 +1864,8 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype) } } + extern_changed = DECL_EXTERNAL (olddecl) && !DECL_EXTERNAL (newdecl); + /* Copy most of the decl-specific fields of NEWDECL into OLDDECL. But preserve OLDDECL's DECL_UID and DECL_CONTEXT. */ { @@ -1886,6 +1908,13 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype) || (TREE_CODE (olddecl) == VAR_DECL && TREE_STATIC (olddecl)))) make_decl_rtl (olddecl); + + /* If we changed a function from DECL_EXTERNAL to !DECL_EXTERNAL, + and the definition is coming from the old version, cgraph needs + to be called again. */ + if (extern_changed && !new_is_definition + && TREE_CODE (olddecl) == FUNCTION_DECL && DECL_INITIAL (olddecl)) + cgraph_finalize_function (olddecl, false); } /* Handle when a new declaration NEWDECL has the same name as an old @@ -3282,6 +3311,17 @@ start_decl (struct c_declarator *declarator, struct c_declspecs *declspecs, warning (OPT_Wattributes, "inline function %q+D given attribute noinline", decl); + /* C99 6.7.4p3: An inline definition of a function with external + linkage shall not contain a definition of a modifiable object + with static storage duration... */ + if (TREE_CODE (decl) == VAR_DECL + && current_scope != file_scope + && TREE_STATIC (decl) + && DECL_DECLARED_INLINE_P (current_function_decl) + && DECL_EXTERNAL (current_function_decl)) + pedwarn ("%q+D is static but declared in inline function %qD " + "which is not static", decl, current_function_decl); + /* Add this decl to the current scope. TEM may equal DECL or it may be a previous decl of the same name. */ tem = pushdecl (decl); @@ -4726,8 +4766,15 @@ grokdeclarator (const struct c_declarator *declarator, GCC to signify a forward declaration of a nested function. */ if (storage_class == csc_auto && current_scope != file_scope) DECL_EXTERNAL (decl) = 0; + /* In C99, a function which is declared 'inline' with 'extern' + is not an external reference (which is confusing). It + means that the later definition of the function must be output + in this file, C99 6.7.4p6. In GNU C89, a function declared + 'extern inline' is an external reference. */ + else if (declspecs->inline_p && storage_class != csc_static) + DECL_EXTERNAL (decl) = (storage_class == csc_extern) == !flag_isoc99; else - DECL_EXTERNAL (decl) = 1; + DECL_EXTERNAL (decl) = !initialized; /* Record absence of global scope for `static' or `auto'. */ TREE_PUBLIC (decl) @@ -4757,11 +4804,7 @@ grokdeclarator (const struct c_declarator *declarator, the abstract origin pointing between the declarations, which will confuse dwarf2out. */ if (initialized) - { - DECL_INLINE (decl) = 1; - if (storage_class == csc_extern) - current_extern_inline = 1; - } + DECL_INLINE (decl) = 1; } /* If -finline-functions, assume it can be inlined. This does two things: let the function be deferred until it is actually @@ -5259,12 +5302,15 @@ start_struct (enum tree_code code, tree name) error ("nested redefinition of %", name); else error ("nested redefinition of %", name); + /* Don't create structures that contain themselves. */ + ref = NULL_TREE; } } - else - { - /* Otherwise create a forward-reference just so the tag is in scope. */ + /* Otherwise create a forward-reference just so the tag is in scope. */ + + if (ref == NULL_TREE || TREE_CODE (ref) != code) + { ref = make_node (code); pushtag (name, ref); } @@ -5956,7 +6002,6 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator, current_function_returns_null = 0; current_function_returns_abnormally = 0; warn_about_return_type = 0; - current_extern_inline = 0; c_switch_stack = NULL; nstack_se = XOBNEW (&parser_obstack, struct c_label_context_se); @@ -6108,12 +6153,6 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator, warning (OPT_Wmissing_declarations, "%q+D was used with no declaration before its definition", decl1); - /* This is a definition, not a reference. - So normally clear DECL_EXTERNAL. - However, `extern inline' acts like a declaration - except for defining how to inline. So set DECL_EXTERNAL in that case. */ - DECL_EXTERNAL (decl1) = current_extern_inline; - /* This function exists in static storage. (This does not mean `static' in the C sense!) */ TREE_STATIC (decl1) = 1; @@ -6846,7 +6885,6 @@ c_push_function_context (struct function *f) p->returns_null = current_function_returns_null; p->returns_abnormally = current_function_returns_abnormally; p->warn_about_return_type = warn_about_return_type; - p->extern_inline = current_extern_inline; } /* Restore the variables used during compilation of a C function. */ @@ -6875,7 +6913,6 @@ c_pop_function_context (struct function *f) current_function_returns_null = p->returns_null; current_function_returns_abnormally = p->returns_abnormally; warn_about_return_type = p->warn_about_return_type; - current_extern_inline = p->extern_inline; f->language = NULL; } diff --git a/gcc/c-tree.h b/gcc/c-tree.h index 4bc4928ff1d..5785e1cb96f 100644 --- a/gcc/c-tree.h +++ b/gcc/c-tree.h @@ -384,7 +384,6 @@ struct language_function GTY(()) int returns_null; int returns_abnormally; int warn_about_return_type; - int extern_inline; }; /* Save lists of labels used or defined in particular contexts. diff --git a/gcc/c-typeck.c b/gcc/c-typeck.c index 2534c25e75d..30b438260c6 100644 --- a/gcc/c-typeck.c +++ b/gcc/c-typeck.c @@ -2109,6 +2109,17 @@ build_external_ref (tree id, int fun, location_t loc) if (context != 0 && context != current_function_decl) DECL_NONLOCAL (ref) = 1; } + /* C99 6.7.4p3: An inline definition of a function with external + linkage ... shall not contain a reference to an identifier with + internal linkage. */ + else if (current_function_decl != 0 + && DECL_DECLARED_INLINE_P (current_function_decl) + && DECL_EXTERNAL (current_function_decl) + && VAR_OR_FUNCTION_DECL_P (ref) + && (TREE_CODE (ref) != VAR_DECL || TREE_STATIC (ref)) + && ! TREE_PUBLIC (ref)) + pedwarn ("%H%qD is static but used in inline function %qD " + "which is not static", &loc, ref, current_function_decl); return ref; } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index e5457e0eff2..479113bda1b 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,18 @@ +2006-10-31 Geoffrey Keating + + * gcc.dg/inline-16.c: New. + + PR 16622 + * gcc.dg/inline-10.c (main): Don't declare 'main' inline without + defining it. + * gcc.dg/inline-13.c: New. + * gcc.dg/inline-14.c: New. + * gcc.dg/inline-15.c: New. + + PR 11377 + * gcc.dg/inline6.c: New. + * gcc.dg/inline7.c: New. + 2006-10-31 Roger Sayle PR middle-end/23470 diff --git a/gcc/testsuite/gcc.dg/inline-10.c b/gcc/testsuite/gcc.dg/inline-10.c index ed6851a615b..f7a7592a6a9 100644 --- a/gcc/testsuite/gcc.dg/inline-10.c +++ b/gcc/testsuite/gcc.dg/inline-10.c @@ -3,4 +3,4 @@ /* { dg-do compile } */ /* { dg-options "-std=gnu99 -ffreestanding -pedantic-errors" } */ -inline int main (void); +inline int main (void) { return 1; } diff --git a/gcc/testsuite/gcc.dg/inline-13.c b/gcc/testsuite/gcc.dg/inline-13.c new file mode 100644 index 00000000000..62a898c3c10 --- /dev/null +++ b/gcc/testsuite/gcc.dg/inline-13.c @@ -0,0 +1,56 @@ +/* Verify basic C99 inline functionality. */ +/* { dg-do compile } */ +/* { dg-options "-std=c99" } */ +/* { dg-final { scan-assembler-not "dontgenerate" } } */ +/* { dg-final { scan-assembler "func1" } } */ +/* { dg-final { scan-assembler "func2" } } */ +/* { dg-final { scan-assembler "func3" } } */ +/* { dg-final { scan-assembler "func4" } } */ +/* { dg-final { scan-assembler "func5" } } */ +/* { dg-final { scan-assembler "func6" } } */ +/* { dg-final { scan-assembler "func7" } } */ +/* { dg-final { scan-assembler "func8" } } */ +/* { dg-final { scan-assembler "func9" } } */ + +inline int dontgenerate1 (void) +{ + return 1; +} + +inline int dontgenerate2 (void); +inline int dontgenerate2 (void) +{ + return 2; +} + +inline int dontgenerate3 (void) +{ + return 3; +} +inline int dontgenerate3 (void); + +extern inline int func1 (void) { return 1; } + +extern inline int func2 (void); +inline int func2 (void) { return 2; } + +inline int func3 (void) { return 3; } +extern inline int func3 (void); + +inline int func4 (void); +extern inline int func4 (void) { return 4; } + +extern inline int func5 (void) { return 5; } +inline int func5 (void); + +extern int func6 (void); +inline int func6 (void) { return 6; } + +inline int func7 (void) { return 7; } +extern int func7 (void); + +inline int func8 (void); +extern int func8 (void) { return 8; } + +extern int func9 (void) { return 9; } +inline int func9 (void); diff --git a/gcc/testsuite/gcc.dg/inline-14.c b/gcc/testsuite/gcc.dg/inline-14.c new file mode 100644 index 00000000000..0987c7cde7b --- /dev/null +++ b/gcc/testsuite/gcc.dg/inline-14.c @@ -0,0 +1,23 @@ +/* Check that you can't redefine a C99 inline function. */ +/* { dg-do compile } */ +/* { dg-options "-std=c99" } */ + +extern inline int func1 (void) +{ /* { dg-error "previous definition" } */ + return 1; +} + +inline int func1 (void) +{ /* { dg-error "redefinition" } */ + return 1; +} + +inline int func2 (void) +{ /* { dg-error "previous definition" } */ + return 2; +} + +inline int func2 (void) +{ /* { dg-error "redefinition" } */ + return 2; +} diff --git a/gcc/testsuite/gcc.dg/inline-15.c b/gcc/testsuite/gcc.dg/inline-15.c new file mode 100644 index 00000000000..7df8af1102e --- /dev/null +++ b/gcc/testsuite/gcc.dg/inline-15.c @@ -0,0 +1,7 @@ +/* Check that an error message is produced when a C99 inline function + is never defined. */ +/* { dg-do compile } */ +/* { dg-options "-std=c99" } */ + +extern inline int func1 (void); /* { dg-error "never defined" } */ +inline int func2 (void); /* { dg-error "never defined" } */ diff --git a/gcc/testsuite/gcc.dg/inline-16.c b/gcc/testsuite/gcc.dg/inline-16.c new file mode 100644 index 00000000000..02aa1bf04a1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/inline-16.c @@ -0,0 +1,21 @@ +/* { dg-do link } */ +/* { dg-options "-std=c99" } */ + +static inline int +func1(const volatile void * base, int byteOffset) +{ + volatile int *addr = (volatile int *)((int)base + byteOffset); + return *addr; +} + +static inline int +func2(int data) +{ + return func1(&data, 0); +} + +int main(int argc, char *argv[]) { + int b = func2(argc); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/inline6.c b/gcc/testsuite/gcc.dg/inline6.c new file mode 100644 index 00000000000..3b9e3e99e95 --- /dev/null +++ b/gcc/testsuite/gcc.dg/inline6.c @@ -0,0 +1,14 @@ +/* { dg-do compile } */ +/* { dg-options "-std=gnu89" } */ +static int i; +extern int j; +extern inline int func1 (void) { + return i++; /* { dg-warning "static" } */ +} +extern inline int func2 (void) { + return j++; +} +inline int func3 (void) +{ + return i++; +} diff --git a/gcc/testsuite/gcc.dg/inline7.c b/gcc/testsuite/gcc.dg/inline7.c new file mode 100644 index 00000000000..54f3360b8a7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/inline7.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-std=gnu89" } */ +extern inline void func1 (void) { + static int i; /* { dg-warning "static" } */ +} +inline void func3 (void) +{ + static int i; +}