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. */
&& 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:
/* 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)
{
{
/* 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);
??? 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))
{
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
}
}
+ /* 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);
}
}
+ 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. */
{
|| (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
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);
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)
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
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);
}
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);
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;
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. */
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;
}
--- /dev/null
+/* 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);