re PR c/16622 ([C99] extern inline is handled wrong in C99 mode)

* 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.

From-SVN: r118356
This commit is contained in:
Geoffrey Keating 2006-11-01 04:47:30 +00:00 committed by Geoffrey Keating
parent 682d039597
commit 71113fcd70
12 changed files with 268 additions and 47 deletions

View File

@ -1,3 +1,32 @@
2006-10-31 Geoffrey Keating <geoffk@apple.com>
* 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 <roger@eyesopen.com>
PR middle-end/23470

View File

@ -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 %<union %E%>", name);
else
error ("nested redefinition of %<struct %E%>", 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;
}

View File

@ -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.

View File

@ -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;
}

View File

@ -1,3 +1,18 @@
2006-10-31 Geoffrey Keating <geoffk@apple.com>
* 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 <roger@eyesopen.com>
PR middle-end/23470

View File

@ -3,4 +3,4 @@
/* { dg-do compile } */
/* { dg-options "-std=gnu99 -ffreestanding -pedantic-errors" } */
inline int main (void);
inline int main (void) { return 1; }

View File

@ -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);

View File

@ -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;
}

View File

@ -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" } */

View File

@ -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;
}

View File

@ -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++;
}

View File

@ -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;
}