On 1/11/19 2:27 PM, Joseph Myers wrote:
On Fri, 11 Jan 2019, Martin Sebor wrote:
gcc/testsuite/ChangeLog:
PR c/88718
* gcc.dg/inline-40.c: New test.
* gcc.dg/inline-41.c: New test.
We already have tests inline-40.c and inline-41.c; these need to be
renumbered accordingly.
Done.
+ if (add)
+ {
+ if (csi->function == func
+ || csi->function)
+ continue;
Since func is non-NULL, this is equivalent to "if (csi->function)".
Yes. Done.
Don't you want to break, not continue, in the case where csi->function (as
all the tentative entries come first)? As-is, this looks like it would
have quadratic cost if a file has many inline functions each referencing
some file-scope static. (The "Avoid adding another tentative record for
this DECL if one already exists." also looks quadratic, but having very
many functions in a file seems more likely than very many references to
statics in a single declaration that is not a definition.)
Yes, good catch!
@@ -5165,6 +5259,11 @@ finish_decl (tree decl, location_t init_loc, tree
set_user_assembler_name (decl, asmspec);
}
+ /* Remove any tentative record of a non-static inline function
+ referencing a static decl made a DECL that is not a definition. */
+ if (TREE_CODE (decl) == FUNCTION_DECL)
+ update_tentative_inline_static (decl);
+
You also need to do the update when the declaration wasn't a function
declaration at all. The present patch produces spurious warnings for:
static int i;
int a[sizeof i];
inline void f (void) {}
because the reference to i in the declaration of a gets treated as if it
were in the definition of f.
Done.
Also, you need to distinguish the case of updating for a function
definition, and updating for a declaration that's not a definition, for a
function that was previously defined. E.g.
inline void f (int a[sizeof (int)]) {}
static int i;
inline void f (int a[sizeof i]);
must not diagnose a reference to i in f (you have such tests with the
declaration before the definition, but none that I can see with the
declaration after the definition). I think this means the caller of
update_tentative_inline_static should specify whether to add or remove,
rather than update_tentative_inline_static trying to deduce it from
properties of a DECL (that don't say whether the particular declaration in
question is a definition or not, just whether a definition has been seen).
Also true. Done.
Attached is a revision with these fixes.
Thanks for the careful review!
Martin
PR c/88718 - Strange inconsistency between old style and new style definitions of inline functions.
gcc/c/ChangeLog:
PR c/88718
* c-decl.c (update_tentative_inline_static): New function.
(check_inline_statics): Handle tentative records for inline
declarations without definitions.
Print static declaration location.
(finish_decl): Add tentative records of references to statics.
(finish_function): Same.
* c-typeck.c (build_external_ref): Handle all references to statics.
gcc/testsuite/ChangeLog:
PR c/88718
* gcc.dg/inline-42.c: New test.
* gcc.dg/inline-43.c: New test.
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 57049f80073..ddc09abb669 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -826,9 +826,11 @@ c_finish_incomplete_decl (tree decl)
}
}
-/* Record that inline function FUNC contains a reference (location
- LOC) to static DECL (file-scope or function-local according to
- TYPE). */
+/* Record that inline function FUNC either does contain or may contain
+ a reference (location LOC) to static DECL (file-scope or function-local
+ according to TYPE). For a null FUNC, a tentative record is created that
+ reflects a reference in the function signature and that is either updated
+ or removed when the function declaration is complete. */
void
record_inline_static (location_t loc, tree func, tree decl,
@@ -843,6 +845,51 @@ record_inline_static (location_t loc, tree func, tree decl,
c_inline_statics = csi;
}
+/* Update tentative records of references to static declarations in
+ an inline declaration function DECL, or remove them if DECL isn't
+ a function declared inline or when ADD is false. A tentative
+ record is one whose FUNCTION is null. */
+
+static void
+update_tentative_inline_static (tree decl, bool add)
+{
+ gcc_assert (decl);
+
+ if (!c_inline_statics)
+ return;
+
+ /* True to associate DECL with the tentative records, false to remove
+ them. */
+ add = (add
+ && TREE_CODE (decl) == FUNCTION_DECL
+ && DECL_DECLARED_INLINE_P (decl)
+ && DECL_EXTERNAL (decl)
+ && DECL_INITIAL (decl));
+
+ /* Iterate over tentative records (all those at the head of the list
+ with a null FUNCTION) and either associate them with DECL when ADD
+ is set or remove them from it otherwise. */
+ for (c_inline_static *csi = c_inline_statics, *last = csi;
+ csi; csi = csi->next)
+ {
+ if (csi->function)
+ break;
+
+ if (add)
+ {
+ if (csi->static_decl)
+ csi->function = decl;
+ }
+ else
+ {
+ if (last == c_inline_statics)
+ c_inline_statics = last = csi->next;
+ else
+ last->next = csi->next;
+ }
+ }
+}
+
/* Check for references to static declarations in inline functions at
the end of the translation unit and diagnose them if the functions
are still inline definitions. */
@@ -853,22 +900,41 @@ check_inline_statics (void)
struct c_inline_static *csi;
for (csi = c_inline_statics; csi; csi = csi->next)
{
- if (DECL_EXTERNAL (csi->function))
- switch (csi->type)
- {
- case csi_internal:
- pedwarn (csi->location, 0,
- "%qD is static but used in inline function %qD "
- "which is not static", csi->static_decl, csi->function);
- break;
- case csi_modifiable:
- pedwarn (csi->location, 0,
- "%q+D is static but declared in inline function %qD "
- "which is not static", csi->static_decl, csi->function);
- break;
- default:
- gcc_unreachable ();
- }
+ /* FUNCTION is unset for a declaration whose signature references
+ a static variable and for which a definition wasn't provided. */
+ if (!csi->function)
+ continue;
+
+ /* Only extern functions are of interest. STATIC_DECL is null
+ for inline functions that have been defined that contain
+ no references to statics (but whose subsequent declarations
+ might). */
+ if (!DECL_EXTERNAL (csi->function)
+ || !csi->static_decl)
+ continue;
+
+ bool warned;
+ switch (csi->type)
+ {
+ case csi_internal:
+ warned = pedwarn (csi->location, 0,
+ "%qD is static but used in inline function %qD "
+ "which is not static",
+ csi->static_decl, csi->function);
+ break;
+ case csi_modifiable:
+ warned = pedwarn (csi->location, 0,
+ "%q+D is static but declared in inline function %qD "
+ "which is not static",
+ csi->static_decl, csi->function);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ if (warned)
+ inform (DECL_SOURCE_LOCATION (csi->static_decl),
+ "%qD declared here", csi->static_decl);
}
c_inline_statics = NULL;
}
@@ -5166,6 +5232,11 @@ finish_decl (tree decl, location_t init_loc, tree init,
set_user_assembler_name (decl, asmspec);
}
+ /* Remove any tentative records of references to static declarations
+ made in the declaration of DECL, which can be a fuction or some
+ other entity. */
+ update_tentative_inline_static (decl, false);
+
if (DECL_FILE_SCOPE_P (decl))
{
if (DECL_INITIAL (decl) == NULL_TREE
@@ -9637,6 +9708,11 @@ finish_function (void)
}
}
+ /* Associate any tentative records of references to static variables
+ with this function declaration if it is inline and not static, or
+ remove them otherwise. */
+ update_tentative_inline_static (fndecl, true);
+
if (!decl_function_context (fndecl))
undef_nested_function = false;
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 63d177f7a6f..f05519d3896 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -52,6 +52,8 @@ along with GCC; see the file COPYING3. If not see
#include "stringpool.h"
#include "attribs.h"
#include "asan.h"
+#include "c-family/c-pragma.h"
+#include "c-parser.h"
/* Possible cases of implicit bad conversions. Used to select
diagnostic messages in convert_for_assignment. */
@@ -2818,19 +2820,26 @@ build_external_ref (location_t loc, tree id, bool fun, tree *type)
if (context != NULL_TREE && 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 != NULL_TREE
+ else if (VAR_OR_FUNCTION_DECL_P (ref)
+ && (!VAR_P (ref) || TREE_STATIC (ref))
+ && ! TREE_PUBLIC (ref))
+ {
+ /* C99 6.7.4p3: An inline definition of a function with external
+ linkage ... shall not contain a reference to an identifier with
+ internal linkage. */
+ if ((current_function_decl
&& DECL_DECLARED_INLINE_P (current_function_decl)
&& DECL_EXTERNAL (current_function_decl)
- && VAR_OR_FUNCTION_DECL_P (ref)
- && (!VAR_P (ref) || TREE_STATIC (ref))
- && ! TREE_PUBLIC (ref)
&& DECL_CONTEXT (ref) != current_function_decl)
- record_inline_static (loc, current_function_decl, ref,
- csi_internal);
-
+ /* If the function declaration is not available yet at this
+ point make a record of the reference if it isn't associated
+ with any context yet and either fill in the details or
+ discard the record when the declaration has been completed. */
+ || (!current_function_decl
+ && !DECL_CONTEXT (ref)))
+ record_inline_static (loc, current_function_decl, ref,
+ csi_internal);
+ }
return ref;
}
diff --git a/gcc/testsuite/gcc.dg/inline-42.c b/gcc/testsuite/gcc.dg/inline-42.c
new file mode 100644
index 00000000000..1e61d820895
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/inline-42.c
@@ -0,0 +1,156 @@
+/* PR c/88718 - Strange inconsistency between old style and new style
+ definitions of inline functions
+ { dg-do compile }
+ { dg-options "-Wall -Wno-unused" } */
+
+extern int global;
+extern const int cst_global;
+
+static int local;
+static const int cst_local;
+
+/* Verify that inline declarations that aren't definitions are accepted
+ without a warning even if their argument lists reference static
+ variables. */
+
+inline void global_decl_arg_ref_global (int (*)[sizeof global]);
+static inline void local_decl_arg_ref_global (int (*)[sizeof global]);
+
+inline void global_decl_arg_ref_cst_global (int (*)[sizeof cst_global]);
+static inline void local_decl_arg_ref_cst_global (int (*)[sizeof cst_global]);
+
+inline void global_decl_arg_ref_local (int (*)[sizeof local]);
+static inline void local_decl_arg_ref_local (int (*)[sizeof local]);
+
+inline void global_decl_arg_ref_cst_local (int (*)[sizeof cst_local]);
+static inline void local_decl_arg_ref_cst_local (int (*)[sizeof cst_local]);
+
+/* Verify that implicitly extern inline definitions trigger a warning
+ when their argument lists reference static (but not extern) variables. */
+
+inline void
+global_decl_arg_ref_global (int (*p)[sizeof global]) { (void)&p; }
+
+inline void
+global_decl_arg_ref_cst_global (int (*p)[sizeof cst_global]) { (void)&p; }
+
+inline void
+global_decl_arg_ref_local (int (*p)[sizeof local])
+/* { dg-warning ".local. is static but used in inline function .global_decl_arg_ref_local." "" { target *-*-* } .-1 } */
+{
+ (void)&p;
+}
+
+inline void
+global_decl_arg_ref_cst_local (int (*p)[sizeof cst_local])
+/* { dg-warning ".cst_local. is static but used in inline function .global_decl_arg_ref_cst_local." "" { target *-*-* } .-1 } */
+{
+ (void)&p;
+}
+
+/* Verify that static inline definitions don't trigger a warning
+ when their argument lists reference static or extern variables. */
+
+static inline void
+local_decl_arg_ref_global (int (*p)[sizeof global]) { (void)&p; }
+static inline void
+local_decl_arg_ref_cst_global (int (*p)[sizeof cst_global]) { (void)&p; }
+static inline void
+local_decl_arg_ref_local (int (*p)[sizeof local]) { (void)&p; }
+static inline void
+local_decl_arg_ref_cst_local (int (*p)[sizeof cst_local]) { (void)&p; }
+
+
+/* Verify that a function declaration that references a static that
+ follows a definition of the same function that doesn't reference
+ any variables is not diagnosed. */
+
+inline void
+global_def_arg_ref_none (int (*p)[sizeof (int)]) { (void)&p; }
+
+static int prior_local;
+
+inline void
+global_def_arg_ref_none (int (*p)[sizeof prior_local]);
+
+
+/* Verify that a function declaration that references a static that
+ follows a definition of the same function that refernces a global
+ is not diagnosed. */
+
+inline void
+global_def_arg_ref_cst_global (int (*p)[sizeof cst_global]) { (void)&p; }
+
+inline void
+global_def_arg_ref_cst_global (int (*p)[sizeof cst_local]);
+
+
+/* Same as above but with return types. */
+
+extern void abort (void);
+
+/* Verify that inline declarations that aren't definitions are accepted
+ without a warning even if their return types reference static
+ variables. */
+
+inline struct { int a[sizeof global]; }
+ global_decl_ret_ref_global (void);
+
+static inline struct { int a[sizeof global]; }
+ local_decl_ret_ref_global (void);
+
+inline struct { int a[sizeof cst_global]; }
+ global_decl_ret_ref_cst_global (void);
+
+static inline struct { int a[sizeof cst_global]; }
+ local_decl_ret_ref_cst_global (void);
+
+inline struct { int a[sizeof local]; }
+ global_decl_ret_ref_local (void);
+static inline struct { int a[sizeof local]; }
+ local_decl_ret_ref_local (void);
+
+inline struct { int a[sizeof cst_local]; }
+ global_decl_ret_ref_cst_local (void);
+
+static inline struct { int a[sizeof global]; }
+ local_decl_ret_ref_cst_local (void);
+
+
+/* Verify that implicitly extern inline definitions trigger a warning
+ when their return types reference static (but not extern) variables. */
+
+inline struct { int a[sizeof global]; }
+ global_def_ret_ref_global (void) { abort (); }
+
+inline struct { int a[sizeof cst_global]; }
+ global_def_ret_ref_cst_global (void) { abort (); }
+
+inline struct { int a[sizeof local]; }
+ global_def_ret_ref_local (void)
+ /* { dg-warning ".local. is static but used in inline function .global_def_ret_ref_local." "" { target *-*-* } .-2 } */
+ {
+ abort ();
+ }
+
+inline struct { int a[sizeof cst_local]; }
+ global_def_ret_ref_cst_local (void)
+ /* { dg-warning ".cst_local. is static but used in inline function .global_def_ret_ref_cst_local." "" { target *-*-* } .-2 } */
+ {
+ abort ();
+ }
+
+/* Verify that static inline definitions don't trigger a warning
+ when their return types reference static or extern variables. */
+
+static inline struct { int a[sizeof global]; }
+ local_def_ret_ref_global (void) { abort (); }
+static inline struct { int a[sizeof cst_global]; }
+ local_def_ret_ref_cst_global (void) { abort (); }
+static inline struct { int a[sizeof local]; }
+ local_def_ret_ref_local (void) { abort (); }
+static inline struct { int a[sizeof cst_local]; }
+ local_def_ret_ref_cst_local (void) { abort (); }
+
+
+/* { dg-prune-output "declared but never defined" } */
diff --git a/gcc/testsuite/gcc.dg/inline-43.c b/gcc/testsuite/gcc.dg/inline-43.c
new file mode 100644
index 00000000000..47709959fcf
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/inline-43.c
@@ -0,0 +1,59 @@
+/* PR c/88718 - Strange inconsistency between old style and new style
+ definitions of inline functions
+ Verify that warnings for references to multiple static variables in
+ inline function signatures are all diagnosed and that the locations
+ of the diagnostics, including the notes, are correct.
+ { dg-do compile }
+ { dg-options "-Wall -Wno-unused" } */
+
+extern const int e1;
+extern const int e2;
+
+static const int s1 = 1; /* { dg-message ".s1. declared here" } */
+static const int s2 = 2; /* { dg-message ".s2. declared here" } */
+
+/* Verify that variable declarations don't cause bogus warnings
+ in subsequent inline function declarations. */
+int extarr[sizeof e1][sizeof e2][sizeof s1][sizeof s2];
+static int locarr[sizeof e1][sizeof e2][sizeof s1][sizeof s2];
+inline void fv (void) { }
+
+inline void fes (int (*)[sizeof e1][sizeof s2]);
+
+
+inline void
+fes (int (*p)
+ [sizeof e1]
+ [sizeof s2]) /* { dg-warning ".s2. is static but used in inline function .fes. which is not static" } */
+{ }
+
+
+inline void fse (int (*)[sizeof s1][sizeof e2]);
+
+inline void
+fse (int (*p)
+ [sizeof s1] /* { dg-warning ".s1. is static but used in inline function .fse. which is not static" } */
+ [sizeof e2])
+{ }
+
+
+static int s3 = 3; /* { dg-message ".s3. declared here" } */
+static int s4 = 4; /* { dg-message ".s4. declared here" } */
+
+/* Use s1 and s2 here and verify that the warnings mention s3 and s4
+ referenced in the definition. */
+inline void fss (int (*)[sizeof s1][sizeof s2]);
+
+inline void
+fss (int (*p)
+ [sizeof s3] /* { dg-warning ".s3. is static but used in inline function .fss. which is not static" } */
+ [sizeof s4]) /* { dg-warning ".s4. is static but used in inline function .fss. which is not static" } */
+{ }
+
+
+/* Use s1 and s2 in the declaration and verify that no warnings are
+ emitted for the definition that doesn't reference any statics. */
+inline void fee (int (*)[sizeof s1][sizeof s2]);
+
+inline void
+fee (int (*p)[sizeof e1][sizeof e2]) { }